aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/cubeb/cubeb.h10
-rw-r--r--src/cubeb-internal.h1
-rw-r--r--src/cubeb.c11
-rw-r--r--src/cubeb_alsa.c43
-rw-r--r--src/cubeb_audiotrack.c15
-rw-r--r--src/cubeb_audiounit.c61
-rw-r--r--src/cubeb_opensl.c32
-rw-r--r--src/cubeb_pulse.c27
-rw-r--r--src/cubeb_sndio.c10
-rw-r--r--src/cubeb_wasapi.cpp30
-rw-r--r--src/cubeb_winmm.c30
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,