Fixed audio data swizzling when the device channel map already matches what SDL expects
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index 574d51b..fe5af60 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -91,6 +91,8 @@ static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
static int (*ALSA_snd_device_name_hint) (int, const char *, void ***);
static char* (*ALSA_snd_device_name_get_hint) (const void *, const char *);
static int (*ALSA_snd_device_name_free_hint) (void **);
+static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *);
+static int (*ALSA_snd_pcm_chmap_print) (const snd_pcm_chmap_t *map, size_t maxlen, char *buf);
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
@@ -155,6 +157,8 @@ load_alsa_syms(void)
SDL_ALSA_SYM(snd_device_name_hint);
SDL_ALSA_SYM(snd_device_name_get_hint);
SDL_ALSA_SYM(snd_device_name_free_hint);
+ SDL_ALSA_SYM(snd_pcm_get_chmap);
+ SDL_ALSA_SYM(snd_pcm_chmap_print);
return 0;
}
@@ -255,25 +259,25 @@ ALSA_WaitDevice(_THIS)
tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
}
-static SDL_INLINE void
+static void
swizzle_alsa_channels_6_64bit(void *buffer, Uint32 bufferlen)
{
SWIZ6(Uint64, buffer, bufferlen);
}
-static SDL_INLINE void
+static void
swizzle_alsa_channels_6_32bit(void *buffer, Uint32 bufferlen)
{
SWIZ6(Uint32, buffer, bufferlen);
}
-static SDL_INLINE void
+static void
swizzle_alsa_channels_6_16bit(void *buffer, Uint32 bufferlen)
{
SWIZ6(Uint16, buffer, bufferlen);
}
-static SDL_INLINE void
+static void
swizzle_alsa_channels_6_8bit(void *buffer, Uint32 bufferlen)
{
SWIZ6(Uint8, buffer, bufferlen);
@@ -286,7 +290,7 @@ swizzle_alsa_channels_6_8bit(void *buffer, Uint32 bufferlen)
* Called right before feeding this->hidden->mixbuf to the hardware. Swizzle
* channels from Windows/Mac order to the format alsalib will want.
*/
-static SDL_INLINE void
+static void
swizzle_alsa_channels(_THIS, void *buffer, Uint32 bufferlen)
{
if (this->spec.channels == 6) {
@@ -302,6 +306,13 @@ swizzle_alsa_channels(_THIS, void *buffer, Uint32 bufferlen)
/* !!! FIXME: update this for 7.1 if needed, later. */
}
+/* Some devices have the right channel map, no swizzling necessary */
+static void
+no_swizzle(_THIS, void *buffer, Uint32 bufferlen)
+{
+ return;
+}
+
static void
ALSA_PlayDevice(_THIS)
@@ -311,7 +322,7 @@ ALSA_PlayDevice(_THIS)
this->spec.channels;
snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
- swizzle_alsa_channels(this, this->hidden->mixbuf, frames_left);
+ this->hidden->swizzle_func(this, this->hidden->mixbuf, frames_left);
while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
int status;
@@ -398,7 +409,7 @@ ALSA_CaptureFromDevice(_THIS, void *buffer, int buflen)
frames_left -= status;
}
- swizzle_alsa_channels(this, buffer, total_frames - frames_left);
+ this->hidden->swizzle_func(this, buffer, total_frames - frames_left);
return (total_frames - frames_left) * frame_size;
}
@@ -548,6 +559,8 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
SDL_AudioFormat test_format = 0;
unsigned int rate = 0;
unsigned int channels = 0;
+ snd_pcm_chmap_t *chmap;
+ char chmap_str[64];
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
@@ -640,6 +653,20 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
}
this->spec.format = test_format;
+ /* Validate number of channels and determine if swizzling is necessary
+ * Assume original swizzling, until proven otherwise.
+ */
+ this->hidden->swizzle_func = swizzle_alsa_channels;
+ chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
+ if (chmap) {
+ ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str);
+ if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
+ SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) {
+ this->hidden->swizzle_func = no_swizzle;
+ }
+ free(chmap);
+ }
+
/* Set the number of channels */
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
this->spec.channels);
diff --git a/src/audio/alsa/SDL_alsa_audio.h b/src/audio/alsa/SDL_alsa_audio.h
index 3080aea..19e4b24 100644
--- a/src/audio/alsa/SDL_alsa_audio.h
+++ b/src/audio/alsa/SDL_alsa_audio.h
@@ -38,6 +38,9 @@ struct SDL_PrivateAudioData
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
+
+ /* swizzle function */
+ void (*swizzle_func)(_THIS, void *buffer, Uint32 bufferlen);
};
#endif /* _SDL_ALSA_audio_h */