diff options
author | Johns <johns98@gmx.net> | 2011-12-08 18:58:10 +0100 |
---|---|---|
committer | Johns <johns98@gmx.net> | 2011-12-08 20:45:20 +0100 |
commit | 3f8ff57e30c82500511d40f718f6f80aafd37d20 (patch) | |
tree | f0081a6a90f3c036d16834eec1fe8eb97f988aca /audio.c | |
parent | fc2580dc2aee2a7b86d3ca5428ce2e2e57c060b2 (diff) | |
download | vdr-plugin-softhddevice-3f8ff57e30c82500511d40f718f6f80aafd37d20.tar.gz vdr-plugin-softhddevice-3f8ff57e30c82500511d40f718f6f80aafd37d20.tar.bz2 |
Audio improvement.
Made audio thread cancelable.
Calculate audio PTS.
Disable alsa message to stderr.
Better buffer flush with threaded play.
Prepared audio resample, for unsupported number of audio channels.
Diffstat (limited to 'audio.c')
-rw-r--r-- | audio.c | 159 |
1 files changed, 124 insertions, 35 deletions
@@ -35,6 +35,7 @@ #include <stdio.h> #include <stdint.h> +#include <inttypes.h> #include <libintl.h> #define _(str) gettext(str) ///< gettext shortcut @@ -63,7 +64,7 @@ static volatile char AudioRunning; ///< thread running / stopped static int AudioPaused; ///< audio paused static unsigned AudioSampleRate; ///< audio sample rate in hz static unsigned AudioChannels; ///< number of audio channels -static uint64_t AudioPTS; ///< audio pts clock +static int64_t AudioPTS; ///< audio pts clock //---------------------------------------------------------------------------- // Alsa variables @@ -75,6 +76,7 @@ static int AlsaUseMmap; ///< use mmap static RingBuffer *AlsaRingBuffer; ///< audio ring buffer static unsigned AlsaStartThreshold; ///< start play, if filled +static int AlsaFlushBuffer; ///< flag empty buffer static snd_mixer_t *AlsaMixer; ///< alsa mixer handle static snd_mixer_elem_t *AlsaMixerElem; ///< alsa pcm mixer element @@ -102,7 +104,8 @@ static int AlsaAddToRingbuffer(const void *samples, int count) // too many bytes are lost } // Update audio clock - AudioPTS += (count * 90000) / (AudioSampleRate * AudioChannels * 2); + AudioPTS += + ((int64_t) count * 90000) / (AudioSampleRate * AudioChannels * 2); if (!AudioRunning) { if (AlsaStartThreshold < RingBufferUsedBytes(AlsaRingBuffer)) { @@ -149,7 +152,7 @@ static int AlsaPlayRingbuffer(void) // happens with broken alsa drivers Error(_("audio/alsa: broken driver %d\n"), avail); } - break; + //break; } n = RingBufferGetReadPointer(AlsaRingBuffer, &p); @@ -373,6 +376,18 @@ static void *AudioPlayHandlerThread(void *dummy) usleep(100 * 1000); continue; } + if (AlsaFlushBuffer) { + // we can flush too many, but wo cares + Debug(3, "audio/alsa: flushing buffers\n"); + RingBufferReadAdvance(AlsaRingBuffer, + RingBufferUsedBytes(AlsaRingBuffer)); + if ((err = snd_pcm_drain(AlsaPCMHandle))) { + Error(_("audio: snd_pcm_drain(): %s\n"), + snd_strerror(err)); + } + AlsaFlushBuffer = 0; + break; + } if ((err = AlsaPlayRingbuffer())) { // empty / error snd_pcm_state_t state; @@ -400,6 +415,10 @@ static void *AudioPlayHandlerThread(void *dummy) */ void AudioEnqueue(const void *samples, int count) { + if (!AlsaRingBuffer || !AlsaPCMHandle) { + Debug(3, "audio/alsa: alsa not ready\n"); + return; + } if (AlsaAddToRingbuffer(samples, count)) { snd_pcm_state_t state; @@ -508,7 +527,7 @@ static void AlsaInitPCM(void) pthread_mutex_init(&AudioMutex, NULL); pthread_cond_init(&AudioStartCond, NULL); pthread_create(&AudioThread, NULL, AudioPlayHandlerThread, NULL); - pthread_detach(AudioThread); + //pthread_detach(AudioThread); } //---------------------------------------------------------------------------- @@ -584,27 +603,42 @@ static void AlsaInitMixer(void) //---------------------------------------------------------------------------- /** +** Set audio clock base. +** +** @param pts audio presentation timestamp +*/ +void AudioSetClock(int64_t pts) +{ + if (AudioPTS != pts) { + Debug(3, "audio: set clock to %#012" PRIx64 " %#012" PRIx64 " pts\n", + AudioPTS, pts); + + AudioPTS = pts; + } +} + +/** ** Get audio delay in time stamps. */ uint64_t AudioGetDelay(void) { int err; snd_pcm_sframes_t delay; + uint64_t pts; if ((err = snd_pcm_delay(AlsaPCMHandle, &delay)) < 0) { //Debug(3, "audio/alsa: no hw delay\n"); delay = 0UL; } else if (snd_pcm_state(AlsaPCMHandle) != SND_PCM_STATE_RUNNING) { - //Debug(3, "audio/alsa: %lu delay ok, but not running\n", delay); + //Debug(3, "audio/alsa: %ld delay ok, but not running\n", delay); } - delay = (delay * 90000) / AudioSampleRate; - //Debug(3, "audio/alsa: hw delay %lu\n", delay); - delay += - (RingBufferUsedBytes(AlsaRingBuffer) * 90000) / (AudioSampleRate * - AudioChannels); - //Debug(3, "audio/alsa: hw+sw delay %lu ms\n", delay / 90); - return delay; + pts = ((uint64_t) delay * 90000) / AudioSampleRate; + pts += ((uint64_t) RingBufferUsedBytes(AlsaRingBuffer) * 90000) + / (AudioSampleRate * AudioChannels); + //Debug(3, "audio/alsa: hw+sw delay %"PRId64" ms\n", pts / 90); + + return pts; } /** @@ -613,40 +647,70 @@ uint64_t AudioGetDelay(void) ** @param freq sample frequency ** @param channels number of channels ** +** @retval 0 everything ok +** @retval 1 didn't support frequency/channels combination +** @retval -1 something gone wrong +** ** @todo audio changes must be queued and done when the buffer is empty */ -void AudioSetup(int freq, int channels) +int AudioSetup(int *freq, int *channels) { snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; int err; + int ret; #if 1 - Debug(3, "audio/alsa: channels %d frequency %d hz\n", channels, freq); + Debug(3, "audio/alsa: channels %d frequency %d hz\n", *channels, *freq); - if (!freq || !channels) { // invalid parameter + // invalid parameter + if (!freq || !channels || !*freq || !*channels) { + Debug(3, "audio: bad channels or frequency parameters\n"); // FIXME: set flag invalid setup - return; + return -1; } - AudioChannels = channels; - AudioSampleRate = freq; - // FIXME: thread!! - RingBufferReadAdvance(AlsaRingBuffer, RingBufferUsedBytes(AlsaRingBuffer)); + AudioChannels = *channels; + AudioSampleRate = *freq; + // flush any buffered data +#ifdef USE_AUDIO_THREAD + if (AudioRunning) { + AlsaFlushBuffer = 1; + } else +#endif + { + RingBufferReadAdvance(AlsaRingBuffer, + RingBufferUsedBytes(AlsaRingBuffer)); + } + + ret = 0; + try_again: if ((err = snd_pcm_set_params(AlsaPCMHandle, SND_PCM_FORMAT_S16, AlsaUseMmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED : - SND_PCM_ACCESS_RW_INTERLEAVED, channels, freq, 1, + SND_PCM_ACCESS_RW_INTERLEAVED, *channels, *freq, 1, 125 * 1000))) { Error(_("audio/alsa: set params error: %s\n"), snd_strerror(err)); - if (channels == 2) { - // FIXME: must stop sound - return; + switch (*channels) { + case 1: + // FIXME: enable channel upmix + ret = 1; + *channels = 2; + goto try_again; + case 2: + return -1; + case 4: + case 6: + // FIXME: enable channel downmix + *channels = 2; + goto try_again; + default: + Error(_("audio/alsa: unsupported number of channels\n")); + // FIXME: must stop sound + return -1; } - // FIXME: enable channel downmix - // AudioChannels = downmix_channels; - return; + return -1; } #else snd_pcm_hw_params_t *hw_params; @@ -731,14 +795,15 @@ void AudioSetup(int freq, int channels) Debug(3, "audio/alsa: state %s\n", snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle))); - AlsaStartThreshold = - snd_pcm_frames_to_bytes(AlsaPCMHandle, buffer_size + period_size); + AlsaStartThreshold = snd_pcm_frames_to_bytes(AlsaPCMHandle, buffer_size); // min 500ms - if (AlsaStartThreshold < (freq * channels * 2U) / 2) { - AlsaStartThreshold = (freq * channels * 2U) / 2; + if (AlsaStartThreshold < (*freq * *channels * 2U) / 2) { + AlsaStartThreshold = (*freq * *channels * 2U) / 2; } Debug(3, "audio/alsa: delay %u ms\n", (AlsaStartThreshold * 1000) / (AudioSampleRate * AudioChannels * 2)); + + return ret; } /** @@ -752,16 +817,39 @@ void AudioSetDevice(const char *device) } /** +** Empty log callback +*/ +static void AlsaNoopCallback( __attribute__ ((unused)) + const char *file, __attribute__ ((unused)) + int line, __attribute__ ((unused)) + const char *function, __attribute__ ((unused)) + int err, __attribute__ ((unused)) + const char *fmt, ...) +{ +} + +/** ** Initialize audio output module. */ void AudioInit(void) { + int freq; + int chan; + +#ifndef DEBUG + // display alsa error messages + snd_lib_error_set_handler(AlsaNoopCallback); +#endif AlsaRingBuffer = RingBufferNew(48000 * 8 * 2); // ~1s 8ch 16bit AlsaInitPCM(); AlsaInitMixer(); - AudioSetup(48000, 2); // set default parameters + freq = 48000; + chan = 2; + if (AudioSetup(&freq, &chan)) { // set default parameters + Error(_("audio: can't do initial setup\n")); + } AudioPaused = 1; } @@ -773,9 +861,10 @@ void AudioExit(void) { void *retval; - pthread_cancel(AudioThread); - pthread_join(AudioThread, &retval); - if (retval != PTHREAD_CANCELED) { + if (pthread_cancel(AudioThread)) { + Error(_("audio: can't queue cancel alsa play thread\n")); + } + if (pthread_join(AudioThread, &retval) || retval != PTHREAD_CANCELED) { Error(_("audio: can't cancel alsa play thread\n")); } pthread_cond_destroy(&AudioStartCond); |