[vlc-commits] ALSA: use channels map functions (fixes #7796)
Rémi Denis-Courmont
git at videolan.org
Sun Dec 9 17:06:16 CET 2012
vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Sun Dec 9 17:53:21 2012 +0200| [345a3e3100b7aecd49a12ca730a8fcbc8be32c23] | committer: Rémi Denis-Courmont
ALSA: use channels map functions (fixes #7796)
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=345a3e3100b7aecd49a12ca730a8fcbc8be32c23
---
modules/audio_output/alsa.c | 230 ++++++++++++++++++++++++++++++-------------
1 file changed, 162 insertions(+), 68 deletions(-)
diff --git a/modules/audio_output/alsa.c b/modules/audio_output/alsa.c
index d1ace1c..98af8b2 100644
--- a/modules/audio_output/alsa.c
+++ b/modules/audio_output/alsa.c
@@ -43,8 +43,9 @@
struct aout_sys_t
{
snd_pcm_t *pcm;
- void (*reorder) (void *, size_t, unsigned);
unsigned rate; /**< Sample rate */
+ uint8_t chans_table[AOUT_CHAN_MAX]; /**< Channels order table */
+ uint8_t chans_to_reorder; /**< Number of channels to reorder */
uint8_t bits; /**< Bits per sample per channel */
bool soft_mute;
float soft_gain;
@@ -154,12 +155,157 @@ static int DeviceChanged (vlc_object_t *obj, const char *varname,
return VLC_SUCCESS;
}
+static unsigned SetupChannelsUnknown (vlc_object_t *obj,
+ uint16_t *restrict mask)
+{
+ uint16_t map = var_InheritInteger (obj, "alsa-audio-channels");
+ uint16_t chans = *mask & map;
+
+ if (unlikely(chans == 0)) /* WTH? */
+ chans = AOUT_CHANS_STEREO;
+
+ if (popcount (chans) < popcount (*mask))
+ msg_Dbg (obj, "downmixing from %u to %u channels",
+ popcount (*mask), popcount (chans));
+ else
+ msg_Dbg (obj, "keeping %u channels", popcount (chans));
+ *mask = chans;
+ return 0;
+}
+
+#if (SND_LIB_VERSION >= 0x01001B)
+static const uint16_t vlc_chans[] = {
+ [SND_CHMAP_MONO] = AOUT_CHAN_CENTER,
+ [SND_CHMAP_FL] = AOUT_CHAN_LEFT,
+ [SND_CHMAP_FR] = AOUT_CHAN_RIGHT,
+ [SND_CHMAP_RL] = AOUT_CHAN_REARLEFT,
+ [SND_CHMAP_RR] = AOUT_CHAN_REARRIGHT,
+ [SND_CHMAP_FC] = AOUT_CHAN_CENTER,
+ [SND_CHMAP_LFE] = AOUT_CHAN_LFE,
+ [SND_CHMAP_SL] = AOUT_CHAN_MIDDLELEFT,
+ [SND_CHMAP_SR] = AOUT_CHAN_MIDDLERIGHT,
+ [SND_CHMAP_RC] = AOUT_CHAN_REARCENTER,
+};
+
+static int Map2Mask (vlc_object_t *obj, const snd_pcm_chmap_t *restrict map)
+{
+ uint16_t mask = 0;
+
+ for (unsigned i = 0; i < map->channels; i++)
+ {
+ const unsigned pos = map->pos[i];
+ uint_fast16_t vlc_chan = 0;
+
+ if (pos < sizeof (vlc_chans) / sizeof (vlc_chans[0]))
+ vlc_chan = vlc_chans[pos];
+ if (vlc_chan == 0)
+ {
+ msg_Dbg (obj, " %s channel %u position %u", "unsupported", i, pos);
+ return -1;
+ }
+ if (mask & vlc_chan)
+ {
+ msg_Dbg (obj, " %s channel %u position %u", "duplicate", i, pos);
+ return -1;
+ }
+ mask |= vlc_chan;
+ }
+ return mask;
+}
+
+/**
+ * Compares a fixed ALSA channels map with the VLC channels order.
+ */
+static unsigned SetupChannelsFixed(const snd_pcm_chmap_t *restrict map,
+ uint16_t *restrict mask, uint8_t *restrict tab)
+{
+ uint32_t chans_out[AOUT_CHAN_MAX];
+
+ for (unsigned i = 0; i < map->channels; i++)
+ {
+ uint_fast16_t vlc_chan = vlc_chans[map->pos[i]];
+
+ chans_out[i] = vlc_chan;
+ *mask |= vlc_chan;
+ }
+
+ return aout_CheckChannelReorder(NULL, chans_out, *mask, tab);
+}
+
+/**
+ * Negotiate channels mapping.
+ */
+static unsigned SetupChannels (vlc_object_t *obj, snd_pcm_t *pcm,
+ uint16_t *restrict mask, uint8_t *restrict tab)
+{
+ snd_pcm_chmap_query_t **maps = snd_pcm_query_chmaps (pcm);
+ if (tab == NULL)
+ { /* Fallback to manual configuration */
+ msg_Dbg(obj, "channels map not provided");
+ return SetupChannelsUnknown (obj, mask);
+ }
+
+ /* Find most appropriate available channels map */
+ unsigned best_offset;
+ unsigned best_score = 0;
+
+ for (snd_pcm_chmap_query_t *const *p = maps; *p != NULL; p++)
+ {
+ snd_pcm_chmap_query_t *map = *p;
+
+ switch (map->type)
+ {
+ case SND_CHMAP_TYPE_FIXED:
+ case SND_CHMAP_TYPE_PAIRED:
+ case SND_CHMAP_TYPE_VAR:
+ break;
+ default:
+ msg_Err (obj, "unknown channels map type %u", map->type);
+ continue;
+ }
+
+ int chans = Map2Mask (obj, &map->map);
+ if (chans == -1)
+ continue;
+
+ unsigned score = popcount (chans & *mask);
+ if (score > best_score)
+ {
+ best_offset = p - maps;
+ best_score = score;
+ }
+ }
+
+ if (best_score == 0)
+ {
+ msg_Err (obj, "cannot find supported channels map");
+ snd_pcm_free_chmaps (maps);
+ return SetupChannelsUnknown (obj, mask);
+ }
+
+ const snd_pcm_chmap_t *map = &maps[best_offset]->map;
+ msg_Dbg (obj, "using channels map %u, type %u, %u channel(s)", best_offset,
+ maps[best_offset]->type, best_score);
+
+ /* Setup channels map */
+ unsigned to_reorder = SetupChannelsFixed(map, mask, tab);
+
+ /* TODO: avoid reordering for PAIRED and VAR types */
+ //snd_pcm_set_chmap (pcm, ...)
+
+ snd_pcm_free_chmaps (maps);
+ return to_reorder;
+}
+#else /* (SND_LIB_VERSION < 0x01001B) */
+# define SetupChannels(obj, pcm, mask, tab) \
+ SetupChannelsUnknown(obj, mask)
+#endif
+
static int TimeGet (audio_output_t *aout, mtime_t *);
static void Play (audio_output_t *, block_t *);
static void Pause (audio_output_t *, bool, mtime_t);
static void PauseDummy (audio_output_t *, bool, mtime_t);
static void Flush (audio_output_t *, bool);
-static void Reorder71 (void *, size_t, unsigned);
/** Initializes an ALSA playback stream */
static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
@@ -246,20 +392,6 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
}
}
- /* ALSA channels */
- /* XXX: maybe this should be shared with other dumb outputs */
- uint32_t map = var_InheritInteger (aout, "alsa-audio-channels");
- map &= fmt->i_physical_channels;
- if (unlikely(map == 0)) /* WTH? */
- map = AOUT_CHANS_STEREO;
-
- unsigned channels = popcount (map);
- if (channels < aout_FormatNbChannels (fmt))
- msg_Dbg (aout, "downmixing from %u to %u channels",
- aout_FormatNbChannels (fmt), channels);
- else
- msg_Dbg (aout, "keeping %u channels", channels);
-
/* Choose the IEC device for S/PDIF output:
if the device is overridden by the user then it will be the one.
Otherwise we compute the default device based on the output format. */
@@ -370,6 +502,17 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
}
/* Set channels count */
+ unsigned channels;
+ if (!spdif)
+ {
+ sys->chans_to_reorder = SetupChannels (VLC_OBJECT(aout), pcm,
+ &fmt->i_physical_channels, sys->chans_table);
+ channels = popcount (fmt->i_physical_channels);
+ }
+ else
+ channels = 2;
+ fmt->i_original_channels = fmt->i_physical_channels;
+
/* By default, ALSA plug will pad missing channels with zeroes, which is
* usually fine. However, it will also discard extraneous channels, which
* is not acceptable. Thus the user must configure the physically
@@ -470,7 +613,6 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
fmt->i_format = fourcc;
fmt->i_rate = rate;
sys->rate = rate;
- sys->reorder = NULL;
if (spdif)
{
fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
@@ -478,14 +620,6 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
}
else
{
- fmt->i_original_channels =
- fmt->i_physical_channels = map;
- switch (popcount (map))
- {
- case 8:
- sys->reorder = Reorder71;
- break;
- }
aout_FormatPrepare (fmt);
sys->bits = fmt->i_bitspersample;
}
@@ -545,8 +679,9 @@ static void Play (audio_output_t *aout, block_t *block)
{
aout_sys_t *sys = aout->sys;
- if (sys->reorder != NULL)
- sys->reorder (block->p_buffer, block->i_nb_samples, sys->bits);
+ if (sys->chans_to_reorder != 0)
+ aout_ChannelReorder(block->p_buffer, block->i_buffer,
+ sys->chans_to_reorder, sys->chans_table, sys->bits);
snd_pcm_t *pcm = sys->pcm;
@@ -637,47 +772,6 @@ static void Stop (audio_output_t *aout)
}
/**
- * Converts from VLC to ALSA order for 7.1.
- * VLC has middle channels in position 2 and 3, ALSA in position 6 and 7.
- */
-static void Reorder71 (void *p, size_t n, unsigned bits)
-{
- switch (bits)
- {
- case 32:
- for (uint64_t *ptr = p; n > 0; ptr += 4, n--)
- {
- uint64_t middle = ptr[1], c_lfe = ptr[2], rear = ptr[3];
- ptr[1] = c_lfe; ptr[2] = rear; ptr[3] = middle;
- }
- break;
- case 16:
- for (uint32_t *ptr = p; n > 0; ptr += 4, n--)
- {
- uint32_t middle = ptr[1], c_lfe = ptr[2], rear = ptr[3];
- ptr[1] = c_lfe; ptr[2] = rear; ptr[3] = middle;
- }
- break;
-
- default:
- for (uint16_t *ptr = p; n > 0; n--)
- {
- uint16_t middle[bits / 8];
- memcpy (middle, ptr + (bits / 8), bits / 4);
- ptr += bits / 4;
- memcpy (ptr, ptr + (bits / 8), bits / 4);
- ptr += bits / 4;
- memcpy (ptr, ptr + (bits / 8), bits / 4);
- ptr += bits / 4;
- memcpy (ptr, middle, bits / 4);
- ptr += bits / 4;
- }
- break;
- }
-}
-
-
-/**
* Enumerates ALSA output devices.
*/
static int EnumDevices(vlc_object_t *obj, char const *varname,
More information about the vlc-commits
mailing list