32#include <alsa/asoundlib.h>
35#include <libavcodec/avcodec.h>
36#include <libavfilter/avfilter.h>
37#include <libavfilter/buffersink.h>
38#include <libavfilter/buffersrc.h>
39#include <libavutil/channel_layout.h>
40#include <libavutil/opt.h>
63 m_pConfig(m_pDevice->Config()),
64 m_pEventReceiver(device),
65 m_downmix(m_pConfig->ConfigAudioDownmix),
66 m_softVolume(m_pConfig->ConfigAudioSoftvol),
67 m_passthrough(m_pConfig->ConfigAudioPassthroughState ? m_pConfig->ConfigAudioPassthroughMask : 0),
68 m_pPCMDevice(m_pConfig->ConfigAudioPCMDevice),
69 m_pPassthroughDevice(m_pConfig->ConfigAudioPassthroughDevice),
70 m_appendAES(m_pConfig->ConfigAudioAutoAES),
71 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
106 for (i = 0; i < size; i += 5) {
117 for (i = 0; i < size; i += 6) {
132 for (i = 0; i < size; i += 8) {
170 for (i = 0; i < n; ++i) {
189 factor = ((INT16_MAX / 8) * 1000U) / (uint32_t) sqrt(avg);
201 LOGDEBUG2(
L_SOUND,
"audio: %s: avg %8d, fac=%6.3f, norm=%6.3f", __FUNCTION__,
220 }
else if (t > INT16_MAX) {
252 factor = (INT16_MAX * 1000) / maxSample;
265 LOGDEBUG2(
L_SOUND,
"audio: %s: max %5d, fac=%6.3f, com=%6.3f", __FUNCTION__, maxSample,
275 }
else if (t > INT16_MAX) {
296 memset(samples, 0, count);
306 }
else if (t > INT16_MAX) {
328 for (i = 0; i < 18; i++) {
397 const AVFilter *abuffer;
398 AVFilterContext *pfilterCtx[3];
400 const AVFilter *aformat;
401 const AVFilter *abuffersink;
402 char channelLayout[64];
403 char optionsStr[1024];
404 int err, i, numFilter = 0;
411 err =
AlsaSetup(audioCtx->ch_layout.nb_channels, audioCtx->sample_rate, 0);
418#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
419 avfilter_register_all();
423 LOGERROR(
"audio: %s: Unable to create filter graph.", __FUNCTION__);
428 if (!(abuffer = avfilter_get_by_name(
"abuffer"))) {
429 LOGWARNING(
"audio: %s: Could not find the abuffer filter.", __FUNCTION__);
434 LOGWARNING(
"audio: %s: Could not allocate the m_pBuffersrcCtx instance.", __FUNCTION__);
439 av_channel_layout_describe(&audioCtx->ch_layout, channelLayout,
sizeof(channelLayout));
441 LOGDEBUG2(
L_SOUND,
"audio: %s: IN channelLayout %s sample_fmt %s sample_rate %d channels %d", __FUNCTION__,
442 channelLayout, av_get_sample_fmt_name(audioCtx->sample_fmt), audioCtx->sample_rate, audioCtx->ch_layout.nb_channels);
444 av_opt_set (
m_pBuffersrcCtx,
"channel_layout", channelLayout, AV_OPT_SEARCH_CHILDREN);
445 av_opt_set (
m_pBuffersrcCtx,
"sample_fmt", av_get_sample_fmt_name(audioCtx->sample_fmt), AV_OPT_SEARCH_CHILDREN);
446 av_opt_set_q (
m_pBuffersrcCtx,
"time_base", (AVRational){ 1, audioCtx->sample_rate }, AV_OPT_SEARCH_CHILDREN);
447 av_opt_set_int(
m_pBuffersrcCtx,
"sample_rate", audioCtx->sample_rate, AV_OPT_SEARCH_CHILDREN);
452 LOGWARNING(
"audio: %s: Could not initialize the abuffer filter.", __FUNCTION__);
459 if (!(eq = avfilter_get_by_name(
"superequalizer"))) {
460 LOGWARNING(
"audio: %s: Could not find the superequalizer filter.", __FUNCTION__);
464 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(
m_pFilterGraph, eq,
"superequalizer"))) {
465 LOGWARNING(
"audio: %s: Could not allocate the superequalizer instance.", __FUNCTION__);
469 snprintf(optionsStr,
sizeof(optionsStr),
"1b=%.2f:2b=%.2f:3b=%.2f:4b=%.2f:5b=%.2f"
470 ":6b=%.2f:7b=%.2f:8b=%.2f:9b=%.2f:10b=%.2f:11b=%.2f:12b=%.2f:13b=%.2f:14b=%.2f:"
476 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
477 LOGWARNING(
"audio: %s: Could not initialize the superequalizer filter.", __FUNCTION__);
485 AVChannelLayout channel_layout;
487 av_channel_layout_describe(&channel_layout, channelLayout,
sizeof(channelLayout));
488 av_channel_layout_uninit(&channel_layout);
490 LOGDEBUG2(
L_SOUND,
"audio: %s: OUT m_downmix %d m_hwNumChannels %d m_hwSampleRate %d channelLayout %s bytes_per_sample %d",
492 if (!(aformat = avfilter_get_by_name(
"aformat"))) {
493 LOGWARNING(
"audio: %s: Could not find the aformat filter.", __FUNCTION__);
497 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(
m_pFilterGraph, aformat,
"aformat"))) {
498 LOGWARNING(
"audio: %s: Could not allocate the aformat instance.", __FUNCTION__);
502 snprintf(optionsStr,
sizeof(optionsStr),
503 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
504 av_get_sample_fmt_name(AV_SAMPLE_FMT_S16),
m_hwSampleRate, channelLayout);
505 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
506 LOGWARNING(
"audio: %s: Could not initialize the aformat filter.", __FUNCTION__);
513 if (!(abuffersink = avfilter_get_by_name(
"abuffersink"))) {
514 LOGWARNING(
"audio: %s: Could not find the abuffersink filter.", __FUNCTION__);
518 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(
m_pFilterGraph, abuffersink,
"sink"))) {
519 LOGWARNING(
"audio: %s: Could not allocate the abuffersink instance.", __FUNCTION__);
523 if (avfilter_init_str(pfilterCtx[numFilter], NULL) < 0) {
524 LOGWARNING(
"audio: %s: Could not initialize the abuffersink instance.", __FUNCTION__);
531 for (i = 0; i < numFilter; i++) {
535 err = avfilter_link(pfilterCtx[i - 1], 0, pfilterCtx[i], 0);
539 LOGWARNING(
"audio: %s: Error connecting audio filters", __FUNCTION__);
546 LOGWARNING(
"audio: %s: Error configuring the audio filter graph", __FUNCTION__);
572 std::lock_guard<std::mutex> lock(
m_mutex);
584 LOGDEBUG2(
L_AV_SYNC,
"audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
609 int byteCount = frame->nb_samples * frame->ch_layout.nb_channels *
m_bytesPerSample;
610 buffer = (uint16_t *)frame->data[0];
620 Enqueue((uint16_t *)buffer, byteCount, frame);
622 av_frame_free(&frame);
634 std::lock_guard<std::mutex> lock(
m_mutex);
643 count = std::max(0, count - oneFrameBytes);
652 if (bytesWritten != count)
653 LOGERROR(
"audio: %s: can't place %d samples in ring buffer", __FUNCTION__, count);
660 LOGDEBUG2(
L_AV_SYNC,
"audio: %s: discontinuity detected in audio PTS %s -> %s%s", __FUNCTION__,
690 err =
AlsaSetup(channels, samplerate, passthrough);
692 LOGERROR(
"audio: %s: failed!", __FUNCTION__);
708 AVFrame *outframe =
nullptr;
709 outframe = av_frame_alloc();
711 LOGERROR(
"audio: %s: Error allocating frame", __FUNCTION__);
717 if (err == AVERROR(EAGAIN)) {
719 av_frame_free(&outframe);
720 }
else if (err == AVERROR_EOF) {
721 LOGERROR(
"audio: %s: Error filtering AVERROR_EOF", __FUNCTION__);
722 av_frame_free(&outframe);
723 }
else if (err < 0) {
724 LOGERROR(
"audio: %s: Error filtering the data", __FUNCTION__);
725 av_frame_free(&outframe);
774 AVFrame *outframe = NULL;
781 av_frame_unref(inframe);
789 av_strerror(err, errbuf,
sizeof(errbuf));
790 LOGERROR(
"audio: %s: Error submitting the frame to the filter fmt %s channels %d %s", __FUNCTION__,
791 av_get_sample_fmt_name(ctx->sample_fmt), ctx->ch_layout.nb_channels, errbuf);
792 av_frame_unref(inframe);
818 std::lock_guard<std::mutex> lock(
m_mutex);
841 std::lock_guard<std::mutex> lock(
m_mutex);
869 std::lock_guard<std::mutex> lock(
m_mutex);
890 std::lock_guard<std::mutex> lock(
m_mutex);
895 snd_pcm_sframes_t delayFrames;
899 if (delayFrames < 0) {
933 }
else if (volume > 1000) {
1058 LOGERROR(
"audio: %s: Cannot recover: %s", __FUNCTION__, snd_strerror(error));
1069 snd_pcm_state_t state;
1074 if (state != SND_PCM_STATE_OPEN) {
1076 LOGERROR(
"audio: %s: snd_pcm_drop(): %s", __FUNCTION__, snd_strerror(err));
1079 LOGERROR(
"audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1081 LOGDEBUG2(
L_SOUND,
"audio: %s: pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1121 }
else if (ret == 0) {
1122 LOGERROR(
"audio: %s: snd_pcm_wait() timeout", __FUNCTION__);
1126 std::lock_guard<std::mutex> lock2(
m_mutex);
1130 if (freeAlsaBufferFrameCount < 0) {
1131 if (freeAlsaBufferFrameCount == -EAGAIN)
1142 int bytesToWrite = std::min(snd_pcm_frames_to_bytes(
m_pAlsaPCMHandle, freeAlsaBufferFrameCount), inputBufferFillLevelBytes);
1144 if (bytesToWrite == 0)
1154 int framesToWrite = snd_pcm_bytes_to_frames(
m_pAlsaPCMHandle, bytesToWrite);
1158 framesWritten = snd_pcm_mmap_writei(
m_pAlsaPCMHandle, data, framesToWrite);
1165 if (framesWritten != framesToWrite) {
1166 if (framesWritten < 0) {
1167 if (framesWritten == -EAGAIN)
1170 LOGWARNING(
"audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1173 LOGERROR(
"audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1177 LOGWARNING(
"audio: %s: not all frames written", __FUNCTION__);
1201 LOGDEBUG2(
L_SOUND,
"audio: %s: try opening %sdevice '%s'", __FUNCTION__, passthrough ?
"pass-through " :
"", device);
1204 if (!(strchr(device,
':'))) {
1205 sprintf(tmp,
"%s:AES0=%d,AES1=%d,AES2=0,AES3=%d",
1207 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1208 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1209 IEC958_AES3_CON_FS_48000);
1211 sprintf(tmp,
"%s,AES0=%d,AES1=%d,AES2=0,AES3=%d",
1213 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1214 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1215 IEC958_AES3_CON_FS_48000);
1217 LOGDEBUG2(
L_SOUND,
"audio: %s: auto append AES: %s -> %s", __FUNCTION__, device, tmp);
1219 sprintf(tmp,
"%s", device);
1224 SND_PCM_NONBLOCK)) < 0) {
1226 LOGWARNING(
"audio: %s: could not open device '%s' error: %s", __FUNCTION__, device, snd_strerror(err));
1230 LOGDEBUG2(
L_SOUND,
"audio: %s: opened %sdevice '%s'", __FUNCTION__, passthrough ?
"pass-through " :
"", device);
1232 return (
char *)device;
1252 err = snd_device_name_hint(-1, devname, (
void ***)&hints);
1254 LOGWARNING(
"audio: %s: Cannot get device names for %s!", __FUNCTION__, hint);
1259 while (*n != NULL) {
1260 name = snd_device_name_get_hint(*n,
"NAME");
1262 if (name && strstr(name, hint)) {
1264 snd_device_name_free_hint((
void **)hints);
1274 snd_device_name_free_hint((
void **)hints);
1283 char *device = NULL;
1284 bool freeDevice =
false;
1305 freeDevice = (device != NULL);
1310 LOGDEBUG2(
L_SOUND,
"audio: %s: Try default:CARD=hdmisound devices...", __FUNCTION__);
1312 freeDevice = (device != NULL);
1319 freeDevice = (device != NULL);
1335 LOGFATAL(
"audio: %s: could not open any device, abort!", __FUNCTION__);
1337 if (!strcmp(device,
"null"))
1338 LOGWARNING(
"audio: %s: using %sdevice '%s'", __FUNCTION__,
1341 LOGINFO(
"audio: using %sdevice '%s'",
1349 LOGERROR(
"audio: %s: can't set block mode: %s", __FUNCTION__, snd_strerror(err));
1363 const char *channel;
1364 snd_mixer_t *alsaMixer;
1365 snd_mixer_elem_t *alsaMixerElem;
1366 long alsaMixerElemMin;
1367 long alsaMixerElemMax;
1370 if (!(device = getenv(
"ALSA_MIXER"))) {
1375 if (!(channel = getenv(
"ALSA_MIXER_CHANNEL"))) {
1379 LOGDEBUG2(
L_SOUND,
"audio: %s: mixer %s - %s open", __FUNCTION__, device, channel);
1380 snd_mixer_open(&alsaMixer, 0);
1381 if (alsaMixer && snd_mixer_attach(alsaMixer, device) >= 0
1382 && snd_mixer_selem_register(alsaMixer, NULL, NULL) >= 0
1383 && snd_mixer_load(alsaMixer) >= 0) {
1385 const char *
const alsaMixerElem_name = channel;
1387 alsaMixerElem = snd_mixer_first_elem(alsaMixer);
1388 while (alsaMixerElem) {
1391 name = snd_mixer_selem_get_name(alsaMixerElem);
1392 if (!strcasecmp(name, alsaMixerElem_name)) {
1393 snd_mixer_selem_get_playback_volume_range(alsaMixerElem, &alsaMixerElemMin, &alsaMixerElemMax);
1394 m_alsaRatio = 1000 * (alsaMixerElemMax - alsaMixerElemMin);
1395 LOGDEBUG2(
L_SOUND,
"audio: %s: %s mixer found %ld - %ld ratio %d", __FUNCTION__, channel, alsaMixerElemMin, alsaMixerElemMax,
m_alsaRatio);
1399 alsaMixerElem = snd_mixer_elem_next(alsaMixerElem);
1405 LOGERROR(
"audio: %s: can't open mixer '%s'", __FUNCTION__, device);
1419 snd_mixer_selem_set_playback_volume(
m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_LEFT, v);
1420 snd_mixer_selem_set_playback_volume(
m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_RIGHT, v);
1439 snd_pcm_hw_params_t *hwparams;
1441 unsigned bufferTimeUs = 100'000;
1452 snd_pcm_hw_params_alloca(&hwparams);
1454 LOGERROR(
"audio: %s: Read HW config failed! %s", __FUNCTION__, snd_strerror(err));
1458 if (!snd_pcm_hw_params_test_access(
m_pAlsaPCMHandle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
1464 LOGERROR(
"audio: %s: SampleRate %d not supported! %s", __FUNCTION__, sample_rate, snd_strerror(err));
1479 if ((err = snd_pcm_hw_params_set_buffer_time_near(
m_pAlsaPCMHandle, hwparams, &bufferTimeUs, NULL)) < 0) {
1480 LOGWARNING(
"audio: %s: bufferTime %d not supported! %s", __FUNCTION__, bufferTimeUs, snd_strerror(err));
1495 LOGERROR(
"audio: %s: set params error: %s\n"
1496 " Channels %d SampleRate %d\n"
1497 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1499 " AlsaBufferTime %dms pcm state: %s",
1504 bufferTimeUs / 1000, snd_pcm_state_name(state));
1508 LOGINFO(
"audio: alsa set up:\n"
1509 " Channels %d SampleRate %d%s\n"
1510 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1512 " AlsaBufferTime %dms",
1513 channels, sample_rate, passthrough ?
" -> passthrough" :
"",
1515 snd_pcm_format_name(SND_PCM_FORMAT_S16),
1517 bufferTimeUs / 1000);
1528 const char *file, __attribute__ ((unused))
1529 int line, __attribute__ ((unused))
1530 const char *function, __attribute__ ((unused))
1531 int err, __attribute__ ((unused))
1532 const char *fmt, ...)
1595 auto now = std::chrono::steady_clock::now();
1604 LOGDEBUG2(
L_SOUND,
"audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
1617 if (hardwareBufferFillLevelFrames < 0)
1618 LOGWARNING(
"audio: %s: snd_pcm_avail() failes: %s", __FUNCTION__, snd_strerror(hardwareBufferFillLevelFrames));
static void ReorderAudioFrame(uint16_t *buf, int size, int channels)
Reorder audio frame.
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,...)
Empty log callback.
Audio and alsa module header file.
#define NORMALIZE_MAX_INDEX
number of average values
#define AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
virtual void OnEventReceived(const Event &)=0
void ResetFramesCounters()
Resets the received and written frames counters.
void UpdateAvgBufferFillLevel(int)
Updates the buffer fill level average.
void ReceivedFrames(int count)
void WroteFrames(int count)
double GetBufferFillLevelFramesAvg()
void Reset()
Resets the filter state.
void Reset()
Resets the internal state (integral sum and error history)
double Update(double, double)
Calculates the new output value.
void SetTargetValue(double value)
void LazyInit(void)
Initialize audio output module.
cSoftHdAudio(cSoftHdDevice *)
cSoftHdAudio constructor
char * OpenAlsaDevice(const char *, int)
Open alsa device.
int m_alsaBufferSizeFrames
alsa buffer size in frames
char * FindAlsaDevice(const char *, const char *, int)
Find alsa device giving some search hints.
bool m_appendAES
flag ato utomatic append AES
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
double FramesToMsDouble(int frames)
int AlsaSetup(int channels, int sample_rate, int passthrough)
Setup alsa audio for requested format.
int Setup(AVCodecContext *, int, int, int)
Setup alsa.
void Enqueue(uint16_t *, int, AVFrame *)
Send audio data to ringbuffer.
int m_compressionMaxFactor
max. compression factor
const char * m_pPCMDevice
PCM device name.
int m_volume
current volume (0 .. 1000)
cAudioThread * m_pAudioThread
pointer to audio thread
void SetStereoDescent(int)
Set stereo loudness descent.
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
std::mutex m_pauseMutex
mutex for a safe thread pausing
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
AVFilterContext * m_pBuffersinkCtx
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
void SetVolume(int)
Set mixer volume (0-1000)
AVFilterContext * m_pBuffersrcCtx
void HandleError(int)
handle error
AVFilterGraph * m_pFilterGraph
int m_passthrough
passthrough mask
const int m_bytesPerSample
number of bytes per sample
const char * m_pMixerChannel
mixer channel name
unsigned int m_hwSampleRate
hardware sample rate in Hz
int64_t PtsToMs(int64_t pts)
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
IEventReceiver * m_pEventReceiver
pointer to event receiver
void ProcessEvents(void)
Process queued events and forward to event receiver.
int MsToFrames(int milliseconds)
std::vector< Event > m_eventQueue
event queue for incoming events
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
AVRational * m_pTimebase
pointer to AVCodecContext pkts_timebase
bool m_compression
flag to use compress volume
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
bool m_normalize
flag to use volume normalize
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
void ClockDriftCompensation(void)
Calculate clock drift compensation.
int m_filterChanged
filter has changed
void AlsaExit(void)
Cleanup alsa audio output module.
void SetCompression(bool, int)
Set volume compression parameters.
snd_mixer_elem_t * m_pAlsaMixerElem
alsa mixer element
void AlsaInit(void)
Initialize alsa audio output module.
void Compress(uint16_t *, int)
Compress audio.
int64_t m_inputPts
pts clock (last pts in ringbuffer)
int m_normalizeFactor
current normalize factor
void AlsaSetVolume(int)
Set alsa mixer volume (0-1000)
int64_t GetOutputPtsMsInternal(void)
cSoftHdConfig * m_pConfig
pointer to config
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
void Exit(void)
Cleanup audio output module.
int m_amplifier
software volume amplify factor
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
void SetPaused(bool)
Set audio playback paused state.
void AlsaInitMixer(void)
Initialize alsa mixer.
const char * m_pPassthroughDevice
passthrough device name
void SetPassthrough(int)
Set audio passthrough mask.
int m_normalizeMaxFactor
max. normalize factor
bool m_alsaUseMmap
use mmap
void Normalize(uint16_t *, int)
Normalize audio.
int m_compressionFactor
current compression factor
int64_t MsToPts(int64_t ptsMs)
const int m_normalizeMinFactor
min. normalize factor
const int m_normalizeSamples
number of normalize samples
int m_filterReady
filter is ready
void SetEq(int[18], int)
Set equalizer bands.
int m_normalizeReady
index normalize counter
const char * m_pMixerDevice
mixer device name (not used)
uint32_t m_normalizeAverage[NORMALIZE_MAX_INDEX]
average of n last normalize sample blocks
bool CyclicCall(void)
Cyclic audio playback call.
void AlsaInitPCMDevice(void)
Search for an alsa pcm device and open it.
unsigned int m_hwNumChannels
number of hardware channels
bool m_initialized
class initialized
int FramesToMs(int frames)
int m_stereoDescent
volume descent for stereo
std::mutex m_mutex
mutex for thread safety
int m_alsaRatio
internal -> mixer ratio * 1000
int m_packetCounter
packet counter for logging
int GetFreeBytes(void)
Get free bytes in audio ringbuffer.
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
void SoftAmplify(int16_t *, int)
Software amplifier.
void FlushAlsaBuffers(void)
Flush alsa buffers.
void SetNormalize(bool, int)
Set normalize volume parameters.
snd_pcm_t * m_pAlsaPCMHandle
alsa pcm handle
int m_downmix
set stereo downmix
int m_useEqualizer
flag to use equalizer
float m_equalizerBand[18]
equalizer band
snd_mixer_t * m_pAlsaMixer
alsa mixer handle
int m_normalizeIndex
index into normalize average table
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
int m_normalizeCounter
normalize sample counter
std::atomic< bool > m_paused
audio is paused
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
int InitFilter(AVCodecContext *)
Init filter.
void FlushBuffers(void)
Flush audio buffers.
bool m_softVolume
flag to use soft volume
bool ConfigAudioNormalize
config use normalize volume
int ConfigAudioStereoDescent
config reduce stereo loudness
bool ConfigAudioCompression
config use volume compression
int ConfigAudioEqBand[18]
config equalizer filter bands
int ConfigAudioMaxCompression
config max volume compression
int ConfigAudioEq
config equalizer filter
int ConfigAudioMaxNormalize
config max normalize factor
size_t UsedBytes(void)
Get used bytes in ring buffer.
size_t FreeBytes(void)
Get free bytes in ring buffer.
size_t ReadAdvance(size_t)
Advance read pointer in ring buffer.
size_t GetReadPointer(const void **)
Get read pointer and used bytes at this position of ring buffer.
size_t Write(const void *, size_t)
Write to a ring buffer.
void Reset(void)
Reset ring buffer pointers.
SoftHdDevice config header file.
State machine and event header file.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent > Event
Logger class header file.
#define LOGFATAL
Logger macros.
Misc function header file.
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Workaround for av_err2str() not working with C++.
Proportinal, Integral, Derivative Controller.
Ringbuffer class header file.
Device class header file.
Thread classes header file.