diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c index 1b635dbf38..8969a6448f 100644 --- a/src/audio/alsa/SDL_alsa_audio.c +++ b/src/audio/alsa/SDL_alsa_audio.c @@ -345,223 +345,6 @@ static char *get_pcm_str(void *handle) return pcm_str; } -// SDL channel map with alsa names "FL FR" -// The literal names are SDL names. -// Faith: loading the whole frame in one shot may help naive compilers. -#define SWIZ2(T) \ - static void swizzle_alsa_channels_2_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \ - { \ - T *ptr = (T *)buffer; \ - Uint32 i; \ - for (i = 0; i < bufferlen; i++, ptr += 2) { \ - const T front_left = ptr[0]; \ - const T front_right = ptr[1]; \ - ptr[swizzle_map[0]] = front_left; \ - ptr[swizzle_map[1]] = front_right; \ - } \ - } -// SDL channel map with alsa names "FL FR LFE" -// The literal names are SDL names. -// Faith: loading the whole frame in one shot may help naive compilers. -#define SWIZ3(T) \ - static void swizzle_alsa_channels_3_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \ - { \ - T *ptr = (T *)buffer; \ - Uint32 i; \ - for (i = 0; i < bufferlen; i++, ptr += 3) { \ - const T front_left = ptr[0]; \ - const T front_right = ptr[1]; \ - const T subwoofer = ptr[2]; \ - ptr[swizzle_map[0]] = front_left; \ - ptr[swizzle_map[1]] = front_right; \ - ptr[swizzle_map[2]] = subwoofer; \ - } \ - } -// SDL channel map with alsa names "FL FR RL RR"; -// The literal names are SDL names. -// Faith: loading the whole frame in one shot may help naive compilers. -#define SWIZ4(T) \ - static void swizzle_alsa_channels_4_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \ - { \ - T *ptr = (T *)buffer; \ - Uint32 i; \ - for (i = 0; i < bufferlen; i++, ptr += 4) { \ - const T front_left = ptr[0]; \ - const T front_right = ptr[1]; \ - const T back_left = ptr[2]; \ - const T back_right = ptr[3]; \ - ptr[swizzle_map[0]] = front_left; \ - ptr[swizzle_map[1]] = front_right; \ - ptr[swizzle_map[2]] = back_left; \ - ptr[swizzle_map[3]] = back_right; \ - } \ - } -// SDL channel map with alsa names "FL FR LFE RL RR" -// The literal names are SDL names. -// Faith: loading the whole frame in one shot may help naive compilers. -#define SWIZ5(T) \ - static void swizzle_alsa_channels_5_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \ - { \ - T *ptr = (T *)buffer; \ - Uint32 i; \ - for (i = 0; i < bufferlen; i++, ptr += 5) { \ - const T front_left = ptr[0]; \ - const T front_right = ptr[1]; \ - const T subwoofer = ptr[2]; \ - const T back_left = ptr[3]; \ - const T back_right = ptr[4]; \ - ptr[swizzle_map[0]] = front_left; \ - ptr[swizzle_map[1]] = front_right; \ - ptr[swizzle_map[2]] = subwoofer; \ - ptr[swizzle_map[3]] = back_left; \ - ptr[swizzle_map[4]] = back_right; \ - } \ - } -// SDL channel map with alsa names "FL FR FC LFE [SL|RL] [SR|RR]" -// The literal names are SDL names. -// Faith: loading the whole frame in one shot may help naive compilers. -#define SWIZ6(T) \ - static void swizzle_alsa_channels_6_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \ - { \ - T *ptr = (T *)buffer; \ - Uint32 i; \ - for (i = 0; i < bufferlen; i++, ptr += 6) { \ - const T front_left = ptr[0]; \ - const T front_right = ptr[1]; \ - const T front_center = ptr[2]; \ - const T subwoofer = ptr[3]; \ - const T side_left = ptr[4]; \ - const T side_right = ptr[5]; \ - ptr[swizzle_map[0]] = front_left; \ - ptr[swizzle_map[1]] = front_right; \ - ptr[swizzle_map[2]] = front_center; \ - ptr[swizzle_map[3]] = subwoofer; \ - ptr[swizzle_map[4]] = side_left; \ - ptr[swizzle_map[5]] = side_right; \ - } \ - } -// SDL channel map with alsa names "FL FR FC LFE RC SL SR". -// The literal names are SDL names. -// Faith: loading the whole frame in one shot may help naive compilers. -#define SWIZ7(T) \ - static void swizzle_alsa_channels_7_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \ - { \ - T *ptr = (T *)buffer; \ - Uint32 i; \ - for (i = 0; i < bufferlen; i++, ptr += 7) { \ - const T front_left = ptr[0]; \ - const T front_right = ptr[1]; \ - const T front_center = ptr[2]; \ - const T subwoofer = ptr[3]; \ - const T back_center = ptr[4]; \ - const T side_left = ptr[5]; \ - const T side_right = ptr[6]; \ - ptr[swizzle_map[0]] = front_left; \ - ptr[swizzle_map[1]] = front_right; \ - ptr[swizzle_map[2]] = front_center; \ - ptr[swizzle_map[3]] = subwoofer; \ - ptr[swizzle_map[4]] = back_center; \ - ptr[swizzle_map[5]] = side_left; \ - ptr[swizzle_map[6]] = side_right; \ - } \ - } - -// SDL channel map with alsa names "FL FR FC LFE RL RR SL SR" -// The literal names are SDL names. -// Faith: loading the whole frame in one shot may help naive compilers. -#define SWIZ8(T) \ - static void swizzle_alsa_channels_8_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \ - { \ - T *ptr = (T *)buffer; \ - Uint32 i; \ - for (i = 0; i < bufferlen; i++, ptr += 8) { \ - const T front_left = ptr[0]; \ - const T front_right = ptr[1]; \ - const T front_center = ptr[2]; \ - const T subwoofer = ptr[3]; \ - const T back_left = ptr[4]; \ - const T back_right = ptr[5]; \ - const T side_left = ptr[6]; \ - const T side_right = ptr[7]; \ - ptr[swizzle_map[0]] = front_left; \ - ptr[swizzle_map[1]] = front_right; \ - ptr[swizzle_map[2]] = front_center; \ - ptr[swizzle_map[3]] = subwoofer; \ - ptr[swizzle_map[4]] = back_left; \ - ptr[swizzle_map[5]] = back_right; \ - ptr[swizzle_map[6]] = side_left; \ - ptr[swizzle_map[7]] = side_right; \ - } \ - } - -#define CHANNEL_SWIZZLE(x) \ - x(Uint64) \ - x(Uint32) \ - x(Uint16) \ - x(Uint8) - -CHANNEL_SWIZZLE(SWIZ2) -CHANNEL_SWIZZLE(SWIZ3) -CHANNEL_SWIZZLE(SWIZ4) -CHANNEL_SWIZZLE(SWIZ5) -CHANNEL_SWIZZLE(SWIZ6) -CHANNEL_SWIZZLE(SWIZ7) -CHANNEL_SWIZZLE(SWIZ8) - -#undef CHANNEL_SWIZZLE -#undef SWIZ2 -#undef SWIZ3 -#undef SWIZ4 -#undef SWIZ5 -#undef SWIZ6 -#undef SWIZ7 -#undef SWIZ8 - -// Called right before feeding device->hidden->mixbuf to the hardware. Swizzle -// channels from Windows/Mac order to the format alsalib will want. -static void swizzle_alsa_channels(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen) -{ - int *swizzle_map = device->hidden->swizzle_map; - switch (device->spec.channels) { -#define CHANSWIZ(chans) \ - case chans: \ - switch ((device->spec.format & (0xFF))) { \ - case 8: \ - swizzle_alsa_channels_##chans##_Uint8(swizzle_map, buffer, bufferlen); \ - break; \ - case 16: \ - swizzle_alsa_channels_##chans##_Uint16(swizzle_map, buffer, bufferlen); \ - break; \ - case 32: \ - swizzle_alsa_channels_##chans##_Uint32(swizzle_map, buffer, bufferlen); \ - break; \ - case 64: \ - swizzle_alsa_channels_##chans##_Uint64(swizzle_map, buffer, bufferlen); \ - break; \ - default: \ - SDL_assert(!"unhandled bitsize"); \ - break; \ - } \ - return; - - CHANSWIZ(2); - CHANSWIZ(3); - CHANSWIZ(4); - CHANSWIZ(5); - CHANSWIZ(6); - CHANSWIZ(7); - CHANSWIZ(8); -#undef CHANSWIZ - default: - break; - } -} - -// Some devices have the right channel map, no swizzling necessary -static void no_swizzle(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen) -{ -} - // This function waits until it is possible to write a full sound buffer static bool ALSA_WaitDevice(SDL_AudioDevice *device) { @@ -597,8 +380,6 @@ static bool ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size); - device->hidden->swizzle_func(device, sample_buf, frames_left); - while ((frames_left > 0) && !SDL_GetAtomicInt(&device->shutdown)) { const int rc = ALSA_snd_pcm_writei(device->hidden->pcm, sample_buf, frames_left); //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA PLAYDEVICE: WROTE %d of %d bytes", (rc >= 0) ? ((int) (rc * frame_size)) : rc, (int) (frames_left * frame_size)); @@ -664,8 +445,6 @@ static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) return -1; } return 0; // go back to WaitDevice and try again. - } else if (rc > 0) { - device->hidden->swizzle_func(device, buffer, total_frames - rc); } //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: recorded %d bytes", rc * frame_size); @@ -692,23 +471,6 @@ static void ALSA_CloseDevice(SDL_AudioDevice *device) } -// Swizzle channels to match SDL defaults. -// These are swizzles _from_ SDL's layouts to what ALSA wants. - -#if 0 -// 5.1 swizzle: -// https://bugzilla.libsdl.org/show_bug.cgi?id=110 -// "For Linux ALSA, this is FL-FR-RL-RR-C-LFE -// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" -static const int swizzle_alsa_channels_6[6] = { 0, 1, 4, 5, 2, 3 }; - -// 7.1 swizzle: -// https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/mapping-stream-formats-to-speaker-configurations -// For Linux ALSA, this appears to be FL-FR-RL-RR-C-LFE-SL-SR -// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-SL-SR-RL-RR" -static const int swizzle_alsa_channels_8[8] = { 0, 1, 6, 7, 2, 3, 4, 5 }; -#endif - // To make easier to track parameters during the whole alsa pcm configuration: struct ALSA_pcm_cfg_ctx { SDL_AudioDevice *device; @@ -876,23 +638,28 @@ static void sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(unsigned int * #undef HAVE_SIDE #undef HAVE_BOTH -static void swizzle_map_compute_alsa_subscan(struct ALSA_pcm_cfg_ctx *ctx, unsigned int sdl_pos_idx) +static void swizzle_map_compute_alsa_subscan(struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, unsigned int sdl_pos_idx) { + swizzle_map[sdl_pos_idx] = -1; for (unsigned int alsa_pos_idx = 0; ; alsa_pos_idx++) { SDL_assert(alsa_pos_idx != ctx->chans_n); // no 0 channels or not found matching position should happen here (actually enforce playback/recording symmetry). if (ctx->alsa_chmap_installed[alsa_pos_idx] == ctx->sdl_chmap[sdl_pos_idx]) { LOGDEBUG("swizzle SDL %u <-> alsa %u", sdl_pos_idx,alsa_pos_idx); - ctx->device->hidden->swizzle_map[sdl_pos_idx] = alsa_pos_idx; + swizzle_map[sdl_pos_idx] = (int) alsa_pos_idx; return; } } } // XXX: this must stay playback/recording symetric. -static void swizzle_map_compute(struct ALSA_pcm_cfg_ctx *ctx) +static void swizzle_map_compute(struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, bool *needs_swizzle) { + *needs_swizzle = false; for (unsigned int sdl_pos_idx = 0; sdl_pos_idx != ctx->chans_n; sdl_pos_idx++) { - swizzle_map_compute_alsa_subscan(ctx, sdl_pos_idx); + swizzle_map_compute_alsa_subscan(ctx, swizzle_map, sdl_pos_idx); + if (swizzle_map[sdl_pos_idx] != sdl_pos_idx) { + *needs_swizzle = true; + } } } @@ -1093,7 +860,6 @@ static int alsa_chmap_cfg(struct ALSA_pcm_cfg_ctx *ctx) if (ctx->chmap_queries == NULL) { // We couldn't query the channel map, assume no swizzle necessary LOGDEBUG("couldn't query channel map, swizzling off"); - ctx->device->hidden->swizzle_func = no_swizzle; return CHMAP_INSTALLED; } @@ -1101,23 +867,39 @@ static int alsa_chmap_cfg(struct ALSA_pcm_cfg_ctx *ctx) status = alsa_chmap_cfg_ordered(ctx); // we prefer first channel maps we don't need to swizzle if (status == CHMAP_INSTALLED) { LOGDEBUG("swizzling off"); - ctx->device->hidden->swizzle_func = no_swizzle; return status; - } - if (status != CHMAP_NOT_FOUND) { + } else if (status != CHMAP_NOT_FOUND) { return status; // < 0 error code } + // Fall-thru //---------------------------------------------------------------------------------------------- status = alsa_chmap_cfg_unordered(ctx); // those we will have to swizzle if (status == CHMAP_INSTALLED) { LOGDEBUG("swizzling on"); - swizzle_map_compute(ctx); // fine grained swizzle configuration - ctx->device->hidden->swizzle_func = swizzle_alsa_channels; - return status; + + bool isstack; + int *swizzle_map = SDL_small_alloc(int, ctx->chans_n, &isstack); + if (!swizzle_map) { + status = -1; + } else { + bool needs_swizzle; + swizzle_map_compute(ctx, swizzle_map, &needs_swizzle); // fine grained swizzle configuration + if (needs_swizzle) { + // let SDL's swizzler handle this one. + ctx->device->chmap = SDL_ChannelMapDup(swizzle_map, ctx->chans_n); + if (!ctx->device->chmap) { + status = -1; + } + } + SDL_small_free(swizzle_map, isstack); + } } - if (status == CHMAP_NOT_FOUND) + + if (status == CHMAP_NOT_FOUND) { return CHANS_N_NEXT; + } + return status; // < 0 error code } @@ -1249,10 +1031,12 @@ static int ALSA_pcm_cfg_hw_chans_n_scan(struct ALSA_pcm_cfg_ctx *ctx, unsigned i // 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 change the target number of channels though. status = alsa_chmap_cfg(ctx); - if (status < 0) + if (status < 0) { return status; // we forward the SDL error - if (status == CHMAP_INSTALLED) + } else if (status == CHMAP_INSTALLED) { 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 diff --git a/src/audio/alsa/SDL_alsa_audio.h b/src/audio/alsa/SDL_alsa_audio.h index 952bfb26b7..e840b2f6c5 100644 --- a/src/audio/alsa/SDL_alsa_audio.h +++ b/src/audio/alsa/SDL_alsa_audio.h @@ -36,12 +36,6 @@ struct SDL_PrivateAudioData // Raw mixing buffer Uint8 *mixbuf; - - // swizzle function - void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen); - // Up to a channel map of 8 channels, will define the sample indexes into the alsa frame - // from a sdl sample index. - int swizzle_map[SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX]; }; #endif // SDL_ALSA_audio_h_