You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2475 lines
76 KiB

4 years ago
///
/// @file audio.c @brief Audio module
4 years ago
///
/// Copyright (c) 2009 - 2014 by Johns. All Rights Reserved.
4 years ago
///
/// Contributor(s):
4 years ago
///
/// License: AGPLv3
4 years ago
///
/// This program is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
/// published by the Free Software Foundation, either version 3 of the
/// License.
4 years ago
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU Affero General Public License for more details.
4 years ago
///
/// $Id: 77fa65030b179e78c13d0bf69a7cc417dae89e1a $
4 years ago
//////////////////////////////////////////////////////////////////////////////
///
/// @defgroup Audio The audio module.
4 years ago
///
/// This module contains all audio output functions.
4 years ago
///
/// ALSA PCM/Mixer api is supported.
/// @see http://www.alsa-project.org/alsa-doc/alsa-lib
4 years ago
///
/// @note alsa async playback is broken, don't use it!
4 years ago
///
///
/// @todo FIXME: there can be problems with little/big endian.
4 years ago
///
#ifdef DEBUG
#undef DEBUG
#endif
#define USE_AUDIO_THREAD ///< use thread for audio playback
#define USE_AUDIO_MIXER ///< use audio module mixer
4 years ago
#include <inttypes.h>
#include <math.h>
#include <sched.h>
4 years ago
#include <stdint.h>
#include <stdio.h>
4 years ago
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <libintl.h>
#define _(str) gettext(str) ///< gettext shortcut
#define _N(str) str ///< gettext_noop shortcut
4 years ago
#include <alsa/asoundlib.h>
#ifdef USE_AUDIO_THREAD
#include <pthread.h>
#include <sys/resource.h>
#include <sys/syscall.h>
4 years ago
#endif
#include "iatomic.h" // portable atomic_t
4 years ago
#include "audio.h"
#include "misc.h"
#include "ringbuffer.h"
4 years ago
//----------------------------------------------------------------------------
// Declarations
4 years ago
//----------------------------------------------------------------------------
/**
** Audio output module structure and typedef.
*/
typedef struct _audio_module_ {
const char *Name; ///< audio output module name
int (*const Thread)(void); ///< module thread handler
void (*const FlushBuffers)(void); ///< flush sample buffers
int64_t (*const GetDelay)(void); ///< get current audio delay
void (*const SetVolume)(int); ///< set output volume
int (*const Setup)(int *, int *, int); ///< setup channels, samplerate
void (*const Play)(void); ///< play audio
void (*const Pause)(void); ///< pause audio
void (*const Init)(void); ///< initialize audio output module
void (*const Exit)(void); ///< cleanup audio output module
4 years ago
} AudioModule;
static const AudioModule NoopModule; ///< forward definition of noop module
4 years ago
//----------------------------------------------------------------------------
// Variables
4 years ago
//----------------------------------------------------------------------------
char AudioAlsaDriverBroken; ///< disable broken driver message
char AudioAlsaNoCloseOpen; ///< disable alsa close/open fix
char AudioAlsaCloseOpenDelay; ///< enable alsa close/open delay fix
4 years ago
static const char *AudioModuleName; ///< which audio module to use
4 years ago
/// Selected audio module.
4 years ago
static const AudioModule *AudioUsedModule = &NoopModule;
static const char *AudioPCMDevice; ///< PCM device name
static const char *AudioPassthroughDevice; ///< Passthrough device name
static char AudioAppendAES; ///< flag automatic append AES
static const char *AudioMixerDevice; ///< mixer device name
static const char *AudioMixerChannel; ///< mixer channel name
static char AudioDoingInit; ///> flag in init, reduce error
static volatile char AudioRunning; ///< thread running / stopped
static volatile char AudioPaused; ///< audio paused
static volatile char AudioVideoIsReady; ///< video ready start early
static int AudioSkip; ///< skip audio to sync to video
static const int AudioBytesProSample = 2; ///< number of bytes per sample
static int AudioBufferTime = 336; ///< audio buffer time in ms
4 years ago
#ifdef USE_AUDIO_THREAD
static pthread_t AudioThread; ///< audio play thread
static pthread_mutex_t AudioMutex; ///< audio condition mutex
static pthread_cond_t AudioStartCond; ///< condition variable
static char AudioThreadStop; ///< stop audio thread
4 years ago
#else
static const int AudioThread; ///< dummy audio thread
4 years ago
#endif
static char AudioSoftVolume; ///< flag use soft volume
static char AudioNormalize; ///< flag use volume normalize
static char AudioCompression; ///< flag use compress volume
static char AudioMute; ///< flag muted
static int AudioAmplifier; ///< software volume factor
static int AudioNormalizeFactor; ///< current normalize factor
static const int AudioMinNormalize = 100; ///< min. normalize factor
static int AudioMaxNormalize; ///< max. normalize factor
static int AudioCompressionFactor; ///< current compression factor
static int AudioMaxCompression; ///< max. compression factor
static int AudioStereoDescent; ///< volume descent for stereo
static int AudioVolume; ///< current volume (0 .. 1000)
extern int VideoAudioDelay; ///< import audio/video delay
4 years ago
/// default ring buffer size ~2s 8ch 16bit (3 * 5 * 7 * 8)
4 years ago
static const unsigned AudioRingBufferSize = 3 * 5 * 7 * 8 * 2 * 1000;
static int AudioChannelsInHw[9]; ///< table which channels are supported
enum _audio_rates { ///< sample rates enumeration
// HW: 32000 44100 48000 88200 96000 176400 192000
// Audio32000, ///< 32.0Khz
Audio44100, ///< 44.1Khz
Audio48000, ///< 48.0Khz
// Audio88200, ///< 88.2Khz
// Audio96000, ///< 96.0Khz
// Audio176400, ///< 176.4Khz
Audio192000, ///< 192.0Khz
AudioRatesMax ///< max index
4 years ago
};
/// table which rates are supported
4 years ago
static int AudioRatesInHw[AudioRatesMax];
/// input to hardware channel matrix
4 years ago
static int AudioChannelMatrix[AudioRatesMax][9];
/// rates tables (must be sorted by frequency)
static const unsigned AudioRatesTable[AudioRatesMax] = {44100, 48000, 192000};
4 years ago
//----------------------------------------------------------------------------
// filter
4 years ago
//----------------------------------------------------------------------------
static const int AudioNormSamples = 4096; ///< number of samples
4 years ago
#define AudioNormMaxIndex 128 ///< number of average values
/// average of n last sample blocks
4 years ago
static uint32_t AudioNormAverage[AudioNormMaxIndex];
static int AudioNormIndex; ///< index into average table
static int AudioNormReady; ///< index counter
static int AudioNormCounter; ///< sample counter
4 years ago
/**
** Audio normalizer.
**
** @param samples sample buffer
** @param count number of bytes in sample buffer
*/
static void AudioNormalizer(int16_t *samples, int count) {
4 years ago
int i;
int l;
int n;
uint32_t avg;
int factor;
int16_t *data;
// average samples
l = count / AudioBytesProSample;
data = samples;
do {
n = l;
if (AudioNormCounter + n > AudioNormSamples) {
n = AudioNormSamples - AudioNormCounter;
}
avg = AudioNormAverage[AudioNormIndex];
for (i = 0; i < n; ++i) {
int t;
t = data[i];
avg += (t * t) / AudioNormSamples;
}
AudioNormAverage[AudioNormIndex] = avg;
AudioNormCounter += n;
if (AudioNormCounter >= AudioNormSamples) {
if (AudioNormReady < AudioNormMaxIndex) {
AudioNormReady++;
} else {
avg = 0;
for (i = 0; i < AudioNormMaxIndex; ++i) {
avg += AudioNormAverage[i] / AudioNormMaxIndex;
}
// calculate normalize factor
if (avg > 0) {
factor = ((INT16_MAX / 8) * 1000U) / (uint32_t)sqrt(avg);
// smooth normalize
AudioNormalizeFactor = (AudioNormalizeFactor * 500 + factor * 500) / 1000;
if (AudioNormalizeFactor < AudioMinNormalize) {
AudioNormalizeFactor = AudioMinNormalize;
}
if (AudioNormalizeFactor > AudioMaxNormalize) {
AudioNormalizeFactor = AudioMaxNormalize;
}
} else {
factor = 1000;
}
Debug(4, "audio/noramlize: avg %8d, fac=%6.3f, norm=%6.3f\n", avg, factor / 1000.0,
AudioNormalizeFactor / 1000.0);
}
AudioNormIndex = (AudioNormIndex + 1) % AudioNormMaxIndex;
AudioNormCounter = 0;
AudioNormAverage[AudioNormIndex] = 0U;
}
data += n;
l -= n;
4 years ago
} while (l > 0);
// apply normalize factor
for (i = 0; i < count / AudioBytesProSample; ++i) {
int t;
4 years ago
t = (samples[i] * AudioNormalizeFactor) / 1000;
if (t < INT16_MIN) {
t = INT16_MIN;
} else if (t > INT16_MAX) {
t = INT16_MAX;
}
samples[i] = t;
4 years ago
}
}
/**
** Reset normalizer.
*/
static void AudioResetNormalizer(void) {
4 years ago
int i;
AudioNormCounter = 0;
AudioNormReady = 0;
for (i = 0; i < AudioNormMaxIndex; ++i) {
AudioNormAverage[i] = 0U;
4 years ago
}
AudioNormalizeFactor = 1000;
}
/**
** Audio compression.
**
** @param samples sample buffer
** @param count number of bytes in sample buffer
*/
static void AudioCompressor(int16_t *samples, int count) {
4 years ago
int max_sample;
int i;
int factor;
// find loudest sample
max_sample = 0;
for (i = 0; i < count / AudioBytesProSample; ++i) {
int t;
4 years ago
t = abs(samples[i]);
if (t > max_sample) {
max_sample = t;
}
4 years ago
}
// calculate compression factor
if (max_sample > 0) {
factor = (INT16_MAX * 1000) / max_sample;
// smooth compression (FIXME: make configurable?)
AudioCompressionFactor = (AudioCompressionFactor * 950 + factor * 50) / 1000;
if (AudioCompressionFactor > factor) {
AudioCompressionFactor = factor; // no clipping
}
if (AudioCompressionFactor > AudioMaxCompression) {
AudioCompressionFactor = AudioMaxCompression;
}
4 years ago
} else {
return; // silent nothing todo
4 years ago
}
Debug(4, "audio/compress: max %5d, fac=%6.3f, com=%6.3f\n", max_sample, factor / 1000.0,
AudioCompressionFactor / 1000.0);
4 years ago
// apply compression factor
for (i = 0; i < count / AudioBytesProSample; ++i) {
int t;
4 years ago
t = (samples[i] * AudioCompressionFactor) / 1000;
if (t < INT16_MIN) {
t = INT16_MIN;
} else if (t > INT16_MAX) {
t = INT16_MAX;
}
samples[i] = t;
4 years ago
}
}
/**
** Reset compressor.
*/
static void AudioResetCompressor(void) {
4 years ago
AudioCompressionFactor = 2000;
if (AudioCompressionFactor > AudioMaxCompression) {
AudioCompressionFactor = AudioMaxCompression;
4 years ago
}
}
/**
** Audio software amplifier.
**
** @param samples sample buffer
** @param count number of bytes in sample buffer
**
** @todo FIXME: this does hard clipping
*/
static void AudioSoftAmplifier(int16_t *samples, int count) {
4 years ago
int i;
// silence
if (AudioMute || !AudioAmplifier) {
memset(samples, 0, count);
return;
4 years ago
}
for (i = 0; i < count / AudioBytesProSample; ++i) {
int t;
4 years ago
t = (samples[i] * AudioAmplifier) / 1000;
if (t < INT16_MIN) {
t = INT16_MIN;
} else if (t > INT16_MAX) {
t = INT16_MAX;
}
samples[i] = t;
4 years ago
}
}
#ifdef USE_AUDIO_MIXER
/**
** Upmix mono to stereo.
**
** @param in input sample buffer
** @param frames number of frames in sample buffer
** @param out output sample buffer
*/
static void AudioMono2Stereo(const int16_t *in, int frames, int16_t *out) {
4 years ago
int i;
for (i = 0; i < frames; ++i) {
int t;
4 years ago
t = in[i];
out[i * 2 + 0] = t;
out[i * 2 + 1] = t;
4 years ago
}
}
/**
** Downmix stereo to mono.
**
** @param in input sample buffer
** @param frames number of frames in sample buffer
** @param out output sample buffer
*/
static void AudioStereo2Mono(const int16_t *in, int frames, int16_t *out) {
4 years ago
int i;
for (i = 0; i < frames; i += 2) {
out[i / 2] = (in[i + 0] + in[i + 1]) / 2;
4 years ago
}
}
/**
** Downmix surround to stereo.
**
** ffmpeg L R C Ls Rs -> alsa L R Ls Rs C
** ffmpeg L R C LFE Ls Rs -> alsa L R Ls Rs C LFE
** ffmpeg L R C LFE Ls Rs Rl Rr -> alsa L R Ls Rs C LFE Rl Rr
**
** @param in input sample buffer
** @param in_chan nr. of input channels
** @param frames number of frames in sample buffer
** @param out output sample buffer
*/
static void AudioSurround2Stereo(const int16_t *in, int in_chan, int frames, int16_t *out) {
4 years ago
while (frames--) {
int l;
int r;
switch (in_chan) {
case 3: // stereo or surround? =>stereo
l = in[0] * 600; // L
r = in[1] * 600; // R
l += in[2] * 400; // C
r += in[2] * 400;
break;
case 4: // quad or surround? =>quad
l = in[0] * 600; // L
r = in[1] * 600; // R
l += in[2] * 400; // Ls
r += in[3] * 400; // Rs
break;
case 5: // 5.0
l = in[0] * 500; // L
r = in[1] * 500; // R
l += in[2] * 200; // Ls
r += in[3] * 200; // Rs
l += in[4] * 300; // C
r += in[4] * 300;
break;
case 6: // 5.1
l = in[0] * 400; // L
r = in[1] * 400; // R
l += in[2] * 200; // Ls
r += in[3] * 200; // Rs
l += in[4] * 300; // C
r += in[4] * 300;
l += in[5] * 100; // LFE
r += in[5] * 100;
break;
case 7: // 7.0
l = in[0] * 400; // L
r = in[1] * 400; // R
l += in[2] * 200; // Ls
r += in[3] * 200; // Rs
l += in[4] * 300; // C
r += in[4] * 300;
l += in[5] * 100; // RL
r += in[6] * 100; // RR
break;
case 8: // 7.1
l = in[0] * 400; // L
r = in[1] * 400; // R
l += in[2] * 150; // Ls
r += in[3] * 150; // Rs
l += in[4] * 250; // C
r += in[4] * 250;
l += in[5] * 100; // LFE
r += in[5] * 100;
l += in[6] * 100; // RL
r += in[7] * 100; // RR
break;
default:
abort();
}
in += in_chan;
out[0] = l / 1000;
out[1] = r / 1000;
out += 2;
4 years ago
}
}
/**
** Upmix @a in_chan channels to @a out_chan.
**
** @param in input sample buffer
** @param in_chan nr. of input channels
** @param frames number of frames in sample buffer
** @param out output sample buffer
** @param out_chan nr. of output channels
*/
static void AudioUpmix(const int16_t *in, int in_chan, int frames, int16_t *out, int out_chan) {
4 years ago
while (frames--) {
int i;
4 years ago
for (i = 0; i < in_chan; ++i) { // copy existing channels
*out++ = *in++;
}
for (; i < out_chan; ++i) { // silents missing channels
*out++ = 0;
}
4 years ago
}
}
/**
** Resample ffmpeg sample format to hardware format.
**
** FIXME: use libswresample for this and move it to codec.
** FIXME: ffmpeg to alsa conversion is already done in codec.c.
**
** ffmpeg L R C Ls Rs -> alsa L R Ls Rs C
** ffmpeg L R C LFE Ls Rs -> alsa L R Ls Rs C LFE
** ffmpeg L R C LFE Ls Rs Rl Rr -> alsa L R Ls Rs C LFE Rl Rr
**
** @param in input sample buffer
** @param in_chan nr. of input channels
** @param frames number of frames in sample buffer
** @param out output sample buffer
** @param out_chan nr. of output channels
*/
static void AudioResample(const int16_t *in, int in_chan, int frames, int16_t *out, int out_chan) {
4 years ago
switch (in_chan * 8 + out_chan) {
case 1 * 8 + 1:
case 2 * 8 + 2:
case 3 * 8 + 3:
case 4 * 8 + 4:
case 5 * 8 + 5:
case 6 * 8 + 6:
case 7 * 8 + 7:
case 8 * 8 + 8: // input = output channels
memcpy(out, in, frames * in_chan * AudioBytesProSample);
break;
case 2 * 8 + 1:
AudioStereo2Mono(in, frames, out);
break;
case 1 * 8 + 2:
AudioMono2Stereo(in, frames, out);
break;
case 3 * 8 + 2:
case 4 * 8 + 2:
case 5 * 8 + 2:
case 6 * 8 + 2:
case 7 * 8 + 2:
case 8 * 8 + 2:
AudioSurround2Stereo(in, in_chan, frames, out);
break;
case 5 * 8 + 6:
case 3 * 8 + 8:
case 5 * 8 + 8:
case 6 * 8 + 8:
AudioUpmix(in, in_chan, frames, out, out_chan);
break;
default:
Error("audio: unsupported %d -> %d channels resample\n", in_chan, out_chan);
// play silence
memset(out, 0, frames * out_chan * AudioBytesProSample);
break;
4 years ago
}
}
#endif
//----------------------------------------------------------------------------
// ring buffer
4 years ago
//----------------------------------------------------------------------------
#define AUDIO_RING_MAX 8 ///< number of audio ring buffers
4 years ago
/**
** Audio ring buffer.
*/
typedef struct _audio_ring_ring_ {
char FlushBuffers; ///< flag: flush buffers
char Passthrough; ///< flag: use pass-through (AC-3, ...)
int16_t PacketSize; ///< packet size
unsigned HwSampleRate; ///< hardware sample rate in Hz
unsigned HwChannels; ///< hardware number of channels
unsigned InSampleRate; ///< input sample rate in Hz
unsigned InChannels; ///< input number of channels
int64_t PTS; ///< pts clock
RingBuffer *RingBuffer; ///< sample ring buffer
4 years ago
} AudioRingRing;
/// ring of audio ring buffers
4 years ago
static AudioRingRing AudioRing[AUDIO_RING_MAX];
static int AudioRingWrite; ///< audio ring write pointer
static int AudioRingRead; ///< audio ring read pointer
static atomic_t AudioRingFilled; ///< how many of the ring is used
static unsigned AudioStartThreshold; ///< start play, if filled
4 years ago
/**
** Add sample-rate, number of channels change to ring.
**
** @param sample_rate sample-rate frequency
** @param channels number of channels
** @param passthrough use /pass-through (AC-3, ...) device
**
** @retval -1 error
** @retval 0 okay
**
** @note this function shouldn't fail. Checks are done during AudoInit.
*/
static int AudioRingAdd(unsigned sample_rate, int channels, int passthrough) {
4 years ago
unsigned u;
// search supported sample-rates
for (u = 0; u < AudioRatesMax; ++u) {
if (AudioRatesTable[u] == sample_rate) {
goto found;
}
if (AudioRatesTable[u] > sample_rate) {
break;
}
4 years ago
}
Error(_("audio: %dHz sample-rate unsupported\n"), sample_rate);
return -1; // unsupported sample-rate
4 years ago
found:
4 years ago
if (!AudioChannelMatrix[u][channels]) {
Error(_("audio: %d channels unsupported\n"), channels);
return -1; // unsupported nr. of channels
4 years ago
}
if (atomic_read(&AudioRingFilled) == AUDIO_RING_MAX) { // no free slot
// FIXME: can wait for ring buffer empty
Error(_("audio: out of ring buffers\n"));
return -1;
4 years ago
}
AudioRingWrite = (AudioRingWrite + 1) % AUDIO_RING_MAX;
AudioRing[AudioRingWrite].FlushBuffers = 0;
AudioRing[AudioRingWrite].Passthrough = passthrough;
AudioRing[AudioRingWrite].PacketSize = 0;
AudioRing[AudioRingWrite].InSampleRate = sample_rate;
AudioRing[AudioRingWrite].InChannels = channels;
AudioRing[AudioRingWrite].HwSampleRate = sample_rate;
AudioRing[AudioRingWrite].HwChannels = AudioChannelMatrix[u][channels];
AudioRing[AudioRingWrite].PTS = AV_NOPTS_VALUE;
4 years ago
RingBufferReset(AudioRing[AudioRingWrite].RingBuffer);
Debug(3, "audio: %d ring buffer prepared\n", atomic_read(&AudioRingFilled) + 1);
4 years ago
atomic_inc(&AudioRingFilled);
#ifdef USE_AUDIO_THREAD
if (AudioThread) {
// tell thread, that there is something todo
AudioRunning = 1;
pthread_cond_signal(&AudioStartCond);
Debug(3, "Start on AudioRingAdd\n");
4 years ago
}
#endif
return 0;
}
/**
** Setup audio ring.
*/
static void AudioRingInit(void) {
4 years ago
int i;
for (i = 0; i < AUDIO_RING_MAX; ++i) {
// ~2s 8ch 16bit
AudioRing[i].RingBuffer = RingBufferNew(AudioRingBufferSize);
4 years ago
}
atomic_set(&AudioRingFilled, 0);
}
/**
** Cleanup audio ring.
*/
static void AudioRingExit(void) {
4 years ago
int i;
for (i = 0; i < AUDIO_RING_MAX; ++i) {
if (AudioRing[i].RingBuffer) {
RingBufferDel(AudioRing[i].RingBuffer);
AudioRing[i].RingBuffer = NULL;
}
AudioRing[i].HwSampleRate = 0; // checked for valid setup
AudioRing[i].InSampleRate = 0;
4 years ago
}
AudioRingRead = 0;
AudioRingWrite = 0;
}
//============================================================================
// A L S A
4 years ago
//============================================================================
//----------------------------------------------------------------------------
// Alsa variables
4 years ago
//----------------------------------------------------------------------------
static snd_pcm_t *AlsaPCMHandle; ///< alsa pcm handle
static char AlsaCanPause; ///< hw supports pause
static int AlsaUseMmap; ///< use mmap
4 years ago
static snd_mixer_t *AlsaMixer; ///< alsa mixer handle
static snd_mixer_elem_t *AlsaMixerElem; ///< alsa pcm mixer element
static int AlsaRatio; ///< internal -> mixer ratio * 1000
4 years ago
//----------------------------------------------------------------------------
// alsa pcm
4 years ago
//----------------------------------------------------------------------------
/**
** Play samples from ringbuffer.
**
** Fill the kernel buffer, as much as possible.
**
** @retval 0 ok
** @retval 1 ring buffer empty
** @retval -1 underrun error
*/
static int AlsaPlayRingbuffer(void) {
4 years ago
int first;
first = 1;
for (;;) { // loop for ring buffer wrap
int avail;
int n;
int err;
int frames;
const void *p;
// how many bytes can be written?
n = snd_pcm_avail_update(AlsaPCMHandle);
if (n < 0) {
if (n == -EAGAIN) {
continue;
}
Warning(_("audio/alsa: avail underrun error? '%s'\n"), snd_strerror(n));
err = snd_pcm_recover(AlsaPCMHandle, n, 0);
if (err >= 0) {
continue;
}
Error(_("audio/alsa: snd_pcm_avail_update(): %s\n"), snd_strerror(n));
return -1;
}
avail = snd_pcm_frames_to_bytes(AlsaPCMHandle, n);
if (avail < 256) { // too much overhead
if (first) {
// happens with broken alsa drivers
if (AudioThread) {
if (!AudioAlsaDriverBroken) {
Error(_("audio/alsa: broken driver %d state '%s'\n"), avail,
snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle)));
}
// try to recover
if (snd_pcm_state(AlsaPCMHandle) == SND_PCM_STATE_PREPARED) {
if ((err = snd_pcm_start(AlsaPCMHandle)) < 0) {
Error(_("audio/alsa: snd_pcm_start(): %s\n"), snd_strerror(err));
}
}
usleep(5 * 1000);
}
}
Debug(4, "audio/alsa: break state '%s'\n", snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle)));
break;
}
n = RingBufferGetReadPointer(AudioRing[AudioRingRead].RingBuffer, &p);
if (!n) { // ring buffer empty
if (first) { // only error on first loop
Debug(4, "audio/alsa: empty buffers %d\n", avail);
// ring buffer empty
// AlsaLowWaterMark = 1;
return 1;
}
return 0;
}
if (n < avail) { // not enough bytes in ring buffer
avail = n;
}
if (!avail) { // full or buffer empty
break;
}
// muting pass-through AC-3, can produce disturbance
if (AudioMute || (AudioSoftVolume && !AudioRing[AudioRingRead].Passthrough)) {
// FIXME: quick&dirty cast
AudioSoftAmplifier((int16_t *)p, avail);
// FIXME: if not all are written, we double amplify them
}
frames = snd_pcm_bytes_to_frames(AlsaPCMHandle, avail);
4 years ago
#ifdef DEBUG
if (avail != snd_pcm_frames_to_bytes(AlsaPCMHandle, frames)) {
Error(_("audio/alsa: bytes lost -> out of sync\n"));
}
4 years ago
#endif
for (;;) {
if (AlsaUseMmap) {
err = snd_pcm_mmap_writei(AlsaPCMHandle, p, frames);
} else {
err = snd_pcm_writei(AlsaPCMHandle, p, frames);
}
// Debug(3, "audio/alsa: wrote %d/%d frames\n", err, frames);
if (err != frames) {
if (err < 0) {
if (err == -EAGAIN) {
continue;
}
/*
if (err == -EBADFD) {
goto again;
}
*/
Warning(_("audio/alsa: writei underrun error? '%s'\n"), snd_strerror(err));
err = snd_pcm_recover(AlsaPCMHandle, err, 0);
if (err >= 0) {
continue;
}
Error(_("audio/alsa: snd_pcm_writei failed: %s\n"), snd_strerror(err));
return -1;
}
// this could happen, if underrun happened
Warning(_("audio/alsa: not all frames written\n"));
avail = snd_pcm_frames_to_bytes(AlsaPCMHandle, err);
}