summaryrefslogtreecommitdiff
path: root/audio.c
diff options
context:
space:
mode:
authorJohns <johns98@gmx.net>2011-12-08 18:58:10 +0100
committerJohns <johns98@gmx.net>2011-12-08 20:45:20 +0100
commit3f8ff57e30c82500511d40f718f6f80aafd37d20 (patch)
treef0081a6a90f3c036d16834eec1fe8eb97f988aca /audio.c
parentfc2580dc2aee2a7b86d3ca5428ce2e2e57c060b2 (diff)
downloadvdr-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.c159
1 files changed, 124 insertions, 35 deletions
diff --git a/audio.c b/audio.c
index 69a324e..f983787 100644
--- a/audio.c
+++ b/audio.c
@@ -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);