From efaa06eb7110df92dc2cae0bdd54b579d72d7f05 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 14 Dec 2024 13:31:52 -0500 Subject: [PATCH] alsa: Pulled in latest changes from @sylware --- src/audio/alsa/SDL_alsa_audio.c | 101 +++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 22 deletions(-) diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c index b2c86faeb2b4e..cad4a5de5f8cf 100644 --- a/src/audio/alsa/SDL_alsa_audio.c +++ b/src/audio/alsa/SDL_alsa_audio.c @@ -869,7 +869,7 @@ static void swizzle_map_compute(struct ALSA_pcm_cfg_ctx *ctx) } } #define CHMAP_INSTALLED 0 -#define REDUCE_CHANS_N 1 +#define CHANS_N_NEXT 1 #define CHMAP_NOT_FOUND 2 // Should always be a queried alsa channel map unless the queried alsa channel map was of type VAR, // namely we can program the channel positions directly from the SDL channel map. @@ -1122,31 +1122,47 @@ static int alsa_chmap_cfg(struct ALSA_pcm_cfg_ctx *ctx) return status; } if (status == CHMAP_NOT_FOUND) - return REDUCE_CHANS_N; + return CHANS_N_NEXT; return status; // < 0 error code } -static int ALSA_pcm_cfg_hw(struct ALSA_pcm_cfg_ctx *ctx) +#define CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N 0 /* target more hardware pressure */ +#define CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N 1 /* target less hardware pressure */ +#define CHANS_N_CONFIGURED 0 +#define CHANS_N_NOT_CONFIGURED 1 +static int ALSA_pcm_cfg_hw_chans_n_scan(struct ALSA_pcm_cfg_ctx *ctx, unsigned int mode) { unsigned int target_chans_n = ctx->device->spec.channels; // we start at what was specified + if (mode == CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N) { + target_chans_n--; + } loop { int status; snd_pcm_format_t alsa_format; const SDL_AudioFormat *closefmts; + if (mode == CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N) { + if (target_chans_n > SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX) { + return CHANS_N_NOT_CONFIGURED; + } + } else {/* CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N */ if (target_chans_n == 0) { - return SDL_SetError("ALSA: tried all numbers of channels"); + return CHANS_N_NOT_CONFIGURED; + } } + LOGDEBUG("target chans_n is %u\n", target_chans_n); status = ALSA_snd_pcm_hw_params_any(ctx->device->hidden->pcm, ctx->hwparams); if (status < 0) { - return SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status)); + return -1; } // SDL only uses interleaved sample output status = ALSA_snd_pcm_hw_params_set_access(ctx->device->hidden->pcm, ctx->hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (status < 0) { - return SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status)); + return -1; } // Try for a closest match on audio format alsa_format = 0; @@ -1189,88 +1205,129 @@ static int ALSA_pcm_cfg_hw(struct ALSA_pcm_cfg_ctx *ctx) } } if (ctx->matched_sdl_format == 0) { - return SDL_SetError("ALSA: Unsupported audio format: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Unsupported audio format: %s", ALSA_snd_strerror(status)); + return -1; } // let alsa approximate the number of channels ctx->chans_n = target_chans_n; status = ALSA_snd_pcm_hw_params_set_channels_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->chans_n)); if (status < 0) { - return SDL_SetError("ALSA: Couldn't set audio channels: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't set audio channels: %s", ALSA_snd_strerror(status)); + return -1; } // let alsa approximate the audio rate ctx->rate = ctx->device->spec.freq; status = ALSA_snd_pcm_hw_params_set_rate_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->rate), NULL); if (status < 0) { - return SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status)); + return -1; } // let approximate the period size to the requested buffer size ctx->persize = ctx->device->sample_frames; status = ALSA_snd_pcm_hw_params_set_period_size_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->persize), NULL); if (status < 0) { - return SDL_SetError("ALSA: Couldn't set the period size: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't set the period size: %s", ALSA_snd_strerror(status)); + return -1; } // let approximate the minimun number of periods per buffer (we target a double buffer) ctx->periods = 2; status = ALSA_snd_pcm_hw_params_set_periods_min(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->periods), NULL); if (status < 0) { - return SDL_SetError("ALSA: Couldn't set the minimum number of periods per buffer: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't set the minimum number of periods per buffer: %s", ALSA_snd_strerror(status)); + return -1; } // restrict the number of periods per buffer to an approximation of the approximated minimum // number of periods per buffer done right above status = ALSA_snd_pcm_hw_params_set_periods_first(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->periods), NULL); if (status < 0) { - return SDL_SetError("ALSA: Couldn't set the number of periods per buffer: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't set the number of periods per buffer: %s", ALSA_snd_strerror(status)); + return -1; } // install the hw parameters status = ALSA_snd_pcm_hw_params(ctx->device->hidden->pcm, ctx->hwparams); if (status < 0) { - return SDL_SetError("ALSA: installation of hardware parameter failed: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: installation of hardware parameter failed: %s", ALSA_snd_strerror(status)); + return -1; } //========================================================================================== // Here the alsa pcm is in SND_PCM_STATE_PREPARED state, let's figure out a good fit for - // SDL channel map, it may request to reduce the number of channels though. + // SDL channel map, it may request to change the target number of channels though. status = alsa_chmap_cfg(ctx); if (status < 0) return status; // we forward the SDL error if (status == CHMAP_INSTALLED) - return 0; // we are finished here - // status == REDUCE_CHANS_N - LOGDEBUG("reducing target chans_n to %u\n",target_chans_n-1); + return CHANS_N_CONFIGURED; // we are finished here + // status == CHANS_N_NEXT ALSA_snd_pcm_free_chmaps(ctx->chmap_queries); ALSA_snd_pcm_hw_free(ctx->device->hidden->pcm); // uninstall those hw params + + if (mode == CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N) { + ++target_chans_n; + } else {/* CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N */ target_chans_n--; } + } } #undef CHMAP_INSTALLED -#undef REDUCE_CHANS_N +#undef CHANS_N_NEXT #undef CHMAP_NOT_FOUND +static int ALSA_pcm_cfg_hw(struct ALSA_pcm_cfg_ctx *ctx) +{ + int status; + + LOGDEBUG("target chans_n, equal or above requested chans_n mode\n"); + status = ALSA_pcm_cfg_hw_chans_n_scan(ctx, CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N); + if (status < 0) /* something went too wrong */ + return status; + if (status == CHANS_N_CONFIGURED) + return 0; + /* Here, status == CHANS_N_NOT_CONFIGURED */ + LOGDEBUG("target chans_n, below requested chans_n mode\n"); + status = ALSA_pcm_cfg_hw_chans_n_scan(ctx, CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N); + if (status < 0) /* something went too wrong */ + return status; + if (status == CHANS_N_CONFIGURED) + return 0; + /* Here, status == CHANS_N_NOT_CONFIGURED */ + SDL_SetError("ALSA: Coudn't configure targetting any SDL supported channel number"); + return -1; +} +#undef CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N +#undef CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N +#undef CHANS_N_CONFIGURED +#undef CHANS_N_NOT_CONFIGURED + static int ALSA_pcm_cfg_sw(struct ALSA_pcm_cfg_ctx *ctx) { int status; status = ALSA_snd_pcm_sw_params_current(ctx->device->hidden->pcm, ctx->swparams); if (status < 0) { - return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status)); + return -1; } status = ALSA_snd_pcm_sw_params_set_avail_min(ctx->device->hidden->pcm, ctx->swparams, ctx->persize); // will become device->sample_frames if the alsa pcm configuration is successful if (status < 0) { - return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status)); + SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status)); + return -1; } status = ALSA_snd_pcm_sw_params_set_start_threshold(ctx->device->hidden->pcm, ctx->swparams, 1); if (status < 0) { - return SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status)); + SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status)); + return -1; } status = ALSA_snd_pcm_sw_params(ctx->device->hidden->pcm, ctx->swparams); if (status < 0) { - return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status)); + SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status)); + return -1; } return 0; }