MythTV master
audiooutput.cpp
Go to the documentation of this file.
1#include "libmythbase/mythconfig.h"
2
3#include <algorithm>
4#include <cstdio>
5#include <cstdlib>
6
7// Qt utils: to parse audio list
8#include <QtGlobal>
9#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
10#include <QtSystemDetection>
11#endif
12#include <QtEndian>
13#include <QFile>
14#include <QDateTime>
15#include <QDir>
16#include <QMutexLocker>
17
18#include "libmythbase/compat.h"
21
22#include "audiooutput.h"
23#include "audiooutputnull.h"
24#ifdef Q_OS_WINDOWS
25#include "audiooutputdx.h"
26#include "audiooutputwin.h"
27#endif
28#if CONFIG_AUDIO_OSS
29#include "audiooutputoss.h"
30#endif
31#if CONFIG_AUDIO_ALSA
32#include "audiooutputalsa.h"
33#endif
34#ifdef Q_OS_DARWIN
35#include "audiooutputca.h"
36#endif
37#if CONFIG_AUDIO_JACK
38#include "audiooutputjack.h"
39#endif
40#if CONFIG_AUDIO_PULSEOUTPUT
41#include "audiooutputpulse.h"
42#endif
43#if CONFIG_AUDIO_PULSE
44#include "audiopulsehandler.h"
45#endif
46#ifdef Q_OS_ANDROID
47#include "audiooutputopensles.h"
49#endif
50
51extern "C" {
52#include "libavcodec/avcodec.h" // to get codec id
53}
54#include "audioconvert.h"
56#include "pink.h"
57
58#define LOC QString("AO: ")
59
61{
62#if CONFIG_AUDIO_PULSE
64#endif
65}
66
68 const QString &main_device, const QString &passthru_device,
69 AudioFormat format, int channels, AVCodecID codec, int samplerate,
70 AudioOutputSource source, bool set_initial_vol, bool passthru,
71 int upmixer_startup, AudioOutputSettings *custom)
72{
73 AudioSettings settings(
74 main_device, passthru_device, format, channels, codec, samplerate,
75 source, set_initial_vol, passthru, upmixer_startup, custom);
76
77 return OpenAudio(settings);
78}
79
81 const QString &main_device, const QString &passthru_device,
82 bool willsuspendpa)
83{
84 AudioSettings settings(main_device, passthru_device);
85
86 return OpenAudio(settings, willsuspendpa);
87}
88
90 [[maybe_unused]] bool willsuspendpa)
91{
92 QString &main_device = settings.m_mainDevice;
93 AudioOutput *ret = nullptr;
94
95 // Don't suspend Pulse if unnecessary. This can save 100mS
96 if (settings.m_format == FORMAT_NONE || settings.m_channels <= 0)
97 willsuspendpa = false;
98
99#if CONFIG_AUDIO_PULSE
100 bool pulsestatus = false;
101#else
102 {
103 static bool warned = false;
104 if (!warned && IsPulseAudioRunning())
105 {
106 warned = true;
107 LOG(VB_GENERAL, LOG_WARNING,
108 "WARNING: ***Pulse Audio is running***");
109 }
110 }
111#endif // CONFIG_AUDIO_PULSE
112
113 settings.FixPassThrough();
114
115 if (main_device.startsWith("PulseAudio:"))
116 {
117#if CONFIG_AUDIO_PULSEOUTPUT
118 return new AudioOutputPulseAudio(settings);
119#else
120 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to PulseAudio "
121 "but PulseAudio support is not compiled in!");
122 return nullptr;
123#endif // CONFIG_AUDIO_PULSEOUTPUT
124 }
125 if (main_device.startsWith("NULL"))
126 {
127 return new AudioOutputNULL(settings);
128 }
129
130#if CONFIG_AUDIO_PULSE
131 if (willsuspendpa)
132 {
133 bool ispulse = false;
134#if CONFIG_AUDIO_ALSA
135 // Check if using ALSA, that the device doesn't contain the word
136 // "pulse" in its hint
137 if (main_device.startsWith("ALSA:"))
138 {
139 QString device_name = main_device;
140
141 device_name.remove(0, 5);
142 QMap<QString, QString> *alsadevs =
144 if (!alsadevs->empty() && alsadevs->contains(device_name))
145 {
146 if (alsadevs->value(device_name).contains("pulse",
147 Qt::CaseInsensitive))
148 {
149 ispulse = true;
150 }
151 }
152 delete alsadevs;
153 }
154#endif // CONFIF_AUDIO_ALSA
155 if (main_device.contains("pulse", Qt::CaseInsensitive))
156 {
157 ispulse = true;
158 }
159 if (!ispulse)
160 {
162 }
163 }
164#endif // CONFIG_AUDIO_PULSE
165
166 if (main_device.startsWith("ALSA:"))
167 {
168#if CONFIG_AUDIO_ALSA
169 settings.TrimDeviceType();
170 ret = new AudioOutputALSA(settings);
171#else
172 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to an ALSA device "
173 "but ALSA support is not compiled in!");
174#endif
175 }
176 else if (main_device.startsWith("JACK:"))
177 {
178#if CONFIG_AUDIO_JACK
179 settings.TrimDeviceType();
180 ret = new AudioOutputJACK(settings);
181#else
182 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a JACK device "
183 "but JACK support is not compiled in!");
184#endif
185 }
186 else if (main_device.startsWith("DirectX:"))
187 {
188#ifdef Q_OS_WINDOWS
189 ret = new AudioOutputDX(settings);
190#else
191 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to DirectX device "
192 "but DirectX support is not compiled in!");
193#endif
194 }
195 else if (main_device.startsWith("Windows:"))
196 {
197#ifdef Q_OS_WINDOWS
198 ret = new AudioOutputWin(settings);
199#else
200 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a Windows "
201 "device but Windows support is not compiled "
202 "in!");
203#endif
204 }
205 else if (main_device.startsWith("OpenSLES:"))
206 {
207#ifdef Q_OS_ANDROID
208 ret = new AudioOutputOpenSLES(settings);
209#else
210 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a OpenSLES "
211 "device but Android support is not compiled "
212 "in!");
213#endif
214 }
215 else if (main_device.startsWith("AudioTrack:"))
216 {
217#ifdef Q_OS_ANDROID
218 ret = new AudioOutputAudioTrack(settings);
219#else
220 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to AudioTrack "
221 "device but Android support is not compiled "
222 "in!");
223#endif
224 }
225#if CONFIG_AUDIO_OSS
226 else
227 {
228 ret = new AudioOutputOSS(settings);
229 }
230#elif defined(Q_OS_DARWIN)
231 else
232 {
233 ret = new AudioOutputCA(settings);
234 }
235#endif
236
237 if (!ret)
238 {
239 LOG(VB_GENERAL, LOG_CRIT, "No useable audio output driver found.");
240 LOG(VB_GENERAL, LOG_ERR, "Don't disable OSS support unless you're "
241 "not running on Linux.");
242#if CONFIG_AUDIO_PULSE
243 if (pulsestatus)
245#endif
246 return nullptr;
247 }
248#if CONFIG_AUDIO_PULSE
249 ret->m_pulseWasSuspended = pulsestatus;
250#endif
251 return ret;
252}
253
255{
256#if CONFIG_AUDIO_PULSE
259#endif
260 av_frame_free(&m_frame);
261}
262
263void AudioOutput::SetStretchFactor(float /*factor*/)
264{
265}
266
268{
269 return new AudioOutputSettings;
270}
271
273{
274 return new AudioOutputSettings;
275}
276
277bool AudioOutput::CanPassthrough(int /*samplerate*/,
278 int /*channels*/,
279 AVCodecID /*codec*/,
280 int /*profile*/) const
281{
282 return false;
283}
284
286 QString &name, const QString &desc, bool willsuspendpa)
287{
288 AudioOutputSettings aosettings(true);
289
290 AudioOutput *ao = OpenAudio(name, QString(), willsuspendpa);
291 if (ao)
292 {
293 aosettings = *(ao->GetOutputSettingsCleaned());
294 delete ao;
295 }
296 if (aosettings.IsInvalid())
297 {
298 if (!willsuspendpa)
299 return nullptr;
300 QString msg = tr("Invalid or unuseable audio device");
301 return new AudioOutput::AudioDeviceConfig(name, msg);
302 }
303
304 QString capabilities = desc;
305 int max_channels = aosettings.BestSupportedChannelsELD();
306 if (aosettings.hasELD())
307 {
308 if (aosettings.getELD().isValid())
309 {
310 capabilities += tr(" (%1 connected to %2)")
311 .arg(aosettings.getELD().product_name().simplified(),
312 aosettings.getELD().connection_name());
313 }
314 else
315 {
316 capabilities += tr(" (No connection detected)");
317 }
318 }
319
320 QString speakers;
321 switch (max_channels)
322 {
323 case 6:
324 speakers = "5.1";
325 break;
326 case 8:
327 speakers = "7.1";
328 break;
329 default:
330 speakers = "2.0";
331 break;
332 }
333
334 capabilities += tr("\nDevice supports up to %1")
335 .arg(speakers);
336 if (aosettings.canPassthrough() >= 0)
337 {
338 if (aosettings.hasELD() && aosettings.getELD().isValid())
339 {
340 // We have an ELD, show actual reported capabilities
341 capabilities += " (" + aosettings.getELD().codecs_desc() + ")";
342 }
343 else
344 {
345 // build capabilities string, in a similar fashion as reported
346 // by ELD
347 int mask = 0;
348 mask |=
349 (static_cast<int>(aosettings.canLPCM()) << 0) |
350 (static_cast<int>(aosettings.canAC3()) << 1) |
351 (static_cast<int>(aosettings.canDTS()) << 2);
352 static const std::array<const std::string,3> s_typeNames { "LPCM", "AC3", "DTS" };
353
354 if (mask != 0)
355 {
356 capabilities += QObject::tr(" (guessing: ");
357 bool found_one = false;
358 for (unsigned int i = 0; i < 3; i++)
359 {
360 if ((mask & (1 << i)) != 0)
361 {
362 if (found_one)
363 capabilities += ", ";
364 capabilities += QString::fromStdString(s_typeNames[i]);
365 found_one = true;
366 }
367 }
368 capabilities += QString(")");
369 }
370 }
371 }
372 LOG(VB_AUDIO, LOG_INFO, QString("Found %1 (%2)") .arg(name, capabilities));
373 auto *adc = new AudioOutput::AudioDeviceConfig(name, capabilities);
374 adc->m_settings = aosettings;
375 return adc;
376}
377
378#if CONFIG_AUDIO_OSS
379static void fillSelectionsFromDir(const QDir &dir,
381{
382 QFileInfoList entries = dir.entryInfoList();
383 for (const auto& fi : std::as_const(entries))
384 {
385 QString name = fi.absoluteFilePath();
386 QString desc = AudioOutput::tr("OSS device");
389 if (!adc)
390 continue;
391 list->append(*adc);
392 delete adc;
393 }
394}
395#endif
396
398{
399 auto *list = new ADCVect;
400
401#if CONFIG_AUDIO_PULSE
403#endif
404
405#if CONFIG_AUDIO_ALSA
406 QMap<QString, QString> *alsadevs = AudioOutputALSA::GetDevices("pcm");
407
408 if (!alsadevs->empty())
409 {
410 for (auto i = alsadevs->cbegin(); i != alsadevs->cend(); ++i)
411 {
412 const QString& key = i.key();
413 const QString& desc = i.value();
414 QString devname = QString("ALSA:%1").arg(key);
415
416 auto *adc = GetAudioDeviceConfig(devname, desc);
417 if (!adc)
418 continue;
419 list->append(*adc);
420 delete adc;
421 }
422 }
423 delete alsadevs;
424#endif
425#if CONFIG_AUDIO_OSS
426 {
427 QDir dev("/dev", "dsp*", QDir::Name, QDir::System);
428 fillSelectionsFromDir(dev, list);
429 dev.setNameFilters(QStringList("adsp*"));
430 fillSelectionsFromDir(dev, list);
431
432 dev.setPath("/dev/sound");
433 if (dev.exists())
434 {
435 dev.setNameFilters(QStringList("dsp*"));
436 fillSelectionsFromDir(dev, list);
437 dev.setNameFilters(QStringList("adsp*"));
438 fillSelectionsFromDir(dev, list);
439 }
440 }
441#endif
442#if CONFIG_AUDIO_JACK
443 {
444 QString name = "JACK:";
445 QString desc = tr("Use JACK default sound server.");
446 auto *adc = GetAudioDeviceConfig(name, desc);
447 if (adc)
448 {
449 list->append(*adc);
450 delete adc;
451 }
452 }
453#endif
454#ifdef Q_OS_DARWIN
455
456 {
457 QMap<QString, QString> *devs = AudioOutputCA::GetDevices(nullptr);
458 if (!devs->empty())
459 {
460 for (QMap<QString, QString>::const_iterator i = devs->begin();
461 i != devs->end(); ++i)
462 {
463 const QString& key = i.key();
464 const QString& desc = i.value();
465 QString devname = QString("CoreAudio:%1").arg(key);
466
467 auto *adc = GetAudioDeviceConfig(devname, desc);
468 if (!adc)
469 continue;
470 list->append(*adc);
471 delete adc;
472 }
473 }
474 delete devs;
475 QString name = "CoreAudio:Default Output Device";
476 QString desc = tr("CoreAudio default output");
477 auto *adc = GetAudioDeviceConfig(name, desc);
478 if (adc)
479 {
480 list->append(*adc);
481 delete adc;
482 }
483 }
484#endif
485#ifdef Q_OS_WINDOWS
486 {
487 QString name = "Windows:";
488 QString desc = "Windows default output";
489 auto adc = GetAudioDeviceConfig(name, desc);
490 if (adc)
491 {
492 list->append(*adc);
493 delete adc;
494 }
495
496 QMap<int, QString> *dxdevs = AudioOutputDX::GetDXDevices();
497
498 if (!dxdevs->empty())
499 {
500 for (QMap<int, QString>::const_iterator i = dxdevs->begin();
501 i != dxdevs->end(); ++i)
502 {
503 QString devdesc = i.value();
504 QString devname = QString("DirectX:%1").arg(devdesc);
505
506 adc = GetAudioDeviceConfig(devname, devdesc);
507 if (!adc)
508 continue;
509 list->append(*adc);
510 delete adc;
511 }
512 }
513 delete dxdevs;
514 }
515#endif
516
517#if CONFIG_AUDIO_PULSE
518 if (pasuspended)
520#endif
521
522#if CONFIG_AUDIO_PULSEOUTPUT
523 {
524 QString name = "PulseAudio:default";
525 QString desc = tr("PulseAudio default sound server.");
526 auto *adc = GetAudioDeviceConfig(name, desc);
527 if (adc)
528 {
529 list->append(*adc);
530 delete adc;
531 }
532 }
533#endif
534
535#ifdef Q_OS_ANDROID
536 {
537 QString name = "OpenSLES:";
538 QString desc = tr("OpenSLES default output. Stereo support only.");
539 auto *adc = GetAudioDeviceConfig(name, desc);
540 if (adc)
541 {
542 list->append(*adc);
543 delete adc;
544 }
545 }
546 {
547 QString name = "AudioTrack:";
548 QString desc = tr("Android AudioTrack output. Supports surround sound.");
549 auto *adc = GetAudioDeviceConfig(name, desc);
550 if (adc)
551 {
552 list->append(*adc);
553 delete adc;
554 }
555 }
556#endif
557
558 QString name = "NULL";
559 QString desc = "NULL device";
560 auto *adc = GetAudioDeviceConfig(name, desc);
561 if (adc)
562 {
563 list->append(*adc);
564 delete adc;
565 }
566 return list;
567}
568
569int AudioOutput::DecodeAudio(AVCodecContext *ctx,
570 uint8_t *buffer, int &data_size,
571 const AVPacket *pkt)
572{
573 bool got_frame = false;
574
575 data_size = 0;
576 if (!m_frame)
577 {
578 m_frame = av_frame_alloc();
579 if (m_frame == nullptr)
580 {
581 return AVERROR(ENOMEM);
582 }
583 }
584 else
585 {
586 av_frame_unref(m_frame);
587 }
588
589// SUGGESTION
590// Now that avcodec_decode_audio4 is deprecated and replaced
591// by 2 calls (receive frame and send packet), this could be optimized
592// into separate routines or separate threads.
593// Also now that it always consumes a whole buffer some code
594// in the caller may be able to be optimized.
595 int ret = avcodec_receive_frame(ctx,m_frame);
596 if (ret == 0)
597 got_frame = true;
598 if (ret == AVERROR(EAGAIN))
599 ret = 0;
600 if (ret == 0)
601 ret = avcodec_send_packet(ctx, pkt);
602 if (ret == AVERROR(EAGAIN))
603 {
604 ret = 0;
605 }
606 else if (ret < 0)
607 {
608 std::string error;
609 LOG(VB_AUDIO, LOG_ERR, LOC +
610 QString("audio decode error: %1 (%2)")
612 .arg(got_frame));
613 return ret;
614 }
615 else
616 {
617 ret = pkt->size;
618 }
619
620 if (!got_frame)
621 {
622 LOG(VB_AUDIO, LOG_DEBUG, LOC +
623 QString("audio decode, no frame decoded (%1)").arg(ret));
624 return ret;
625 }
626
627 auto format = (AVSampleFormat)m_frame->format;
628 AudioFormat fmt =
629 AudioOutputSettings::AVSampleFormatToFormat(format, ctx->bits_per_raw_sample);
630
631 data_size = m_frame->nb_samples * m_frame->ch_layout.nb_channels * av_get_bytes_per_sample(format);
632
633 // May need to convert audio to S16
634 AudioConvert converter(fmt, CanProcess(fmt) ? fmt : FORMAT_S16);
635 uint8_t* src = nullptr;
636
637 if (av_sample_fmt_is_planar(format))
638 {
639 src = buffer;
640 converter.InterleaveSamples(m_frame->ch_layout.nb_channels,
641 src,
642 (const uint8_t **)m_frame->extended_data,
643 data_size);
644 }
645 else
646 {
647 // data is already compacted...
648 src = m_frame->extended_data[0];
649 }
650
651 uint8_t* transit = buffer;
652
653 if (!CanProcess(fmt) &&
654 av_get_bytes_per_sample(ctx->sample_fmt) < AudioOutputSettings::SampleSize(converter.Out()))
655 {
656 // this conversion can't be done in place
657 transit = (uint8_t*)av_malloc(data_size * av_get_bytes_per_sample(ctx->sample_fmt)
658 / AudioOutputSettings::SampleSize(converter.Out()));
659 if (!transit)
660 {
661 LOG(VB_AUDIO, LOG_ERR, LOC +
662 QString("audio decode, out of memory"));
663 data_size = 0;
664 return ret;
665 }
666 }
667 if (!CanProcess(fmt) || src != transit)
668 {
669 data_size = converter.Process(transit, src, data_size, true);
670 }
671 if (transit != buffer)
672 {
673 av_free(transit);
674 }
675 return ret;
676}
677
678void AudioOutput::dispatchError(const QString &e)
679{
680 Event event(e);
681 dispatch(event);
682}
683
685{
686#ifdef __cpp_lib_ranges_contains
687 if (!std::ranges::contains(m_visuals, v))
688#else
689 auto it = std::ranges::find(m_visuals, v);
690 if (it == m_visuals.end())
691#endif
692 m_visuals.push_back(v);
693}
694
696{
697 auto it = std::ranges::find(m_visuals, v);
698 if (it != m_visuals.end())
699 m_visuals.erase(it);
700}
701
702void AudioOutput::dispatchVisual(uchar *buffer, unsigned long b_len,
703 std::chrono::milliseconds timecode,
704 int chan, int prec)
705{
706 if (! buffer)
707 return;
708
709 for (auto & visual : m_visuals)
710 {
711 QMutexLocker locker(visual->mutex());
712 visual->add(buffer, b_len, timecode, chan, prec);
713 }
714}
715
717{
718 for (auto & visual : m_visuals)
719 {
720 QMutexLocker locker(visual->mutex());
721 visual->prepare();
722 }
723}
724
725static char* generatePinkFrames(char* frames, int channels, int channel, int count, int bits)
726{
727 pink_noise_t pink{};
728
730
731 auto *samp16 = (int16_t*) frames;
732 auto *samp32 = (int32_t*) frames;
733
734 while (count-- > 0)
735 {
736 for(int chn = 0 ; chn < channels; chn++)
737 {
738 if (chn==channel)
739 {
740 /* Don't use MAX volume */
741 double res = generate_pink_noise_sample(&pink) *
742 static_cast<float>(0x03fffffff);
743 int32_t ires = res;
744 if (bits == 16)
745 *samp16++ = qToLittleEndian<qint16>(ires >> 16);
746 else
747 *samp32++ = qToLittleEndian<qint32>(ires);
748 }
749 else
750 {
751 if (bits == 16)
752 *samp16++ = 0;
753 else
754 *samp32++ = 0;
755 }
756 }
757 }
758 return frames;
759}
760
761bool AudioOutput::playPinkNoise(char *frames, int channels, int channel, int count, int bits)
762{
763 generatePinkFrames(frames, channels, channel, count, bits);
764 return AddFrames(frames, count, -1ms);
765}
#define LOC
Definition: audiooutput.cpp:58
static char * generatePinkFrames(char *frames, int channels, int channel, int count, int bits)
@ FORMAT_NONE
@ FORMAT_S16
AudioOutputSource
Definition: audiosettings.h:21
AudioFormat Out(void)
Definition: audioconvert.h:49
void InterleaveSamples(int channels, uint8_t *output, const uint8_t *const *input, int data_size)
int Process(void *out, const void *in, int bytes, bool noclip=false)
Process Parameters: out : destination buffer where converted samples will be copied in : source buffe...
static QMap< QString, QString > * GetDevices(const char *type)
Implements Core Audio (Mac OS X Hardware Abstraction Layer) output.
Definition: audiooutputca.h:14
static QMap< QString, QString > * GetDevices(const char *type=nullptr)
static QMap< int, QString > * GetDXDevices(void)
eld & getELD(void)
retrieve ELD data
static int SampleSize(AudioFormat format)
bool canLPCM() const
return true if device supports multichannels PCM (deprecated, see canFeature())
bool canAC3() const
return true if device can or may support AC3 (deprecated, see canFeature())
int BestSupportedChannelsELD()
Reports best supported channel number, restricted to ELD range.
bool hasELD() const
get the ELD flag
static AudioFormat AVSampleFormatToFormat(AVSampleFormat format, int bits=0)
Return AVSampleFormat closest equivalent to AudioFormat.
bool canDTS() const
return true if device can or may support DTS (deprecated, see canFeature())
bool IsInvalid() const
return true if class instance is marked invalid.
void addVisual(Visualization *v)
~AudioOutput() override
void removeVisual(Visualization *v)
static AudioOutput * OpenAudio(const QString &main_device, const QString &passthru_device, AudioFormat format, int channels, AVCodecID codec, int samplerate, AudioOutputSource source, bool set_initial_vol, bool passthru, int upmixer_startup=0, AudioOutputSettings *custom=nullptr)
Definition: audiooutput.cpp:67
static ADCVect * GetOutputList(void)
bool m_pulseWasSuspended
Definition: audiooutput.h:227
virtual AudioOutputSettings * GetOutputSettingsUsers(bool digital=true)
QVector< AudioDeviceConfig > ADCVect
Definition: audiooutput.h:55
void dispatchVisual(uchar *b, unsigned long b_len, std::chrono::milliseconds timecode, int chan, int prec)
int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, const AVPacket *pkt)
Utility routine.
virtual bool CanPassthrough(int samplerate, int channels, AVCodecID codec, int profile) const
static AudioDeviceConfig * GetAudioDeviceConfig(QString &name, const QString &desc, bool willsuspendpa=false)
void dispatchError(const QString &e)
void prepareVisuals()
virtual bool AddFrames(void *buffer, int frames, std::chrono::milliseconds timecode)=0
Add frames to the audiobuffer for playback.
virtual AudioOutputSettings * GetOutputSettingsCleaned(bool digital=true)
virtual uint32_t CanProcess(void)
Definition: audiooutput.h:179
virtual void SetStretchFactor(float factor)
static void Cleanup(void)
Definition: audiooutput.cpp:60
bool playPinkNoise(char *frames, int channels, int channel, int count, int bits)
Generates and plays pink noise from a speaker for testing.
std::vector< Visualization * > m_visuals
Definition: audiooutput.h:229
AVFrame * m_frame
Definition: audiooutput.h:228
const int & channels() const
Definition: audiooutput.h:254
QString m_mainDevice
Definition: audiosettings.h:70
void TrimDeviceType(void)
AudioFormat m_format
Definition: audiosettings.h:72
void FixPassThrough(void)
Event details.
Definition: zmdefines.h:28
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
static bool Suspend(enum PulseAction action)
QString codecs_desc() const
Definition: eldutils.cpp:475
bool isValid() const
Definition: eldutils.cpp:411
QString connection_name() const
Definition: eldutils.cpp:445
QString product_name() const
Definition: eldutils.cpp:440
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
char * av_make_error_stdstring(std::string &errbuf, int errnum)
A C++ equivalent to av_make_error_string.
Definition: mythaverror.cpp:42
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
bool IsPulseAudioRunning(void)
Is A/V Sync destruction daemon is running on this host?
def error(message)
Definition: smolt.py:409
void initialize_pink_noise(pink_noise_t *pink, int num_rows)
Definition: pink.cpp:42
float generate_pink_noise_sample(pink_noise_t *pink)
Definition: pink.cpp:56
uint fillSelectionsFromDir(const QDir &dir, uint minor_min, uint minor_max, const QString &card, const QRegularExpression &driver, bool allow_duplicates, V2CaptureDeviceList *pList, const QString &cardType)