mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
ALSA: Fix a hardware parameter issue with dmix devices.
Relates to issue #1.
This commit is contained in:
@@ -633,10 +633,17 @@ struct mal_context
|
||||
mal_proc snd_pcm_hw_params_set_format_first;
|
||||
mal_proc snd_pcm_hw_params_get_format_mask;
|
||||
mal_proc snd_pcm_hw_params_set_channels_near;
|
||||
mal_proc snd_pcm_hw_params_set_rate_resample;
|
||||
mal_proc snd_pcm_hw_params_set_rate_near;
|
||||
mal_proc snd_pcm_hw_params_set_buffer_size_near;
|
||||
mal_proc snd_pcm_hw_params_set_periods_near;
|
||||
mal_proc snd_pcm_hw_params_set_access;
|
||||
mal_proc snd_pcm_hw_params_get_format;
|
||||
mal_proc snd_pcm_hw_params_get_channels;
|
||||
mal_proc snd_pcm_hw_params_get_rate;
|
||||
mal_proc snd_pcm_hw_params_get_buffer_size;
|
||||
mal_proc snd_pcm_hw_params_get_periods;
|
||||
mal_proc snd_pcm_hw_params_get_access;
|
||||
mal_proc snd_pcm_hw_params;
|
||||
mal_proc snd_pcm_sw_params_sizeof;
|
||||
mal_proc snd_pcm_sw_params_current;
|
||||
@@ -4917,10 +4924,17 @@ typedef int (* mal_snd_pcm_hw_params_set_format_proc) (sn
|
||||
typedef int (* mal_snd_pcm_hw_params_set_format_first_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t *format);
|
||||
typedef void (* mal_snd_pcm_hw_params_get_format_mask_proc) (snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask);
|
||||
typedef int (* mal_snd_pcm_hw_params_set_channels_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val);
|
||||
typedef int (* mal_snd_pcm_hw_params_set_rate_resample_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
|
||||
typedef int (* mal_snd_pcm_hw_params_set_rate_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
||||
typedef int (* mal_snd_pcm_hw_params_set_buffer_size_near_proc)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
|
||||
typedef int (* mal_snd_pcm_hw_params_set_periods_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
||||
typedef int (* mal_snd_pcm_hw_params_set_access_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access);
|
||||
typedef int (* mal_snd_pcm_hw_params_get_format_proc) (snd_pcm_hw_params_t *params, snd_pcm_format_t *format);
|
||||
typedef int (* mal_snd_pcm_hw_params_get_channels_proc) (snd_pcm_hw_params_t *params, unsigned int *val);
|
||||
typedef int (* mal_snd_pcm_hw_params_get_rate_proc) (snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
|
||||
typedef int (* mal_snd_pcm_hw_params_get_buffer_size_proc) (snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
|
||||
typedef int (* mal_snd_pcm_hw_params_get_periods_proc) (snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
||||
typedef int (* mal_snd_pcm_hw_params_get_access_proc) (snd_pcm_hw_params_t *params, snd_pcm_access_t *_access);
|
||||
typedef int (* mal_snd_pcm_hw_params_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
|
||||
typedef size_t (* mal_snd_pcm_sw_params_sizeof_proc) (void);
|
||||
typedef int (* mal_snd_pcm_sw_params_current_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
|
||||
@@ -5025,10 +5039,17 @@ mal_result mal_context_init__alsa(mal_context* pContext)
|
||||
pContext->alsa.snd_pcm_hw_params_set_format_first = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
|
||||
pContext->alsa.snd_pcm_hw_params_get_format_mask = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
|
||||
pContext->alsa.snd_pcm_hw_params_set_channels_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
|
||||
pContext->alsa.snd_pcm_hw_params_set_rate_resample = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
|
||||
pContext->alsa.snd_pcm_hw_params_set_rate_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
|
||||
pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near");
|
||||
pContext->alsa.snd_pcm_hw_params_set_periods_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
|
||||
pContext->alsa.snd_pcm_hw_params_set_access = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
|
||||
pContext->alsa.snd_pcm_hw_params_get_format = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
|
||||
pContext->alsa.snd_pcm_hw_params_get_channels = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
|
||||
pContext->alsa.snd_pcm_hw_params_get_rate = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
|
||||
pContext->alsa.snd_pcm_hw_params_get_buffer_size = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
|
||||
pContext->alsa.snd_pcm_hw_params_get_periods = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
|
||||
pContext->alsa.snd_pcm_hw_params_get_access = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
|
||||
pContext->alsa.snd_pcm_hw_params = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params");
|
||||
pContext->alsa.snd_pcm_sw_params_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
|
||||
pContext->alsa.snd_pcm_sw_params_current = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
|
||||
@@ -5527,6 +5548,8 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Hardware parameters.
|
||||
snd_pcm_hw_params_t* pHWParams = (snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)());
|
||||
mal_zero_memory(pHWParams, ((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)());
|
||||
|
||||
@@ -5536,6 +5559,24 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t
|
||||
}
|
||||
|
||||
|
||||
// MMAP Mode
|
||||
//
|
||||
// Try using interleaved MMAP access. If this fails, fall back to standard readi/writei.
|
||||
pDevice->alsa.isUsingMMap = MAL_FALSE;
|
||||
if (!pConfig->alsa.noMMap && pDevice->type != mal_device_type_capture) { // <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it so I can test it... Contributions welcome.
|
||||
if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) {
|
||||
pDevice->alsa.isUsingMMap = MAL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pDevice->alsa.isUsingMMap) {
|
||||
if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {;
|
||||
mal_device_uninit__alsa(pDevice);
|
||||
return mal_post_error(pDevice, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", MAL_FORMAT_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
|
||||
// find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
|
||||
|
||||
@@ -5591,7 +5632,24 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t
|
||||
}
|
||||
pDevice->internalChannels = channels;
|
||||
|
||||
// Sample Rate
|
||||
|
||||
// Sample Rate. It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling
|
||||
// enabled causes problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we
|
||||
// need to disable resampling.
|
||||
//
|
||||
// To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
|
||||
// sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
|
||||
// doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly
|
||||
// faster rate.
|
||||
//
|
||||
// mini_al has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
|
||||
// for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very
|
||||
// good quality until I get a chance to improve the quality of mini_al's software sample rate conversion.
|
||||
//
|
||||
// I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce
|
||||
// this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
|
||||
((mal_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, 0);
|
||||
|
||||
mal_uint32 sampleRate = pConfig->sampleRate;
|
||||
if (((mal_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &sampleRate, 0) < 0) {
|
||||
mal_device_uninit__alsa(pDevice);
|
||||
@@ -5599,7 +5657,6 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t
|
||||
}
|
||||
pDevice->internalSampleRate = sampleRate;
|
||||
|
||||
|
||||
|
||||
// Buffer Size
|
||||
snd_pcm_uframes_t actualBufferSize = pDevice->bufferSizeInFrames;
|
||||
@@ -5607,7 +5664,7 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t
|
||||
mal_device_uninit__alsa(pDevice);
|
||||
return mal_post_error(pDevice, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", MAL_FORMAT_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
pDevice->bufferSizeInFrames = actualBufferSize;
|
||||
|
||||
// Periods.
|
||||
mal_uint32 periods = pConfig->periods;
|
||||
@@ -5616,37 +5673,19 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t
|
||||
mal_device_uninit__alsa(pDevice);
|
||||
return mal_post_error(pDevice, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MAL_FORMAT_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
pDevice->bufferSizeInFrames = actualBufferSize;
|
||||
pDevice->periods = periods;
|
||||
|
||||
|
||||
// MMAP Mode
|
||||
//
|
||||
// Try using interleaved MMAP access. If this fails, fall back to standard readi/writei.
|
||||
pDevice->alsa.isUsingMMap = MAL_FALSE;
|
||||
if (!pConfig->alsa.noMMap && pDevice->type != mal_device_type_capture) { // <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it so I can test it... Contributions welcome.
|
||||
if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) {
|
||||
pDevice->alsa.isUsingMMap = MAL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pDevice->alsa.isUsingMMap) {
|
||||
if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {;
|
||||
mal_device_uninit__alsa(pDevice);
|
||||
return mal_post_error(pDevice, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", MAL_FORMAT_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Apply hardware parameters.
|
||||
if (((mal_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) {
|
||||
mal_device_uninit__alsa(pDevice);
|
||||
return mal_post_error(pDevice, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS);
|
||||
return mal_post_error(pDevice, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MAL_ALSA_FAILED_TO_SET_HW_PARAMS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Software parameters.
|
||||
snd_pcm_sw_params_t* pSWParams = (snd_pcm_sw_params_t*)alloca(((mal_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)());
|
||||
mal_zero_memory(pSWParams, ((mal_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user