Go to the documentation of this file.
11 #if __has_include(<soundtouch/SoundTouch.h>)
12 #include <soundtouch/SoundTouch.h>
14 #include <SoundTouch.h>
19 #include <QMutexLocker>
23 #include "libmythbase/mythconfig.h"
34 #if defined(Q_OS_ANDROID)
35 #define DISABLE_AC3_ENCODE
38 #define LOC QString("AOBase: ")
43 #define WPOS (&m_audioBuffer[org_waud])
44 #define RPOS (&m_audioBuffer[m_raud])
45 #define ABUF (m_audioBuffer.data())
46 #define STST soundtouch::SAMPLETYPE
61 default:
return "unknown";
68 m_mainDevice(settings.GetMainDevice()),
69 m_passthruDevice(settings.GetPassthruDevice()),
70 m_source(settings.m_source),
71 m_setInitialVol(settings.m_setInitialVol)
98 "~AudioOutputBase called, but KillAudio has not been called!");
204 aosettings->GetUsers();
215 AVCodecID codec,
int profile)
const
222 case AV_CODEC_ID_AC3:
225 case AV_CODEC_ID_DTS:
229 case FF_PROFILE_DTS_ES:
230 case FF_PROFILE_DTS_96_24:
233 case FF_PROFILE_DTS_HD_HRA:
234 case FF_PROFILE_DTS_HD_MA:
241 case AV_CODEC_ID_EAC3:
244 case AV_CODEC_ID_TRUEHD:
256 ret &= (samplerate == 48000) ||
260 ret &= samplerate >= 44100;
310 VBGENERAL(QString(
"Cancelling time stretch"));
317 VBGENERAL(QString(
"Changing time stretch to %1")
322 else if (willstretch)
329 #if defined(Q_PROCESSOR_ARM) || defined(Q_OS_ANDROID)
407 return m_sourceChannels <= 2 && m_maxChannels > 2;
415 int &samplerate_tmp,
int &channels_tmp)
417 if (codec == AV_CODEC_ID_DTS &&
422 codec_profile = FF_PROFILE_DTS;
425 codec, codec_profile,
426 samplerate_tmp, channels_tmp,
439 switch(codec_profile)
442 case FF_PROFILE_DTS_ES:
443 case FF_PROFILE_DTS_96_24:
446 case FF_PROFILE_DTS_HD_HRA:
447 case FF_PROFILE_DTS_HD_MA:
479 bool lneeds_upmix =
false;
480 bool lneeds_downmix =
false;
487 lconfigured_channels =
489 bool cando_channels =
493 #ifndef DISABLE_AC3_ENCODE
496 lconfigured_channels > 2 && lconfigured_channels <= 6);
498 if (!lenc && !cando_channels)
503 switch (lconfigured_channels)
506 lconfigured_channels = 8;
510 lconfigured_channels = 6;
516 lconfigured_channels = 2;
522 lconfigured_channels = 2;
528 if (lsource_channels <= 6)
529 lconfigured_channels = std::min(lconfigured_channels, 6);
530 lconfigured_channels = std::min(lconfigured_channels,
m_maxChannels);
535 #ifndef DISABLE_AC3_ENCODE
538 lconfigured_channels > 2) ||
543 (settings.
m_codec == AV_CODEC_ID_AC3 ||
544 settings.
m_codec == AV_CODEC_ID_DTS))
553 VBAUDIO(QString(
"Needs upmix from %1 -> %2 channels")
554 .arg(settings.
m_channels).arg(lconfigured_channels));
558 else if (settings.
m_channels > lconfigured_channels)
560 VBAUDIO(QString(
"Needs downmix from %1 -> %2 channels")
561 .arg(settings.
m_channels).arg(lconfigured_channels));
563 lneeds_downmix =
true;
569 bool general_deps =
true;
573 int samplerate_tmp = 0;
574 int channels_tmp = 0;
579 samplerate_tmp, channels_tmp);
599 VBAUDIO(
"Reconfigure(): No change -> exiting");
633 SilentError(QString(
"Aborting Audio Reconfigure. ") +
634 QString(
"Invalid audio parameters ch %1 fmt %2 @ %3Hz")
639 VBAUDIO(QString(
"Original codec was %1, %2, %3 kHz, %4 channels")
640 .arg(avcodec_get_name(
m_codec),
647 Error(QObject::tr(
"Aborting Audio Reconfigure. "
648 "Can't handle audio with more than 8 channels."));
652 VBAUDIO(QString(
"enc(%1), passthru(%2), features (%3) "
653 "configured_channels(%4), %5 channels supported(%6) "
671 VBAUDIO(
"Forcing resample to 48 kHz");
689 VBGENERAL(QString(
"Resampling from %1 kHz to %2 kHz with quality %3")
699 Error(QObject::tr(
"Error creating resampler: %1")
700 .arg(src_strerror(
error)));
713 VBAUDIO(QString(
"Resampler allocating %1").arg(newsize));
725 VBAUDIO(
"Reencoding decoded AC-3/DTS to AC-3");
727 VBAUDIO(QString(
"Creating AC-3 Encoder with sr = %1, ch = %2")
734 Error(QObject::tr(
"AC-3 encoder initialization failed"));
764 VBAUDIO(
"Audio processing enabled");
783 QString(
"Opening audio device '%1' ch %2(%3) sr %4 sf %5 reenc %6")
797 Error(QObject::tr(
"Aborting reconfigure"));
809 VBAUDIO(
"Software volume enabled");
824 VBAUDIO(QString(
"Create %1 quality upmixer done")
841 VBAUDIO(
"Ending Reconfigure()");
872 VBAUDIO(
"Killing AudioOutputDSP");
914 VBAUDIO(QString(
"Pause %1").arg(paused));
984 VBAUDIO(QString(
"SetEffDsp: %1").arg(dsprate));
1068 std::chrono::milliseconds oldaudiotime =
m_audioTime;
1077 * 80 / int64_t(
m_effDsp) / obpf) : 0);
1085 VBAUDIOTS(QString(
"GetAudiotime audt=%1 abtc=%2 mb=%3 sb=%4 tb=%5 "
1086 "sr=%6 obpf=%7 bpf=%8 esf=%9 edsp=%10 sbr=%11")
1090 .arg(soundcard_buffer)
1091 .arg(main_buffer+soundcard_buffer)
1108 int64_t processframes_stretched = 0;
1109 int64_t processframes_unstretched = 0;
1120 processframes_unstretched -=
m_pSoundStretch->numUnprocessedSamples();
1130 timecode + std::chrono::milliseconds(
m_effDsp ? ((frames + processframes_unstretched) * 100000 +
1140 VBAUDIOTS(QString(
"SetAudiotime atc=%1 tc=%2 f=%3 pfu=%4 pfs=%5")
1142 .arg(timecode.count())
1144 .arg(processframes_unstretched)
1145 .arg(processframes_stretched));
1146 #ifdef AUDIOTSTESTING
1194 int len = frames * bpf;
1200 VBERROR(QString(
"Audio buffer overflow, %1 frames lost!")
1201 .arg(frames - (afree / bpf)));
1203 frames = afree / bpf;
1212 VBERROR(QString(
"Error occurred while resetting resampler: %1")
1213 .arg(src_strerror(
error)));
1239 memcpy(
WPOS, buffer, bdiff);
1245 memcpy(
WPOS, buffer + off, num);
1255 int bdFrames = bdiff / bpf;
1256 if (bdFrames <= frames)
1260 off = bdFrames *
sizeof(float);
1287 if (bdFrames < nFrames)
1289 if ((org_waud % bpf) != 0)
1291 VBERROR(QString(
"Upmixing: org_waud = %1 (bpf = %2)")
1296 nFrames -= bdFrames;
1313 std::chrono::milliseconds timecode)
1325 std::chrono::milliseconds timecode,
1335 LOG(VB_GENERAL, LOG_ERR,
"AddData called with audio framework not "
1365 LOG(VB_AUDIO, LOG_INFO,
1366 "Passthrough activated with audio processing. Dropping audio");
1384 VBAUDIOTS(QString(
"AddData frames=%1, bytes=%2, used=%3, free=%4, "
1385 "timecode=%5 needsupmix=%6")
1386 .arg(frames).arg(len).arg(used).arg(afree).arg(timecode.count())
1408 if (sampleSize <= 0)
1411 VBERROR(
"Sample size is <= 0, AddData returning false");
1416 len =
sizeof(
m_srcInBuf[0]) / sampleSize * len;
1424 len = lround(ceil(
static_cast<double>(len) *
m_srcData.src_ratio));
1441 VBERROR(
"Buffer is full, AddData returning false");
1445 int frames_remaining = frames;
1446 int frames_final = 0;
1450 while(frames_remaining > 0)
1452 void *buffer = (
char *)in_buffer + offset;
1453 frames = frames_remaining;
1458 if (frames > maxframes)
1468 frames_remaining -= frames;
1476 VBERROR(
"Error occurred while downmixing");
1486 VBERROR(QString(
"Error occurred while resampling audio: %1")
1487 .arg(src_strerror(
error)));
1500 if ((len =
CopyWithUpmix((
char *)buffer, frames, org_waud)) <= 0)
1506 frames_final += frames;
1509 if ((len % bpf) != 0 && bdiff < len)
1511 VBERROR(QString(
"AddData: Corruption likely: len = %1 (bpf = %2)")
1515 if ((bdiff % bpf) != 0 && bdiff < len)
1517 VBERROR(QString(
"AddData: Corruption likely: bdiff = %1 (bpf = %2)")
1526 int bdFrames = bdiff / bpf;
1537 if (nFrames > frames)
1540 len = nFrames * bpf;
1542 if (nFrames > bdFrames)
1590 if (bdiff <= to_get)
1651 auto *fragment =
new(std::align_val_t(16)) uchar[
m_fragmentSize];
1665 VBAUDIO(
"OutputAudioLoop: audio paused");
1680 VBAUDIO(
"OutputAudioLoop: Play Event");
1694 VBAUDIOTS(QString(
"audio waiting for buffer to fill: "
1703 #ifdef AUDIOTSTESTING
1721 #ifdef AUDIOTSTESTING
1730 VBAUDIO(
"OutputAudioLoop: Stop Event");
1743 volatile uint *local_raud)
1746 #define LRPOS (&m_audioBuffer[*local_raud])
1750 int frag_size = size;
1751 int written_size = size;
1753 if (local_raud ==
nullptr)
1756 if (!full_buffer && (size > avail_size))
1759 frag_size = avail_size;
1760 written_size = frag_size;
1763 if (!avail_size || (frag_size > avail_size))
1776 if (fromFloats && obytes !=
sizeof(
float))
1777 frag_size *=
sizeof(float) / obytes;
1781 if (bdiff <= frag_size)
1790 memcpy(buffer,
LRPOS, bdiff);
1806 memcpy(buffer + off,
LRPOS, frag_size);
1810 *local_raud += frag_size;
1820 buffer, written_size);
1823 return written_size;
1848 VBAUDIO(QString(
"kickoffOutputAudioLoop: pid = %1").arg(getpid()));
1850 VBAUDIO(
"kickoffOutputAudioLoop exiting");
1856 VBERROR(
"AudioOutputBase should not be getting asked to readOutputData()");
QString GetError(void) const
AudioOutputSettings * m_outputSettingsRaw
void Reset(void) override
Reset the audiobuffer, timecode and mythmusic visualisation.
soundtouch::SoundTouch * m_pSoundStretch
bool IsSupportedChannels(int channels)
void SetTimecode(std::chrono::milliseconds timecode) override
Set the timecode of the samples most recently added to the audiobuffer.
bool IsSupportedRate(int rate)
void Pause(bool paused) override
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
volatile uint m_raud
Audio circular buffer.
bool AddData(void *buffer, int len, std::chrono::milliseconds timecode, int frames) override
Add data to the audiobuffer and perform any required processing.
int CheckFreeSpace(int &frames)
Check that there's enough space in the audiobuffer to write the provided number of frames.
int audiolen() const
Get the number of bytes in the audiobuffer.
bool Init(AVCodecID codec_id, int bitrate, int samplerate, int channels)
AsyncLooseLock m_resetActive
int GetFrames(void *ptr, int maxlen)
QMutex m_avsyncLock
must hold avsync_lock to read or write 'audiotime' and 'audiotime_updated'
bool CanPassthrough(int samplerate, int channels, AVCodecID codec, int profile) const override
Test if we can output digital audio and if sample rate is supported.
void Error(const QString &msg)
virtual void CloseDevice(void)=0
bool m_configureSucceeded
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
uint m_memoryCorruptionTest2
bool SetupPassthrough(AVCodecID codec, int codec_profile, int &samplerate_tmp, int &channels_tmp)
static int toFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert integer samples to floats.
int audiofree() const
Get the free space in the audiobuffer in bytes.
void Reset()
Reset the internal encoder buffer.
void KillAudio(void)
Kill the output thread and cleanup.
static int FormatToBits(AudioFormat format)
static void usleep(std::chrono::microseconds time)
int readOutputData(unsigned char *read_buffer, size_t max_length) override
std::chrono::milliseconds GetAudiotime(void) override
Calculate the timecode of the samples that are about to become audible.
float GetStretchFactor(void) const override
Get the timetretch factor.
static int SampleSize(AudioFormat format)
static constexpr bool IS_VALID_UPMIX_CHANNEL(int ch)
bool IsUpmixing(void) override
Source is currently being upmixed.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
void SetEffDsp(int dsprate) override
Set the effective DSP rate.
std::chrono::milliseconds m_audbufTimecode
timecode of audio most recently placed into buffer
static const char * quality_string(int q)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
void error(const QString &e)
uint putFrames(void *buffer, uint numFrames, uint numChannels)
void SetAudiotime(int frames, std::chrono::milliseconds timecode)
Set the timecode of the top of the ringbuffer Exclude all other processing elements as they dont vary...
uint numUnprocessedFrames() const
virtual int GetBufferedOnSoundcard(void) const =0
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
int m_kAudioSRCOutputSize
AudioOutputSettings * GetCleaned(bool newcopy=false)
Returns capabilities supported by the audio device amended to take into account the digital audio opt...
std::chrono::milliseconds m_lengthLastData
static void MonoToStereo(void *dst, const void *src, int samples)
Convert a mono stream to stereo by copying and interleaving samples.
void Drain(void) override
Block until all available frames have been written to the device.
std::array< float, kAudioSRCInputSize > m_srcInBuf
int GetMaxHDRate() const
return the highest iec958 rate supported.
AudioOutputBase(const AudioSettings &settings)
int BestSupportedChannels()
AudioOutputSettings * GetOutputSettingsCleaned(bool digital=true) override
Returns capabilities supported by the audio device amended to take into account the digital audio opt...
bool SetMaxHDRate(int rate)
Set the maximum HD rate.
virtual void StopOutputThread(void)
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
virtual AudioOutputSettings * GetOutputSettings(bool)
~AudioOutputBase() override
Destructor.
uint m_memoryCorruptionTest1
int GetAudioData(uchar *buffer, int buf_size, bool full_buffer, volatile uint *local_raud=nullptr)
Copy frames from the audiobuffer into the buffer provided.
virtual MuteState GetMuteState(void) const
uint frameLatency() const
std::chrono::seconds m_currentSeconds
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
int Encode(void *input, int len, AudioFormat format)
AudioOutputSettings * m_outputSettingsDigitalRaw
AudioOutputSettings * m_custom
custom contains a pointer to the audio device capabilities if defined, AudioOutput will not try to au...
bool canFeature(DigitalFeature arg) const
return DigitalFeature mask.
static const uint kAudioRingBufferSize
Audio Buffer Size – should be divisible by 32,24,16,12,10,8,6,4,2..
void run() override
Main routine for the output thread.
int m_sourceBytesPerFrame
void WriteFrame(unsigned char *data, int size)
Encode data through created muxer unsigned char data: pointer to data to encode int size: size of dat...
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool SWVolume(void) const
void OutputAudioLoop(void)
Run in the output thread, write frames to the output device as they become available and there's spac...
int GetNumSetting(const QString &key, int defaultval=0)
AudioFormat m_outputFormat
AudioOutputDigitalEncoder * m_encoder
int GetSWVolume(void) override
Get the volume for software volume control.
bool GetBoolSetting(const QString &key, bool defaultval=false)
int audioready() const
Get the scaled number of bytes in the audiobuffer, i.e.
int CopyWithUpmix(char *buffer, int frames, uint &org_waud)
Copy frames into the audiobuffer, upmixing en route if necessary.
SPDIFEncoder * m_spdifEnc
void SetSWVolume(int new_volume, bool save) override
Set the volume for software volume control.
bool CanUpmix(void) override
Upmixing of the current source is available if requested.
virtual void Status(void)
Report status via an OutputEvent.
virtual void WriteAudio(unsigned char *aubuf, int size)=0
int m_outputBytesPerFrame
static int DownmixFrames(int channels_in, int channels_out, float *dst, const float *src, int frames)
void SilentError(const QString &msg)
uint m_memoryCorruptionTest0
bool IsSupportedFormat(AudioFormat format)
static QString GetPassthroughParams(int codec, int codec_profile, int &samplerate, int &channels, bool canDTSHDMA)
Setup samplerate and number of channels for passthrough.
static void AdjustVolume(void *buffer, int len, int volume, bool music, bool upmix)
Adjust the volume of samples.
void dispatchVisual(uchar *b, unsigned long b_len, std::chrono::milliseconds timecode, int chan, int prec)
bool AddFrames(void *buffer, int frames, std::chrono::milliseconds timecode) override
Add frames to the audiobuffer and perform any required processing.
int NearestSupportedRate(int rate)
void SetStretchFactor(float factor) override
Set the timestretch factor.
unsigned char * GetProcessedBuffer()
void InitSettings(const AudioSettings &settings)
virtual bool OpenDevice(void)=0
void PauseUntilBuffered(void) override
This is a wrapper around QThread that does several additional things.
virtual bool StartOutputThread(void)
std::array< uchar, kAudioRingBufferSize > m_audioBuffer
main audio buffer
QMutex m_audioBufLock
Writes to the audiobuffer, reconfigures and audiobuffer resets can only take place while holding this...
static QString FeaturesToString(DigitalFeature arg)
Display in human readable form the digital features supported by the output device.
AudioOutputSource m_source
AudioOutputSettings * GetOutputSettingsUsers(bool digital=false) override
Returns capabilities supported by the audio device amended to take into account the digital audio opt...
void SetChannels(int new_channels)
static const char * FormatToString(AudioFormat format)
void SetStretchFactorLocked(float factor)
Set the timestretch factor.
static constexpr int UPMIX_CHANNEL_MASK
AudioFormat BestSupportedFormat()
static int fromFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert float samples to integers.
void SaveSetting(const QString &key, int newValue)
AudioOutputSettings * m_outputSettings
void GetBufferStatus(uint &fill, uint &total) override
Fill in the number of bytes in the audiobuffer and the total size of the audiobuffer.
std::chrono::milliseconds GetAudioBufferedTime(void) override
Get the difference in timecode between the samples that are about to become audible and the samples m...
uint receiveFrames(void *buffer, uint maxFrames)
static void MuteChannel(int obits, int channels, int ch, void *buffer, int bytes)
Mute individual channels through mono->stereo duplication.
std::chrono::milliseconds m_audioTime
timecode of audio leaving the soundcard (same units as timecodes)
static const uint kAudioSRCInputSize
uint m_memoryCorruptionTest3
bool ToggleUpmix(void) override
Toggle between stereo and upmixed 5.1 if the source material is stereo.
void SetSourceBitrate(int rate) override
Set the bitrate of the source material, reported in periodic OutputEvents.
QString GetSetting(const QString &key, const QString &defaultval="")
std::enable_if_t< std::is_floating_point_v< T >, std::chrono::milliseconds > millisecondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
AudioOutputSettings * m_outputSettingsDigital
AudioOutputSettings * OutputSettings(bool digital=true)