diff options
-rw-r--r-- | include/cubeb/cubeb.h | 10 | ||||
-rw-r--r-- | src/cubeb-internal.h | 1 | ||||
-rw-r--r-- | src/cubeb.c | 11 | ||||
-rw-r--r-- | src/cubeb_alsa.c | 43 | ||||
-rw-r--r-- | src/cubeb_audiotrack.c | 15 | ||||
-rw-r--r-- | src/cubeb_audiounit.c | 61 | ||||
-rw-r--r-- | src/cubeb_opensl.c | 32 | ||||
-rw-r--r-- | src/cubeb_pulse.c | 27 | ||||
-rw-r--r-- | src/cubeb_sndio.c | 10 | ||||
-rw-r--r-- | src/cubeb_wasapi.cpp | 30 | ||||
-rw-r--r-- | src/cubeb_winmm.c | 30 |
11 files changed, 238 insertions, 32 deletions
diff --git a/include/cubeb/cubeb.h b/include/cubeb/cubeb.h index effd21a..7ca3862 100644 --- a/include/cubeb/cubeb.h +++ b/include/cubeb/cubeb.h @@ -189,7 +189,7 @@ char const * cubeb_get_backend_id(cubeb * context); int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels); /** Get the minimal latency value, in milliseconds, that is guaranteed to work - when creating a stream for the specified samplerate. This is platform and + when creating a stream for the specified sample rate. This is platform and backend dependant. @param context @param params On some backends, the minimum achievable latency depends on @@ -199,6 +199,14 @@ int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels); @retval CUBEB_OK */ int cubeb_get_min_latency(cubeb * context, cubeb_stream_params params, uint32_t * latency_ms); +/** Get the preferred sample rate for this backend: this is hardware and platform + dependant, and can avoid resampling, and/or trigger fastpaths. + @param context + @param samplerate The samplerate (in Hz) the current configuration prefers. + @return CUBEB_ERROR_INVALID_PARAMETER + @return CUBEB_OK */ +int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate); + /** Destroy an application context. @param context */ void cubeb_destroy(cubeb * context); diff --git a/src/cubeb-internal.h b/src/cubeb-internal.h index 51c76f2..b3c46e9 100644 --- a/src/cubeb-internal.h +++ b/src/cubeb-internal.h @@ -16,6 +16,7 @@ struct cubeb_ops { int (* get_min_latency)(cubeb * context, cubeb_stream_params params, uint32_t * latency_ms); + int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate); void (* destroy)(cubeb * context); int (* stream_init)(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, diff --git a/src/cubeb.c b/src/cubeb.c index 08b06d3..7789a06 100644 --- a/src/cubeb.c +++ b/src/cubeb.c @@ -159,12 +159,21 @@ cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels) int cubeb_get_min_latency(cubeb * context, cubeb_stream_params params, uint32_t * latency_ms) { - if (!latency_ms || !params) { + if (!latency_ms) { return CUBEB_ERROR_INVALID_PARAMETER; } return context->ops->get_min_latency(context, params, latency_ms); } +int +cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate) +{ + if (!rate) { + return CUBEB_ERROR_INVALID_PARAMETER; + } + return context->ops->get_preferred_sample_rate(context, rate); +} + void cubeb_destroy(cubeb * context) { diff --git a/src/cubeb_alsa.c b/src/cubeb_alsa.c index 3daf902..423c430 100644 --- a/src/cubeb_alsa.c +++ b/src/cubeb_alsa.c @@ -930,6 +930,48 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } static int +alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { + int rv, dir; + snd_pcm_t * pcm; + snd_pcm_hw_params_t * hw_params; + + snd_pcm_hw_params_alloca(&hw_params); + + /* get a pcm, disabling resampling, so we get a rate the + * hardware/dmix/pulse/etc. supports. */ + rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0); + if (rv < 0) { + return CUBEB_ERROR; + } + + rv = snd_pcm_hw_params_any(pcm, hw_params); + if (rv < 0) { + snd_pcm_close(pcm); + return CUBEB_ERROR; + } + + rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir); + if (rv >= 0) { + /* There is a default rate: use it. */ + snd_pcm_close(pcm); + return CUBEB_OK; + } + + /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */ + *rate = 44100; + + rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL); + if (rv < 0) { + snd_pcm_close(pcm); + return CUBEB_ERROR; + } + + snd_pcm_close(pcm); + + return CUBEB_OK; +} + +static int alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) { /* This is found to be an acceptable minimum, even on a super low-end @@ -1039,6 +1081,7 @@ static struct cubeb_ops const alsa_ops = { .get_backend_id = alsa_get_backend_id, .get_max_channel_count = alsa_get_max_channel_count, .get_min_latency = alsa_get_min_latency, + .get_preferred_sample_rate = alsa_get_preferred_sample_rate, .destroy = alsa_destroy, .stream_init = alsa_stream_init, .stream_destroy = alsa_stream_destroy, diff --git a/src/cubeb_audiotrack.c b/src/cubeb_audiotrack.c index 2ace7d6..2a6126e 100644 --- a/src/cubeb_audiotrack.c +++ b/src/cubeb_audiotrack.c @@ -68,8 +68,8 @@ struct AudioTrack { status_t (*get_position)(void* instance, uint32_t* position); /* only used on froyo. */ /* static */ int (*get_output_frame_count)(int* frame_count, int stream); - /* static */ int (*get_output_latency)(uint32_t* frame_count, int stream); - /* static */ int (*get_output_samplingrate)(int* frame_count, int stream); + /* static */ int (*get_output_latency)(uint32_t* latency, int stream); + /* static */ int (*get_output_samplingrate)(int* samplerate, int stream); status_t (*set_marker_position)(void* instance, unsigned int); }; @@ -308,6 +308,16 @@ audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * l return CUBEB_OK; } +static int +audiotrack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ + status_t rv; + + rv = ctx->klass.get_output_samplingrate(rate, 3 /* MUSIC */); + + return rv == 0 ? CUBEB_OK : CUBEB_ERROR; +} + void audiotrack_destroy(cubeb * context) { @@ -462,6 +472,7 @@ static struct cubeb_ops const audiotrack_ops = { .get_backend_id = audiotrack_get_backend_id, .get_max_channel_count = audiotrack_get_max_channel_count, .get_min_latency = audiotrack_get_min_latency, + .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate, .destroy = audiotrack_destroy, .stream_init = audiotrack_stream_init, .stream_destroy = audiotrack_stream_destroy, diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index 920f797..a7d5522 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -140,14 +140,14 @@ audiounit_get_output_device_id(AudioDeviceID * device_id) kAudioObjectPropertyElementMaster }; - size = sizeof(device_id); + size = sizeof(*device_id); r = AudioObjectGetPropertyData(kAudioObjectSystemObject, &output_device_address, 0, - 0, + NULL, &size, - &device_id); + device_id); if (r != noErr) { return CUBEB_ERROR; } @@ -196,11 +196,6 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) OSStatus r; AudioDeviceID output_device_id; AudioStreamBasicDescription stream_format; - AudioObjectPropertyAddress output_device_address = { - kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; AudioObjectPropertyAddress stream_format_address = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, @@ -244,6 +239,41 @@ audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * la return CUBEB_OK; } +static int +audiounit_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ + UInt32 size; + OSStatus r; + Float64 fsamplerate; + AudioDeviceID output_device_id; + AudioObjectPropertyAddress samplerate_address = { + kAudioDevicePropertyNominalSampleRate, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) { + return CUBEB_ERROR; + } + + size = sizeof(fsamplerate); + r = AudioObjectGetPropertyData(output_device_id, + &samplerate_address, + 0, + NULL, + &size, + &fsamplerate); + + if (r != noErr) { + return CUBEB_ERROR; + } + + *rate = (uint32_t)fsamplerate; + + return CUBEB_OK; +} + + static void audiounit_destroy(cubeb * ctx) { @@ -466,12 +496,6 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) double unit_latency_sec; AudioDeviceID output_device_id; OSStatus r; - - AudioObjectPropertyAddress output_device_address = { - kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; AudioObjectPropertyAddress latency_address = { kAudioDevicePropertyLatency, kAudioDevicePropertyScopeOutput, @@ -483,14 +507,8 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) kAudioObjectPropertyElementMaster }; + r = audiounit_get_output_device_id(&output_device_id); - size = sizeof(output_device_id); - r = AudioObjectGetPropertyData(kAudioObjectSystemObject, - &output_device_address, - 0, - 0, - &size, - &output_device_id); if (r != noErr) { pthread_mutex_unlock(&stm->mutex); return CUBEB_ERROR; @@ -550,6 +568,7 @@ static struct cubeb_ops const audiounit_ops = { .get_backend_id = audiounit_get_backend_id, .get_max_channel_count = audiounit_get_max_channel_count, .get_min_latency = audiounit_get_min_latency, + .get_preferred_sample_rate = audiounit_get_preferred_sample_rate, .destroy = audiounit_destroy, .stream_init = audiounit_stream_init, .stream_destroy = audiounit_stream_destroy, diff --git a/src/cubeb_opensl.c b/src/cubeb_opensl.c index caaf663..9744e96 100644 --- a/src/cubeb_opensl.c +++ b/src/cubeb_opensl.c @@ -276,6 +276,37 @@ opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten return CUBEB_OK; } +static int +opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ + /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html + * We don't want to deal with JNI here (and we don't have Java on b2g anyways, + * so we just dlopen the library and get the two symbols we need. */ + int rv; + void * libmedia; + uint32_t (*get_primary_output_samplingrate)(); + uint32_t primary_sampling_rate; + + libmedia = dlopen("libmedia.so", RTLD_LAZY); + if (!libmedia) { + return CUBEB_ERROR; + } + + /* uint32_t AudioSystem::getPrimaryOutputSamplingRate(void) */ + get_primary_output_samplingrate = + dlsym(libmedia, "_ZN7android11AudioSystem28getPrimaryOutputSamplingRateEv"); + if (!get_primary_output_samplingrate) { + dlclose(libmedia); + return CUBEB_ERROR; + } + + *rate = get_primary_output_samplingrate(); + + dlclose(libmedia); + + return CUBEB_OK; +} + static void opensl_destroy(cubeb * ctx) @@ -483,6 +514,7 @@ static struct cubeb_ops const opensl_ops = { .get_backend_id = opensl_get_backend_id, .get_max_channel_count = opensl_get_max_channel_count, .get_min_latency = opensl_get_min_latency, + .get_preferred_sample_rate = opensl_get_preferred_sample_rate, .destroy = opensl_destroy, .stream_init = opensl_stream_init, .stream_destroy = opensl_stream_destroy, diff --git a/src/cubeb_pulse.c b/src/cubeb_pulse.c index bed296c..f02b235 100644 --- a/src/cubeb_pulse.c +++ b/src/cubeb_pulse.c @@ -91,13 +91,14 @@ enum cork_state { static void sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u) - { +{ cubeb * ctx = u; if (!eol) { ctx->default_sink_info = malloc(sizeof(pa_sink_info)); memcpy(ctx->default_sink_info, info, sizeof(pa_sink_info)); - } - } + } + WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); +} static void server_info_callback(pa_context * context, const pa_server_info * info, void * u) @@ -343,6 +344,7 @@ pulse_init(cubeb ** context, char const * context_name) ctx->mainloop = WRAP(pa_threaded_mainloop_new)(); ctx->context = WRAP(pa_context_new)(WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), context_name); + ctx->default_sink_info = NULL; WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx); WRAP(pa_threaded_mainloop_start)(ctx->mainloop); @@ -384,10 +386,24 @@ pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } static int -pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t latency) +pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ + WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); + while (!ctx->default_sink_info) { + WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); + } + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); + + *rate = ctx->default_sink_info->sample_spec.rate; + + return CUBEB_OK; +} + +static int +pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) { // According to PulseAudio developers, this is a safe minimum. - *latency = 40; + *latency_ms = 40; return CUBEB_OK; } @@ -602,6 +618,7 @@ static struct cubeb_ops const pulse_ops = { .get_backend_id = pulse_get_backend_id, .get_max_channel_count = pulse_get_max_channel_count, .get_min_latency = pulse_get_min_latency, + .get_preferred_sample_rate = pulse_get_preferred_sample_rate, .destroy = pulse_destroy, .stream_init = pulse_stream_init, .stream_destroy = pulse_stream_destroy, diff --git a/src/cubeb_sndio.c b/src/cubeb_sndio.c index c45997b..636f231 100644 --- a/src/cubeb_sndio.c +++ b/src/cubeb_sndio.c @@ -259,6 +259,15 @@ sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } static int +sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ + // XXX Not yet implemented. + *rate = 44100; + + return CUBEB_OK; +} + +static int sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) { // XXX Not yet implemented. @@ -337,6 +346,7 @@ static struct cubeb_ops const sndio_ops = { .get_backend_id = sndio_get_backend_id, .get_max_channel_count = sndio_get_max_channel_count, .get_min_latency = sndio_get_min_latency, + .get_preferred_sample_rate = sndio_get_preferred_sample_rate, .destroy = sndio_destroy, .stream_init = sndio_stream_init, .stream_destroy = sndio_stream_destroy, diff --git a/src/cubeb_wasapi.cpp b/src/cubeb_wasapi.cpp index 0a2be57..32058b1 100644 --- a/src/cubeb_wasapi.cpp +++ b/src/cubeb_wasapi.cpp @@ -564,6 +564,35 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten return CUBEB_OK; } +int +wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ + HRESULT hr; + IAudioClient * client; + WAVEFORMATEX * mix_format; + + hr = ctx->device->Activate(__uuidof(IAudioClient), + CLSCTX_INPROC_SERVER, + NULL, (void **)&client); + + if (FAILED(hr)) { + return CUBEB_ERROR; + } + + hr = client->GetMixFormat(&mix_format); + + if (FAILED(hr)) { + SafeRelease(client); + return CUBEB_ERROR; + } + + *rate = mix_format->nSamplesPerSec; + + CoTaskMemFree(mix_format); + SafeRelease(client); + + return CUBEB_OK; +} void wasapi_stream_destroy(cubeb_stream * stm); @@ -928,6 +957,7 @@ cubeb_ops const wasapi_ops = { /*.get_backend_id =*/ wasapi_get_backend_id, /*.get_max_channel_count =*/ wasapi_get_max_channel_count, /*.get_min_latency =*/ wasapi_get_min_latency, + /*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate, /*.destroy =*/ wasapi_destroy, /*.stream_init =*/ wasapi_stream_init, /*.stream_destroy =*/ wasapi_stream_destroy, diff --git a/src/cubeb_winmm.c b/src/cubeb_winmm.c index be15632..8376ddd 100644 --- a/src/cubeb_winmm.c +++ b/src/cubeb_winmm.c @@ -511,8 +511,6 @@ winmm_stream_destroy(cubeb_stream * stm) static int winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) { - MMRESULT rv; - LPWAVEOUTCAPS waveout_caps; assert(ctx && max_channels); /* We don't support more than two channels in this backend. */ @@ -531,6 +529,33 @@ winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latenc } static int +winmm_get_preferred_sample_rate(cubeb * ctx, cubeb_stream_params params, uint32_t * rate) +{ + LPWAVEOUTCAPS pwoc; + UINT cbwoc; + MMRESULT r; + + cbwoc = sizeof(WAVEOUTCAPS); + + r = waveOutGetDevCaps(WAVE_MAPPER, pwoc, cbwoc); + + if (r != MMSYSERR_NOERROR) { + return CUBEB_ERROR; + } + + /* Check if we support 48kHz, but not 44.1kHz. */ + if (!(pwoc->dwFormats & WAVE_FORMAT_4S16) && + pwoc->dwFormats & WAVE_FORMAT_48S16) { + *rate = 48000; + return CUBEB_OK; + } + /* Prefer 44.1kHz between 44.1kHz and 48kHz. */ + *rate = 44100; + + return CUBEB_OK; +} + +static int winmm_stream_start(cubeb_stream * stm) { MMRESULT r; @@ -609,6 +634,7 @@ static struct cubeb_ops const winmm_ops = { /*.get_backend_id =*/ winmm_get_backend_id, /*.get_max_channel_count=*/ winmm_get_max_channel_count, /*.get_min_latency=*/ winmm_get_min_latency, + /*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate, /*.destroy =*/ winmm_destroy, /*.stream_init =*/ winmm_stream_init, /*.stream_destroy =*/ winmm_stream_destroy, |