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!");
199 aosettings->GetUsers();
210 AVCodecID codec,
int profile)
const
217 case AV_CODEC_ID_AC3:
220 case AV_CODEC_ID_DTS:
224 case FF_PROFILE_DTS_ES:
225 case FF_PROFILE_DTS_96_24:
228 case FF_PROFILE_DTS_HD_HRA:
229 case FF_PROFILE_DTS_HD_MA:
236 case AV_CODEC_ID_EAC3:
239 case AV_CODEC_ID_TRUEHD:
251 ret &= (samplerate == 48000) ||
255 ret &= samplerate >= 44100;
305 VBGENERAL(QString(
"Cancelling time stretch"));
312 VBGENERAL(QString(
"Changing time stretch to %1")
317 else if (willstretch)
324 #if defined(Q_PROCESSOR_ARM) || defined(Q_OS_ANDROID)
402 return m_sourceChannels <= 2 && m_maxChannels > 2;
410 int &samplerate_tmp,
int &channels_tmp)
412 if (codec == AV_CODEC_ID_DTS &&
417 codec_profile = FF_PROFILE_DTS;
420 codec, codec_profile,
421 samplerate_tmp, channels_tmp,
434 switch(codec_profile)
437 case FF_PROFILE_DTS_ES:
438 case FF_PROFILE_DTS_96_24:
441 case FF_PROFILE_DTS_HD_HRA:
442 case FF_PROFILE_DTS_HD_MA:
474 bool lneeds_upmix =
false;
475 bool lneeds_downmix =
false;
482 lconfigured_channels =
484 bool cando_channels =
488 #ifndef DISABLE_AC3_ENCODE
491 lconfigured_channels > 2 && lconfigured_channels <= 6);
493 if (!lenc && !cando_channels)
498 switch (lconfigured_channels)
501 lconfigured_channels = 8;
505 lconfigured_channels = 6;
511 lconfigured_channels = 2;
517 lconfigured_channels = 2;
523 if (lsource_channels <= 6)
524 lconfigured_channels = std::min(lconfigured_channels, 6);
525 lconfigured_channels = std::min(lconfigured_channels,
m_maxChannels);
530 #ifndef DISABLE_AC3_ENCODE
533 lconfigured_channels > 2) ||
538 (settings.
m_codec == AV_CODEC_ID_AC3 ||
539 settings.
m_codec == AV_CODEC_ID_DTS))
548 VBAUDIO(QString(
"Needs upmix from %1 -> %2 channels")
549 .arg(settings.
m_channels).arg(lconfigured_channels));
553 else if (settings.
m_channels > lconfigured_channels)
555 VBAUDIO(QString(
"Needs downmix from %1 -> %2 channels")
556 .arg(settings.
m_channels).arg(lconfigured_channels));
558 lneeds_downmix =
true;
564 bool general_deps =
true;
568 int samplerate_tmp = 0;
569 int channels_tmp = 0;
574 samplerate_tmp, channels_tmp);
594 VBAUDIO(
"Reconfigure(): No change -> exiting");
628 SilentError(QString(
"Aborting Audio Reconfigure. ") +
629 QString(
"Invalid audio parameters ch %1 fmt %2 @ %3Hz")
634 VBAUDIO(QString(
"Original codec was %1, %2, %3 kHz, %4 channels")
635 .arg(avcodec_get_name(
m_codec),
642 Error(QObject::tr(
"Aborting Audio Reconfigure. "
643 "Can't handle audio with more than 8 channels."));
647 VBAUDIO(QString(
"enc(%1), passthru(%2), features (%3) "
648 "configured_channels(%4), %5 channels supported(%6) "
666 VBAUDIO(
"Forcing resample to 48 kHz");
688 VBGENERAL(QString(
"Resampling from %1 kHz to %2 kHz with quality %3")
698 Error(QObject::tr(
"Error creating resampler: %1")
699 .arg(src_strerror(
error)));
712 VBAUDIO(QString(
"Resampler allocating %1").arg(newsize));
724 VBAUDIO(
"Reencoding decoded AC-3/DTS to AC-3");
726 VBAUDIO(QString(
"Creating AC-3 Encoder with sr = %1, ch = %2")
733 Error(QObject::tr(
"AC-3 encoder initialization failed"));
763 VBAUDIO(
"Audio processing enabled");
782 QString(
"Opening audio device '%1' ch %2(%3) sr %4 sf %5 reenc %6")
796 Error(QObject::tr(
"Aborting reconfigure"));
808 VBAUDIO(
"Software volume enabled");
823 VBAUDIO(QString(
"Create %1 quality upmixer done")
840 VBAUDIO(
"Ending Reconfigure()");
871 VBAUDIO(
"Killing AudioOutputDSP");
913 VBAUDIO(QString(
"Pause %1").arg(paused));
983 VBAUDIO(QString(
"SetEffDsp: %1").arg(dsprate));
1067 std::chrono::milliseconds oldaudiotime =
m_audioTime;
1076 * 80 / int64_t(
m_effDsp) / obpf) : 0);
1084 VBAUDIOTS(QString(
"GetAudiotime audt=%1 abtc=%2 mb=%3 sb=%4 tb=%5 "
1085 "sr=%6 obpf=%7 bpf=%8 esf=%9 edsp=%10 sbr=%11")
1089 .arg(soundcard_buffer)
1090 .arg(main_buffer+soundcard_buffer)
1107 int64_t processframes_stretched = 0;
1108 int64_t processframes_unstretched = 0;
1119 processframes_unstretched -=
m_pSoundStretch->numUnprocessedSamples();
1129 timecode + std::chrono::milliseconds(
m_effDsp ? ((frames + processframes_unstretched) * 100000 +
1139 VBAUDIOTS(QString(
"SetAudiotime atc=%1 tc=%2 f=%3 pfu=%4 pfs=%5")
1141 .arg(timecode.count())
1143 .arg(processframes_unstretched)
1144 .arg(processframes_stretched));
1145 #ifdef AUDIOTSTESTING
1193 int len = frames * bpf;
1199 VBERROR(QString(
"Audio buffer overflow, %1 frames lost!")
1200 .arg(frames - (afree / bpf)));
1202 frames = afree / bpf;
1211 VBERROR(QString(
"Error occurred while resetting resampler: %1")
1212 .arg(src_strerror(
error)));
1238 memcpy(
WPOS, buffer, bdiff);
1244 memcpy(
WPOS, buffer + off, num);
1254 int bdFrames = bdiff / bpf;
1255 if (bdFrames <= frames)
1259 off = bdFrames *
sizeof(float);
1286 if (bdFrames < nFrames)
1288 if ((org_waud % bpf) != 0)
1290 VBERROR(QString(
"Upmixing: org_waud = %1 (bpf = %2)")
1295 nFrames -= bdFrames;
1312 std::chrono::milliseconds timecode)
1324 std::chrono::milliseconds timecode,
1334 LOG(VB_GENERAL, LOG_ERR,
"AddData called with audio framework not "
1364 LOG(VB_AUDIO, LOG_INFO,
1365 "Passthrough activated with audio processing. Dropping audio");
1383 VBAUDIOTS(QString(
"AddData frames=%1, bytes=%2, used=%3, free=%4, "
1384 "timecode=%5 needsupmix=%6")
1385 .arg(frames).arg(len).arg(used).arg(afree).arg(timecode.count())
1407 if (sampleSize <= 0)
1410 VBERROR(
"Sample size is <= 0, AddData returning false");
1415 len =
sizeof(
m_srcInBuf[0]) / sampleSize * len;
1423 len = lround(ceil(
static_cast<double>(len) *
m_srcData.src_ratio));
1440 VBERROR(
"Buffer is full, AddData returning false");
1444 int frames_remaining = frames;
1445 int frames_final = 0;
1449 while(frames_remaining > 0)
1451 void *buffer = (
char *)in_buffer + offset;
1452 frames = frames_remaining;
1457 if (frames > maxframes)
1467 frames_remaining -= frames;
1475 VBERROR(
"Error occurred while downmixing");
1485 VBERROR(QString(
"Error occurred while resampling audio: %1")
1486 .arg(src_strerror(
error)));
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.
static const Type kStopped
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.
static const Type kPlaying
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.
static const Type kPaused
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)