diff --git a/src/alsa.cpp b/src/alsa.cpp index 9ce3793..007211a 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -308,6 +308,20 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, snd_pcm_uframes_t min_frames; snd_pcm_uframes_t max_frames; + + if ((err = snd_pcm_hw_params_set_period_size_integer(handle, hwparams)) < 0) + return SoundIoErrorIncompatibleDevice; + + if ((err = snd_pcm_hw_params_get_period_size_min(hwparams, &min_frames, nullptr)) < 0) + return SoundIoErrorIncompatibleDevice; + + if ((err = snd_pcm_hw_params_get_period_size_max(hwparams, &max_frames, nullptr)) < 0) + return SoundIoErrorIncompatibleDevice; + + + device->period_duration_min = ((double)min_frames) / (double)device->sample_rate_max; + device->period_duration_max = ((double)max_frames) / (double)device->sample_rate_max; + if ((err = snd_pcm_hw_params_get_buffer_size_min(hwparams, &min_frames)) < 0) return SoundIoErrorOpeningDevice; if ((err = snd_pcm_hw_params_get_buffer_size_max(hwparams, &max_frames)) < 0) @@ -317,26 +331,6 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, device->buffer_duration_min = ((double)min_frames) / (double)device->sample_rate_max; device->buffer_duration_max = ((double)max_frames) / (double)device->sample_rate_max; - if ((err = snd_pcm_hw_params_set_periods_integer(handle, hwparams)) < 0) - return SoundIoErrorOpeningDevice; - - dir = 0; - if ((err = snd_pcm_hw_params_get_periods_min(hwparams, &num, &dir)) < 0) - return SoundIoErrorOpeningDevice; - - if (dir != 0) - return SoundIoErrorOpeningDevice; - - device->period_count_min = num; - - dir = 0; - if ((err = snd_pcm_hw_params_get_periods_max(hwparams, &num, &dir)) < 0) - return SoundIoErrorOpeningDevice; - - if (dir != 0) - return SoundIoErrorOpeningDevice; - - device->period_count_max = num; snd_pcm_format_mask_t *fmt_mask; @@ -438,8 +432,8 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { if (device->buffer_duration_min == device->buffer_duration_max) device->buffer_duration_current = device->buffer_duration_min; - if (device->period_count_min == device->period_count_max) - device->period_count_current = device->period_count_min; + if (device->period_duration_min == device->period_duration_max) + device->period_duration_current = device->period_duration_min; // now say that resampling is OK and see what the real min and max is. if ((err = probe_open_device(device, handle, hwparams, 1)) < 0) { @@ -993,20 +987,22 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) return SoundIoErrorOpeningDevice; } + snd_pcm_uframes_t period_frames = ceil(outstream->period_duration * (double)outstream->sample_rate); + if ((err = snd_pcm_hw_params_set_period_size_near(osa->handle, hwparams, &period_frames, nullptr)) < 0) { + outstream_destroy_alsa(si, os); + return SoundIoErrorOpeningDevice; + } + outstream->period_duration = ((double)period_frames) / (double)outstream->sample_rate; + snd_pcm_uframes_t buffer_size_frames = ceil(outstream->buffer_duration * (double)outstream->sample_rate); + if ((err = snd_pcm_hw_params_set_buffer_size_near(osa->handle, hwparams, &buffer_size_frames)) < 0) { outstream_destroy_alsa(si, os); return SoundIoErrorOpeningDevice; } outstream->buffer_duration = ((double)buffer_size_frames) / (double)outstream->sample_rate; - unsigned int actual_periods_count = outstream->period_count; - if ((err = snd_pcm_hw_params_set_periods_near(osa->handle, hwparams, &actual_periods_count, nullptr)) < 0) { - outstream_destroy_alsa(si, os); - return SoundIoErrorOpeningDevice; - } - outstream->period_count = actual_periods_count; snd_pcm_uframes_t period_size; @@ -1017,6 +1013,7 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) // write the hardware parameters to device if ((err = snd_pcm_hw_params(osa->handle, hwparams)) < 0) { + //assert(err != -EINVAL); outstream_destroy_alsa(si, os); return SoundIoErrorOpeningDevice; } diff --git a/src/dummy.cpp b/src/dummy.cpp index 4e5c05c..9e6afcd 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -19,7 +19,6 @@ struct SoundIoOutStreamDummy { struct SoundIoOsCond *cond; atomic_flag abort_flag; int buffer_size; - double period; struct SoundIoRingBuffer ring_buffer; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; }; @@ -44,7 +43,7 @@ static void playback_thread_run(void *arg) { double time_per_frame = 1.0 / (double)outstream->sample_rate; while (osd->abort_flag.test_and_set()) { - soundio_os_cond_timed_wait(osd->cond, nullptr, osd->period); + soundio_os_cond_timed_wait(osd->cond, nullptr, outstream->period_duration); double now = soundio_os_get_time(); double total_time = now - start_time; @@ -142,7 +141,6 @@ static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) os->backend_data = osd; osd->buffer_size = outstream->bytes_per_frame * outstream->buffer_duration; - osd->period = outstream->buffer_duration / (double)outstream->period_count; soundio_ring_buffer_init(&osd->ring_buffer, osd->buffer_size); @@ -337,9 +335,9 @@ int soundio_dummy_init(SoundIoPrivate *si) { device->sample_rate_min = 2; device->sample_rate_max = 5644800; device->sample_rate_current = 48000; - device->period_count_min = 1; - device->period_count_max = 16; - device->period_count_current = 2; + device->period_duration_min = 0.01; + device->period_duration_max = 2; + device->period_duration_current = 0.05; device->purpose = SoundIoDevicePurposeOutput; if (si->safe_devices_info->output_devices.append(device)) { @@ -388,9 +386,9 @@ int soundio_dummy_init(SoundIoPrivate *si) { device->sample_rate_min = 2; device->sample_rate_max = 5644800; device->sample_rate_current = 48000; - device->period_count_min = 1; - device->period_count_max = 16; - device->period_count_current = 2; + device->period_duration_min = 0.01; + device->period_duration_max = 2; + device->period_duration_current = 0.05; device->purpose = SoundIoDevicePurposeInput; if (si->safe_devices_info->input_devices.append(device)) { diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp index e6ceb1d..8325640 100644 --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -296,6 +296,7 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in //device->default_latency = usec_to_sec(info->configured_latency); // TODO set min, max, current sample rate //device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec); + // TODO set min, max, current period size device->purpose = SoundIoDevicePurposeOutput; if (sipa->current_devices_info->output_devices.append(device)) @@ -330,6 +331,7 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info //device->default_latency = usec_to_sec(info->configured_latency); // TODO set min, max, current sample rate //device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec); + // TODO set min, max, current period size device->purpose = SoundIoDevicePurposeInput; if (sipa->current_devices_info->input_devices.append(device)) @@ -623,6 +625,8 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { sample_spec.channels = outstream->layout.channel_count; pa_channel_map channel_map = to_pulseaudio_channel_map(&outstream->layout); + // TODO handle period_duration + // TODO make this value ("SoundIo") configurable ospa->stream = pa_stream_new(sipa->pulse_context, "SoundIo", &sample_spec, &channel_map); if (!ospa->stream) { @@ -796,6 +800,8 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { pa_channel_map channel_map = to_pulseaudio_channel_map(&instream->layout); + // TODO handle period_duration + // TODO make this value ("SoundIo") private ispa->stream = pa_stream_new(sipa->pulse_context, "SoundIo", &sample_spec, &channel_map); if (!ispa->stream) { diff --git a/src/soundio.cpp b/src/soundio.cpp index 71ec483..4160ff1 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -385,7 +385,7 @@ struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) outstream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0]; outstream->sample_rate = clamp(device->sample_rate_min, 48000, device->sample_rate_max); outstream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max); - outstream->period_count = clamp(device->period_count_min, 2, device->period_count_max); + outstream->period_duration = -1.0; return outstream; } @@ -394,6 +394,11 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) { if (outstream->format <= SoundIoFormatInvalid) return SoundIoErrorInvalid; + if (outstream->period_duration == -1.0) { + outstream->period_duration = clamp(outstream->device->period_duration_min, + outstream->buffer_duration / 2.0, outstream->device->period_duration_max); + } + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count); outstream->bytes_per_sample = soundio_get_bytes_per_sample(outstream->format); @@ -441,7 +446,7 @@ struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) { instream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0]; instream->sample_rate = clamp(device->sample_rate_min, 48000, device->sample_rate_max); instream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max); - instream->period_count = clamp(device->period_count_min, 8, device->period_count_max); + instream->period_duration = -1.0; return instream; } @@ -449,6 +454,12 @@ struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) { int soundio_instream_open(struct SoundIoInStream *instream) { if (instream->format <= SoundIoFormatInvalid) return SoundIoErrorInvalid; + + if (instream->period_duration == -1.0) { + instream->period_duration = clamp(instream->device->period_duration_min, + instream->buffer_duration / 8.0, instream->device->period_duration_max); + } + instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count); instream->bytes_per_sample = soundio_get_bytes_per_sample(instream->format); SoundIo *soundio = instream->device->soundio; diff --git a/src/soundio.h b/src/soundio.h index 1caa9b3..d0a38a1 100644 --- a/src/soundio.h +++ b/src/soundio.h @@ -249,11 +249,11 @@ struct SoundIoDevice { double buffer_duration_max; double buffer_duration_current; - // How many slices it is possible to cut the buffer into. - // TODO change this to duration units? - int period_count_min; - int period_count_max; - int period_count_current; + // Period duration in seconds. After this much time passes, write_callback + // is called. + double period_duration_min; + double period_duration_max; + double period_duration_current; // Tells whether this device is an input device or an output device. enum SoundIoDevicePurpose purpose; @@ -292,19 +292,17 @@ struct SoundIoOutStream { struct SoundIoChannelLayout layout; // Buffer duration in seconds. - // (buffer_duration / period_count) is the latency; how much time it takes - // for a sample put in the buffer to get played. // After you call soundio_outstream_open this value is replaced with the // actual duration, as near to this value as possible. // Defaults to 1 second (and then clamped into range). double buffer_duration; - // How many slices the buffer is cut into. The IRQ will happen every - // (buffer_frame_count / period_count) frames. - // After you call soundio_outstream_open this value is replaced with the - // actual period count, as near to this value as possible. - // Defaults to 2 (and then clamped into range). - int period_count; + // `period_duration` is the latency; how much time it takes + // for a sample put in the buffer to get played. + // After you call `soundio_outstream_open` this value is replaced with the + // actual period duration, as near to this value as possible. + // Defaults to `buffer_duration / 2` (and then clamped into range). + double period_duration; // Defaults to NULL. void *userdata; @@ -341,11 +339,10 @@ struct SoundIoInStream { // Defaults to 1 second (and then clamped into range). double buffer_duration; - // How many slices the buffer is cut into. The IRQ will happen every - // (buffer_duration / period_count) seconds, and that is the latency of the - // captured audio. This value must be a power of 2. - // Defaults to 8. - int period_count; + // The latency of the captured audio. After this many seconds pass, + // `read_callback` is called. + // Defaults to `buffer_duration / 8`. + double period_duration; void *userdata; void (*read_callback)(struct SoundIoInStream *);