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)
97 "~AudioOutputBase called, but KillAudio has not been called!");
202 aosettings->GetUsers();
213 AVCodecID codec,
int profile)
const
220 case AV_CODEC_ID_AC3:
223 case AV_CODEC_ID_DTS:
227 case FF_PROFILE_DTS_ES:
228 case FF_PROFILE_DTS_96_24:
231 case FF_PROFILE_DTS_HD_HRA:
232 case FF_PROFILE_DTS_HD_MA:
239 case AV_CODEC_ID_EAC3:
242 case AV_CODEC_ID_TRUEHD:
254 ret &= (samplerate == 48000) ||
258 ret &= samplerate >= 44100;
308 VBGENERAL(QString(
"Cancelling time stretch"));
315 VBGENERAL(QString(
"Changing time stretch to %1")
320 else if (willstretch)
327 #if defined(Q_PROCESSOR_ARM) || defined(Q_OS_ANDROID)
405 return m_sourceChannels <= 2 && m_maxChannels > 2;
413 int &samplerate_tmp,
int &channels_tmp)
415 if (codec == AV_CODEC_ID_DTS &&
420 codec_profile = FF_PROFILE_DTS;
423 codec, codec_profile,
424 samplerate_tmp, channels_tmp,
437 switch(codec_profile)
440 case FF_PROFILE_DTS_ES:
441 case FF_PROFILE_DTS_96_24:
444 case FF_PROFILE_DTS_HD_HRA:
445 case FF_PROFILE_DTS_HD_MA:
477 bool lneeds_upmix =
false;
478 bool lneeds_downmix =
false;
485 lconfigured_channels =
487 bool cando_channels =
491 #ifndef DISABLE_AC3_ENCODE
494 lconfigured_channels > 2 && lconfigured_channels <= 6);
496 if (!lenc && !cando_channels)
501 switch (lconfigured_channels)
504 lconfigured_channels = 8;
508 lconfigured_channels = 6;
514 lconfigured_channels = 2;
520 lconfigured_channels = 2;
526 if (lsource_channels <= 6)
527 lconfigured_channels = std::min(lconfigured_channels, 6);
528 lconfigured_channels = std::min(lconfigured_channels,
m_maxChannels);
533 #ifndef DISABLE_AC3_ENCODE
536 lconfigured_channels > 2) ||
541 (settings.
m_codec == AV_CODEC_ID_AC3 ||
542 settings.
m_codec == AV_CODEC_ID_DTS))
551 VBAUDIO(QString(
"Needs upmix from %1 -> %2 channels")
552 .arg(settings.
m_channels).arg(lconfigured_channels));
556 else if (settings.
m_channels > lconfigured_channels)
558 VBAUDIO(QString(
"Needs downmix from %1 -> %2 channels")
559 .arg(settings.
m_channels).arg(lconfigured_channels));
561 lneeds_downmix =
true;
567 bool general_deps =
true;
571 int samplerate_tmp = 0;
572 int channels_tmp = 0;
577 samplerate_tmp, channels_tmp);
597 VBAUDIO(
"Reconfigure(): No change -> exiting");
631 SilentError(QString(
"Aborting Audio Reconfigure. ") +
632 QString(
"Invalid audio parameters ch %1 fmt %2 @ %3Hz")
637 VBAUDIO(QString(
"Original codec was %1, %2, %3 kHz, %4 channels")
638 .arg(avcodec_get_name(
m_codec),
645 Error(QObject::tr(
"Aborting Audio Reconfigure. "
646 "Can't handle audio with more than 8 channels."));
650 VBAUDIO(QString(
"enc(%1), passthru(%2), features (%3) "
651 "configured_channels(%4), %5 channels supported(%6) "
669 VBAUDIO(
"Forcing resample to 48 kHz");
691 VBGENERAL(QString(
"Resampling from %1 kHz to %2 kHz with quality %3")
701 Error(QObject::tr(
"Error creating resampler: %1")
702 .arg(src_strerror(
error)));
715 VBAUDIO(QString(
"Resampler allocating %1").arg(newsize));
727 VBAUDIO(
"Reencoding decoded AC-3/DTS to AC-3");
729 VBAUDIO(QString(
"Creating AC-3 Encoder with sr = %1, ch = %2")
736 Error(QObject::tr(
"AC-3 encoder initialization failed"));
766 VBAUDIO(
"Audio processing enabled");
785 QString(
"Opening audio device '%1' ch %2(%3) sr %4 sf %5 reenc %6")
799 Error(QObject::tr(
"Aborting reconfigure"));
811 VBAUDIO(
"Software volume enabled");
826 VBAUDIO(QString(
"Create %1 quality upmixer done")
843 VBAUDIO(
"Ending Reconfigure()");
874 VBAUDIO(
"Killing AudioOutputDSP");
916 VBAUDIO(QString(
"Pause %1").arg(paused));
986 VBAUDIO(QString(
"SetEffDsp: %1").arg(dsprate));
1072 std::chrono::milliseconds oldaudiotime =
m_audioTime;
1081 * 80 / int64_t(
m_effDsp) / obpf) : 0);
1089 VBAUDIOTS(QString(
"GetAudiotime audt=%1 abtc=%2 mb=%3 sb=%4 tb=%5 "
1090 "sr=%6 obpf=%7 bpf=%8 esf=%9 edsp=%10 sbr=%11")
1094 .arg(soundcard_buffer)
1095 .arg(main_buffer+soundcard_buffer)
1112 int64_t processframes_stretched = 0;
1113 int64_t processframes_unstretched = 0;
1124 processframes_unstretched -=
m_pSoundStretch->numUnprocessedSamples();
1134 timecode + std::chrono::milliseconds(
m_effDsp ? ((frames + processframes_unstretched) * 100000 +
1144 VBAUDIOTS(QString(
"SetAudiotime atc=%1 tc=%2 f=%3 pfu=%4 pfs=%5")
1146 .arg(timecode.count())
1148 .arg(processframes_unstretched)
1149 .arg(processframes_stretched));
1150 #ifdef AUDIOTSTESTING
1198 int len = frames * bpf;
1204 VBERROR(QString(
"Audio buffer overflow, %1 frames lost!")
1205 .arg(frames - (afree / bpf)));
1207 frames = afree / bpf;
1216 VBERROR(QString(
"Error occurred while resetting resampler: %1")
1217 .arg(src_strerror(
error)));
1243 memcpy(
WPOS, buffer, bdiff);
1249 memcpy(
WPOS, buffer + off, num);
1259 int bdFrames = bdiff / bpf;
1260 if (bdFrames <= frames)
1264 off = bdFrames *
sizeof(float);
1291 if (bdFrames < nFrames)
1293 if ((org_waud % bpf) != 0)
1295 VBERROR(QString(
"Upmixing: org_waud = %1 (bpf = %2)")
1300 nFrames -= bdFrames;
1317 std::chrono::milliseconds timecode)
1329 std::chrono::milliseconds timecode,
1339 LOG(VB_GENERAL, LOG_ERR,
"AddData called with audio framework not "
1369 LOG(VB_AUDIO, LOG_INFO,
1370 "Passthrough activated with audio processing. Dropping audio");
1390 VBAUDIOTS(QString(
"AddData frames=%1, bytes=%2, used=%3, free=%4, "
1391 "timecode=%5 needsupmix=%6")
1392 .arg(frames).arg(len).arg(used).arg(afree).arg(timecode.count())
1414 if (sampleSize <= 0)
1417 VBERROR(
"Sample size is <= 0, AddData returning false");
1422 len =
sizeof(
m_srcInBuf[0]) / sampleSize * len;
1430 len = lround(ceil(
static_cast<double>(len) *
m_srcData.src_ratio));
1447 VBERROR(
"Buffer is full, AddData returning false");
1451 int frames_remaining = frames;
1452 int frames_final = 0;
1456 while(frames_remaining > 0)
1458 void *buffer = (
char *)in_buffer + offset;
1459 frames = frames_remaining;
1464 if (frames > maxframes)
1474 frames_remaining -= frames;
1482 VBERROR(
"Error occurred while downmixing");
1492 VBERROR(QString(
"Error occurred while resampling audio: %1")
1493 .arg(src_strerror(
error)));
1515 frames_final += frames;
1518 if ((len % bpf) != 0 && bdiff < len)
1520 VBERROR(QString(
"AddData: Corruption likely: len = %1 (bpf = %2)")
1524 if ((bdiff % bpf) != 0 && bdiff < len)
1526 VBERROR(QString(
"AddData: Corruption likely: bdiff = %1 (bpf = %2)")
1535 int bdFrames = bdiff / bpf;
1548 if (nFrames > frames)
1551 len = nFrames * bpf;
1553 if (nFrames > bdFrames)
1601 if (bdiff <= to_get)
1662 auto *fragment =
new(std::align_val_t(16)) uchar[
m_fragmentSize];
1667 zero_fragment_size = std::min(zero_fragment_size,
m_fragmentSize);
1675 VBAUDIO(
"OutputAudioLoop: audio paused");
1690 VBAUDIO(
"OutputAudioLoop: Play Event");
1704 VBAUDIOTS(QString(
"audio waiting for buffer to fill: "
1713 #ifdef AUDIOTSTESTING
1731 #ifdef AUDIOTSTESTING
1740 VBAUDIO(
"OutputAudioLoop: Stop Event");
1753 volatile uint *local_raud)
1756 #define LRPOS (&m_audioBuffer[*local_raud])
1760 int frag_size = size;
1761 int written_size = size;
1763 if (local_raud ==
nullptr)
1766 if (!full_buffer && (size > avail_size))
1769 frag_size = avail_size;
1770 written_size = frag_size;
1773 if (!avail_size || (frag_size > avail_size))
1786 if (fromFloats && obytes !=
sizeof(
float))
1787 frag_size *=
sizeof(float) / obytes;
1791 if (bdiff <= frag_size)
1800 memcpy(buffer,
LRPOS, bdiff);
1816 memcpy(buffer + off,
LRPOS, frag_size);
1820 *local_raud += frag_size;
1830 buffer, written_size);
1833 return written_size;
1858 VBAUDIO(QString(
"kickoffOutputAudioLoop: pid = %1").arg(getpid()));
1860 VBAUDIO(
"kickoffOutputAudioLoop exiting");
1866 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.
None log(str msg, int level=LOGDEBUG)
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)