11#define LOC QString("ALSA: ")
22 { SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_FORMAT | SND_PCM_NO_AUTO_CHANNELS };
27#define AERROR(str) LOG(VB_GENERAL, LOG_ERR, LOC + (str) + QString(": %1").arg(snd_strerror(err)))
28#define CHECKERR(str) { if (err < 0) { AERROR(str); return err; } }
48 QString iecarg = QString(
"AES0=6,AES1=0x82,AES2=0x00") +
49 (s48k ? QString() : QString(
",AES3=0x01"));
50 QString iecarg2 = QString(
"AES0=6 AES1=0x82 AES2=0x00") +
51 (s48k ? QString() : QString(
" AES3=0x01"));
54 if (parts.length() == 1)
61 QString params = parts[1].trimmed();
67 else if (params[0] !=
'{')
77 QString oldparams = params.mid(1,params.size()-2);
79 .arg(parts[0], oldparams.trimmed(), iecarg2);
111 err = snd_pcm_open(&
m_pcmHandle, dev_ba.constData(),
112 SND_PCM_STREAM_PLAYBACK, open_mode);
121 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Auto setting passthrough failed (%1), defaulting "
122 "to main device").arg(snd_strerror(err)));
125 if (!try_ac3 || err < 0)
130 err = snd_pcm_open(&
m_pcmHandle, dev_ba.constData(),
131 SND_PCM_STREAM_PLAYBACK, open_mode);
151 snd_pcm_info_t *pcm_info =
nullptr;
153 snd_pcm_info_alloca(&pcm_info);
158 err = snd_pcm_info_get_card(pcm_info);
162 err = snd_pcm_info_get_device(pcm_info);
163 CHECKERR(
"snd_pcm_info_get_device");
166 err = snd_pcm_info_get_subdevice(pcm_info);
167 CHECKERR(
"snd_pcm_info_get_subdevice");
168 int tsubdevice = err;
188 const QString pf = QString(
"/proc/asound/card%1/pcm%2p/sub%3/prealloc")
189 .arg(card).arg(
device).arg(subdevice);
192 QFile mfile(pf +
"_max");
194 if (!pfile.open(QIODevice::ReadOnly))
196 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error opening %1. Fix reading permissions.").arg(pf));
200 if (!mfile.open(QIODevice::ReadOnly))
202 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error opening %1").arg(pf +
"_max"));
206 int cur = pfile.readAll().trimmed().toInt();
207 int max = mfile.readAll().trimmed().toInt();
209 int size = ((int)(cur * (
float)requested / (float)buffer_time)
212 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Hardware audio buffer cur: %1 need: %2 max allowed: %3")
213 .arg(cur).arg(size).arg(max));
222 if (size > max || !size)
230 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"Try to manually increase audio buffer with: echo %1 "
231 "| sudo tee %2").arg(size).arg(pf));
237 QByteArray *result =
nullptr;
238 snd_hctl_t *hctl =
nullptr;
239 snd_ctl_elem_info_t *
info =
nullptr;
240 snd_ctl_elem_id_t *
id =
nullptr;
241 snd_ctl_elem_value_t *
control =
nullptr;
243 snd_ctl_elem_info_alloca(&
info);
244 snd_ctl_elem_id_alloca(&
id);
245 snd_ctl_elem_value_alloca(&
control);
247 snd_ctl_elem_id_set_interface(
id, SND_CTL_ELEM_IFACE_PCM);
248 snd_ctl_elem_id_set_name(
id,
"ELD");
249 snd_ctl_elem_id_set_device(
id,
device);
250 snd_ctl_elem_id_set_subdevice(
id, subdevice);
252 int err = snd_hctl_open(&hctl,
253 QString(
"hw:%1").arg(card).toLatin1().constData(),
257 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 open error: %2")
259 .arg(snd_strerror(err)));
262 err = snd_hctl_load(hctl);
265 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 load error: %2")
267 .arg(snd_strerror(err)));
271 snd_hctl_elem_t *
elem = snd_hctl_find_elem(hctl,
id);
274 err = snd_hctl_elem_info(
elem,
info);
277 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 snd_hctl_elem_info error: %2")
279 .arg(snd_strerror(err)));
280 snd_hctl_close(hctl);
283 unsigned int count = snd_ctl_elem_info_get_count(
info);
284 snd_ctl_elem_type_t
type = snd_ctl_elem_info_get_type(
info);
285 if (!snd_ctl_elem_info_is_readable(
info))
287 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 element info is not readable")
289 snd_hctl_close(hctl);
295 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 element read error: %2")
297 .arg(snd_strerror(err)));
298 snd_hctl_close(hctl);
301 if (
type != SND_CTL_ELEM_TYPE_BYTES)
303 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 element is of the wrong type")
305 snd_hctl_close(hctl);
308 result =
new QByteArray((
char *)snd_ctl_elem_value_get_bytes(
control),
311 snd_hctl_close(hctl);
317 snd_pcm_hw_params_t *params =
nullptr;
318 snd_pcm_format_t afmt = SND_PCM_FORMAT_UNKNOWN;
337 snd_pcm_hw_params_alloca(¶ms);
339 if (snd_pcm_hw_params_any(
m_pcmHandle, params) < 0)
352 AERROR(
"No playback configurations available");
358 Warn(
"Supported audio format detection will be inacurrate "
363 while (
int rate = settings->GetNextRate())
364 if(snd_pcm_hw_params_test_rate(
m_pcmHandle, params, rate, 0) >= 0)
365 settings->AddSupportedRate(rate);
367 while ((fmt = settings->GetNextFormat()))
371 case FORMAT_U8: afmt = SND_PCM_FORMAT_U8;
break;
372 case FORMAT_S16: afmt = SND_PCM_FORMAT_S16;
break;
375 case FORMAT_S24: afmt = SND_PCM_FORMAT_S24;
break;
376 case FORMAT_S32: afmt = SND_PCM_FORMAT_S32;
break;
377 case FORMAT_FLT: afmt = SND_PCM_FORMAT_FLOAT;
break;
380 if (snd_pcm_hw_params_test_format(
m_pcmHandle, params, afmt) >= 0)
381 settings->AddSupportedFormat(fmt);
385 if (snd_pcm_hw_params_test_channels(
m_pcmHandle, params, channels) >= 0)
386 settings->AddSupportedChannels(channels);
397 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Successfully retrieved ELD data"));
398 settings->setELD(
eld);
404 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Can't get card and device number");
412 QMap<QString, QString> *alsadevs =
GetDevices(
"pcm");
418 QString desc = alsadevs->value(real_device);
420 settings->setPassthrough(1);
421 if (real_device.contains(
"digital", Qt::CaseInsensitive) ||
422 desc.contains(
"digital", Qt::CaseInsensitive))
424 if (real_device.contains(
"iec958", Qt::CaseInsensitive))
426 if (real_device.contains(
"spdif", Qt::CaseInsensitive))
428 if (real_device.contains(
"hdmi", Qt::CaseInsensitive))
431 settings->setPassthrough(-1);
433 if (real_device.contains(
"pulse", Qt::CaseInsensitive) ||
434 desc.contains(
"pulse", Qt::CaseInsensitive))
436 if (real_device.contains(
"analog", Qt::CaseInsensitive) ||
437 desc.contains(
"analog", Qt::CaseInsensitive))
439 if (real_device.contains(
"surround", Qt::CaseInsensitive) ||
440 desc.contains(
"surround", Qt::CaseInsensitive))
443 settings->setPassthrough(0);
452 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
468 case FORMAT_U8: format = SND_PCM_FORMAT_U8;
break;
469 case FORMAT_S16: format = SND_PCM_FORMAT_S16;
break;
472 case FORMAT_S24: format = SND_PCM_FORMAT_S24;
break;
473 case FORMAT_S32: format = SND_PCM_FORMAT_S32;
break;
474 case FORMAT_FLT: format = SND_PCM_FORMAT_FLOAT;
break;
483 uint period_time = 4;
486 buffer_time, period_time);
489 AERROR(
"Unable to set ALSA parameters");
495 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to open audio mixer. Volume control disabled");
504 snd_mixer_close(
m_mixer.handle);
514template <
class AudioDataType>
519 AudioDataType tmpLFE;
520 AudioDataType *buf2 =
nullptr;
522 for (
uint i = 0; i < frames; i++)
524 buf = buf2 = buf + 2;
549 uchar *tmpbuf = aubuf;
554 Error(
"WriteAudio() called with pcm_handle == nullptr!");
564 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
565 QString(
"WriteAudio: Preparing %1 bytes (%2 frames)")
566 .arg(size).arg(frames));
570 int lw = snd_pcm_writei(
m_pcmHandle, tmpbuf, frames);
574 if ((
uint)lw < frames)
575 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"WriteAudio: short write %1 bytes (ok)")
588 if (snd_pcm_state(
m_pcmHandle) == SND_PCM_STATE_XRUN)
590 LOG(VB_AUDIO, LOG_INFO,
LOC +
"WriteAudio: buffer underrun");
594 AERROR(
"WriteAudio: unable to recover from xrun");
602 LOG(VB_AUDIO, LOG_INFO,
LOC +
"WriteAudio: device is suspended");
603 while ((err = snd_pcm_resume(
m_pcmHandle)) == -EAGAIN)
608 LOG(VB_GENERAL, LOG_ERR,
LOC +
"WriteAudio: resume failed");
612 AERROR(
"WriteAudio: unable to recover from suspend");
621 QString(
"WriteAudio: device is in a bad state (state = %1)")
626 AERROR(QString(
"WriteAudio: Write failed, state: %1, err")
637 LOG(VB_GENERAL, LOG_ERR,
LOC +
"getBufferedOnSoundcard() called with pcm_handle == nullptr!");
641 snd_pcm_sframes_t delay = 0;
663 snd_pcm_hw_params_t *params =
nullptr;
664 snd_pcm_sw_params_t *swparams =
nullptr;
672 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"SetParameters(format=%1, channels=%2, rate=%3, "
673 "buffer_time=%4, period_time=%5)")
674 .arg(format).arg(channels).arg(rate).arg(buffer_time)
679 Error(QObject::tr(
"SetParameters() called with handle == nullptr!"));
683 snd_pcm_hw_params_alloca(¶ms);
684 snd_pcm_sw_params_alloca(&swparams);
687 int err = snd_pcm_hw_params_any(
handle, params);
688 CHECKERR(
"No playback configurations available");
691 err = snd_pcm_hw_params_set_access(
handle, params,
692 SND_PCM_ACCESS_RW_INTERLEAVED);
693 CHECKERR(QString(
"Interleaved RW audio not available"));
696 err = snd_pcm_hw_params_set_format(
handle, params, format);
697 CHECKERR(QString(
"Sample format %1 not available").arg(format));
700 err = snd_pcm_hw_params_set_channels(
handle, params, channels);
701 CHECKERR(QString(
"Channels count %1 not available").arg(channels));
706 err = snd_pcm_hw_params_set_rate_resample(
handle, params, 1);
707 CHECKERR(QString(
"Resampling setup failed").arg(rate));
710 err = snd_pcm_hw_params_set_rate_near(
handle, params, &rrate,
nullptr);
711 CHECKERR(QString(
"Rate %1Hz not available for playback: %s").arg(rate));
715 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Rate doesn't match (requested %1Hz, got %2Hz)")
716 .arg(rate).arg(err));
722 err = snd_pcm_hw_params_set_rate(
handle, params, rate, 0);
723 CHECKERR(QString(
"Samplerate %1 Hz not available").arg(rate));
727 (void) snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min);
728 (void) snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max);
729 (void) snd_pcm_hw_params_get_period_size_min(params, &period_size_min,
nullptr);
730 (void) snd_pcm_hw_params_get_period_size_max(params, &period_size_max,
nullptr);
731 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffer size range from %1 to %2")
732 .arg(buffer_size_min)
733 .arg(buffer_size_max));
734 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Period size range from %1 to %2")
735 .arg(period_size_min)
736 .arg(period_size_max));
739 uint original_buffer_time = buffer_time;
740 bool canincrease =
true;
741 err = snd_pcm_hw_params_set_buffer_time_near(
handle, params,
742 &buffer_time,
nullptr);
746 uint buftmp = buffer_time;
749 err = snd_pcm_hw_params_set_buffer_time_near(
handle, params,
758 if ((buffer_time <= 100000) ||
759 (attempt > 0 && buffer_time == buftmp))
761 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Couldn't set buffer time, giving up");
764 AERROR(QString(
"Unable to set buffer time to %1us, retrying")
766 buffer_time -= 100000;
769 err = snd_pcm_hw_params_set_buffer_time_near(
handle, params,
776 if (buffer_time * 1.10F < (
float)original_buffer_time)
778 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"Requested %1us got %2 buffer time")
779 .arg(original_buffer_time).arg(buffer_time));
787 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffer time = %1 us").arg(buffer_time));
790 err = snd_pcm_hw_params_set_periods_near(
handle, params,
791 &period_time,
nullptr);
792 CHECKERR(QString(
"Unable to set period time %1").arg(period_time));
793 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Period time = %1 periods").arg(period_time));
796 err = snd_pcm_hw_params(
handle, params);
797 CHECKERR(
"Unable to set hw params for playback");
799 err = snd_pcm_get_params(
handle, &buffer_size, &period_size);
800 CHECKERR(
"Unable to get PCM params");
801 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffer size = %1 | Period size = %2")
802 .arg(buffer_size).arg(period_size));
809 err = snd_pcm_sw_params_current(
handle, swparams);
810 CHECKERR(
"Unable to get current swparams");
813 err = snd_pcm_sw_params_set_start_threshold(
handle, swparams, period_size);
814 CHECKERR(
"Unable to set start threshold");
817 err = snd_pcm_sw_params_set_avail_min(
handle, swparams, period_size);
818 CHECKERR(
"Unable to set avail min");
821 err = snd_pcm_sw_params(
handle, swparams);
822 CHECKERR(
"Unable to set sw params");
824 err = snd_pcm_prepare(
handle);
825 CHECKERR(
"Unable to prepare the PCM");
837 auto chan = (snd_mixer_selem_channel_id_t) channel;
838 if (!snd_mixer_selem_has_playback_channel(
m_mixer.elem, chan))
842 int chk = snd_mixer_selem_get_playback_volume(
m_mixer.elem, chan,
846 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to get channel %1 volume, mixer %2/%3: %4")
847 .arg(QString::number(channel),
m_mixer.device,
848 m_mixer.control, snd_strerror(chk)));
852 retvol = (
m_mixer.volrange != 0L)
853 ? ((mixervol -
m_mixer.volmin) * 100.0F /
m_mixer.volrange) + 0.5F
855 retvol = std::max(retvol, 0);
856 retvol = std::min(retvol, 100);
857 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"get volume channel %1: %2")
858 .arg(channel).arg(retvol));
868 long mixervol = ((int64_t(volume) *
m_mixer.volrange) / 100) +
m_mixer.volmin;
869 mixervol = std::max(mixervol,
m_mixer.volmin);
870 mixervol = std::min(mixervol,
m_mixer.volmax);
872 auto chan = (snd_mixer_selem_channel_id_t) channel;
874 if (snd_mixer_selem_has_playback_switch(
m_mixer.elem))
875 snd_mixer_selem_set_playback_switch(
m_mixer.elem, chan,
static_cast<int>(volume > 0));
877 if (snd_mixer_selem_set_playback_volume(
m_mixer.elem, chan, mixervol) < 0)
878 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to set channel %1 volume").arg(channel));
880 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"channel %1 volume set %2 => %3")
881 .arg(channel).arg(volume).arg(mixervol));
888 LOG(VB_GENERAL, LOG_ERR,
LOC +
"mixer setup without a pcm");
893 if (
m_mixer.device.toLower() ==
"software")
898 QString mixer_device_tag = QString(
"mixer device %1").arg(
m_mixer.device);
900 int chk = snd_mixer_open(&
m_mixer.handle, 0);
903 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to open mixer device %1: %2")
904 .arg(mixer_device_tag, snd_strerror(chk)));
908 QByteArray dev_ba =
m_mixer.device.toLatin1();
909 struct snd_mixer_selem_regopt regopts =
910 {1, SND_MIXER_SABSTRACT_NONE, dev_ba.constData(),
nullptr,
nullptr};
912 chk = snd_mixer_selem_register(
m_mixer.handle, ®opts,
nullptr);
915 snd_mixer_close(
m_mixer.handle);
917 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to register %1: %2")
918 .arg(mixer_device_tag, snd_strerror(chk)));
922 chk = snd_mixer_load(
m_mixer.handle);
925 snd_mixer_close(
m_mixer.handle);
927 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to load %1: %2")
928 .arg(mixer_device_tag, snd_strerror(chk)));
933 uint elcount = snd_mixer_get_count(
m_mixer.handle);
934 snd_mixer_elem_t* elx = snd_mixer_first_elem(
m_mixer.handle);
936 for (
uint ctr = 0; elx !=
nullptr && ctr < elcount; ctr++)
938 QString
tmp = QString(snd_mixer_selem_get_name(elx));
940 !snd_mixer_selem_is_enumerated(elx) &&
941 snd_mixer_selem_has_playback_volume(elx) &&
942 snd_mixer_selem_is_active(elx))
945 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"found playback control %1 on %2")
946 .arg(
m_mixer.control, mixer_device_tag));
949 elx = snd_mixer_elem_next(elx);
953 snd_mixer_close(
m_mixer.handle);
955 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"no playback control %1 found on %2")
956 .arg(
m_mixer.control, mixer_device_tag));
959 if ((snd_mixer_selem_get_playback_volume_range(
m_mixer.elem,
963 snd_mixer_close(
m_mixer.handle);
965 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to get volume range on %1/%2")
966 .arg(mixer_device_tag,
m_mixer.control));
971 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"mixer volume range on %1/%2 - min %3, max %4, range %5")
972 .arg(mixer_device_tag,
m_mixer.control)
974 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"%1/%2 set up successfully")
975 .arg(mixer_device_tag,
m_mixer.control));
979 int initial_vol = 80;
993 auto *alsadevs =
new QMap<QString, QString>();
994 void **hints =
nullptr;
997 if (snd_device_name_hint(-1,
type, &hints) < 0)
1001 while (*n !=
nullptr)
1003 QString name = snd_device_name_get_hint(*n,
"NAME");
1004 QString desc = snd_device_name_get_hint(*n,
"DESC");
1005 if (!name.isEmpty() && !desc.isEmpty() && (name !=
"null"))
1006 alsadevs->insert(name, desc);
1009 snd_device_name_free_hint(hints);
1012#if SND_LIB_MAJOR == 1
1013#if SND_LIB_MINOR == 0
1014#if SND_LIB_SUBMINOR < 22
1015 snd_config_update_free_global();
static constexpr int FILTER_FLAGS
static constexpr uint8_t CHANNELS_MAX
static void tReorderSmpteToAlsa(AudioDataType *buf, uint frames, uint extrach)
static constexpr int OPEN_FLAGS
static constexpr uint8_t CHANNELS_MIN
static void ReorderSmpteToAlsa(void *buf, uint frames, AudioFormat format, uint extrach)
int SetParameters(snd_pcm_t *handle, snd_pcm_format_t format, uint channels, uint rate, uint buffer_time, uint period_time)
Set the various ALSA audio parameters.
int TryOpenDevice(int open_mode, bool try_ac3)
struct AudioOutputALSA::@0 m_mixer
static QMap< QString, QString > * GetDevices(const char *type)
bool OpenDevice(void) override
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
bool IncPreallocBufferSize(int requested, int buffer_time)
void CloseDevice(void) override
~AudioOutputALSA() override
int GetVolumeChannel(int channel) const override
AudioOutputALSA(const AudioSettings &settings)
int GetPCMInfo(int &card, int &device, int &subdevice)
void WriteAudio(unsigned char *aubuf, int size) override
AudioOutputSettings * GetOutputSettings(bool passthrough) override
static QByteArray * GetELD(int card, int device, int subdevice)
void SetVolumeChannel(int channel, int volume) override
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
AudioFormat m_outputFormat
int m_outputBytesPerFrame
long m_soundcardBufferSize
void InitSettings(const AudioSettings &settings)
static int FormatToBits(AudioFormat format)
void Error(const QString &msg)
void Warn(const QString &msg)
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
static void usleep(std::chrono::microseconds time)
QString GetSetting(const QString &key, const QString &defaultval="")
int GetNumSetting(const QString &key, int defaultval=0)
bool GetBoolSetting(const QString &key, bool defaultval=false)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)