mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
2ecd0a5c7e
commit
9c08b365a4
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user