Bug 918861 - Add an API to get the native samplerate for a given audio backend. r=kinetik

--HG--
extra : rebase_source : 592445a01f00a89289d52c52d9c758e10b9d9a00
This commit is contained in:
Paul Adenot 2013-10-17 15:44:52 +02:00
parent 5d07a5937b
commit 4c8c44228f
12 changed files with 239 additions and 32 deletions

View File

@ -116,6 +116,7 @@ cubeb_destroy
cubeb_init
cubeb_get_max_channel_count
cubeb_get_min_latency
cubeb_get_preferred_sample_rate
cubeb_stream_destroy
cubeb_stream_get_position
cubeb_stream_init

View File

@ -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);

View File

@ -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,

View File

@ -153,12 +153,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)
{

View File

@ -929,6 +929,48 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
return CUBEB_OK;
}
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)
{
@ -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,

View File

@ -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,

View File

@ -141,14 +141,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;
}
@ -197,11 +197,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,
@ -245,6 +240,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)
{
@ -467,12 +497,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,
@ -484,14 +508,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;
@ -551,6 +569,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,

View File

@ -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,

View File

@ -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,

View File

@ -258,6 +258,15 @@ sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
return CUBEB_OK;
}
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)
{
@ -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,

View File

@ -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,

View File

@ -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. */
@ -530,6 +528,33 @@ winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latenc
return CUBEB_OK;
}
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)
{
@ -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,