diff --git a/modules/audio_output/alsa.c b/modules/audio_output/alsa.c index 9e0a00d28562b831867ef36eb5ea74f6b845e5b8..68a3c4aa2326a7ec924d69a173d55855daf2934d 100644 --- a/modules/audio_output/alsa.c +++ b/modules/audio_output/alsa.c @@ -39,75 +39,6 @@ #include <alsa/asoundlib.h> #include <alsa/version.h> -/** Private data for an ALSA PCM playback stream */ -typedef struct -{ - snd_pcm_t *pcm; - unsigned rate; /**< Sample rate */ - vlc_fourcc_t format; /**< Sample format */ - uint8_t chans_table[AOUT_CHAN_MAX]; /**< Channels order table */ - uint8_t chans_to_reorder; /**< Number of channels to reorder */ - - bool soft_mute; - float soft_gain; - char *device; -} aout_sys_t; - -enum { - PASSTHROUGH_NONE, - PASSTHROUGH_SPDIF, - PASSTHROUGH_HDMI, -}; - -#include "audio_output/volume.h" - -#define A52_FRAME_NB 1536 - -static int Open (vlc_object_t *); -static void Close (vlc_object_t *); -static int EnumDevices(char const *, char ***, char ***); - -#define AUDIO_DEV_TEXT N_("Audio output device") -#define AUDIO_DEV_LONGTEXT N_("Audio output device (using ALSA syntax).") - -#define AUDIO_CHAN_TEXT N_("Audio output channels") -#define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \ - "If the input has more channels than the output, it will be down-mixed. " \ - "This parameter is ignored when digital pass-through is active.") -static const int channels[] = { - AOUT_CHAN_CENTER, AOUT_CHANS_STEREO, AOUT_CHANS_4_0, AOUT_CHANS_4_1, - AOUT_CHANS_5_0, AOUT_CHANS_5_1, AOUT_CHANS_7_1, -}; -static const char *const channels_text[] = { - N_("Mono"), N_("Stereo"), N_("Surround 4.0"), N_("Surround 4.1"), - N_("Surround 5.0"), N_("Surround 5.1"), N_("Surround 7.1"), -}; - -#define PASSTHROUGH_TEXT N_("Audio passthrough mode") -static const int passthrough_modes[] = { - PASSTHROUGH_NONE, PASSTHROUGH_SPDIF, PASSTHROUGH_HDMI, -}; -static const char *const passthrough_modes_text[] = { - N_("None"), N_("S/PDIF"), N_("HDMI"), -}; - -vlc_module_begin () - set_shortname( "ALSA" ) - set_description( N_("ALSA audio output") ) - set_subcategory( SUBCAT_AUDIO_AOUT ) - add_string ("alsa-audio-device", "default", - AUDIO_DEV_TEXT, AUDIO_DEV_LONGTEXT) - add_integer ("alsa-audio-channels", AOUT_CHANS_FRONT, - AUDIO_CHAN_TEXT, AUDIO_CHAN_LONGTEXT) - change_integer_list (channels, channels_text) - add_integer("alsa-passthrough", PASSTHROUGH_NONE, PASSTHROUGH_TEXT, - NULL) - change_integer_list(passthrough_modes, passthrough_modes_text) - add_sw_gain () - set_capability( "audio output", 150 ) - set_callbacks( Open, Close ) -vlc_module_end () - /** Helper for ALSA -> VLC debugging output */ static void Dump (vlc_object_t *obj, const char *msg, int (*cb)(void *, snd_output_t *), void *p) @@ -159,6 +90,149 @@ static void DumpDeviceStatus (vlc_object_t *obj, snd_pcm_t *pcm) } #define DumpDeviceStatus(o, p) DumpDeviceStatus(VLC_OBJECT(o), p) +/** Private data for an ALSA PCM playback stream */ +typedef struct +{ + snd_pcm_t *pcm; + unsigned rate; /**< Sample rate */ + vlc_fourcc_t format; /**< Sample format */ + uint8_t chans_table[AOUT_CHAN_MAX]; /**< Channels order table */ + uint8_t chans_to_reorder; /**< Number of channels to reorder */ + + bool soft_mute; + float soft_gain; + char *device; +} aout_sys_t; + +#include "audio_output/volume.h" + +static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay) +{ + aout_sys_t *sys = aout->sys; + snd_pcm_sframes_t frames; + + int val = snd_pcm_delay(sys->pcm, &frames); + if (val) + { + msg_Err(aout, "cannot estimate delay: %s", snd_strerror(val)); + return -1; + } + *delay = vlc_tick_from_samples(frames, sys->rate); + return 0; +} + +/** + * Queues one audio buffer to the hardware. + */ +static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date) +{ + aout_sys_t *sys = aout->sys; + + if (sys->chans_to_reorder != 0) + aout_ChannelReorder(block->p_buffer, block->i_buffer, + sys->chans_to_reorder, sys->chans_table, + sys->format); + + snd_pcm_t *pcm = sys->pcm; + + /* TODO: better overflow handling */ + /* TODO: no period wake ups */ + + while (block->i_nb_samples > 0) + { + snd_pcm_sframes_t frames; + + frames = snd_pcm_writei(pcm, block->p_buffer, block->i_nb_samples); + if (frames >= 0) + { + size_t bytes = snd_pcm_frames_to_bytes(pcm, frames); + block->i_nb_samples -= frames; + block->p_buffer += bytes; + block->i_buffer -= bytes; + // pts, length + } + else + { + int val = snd_pcm_recover(pcm, frames, 1); + if (val) + { + msg_Err(aout, "cannot recover playback stream: %s", + snd_strerror (val)); + DumpDeviceStatus(aout, pcm); + break; + } + msg_Warn(aout, "cannot write samples: %s", snd_strerror(frames)); + } + } + block_Release(block); + (void) date; +} + +static void PauseDummy(audio_output_t *aout, bool pause, vlc_tick_t date) +{ + aout_sys_t *p_sys = aout->sys; + snd_pcm_t *pcm = p_sys->pcm; + + /* Stupid device cannot pause. Discard samples. */ + if (pause) + snd_pcm_drop(pcm); + else + snd_pcm_prepare(pcm); + (void) date; +} + +/** + * Pauses/resumes the audio playback. + */ +static void Pause(audio_output_t *aout, bool pause, vlc_tick_t date) +{ + aout_sys_t *p_sys = aout->sys; + snd_pcm_t *pcm = p_sys->pcm; + + int val = snd_pcm_pause(pcm, pause); + if (unlikely(val)) + PauseDummy(aout, pause, date); +} + +/** + * Flushes the audio playback buffer. + */ +static void Flush (audio_output_t *aout) +{ + aout_sys_t *p_sys = aout->sys; + snd_pcm_t *pcm = p_sys->pcm; + + snd_pcm_drop(pcm); + snd_pcm_prepare(pcm); +} + +/** + * Drains the audio playback buffer. + */ +static void Drain (audio_output_t *aout) +{ + aout_sys_t *p_sys = aout->sys; + snd_pcm_t *pcm = p_sys->pcm; + + /* XXX: Synchronous drain, not interruptible. */ + snd_pcm_drain(pcm); + snd_pcm_prepare(pcm); + + aout_DrainedReport(aout); +} + +/** + * Releases the audio output. + */ +static void Stop (audio_output_t *aout) +{ + aout_sys_t *sys = aout->sys; + snd_pcm_t *pcm = sys->pcm; + + snd_pcm_drop(pcm); + snd_pcm_close(pcm); +} + #if (SND_LIB_VERSION >= 0x01001B) static const uint16_t vlc_chans[] = { [SND_CHMAP_MONO] = AOUT_CHAN_CENTER, @@ -288,12 +362,13 @@ out: # define SetupChannels(obj, pcm, mask, tab) (0) #endif -static int TimeGet (audio_output_t *aout, vlc_tick_t *); -static void Play(audio_output_t *, block_t *, vlc_tick_t); -static void Pause (audio_output_t *, bool, vlc_tick_t); -static void PauseDummy (audio_output_t *, bool, vlc_tick_t); -static void Flush (audio_output_t *); -static void Drain (audio_output_t *); +enum { + PASSTHROUGH_NONE, + PASSTHROUGH_SPDIF, + PASSTHROUGH_HDMI, +}; + +#define A52_FRAME_NB 1536 /** Initializes an ALSA playback stream */ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) @@ -631,131 +706,6 @@ error: return VLC_EGENERIC; } -static int TimeGet (audio_output_t *aout, vlc_tick_t *restrict delay) -{ - aout_sys_t *sys = aout->sys; - snd_pcm_sframes_t frames; - - int val = snd_pcm_delay (sys->pcm, &frames); - if (val) - { - msg_Err (aout, "cannot estimate delay: %s", snd_strerror (val)); - return -1; - } - *delay = vlc_tick_from_samples(frames, sys->rate); - return 0; -} - -/** - * Queues one audio buffer to the hardware. - */ -static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date) -{ - aout_sys_t *sys = aout->sys; - - if (sys->chans_to_reorder != 0) - aout_ChannelReorder(block->p_buffer, block->i_buffer, - sys->chans_to_reorder, sys->chans_table, sys->format); - - snd_pcm_t *pcm = sys->pcm; - - /* TODO: better overflow handling */ - /* TODO: no period wake ups */ - - while (block->i_nb_samples > 0) - { - snd_pcm_sframes_t frames; - - frames = snd_pcm_writei (pcm, block->p_buffer, block->i_nb_samples); - if (frames >= 0) - { - size_t bytes = snd_pcm_frames_to_bytes (pcm, frames); - block->i_nb_samples -= frames; - block->p_buffer += bytes; - block->i_buffer -= bytes; - // pts, length - } - else - { - int val = snd_pcm_recover (pcm, frames, 1); - if (val) - { - msg_Err (aout, "cannot recover playback stream: %s", - snd_strerror (val)); - DumpDeviceStatus (aout, pcm); - break; - } - msg_Warn (aout, "cannot write samples: %s", snd_strerror (frames)); - } - } - block_Release (block); - (void) date; -} - -/** - * Pauses/resumes the audio playback. - */ -static void Pause (audio_output_t *aout, bool pause, vlc_tick_t date) -{ - aout_sys_t *p_sys = aout->sys; - snd_pcm_t *pcm = p_sys->pcm; - - int val = snd_pcm_pause (pcm, pause); - if (unlikely(val)) - PauseDummy (aout, pause, date); -} - -static void PauseDummy (audio_output_t *aout, bool pause, vlc_tick_t date) -{ - aout_sys_t *p_sys = aout->sys; - snd_pcm_t *pcm = p_sys->pcm; - - /* Stupid device cannot pause. Discard samples. */ - if (pause) - snd_pcm_drop (pcm); - else - snd_pcm_prepare (pcm); - (void) date; -} - -/** - * Flushes the audio playback buffer. - */ -static void Flush (audio_output_t *aout) -{ - aout_sys_t *p_sys = aout->sys; - snd_pcm_t *pcm = p_sys->pcm; - snd_pcm_drop (pcm); - snd_pcm_prepare (pcm); -} - -/** - * Drains the audio playback buffer. - */ -static void Drain (audio_output_t *aout) -{ - aout_sys_t *p_sys = aout->sys; - snd_pcm_t *pcm = p_sys->pcm; - - /* XXX: Synchronous drain, not interruptible. */ - snd_pcm_drain (pcm); - snd_pcm_prepare (pcm); - - aout_DrainedReport(aout); -} - -/** - * Releases the audio output. - */ -static void Stop (audio_output_t *aout) -{ - aout_sys_t *sys = aout->sys; - snd_pcm_t *pcm = sys->pcm; - - snd_pcm_drop (pcm); - snd_pcm_close (pcm); -} - /** * Enumerates ALSA output devices. */ @@ -884,3 +834,44 @@ static void Close(vlc_object_t *obj) free (sys->device); free (sys); } + +#define AUDIO_DEV_TEXT N_("Audio output device") +#define AUDIO_DEV_LONGTEXT N_("Audio output device (using ALSA syntax).") + +#define AUDIO_CHAN_TEXT N_("Audio output channels") +#define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \ + "If the input has more channels than the output, it will be down-mixed. " \ + "This parameter is ignored when digital pass-through is active.") +static const int channels[] = { + AOUT_CHAN_CENTER, AOUT_CHANS_STEREO, AOUT_CHANS_4_0, AOUT_CHANS_4_1, + AOUT_CHANS_5_0, AOUT_CHANS_5_1, AOUT_CHANS_7_1, +}; +static const char *const channels_text[] = { + N_("Mono"), N_("Stereo"), N_("Surround 4.0"), N_("Surround 4.1"), + N_("Surround 5.0"), N_("Surround 5.1"), N_("Surround 7.1"), +}; + +#define PASSTHROUGH_TEXT N_("Audio passthrough mode") +static const int passthrough_modes[] = { + PASSTHROUGH_NONE, PASSTHROUGH_SPDIF, PASSTHROUGH_HDMI, +}; +static const char *const passthrough_modes_text[] = { + N_("None"), N_("S/PDIF"), N_("HDMI"), +}; + +vlc_module_begin() + set_shortname("ALSA") + set_description(N_("ALSA audio output")) + set_subcategory(SUBCAT_AUDIO_AOUT) + add_string("alsa-audio-device", "default", + AUDIO_DEV_TEXT, AUDIO_DEV_LONGTEXT) + add_integer("alsa-audio-channels", AOUT_CHANS_FRONT, + AUDIO_CHAN_TEXT, AUDIO_CHAN_LONGTEXT) + change_integer_list (channels, channels_text) + add_integer("alsa-passthrough", PASSTHROUGH_NONE, PASSTHROUGH_TEXT, + NULL) + change_integer_list(passthrough_modes, passthrough_modes_text) + add_sw_gain() + set_capability("audio output", 150) + set_callbacks(Open, Close) +vlc_module_end()