12 #define LOC QString("ALSA: ")
23 { SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_FORMAT | SND_PCM_NO_AUTO_CHANNELS };
28 #define AERROR(str) LOG(VB_GENERAL, LOG_ERR, LOC + (str) + QString(": %1").arg(snd_strerror(err)))
29 #define CHECKERR(str) { if (err < 0) { AERROR(str); return err; } }
52 QString iecarg = QString(
"AES0=6,AES1=0x82,AES2=0x00") +
53 (s48k ? QString() : QString(
",AES3=0x01"));
54 QString iecarg2 = QString(
"AES0=6 AES1=0x82 AES2=0x00") +
55 (s48k ? QString() : QString(
" AES3=0x01"));
118 err = snd_pcm_open(&
m_pcmHandle, dev_ba.constData(),
119 SND_PCM_STREAM_PLAYBACK, open_mode);
128 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Auto setting passthrough failed (%1), defaulting "
129 "to main device").arg(snd_strerror(err)));
132 if (!try_ac3 || err < 0)
137 err = snd_pcm_open(&
m_pcmHandle, dev_ba.constData(),
138 SND_PCM_STREAM_PLAYBACK, open_mode);
158 snd_pcm_info_t *pcm_info =
nullptr;
160 snd_pcm_info_alloca(&pcm_info);
165 err = snd_pcm_info_get_card(pcm_info);
169 err = snd_pcm_info_get_device(pcm_info);
170 CHECKERR(
"snd_pcm_info_get_device");
173 err = snd_pcm_info_get_subdevice(pcm_info);
174 CHECKERR(
"snd_pcm_info_get_subdevice");
175 int tsubdevice = err;
195 const QString pf = QString(
"/proc/asound/card%1/pcm%2p/sub%3/prealloc")
196 .arg(card).arg(
device).arg(subdevice);
199 QFile mfile(pf +
"_max");
201 if (!pfile.open(QIODevice::ReadOnly))
203 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error opening %1. Fix reading permissions.").arg(pf));
207 if (!mfile.open(QIODevice::ReadOnly))
209 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error opening %1").arg(pf +
"_max"));
213 int cur = pfile.readAll().trimmed().toInt();
214 int max = mfile.readAll().trimmed().toInt();
216 int size = ((int)(cur * (
float)requested / (float)buffer_time)
219 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Hardware audio buffer cur: %1 need: %2 max allowed: %3")
220 .arg(cur).arg(size).arg(max));
229 if (size > max || !size)
237 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"Try to manually increase audio buffer with: echo %1 "
238 "| sudo tee %2").arg(size).arg(pf));
244 QByteArray *result =
nullptr;
245 snd_hctl_t *hctl =
nullptr;
246 snd_ctl_elem_info_t *
info =
nullptr;
247 snd_ctl_elem_id_t *
id =
nullptr;
248 snd_ctl_elem_value_t *
control =
nullptr;
250 snd_ctl_elem_info_alloca(&
info);
251 snd_ctl_elem_id_alloca(&
id);
252 snd_ctl_elem_value_alloca(&
control);
254 snd_ctl_elem_id_set_interface(
id, SND_CTL_ELEM_IFACE_PCM);
255 snd_ctl_elem_id_set_name(
id,
"ELD");
256 snd_ctl_elem_id_set_device(
id,
device);
257 snd_ctl_elem_id_set_subdevice(
id, subdevice);
259 int err = snd_hctl_open(&hctl,
260 QString(
"hw:%1").arg(card).toLatin1().constData(),
264 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 open error: %2")
266 .arg(snd_strerror(err)));
269 err = snd_hctl_load(hctl);
272 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 load error: %2")
274 .arg(snd_strerror(err)));
278 snd_hctl_elem_t *
elem = snd_hctl_find_elem(hctl,
id);
281 err = snd_hctl_elem_info(
elem,
info);
284 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 snd_hctl_elem_info error: %2")
286 .arg(snd_strerror(err)));
287 snd_hctl_close(hctl);
290 unsigned int count = snd_ctl_elem_info_get_count(
info);
291 snd_ctl_elem_type_t
type = snd_ctl_elem_info_get_type(
info);
292 if (!snd_ctl_elem_info_is_readable(
info))
294 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 element info is not readable")
296 snd_hctl_close(hctl);
302 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 element read error: %2")
304 .arg(snd_strerror(err)));
305 snd_hctl_close(hctl);
308 if (
type != SND_CTL_ELEM_TYPE_BYTES)
310 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Control %1 element is of the wrong type")
312 snd_hctl_close(hctl);
315 result =
new QByteArray((
char *)snd_ctl_elem_value_get_bytes(
control),
318 snd_hctl_close(hctl);
324 snd_pcm_hw_params_t *params =
nullptr;
325 snd_pcm_format_t afmt = SND_PCM_FORMAT_UNKNOWN;
344 snd_pcm_hw_params_alloca(¶ms);
346 if (snd_pcm_hw_params_any(
m_pcmHandle, params) < 0)
359 AERROR(
"No playback configurations available");
365 Warn(
"Supported audio format detection will be inacurrate "
370 while (
int rate = settings->GetNextRate())
371 if(snd_pcm_hw_params_test_rate(
m_pcmHandle, params, rate, 0) >= 0)
372 settings->AddSupportedRate(rate);
374 while ((fmt = settings->GetNextFormat()))
378 case FORMAT_U8: afmt = SND_PCM_FORMAT_U8;
break;
379 case FORMAT_S16: afmt = SND_PCM_FORMAT_S16;
break;
382 case FORMAT_S24: afmt = SND_PCM_FORMAT_S24;
break;
383 case FORMAT_S32: afmt = SND_PCM_FORMAT_S32;
break;
384 case FORMAT_FLT: afmt = SND_PCM_FORMAT_FLOAT;
break;
387 if (snd_pcm_hw_params_test_format(
m_pcmHandle, params, afmt) >= 0)
388 settings->AddSupportedFormat(fmt);
392 if (snd_pcm_hw_params_test_channels(
m_pcmHandle, params, channels) >= 0)
393 settings->AddSupportedChannels(channels);
404 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Successfully retrieved ELD data"));
405 settings->setELD(
eld);
411 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Can't get card and device number");
419 QMap<QString, QString> *alsadevs =
GetDevices(
"pcm");
425 QString desc = alsadevs->value(real_device);
427 settings->setPassthrough(1);
428 if (real_device.contains(
"digital", Qt::CaseInsensitive) ||
429 desc.contains(
"digital", Qt::CaseInsensitive))
431 if (real_device.contains(
"iec958", Qt::CaseInsensitive))
433 if (real_device.contains(
"spdif", Qt::CaseInsensitive))
435 if (real_device.contains(
"hdmi", Qt::CaseInsensitive))
438 settings->setPassthrough(-1);
440 if (real_device.contains(
"pulse", Qt::CaseInsensitive) ||
441 desc.contains(
"pulse", Qt::CaseInsensitive))
443 if (real_device.contains(
"analog", Qt::CaseInsensitive) ||
444 desc.contains(
"analog", Qt::CaseInsensitive))
446 if (real_device.contains(
"surround", Qt::CaseInsensitive) ||
447 desc.contains(
"surround", Qt::CaseInsensitive))
450 settings->setPassthrough(0);
459 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
475 case FORMAT_U8: format = SND_PCM_FORMAT_U8;
break;
476 case FORMAT_S16: format = SND_PCM_FORMAT_S16;
break;
479 case FORMAT_S24: format = SND_PCM_FORMAT_S24;
break;
480 case FORMAT_S32: format = SND_PCM_FORMAT_S32;
break;
481 case FORMAT_FLT: format = SND_PCM_FORMAT_FLOAT;
break;
490 uint period_time = 4;
493 buffer_time, period_time);
496 AERROR(
"Unable to set ALSA parameters");
502 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to open audio mixer. Volume control disabled");
511 snd_mixer_close(
m_mixer.handle);
521 template <
class AudioDataType>
526 AudioDataType tmpLFE;
527 AudioDataType *buf2 =
nullptr;
529 for (
uint i = 0; i < frames; i++)
531 buf = buf2 = buf + 2;
556 uchar *tmpbuf = aubuf;
561 Error(
"WriteAudio() called with pcm_handle == nullptr!");
571 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
572 QString(
"WriteAudio: Preparing %1 bytes (%2 frames)")
573 .arg(size).arg(frames));
577 int lw = snd_pcm_writei(
m_pcmHandle, tmpbuf, frames);
581 if ((
uint)lw < frames)
582 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"WriteAudio: short write %1 bytes (ok)")
595 if (snd_pcm_state(
m_pcmHandle) == SND_PCM_STATE_XRUN)
597 LOG(VB_AUDIO, LOG_INFO,
LOC +
"WriteAudio: buffer underrun");
601 AERROR(
"WriteAudio: unable to recover from xrun");
607 #if ESTRPIPE != EPIPE
609 LOG(VB_AUDIO, LOG_INFO,
LOC +
"WriteAudio: device is suspended");
610 while ((err = snd_pcm_resume(
m_pcmHandle)) == -EAGAIN)
615 LOG(VB_GENERAL, LOG_ERR,
LOC +
"WriteAudio: resume failed");
619 AERROR(
"WriteAudio: unable to recover from suspend");
628 QString(
"WriteAudio: device is in a bad state (state = %1)")
633 AERROR(QString(
"WriteAudio: Write failed, state: %1, err")
644 LOG(VB_GENERAL, LOG_ERR,
LOC +
"getBufferedOnSoundcard() called with pcm_handle == nullptr!");
648 snd_pcm_sframes_t delay = 0;
670 snd_pcm_hw_params_t *params =
nullptr;
671 snd_pcm_sw_params_t *swparams =
nullptr;
672 snd_pcm_uframes_t period_size = 0;
673 snd_pcm_uframes_t period_size_min = 0;
674 snd_pcm_uframes_t period_size_max = 0;
675 snd_pcm_uframes_t buffer_size = 0;
676 snd_pcm_uframes_t buffer_size_min = 0;
677 snd_pcm_uframes_t buffer_size_max = 0;
679 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"SetParameters(format=%1, channels=%2, rate=%3, "
680 "buffer_time=%4, period_time=%5)")
681 .arg(format).arg(channels).arg(rate).arg(buffer_time)
686 Error(QObject::tr(
"SetParameters() called with handle == nullptr!"));
690 snd_pcm_hw_params_alloca(¶ms);
691 snd_pcm_sw_params_alloca(&swparams);
694 int err = snd_pcm_hw_params_any(
handle, params);
695 CHECKERR(
"No playback configurations available");
698 err = snd_pcm_hw_params_set_access(
handle, params,
699 SND_PCM_ACCESS_RW_INTERLEAVED);
700 CHECKERR(QString(
"Interleaved RW audio not available"));
703 err = snd_pcm_hw_params_set_format(
handle, params, format);
704 CHECKERR(QString(
"Sample format %1 not available").arg(format));
707 err = snd_pcm_hw_params_set_channels(
handle, params, channels);
708 CHECKERR(QString(
"Channels count %1 not available").arg(channels));
713 err = snd_pcm_hw_params_set_rate_resample(
handle, params, 1);
714 CHECKERR(QString(
"Resampling setup failed").arg(rate));
717 err = snd_pcm_hw_params_set_rate_near(
handle, params, &rrate,
nullptr);
718 CHECKERR(QString(
"Rate %1Hz not available for playback: %s").arg(rate));
722 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Rate doesn't match (requested %1Hz, got %2Hz)")
723 .arg(rate).arg(err));
729 err = snd_pcm_hw_params_set_rate(
handle, params, rate, 0);
730 CHECKERR(QString(
"Samplerate %1 Hz not available").arg(rate));
734 (void) snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min);
735 (void) snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max);
736 (void) snd_pcm_hw_params_get_period_size_min(params, &period_size_min,
nullptr);
737 (void) snd_pcm_hw_params_get_period_size_max(params, &period_size_max,
nullptr);
738 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffer size range from %1 to %2")
739 .arg(buffer_size_min)
740 .arg(buffer_size_max));
741 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Period size range from %1 to %2")
742 .arg(period_size_min)
743 .arg(period_size_max));
746 uint original_buffer_time = buffer_time;
747 bool canincrease =
true;
748 err = snd_pcm_hw_params_set_buffer_time_near(
handle, params,
749 &buffer_time,
nullptr);
753 uint buftmp = buffer_time;
757 err = snd_pcm_hw_params_set_buffer_time_near(
handle, params,
761 AERROR(QString(
"Unable to set buffer time to %1us, retrying")
768 if ((buffer_time <= 100000) ||
769 (attempt > 0 && buffer_time == buftmp))
771 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Couldn't set buffer time, giving up");
774 buffer_time -= 100000;
784 if (buffer_time * 1.10F < (
float)original_buffer_time)
786 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"Requested %1us got %2 buffer time")
787 .arg(original_buffer_time).arg(buffer_time));
795 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffer time = %1 us").arg(buffer_time));
798 err = snd_pcm_hw_params_set_periods_near(
handle, params,
799 &period_time,
nullptr);
800 CHECKERR(QString(
"Unable to set period time %1").arg(period_time));
801 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Period time = %1 periods").arg(period_time));
804 err = snd_pcm_hw_params(
handle, params);
805 CHECKERR(
"Unable to set hw params for playback");
807 err = snd_pcm_get_params(
handle, &buffer_size, &period_size);
808 CHECKERR(
"Unable to get PCM params");
809 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffer size = %1 | Period size = %2")
810 .arg(buffer_size).arg(period_size));
817 err = snd_pcm_sw_params_current(
handle, swparams);
818 CHECKERR(
"Unable to get current swparams");
821 err = snd_pcm_sw_params_set_start_threshold(
handle, swparams, period_size);
822 CHECKERR(
"Unable to set start threshold");
825 err = snd_pcm_sw_params_set_avail_min(
handle, swparams, period_size);
826 CHECKERR(
"Unable to set avail min");
829 err = snd_pcm_sw_params(
handle, swparams);
830 CHECKERR(
"Unable to set sw params");
832 err = snd_pcm_prepare(
handle);
833 CHECKERR(
"Unable to prepare the PCM");
845 auto chan = (snd_mixer_selem_channel_id_t) channel;
846 if (!snd_mixer_selem_has_playback_channel(
m_mixer.elem, chan))
850 int chk = snd_mixer_selem_get_playback_volume(
m_mixer.elem, chan,
854 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to get channel %1 volume, mixer %2/%3: %4")
855 .arg(QString::number(channel),
m_mixer.device,
856 m_mixer.control, snd_strerror(chk)));
860 retvol = (
m_mixer.volrange != 0L)
861 ? ((mixervol -
m_mixer.volmin) * 100.0F /
m_mixer.volrange) + 0.5F
863 retvol = std::max(retvol, 0);
864 retvol = std::min(retvol, 100);
865 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"get volume channel %1: %2")
866 .arg(channel).arg(retvol));
876 long mixervol = ((int64_t(volume) *
m_mixer.volrange) / 100) +
m_mixer.volmin;
877 mixervol = std::max(mixervol,
m_mixer.volmin);
878 mixervol = std::min(mixervol,
m_mixer.volmax);
880 auto chan = (snd_mixer_selem_channel_id_t) channel;
882 if (snd_mixer_selem_has_playback_switch(
m_mixer.elem))
883 snd_mixer_selem_set_playback_switch(
m_mixer.elem, chan,
static_cast<int>(volume > 0));
885 if (snd_mixer_selem_set_playback_volume(
m_mixer.elem, chan, mixervol) < 0)
886 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to set channel %1 volume").arg(channel));
888 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"channel %1 volume set %2 => %3")
889 .arg(channel).arg(volume).arg(mixervol));
896 LOG(VB_GENERAL, LOG_ERR,
LOC +
"mixer setup without a pcm");
901 if (
m_mixer.device.toLower() ==
"software")
906 QString mixer_device_tag = QString(
"mixer device %1").arg(
m_mixer.device);
908 int chk = snd_mixer_open(&
m_mixer.handle, 0);
911 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to open mixer device %1: %2")
912 .arg(mixer_device_tag, snd_strerror(chk)));
916 QByteArray dev_ba =
m_mixer.device.toLatin1();
917 struct snd_mixer_selem_regopt regopts =
918 {1, SND_MIXER_SABSTRACT_NONE, dev_ba.constData(),
nullptr,
nullptr};
920 chk = snd_mixer_selem_register(
m_mixer.handle, ®opts,
nullptr);
923 snd_mixer_close(
m_mixer.handle);
925 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to register %1: %2")
926 .arg(mixer_device_tag, snd_strerror(chk)));
930 chk = snd_mixer_load(
m_mixer.handle);
933 snd_mixer_close(
m_mixer.handle);
935 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to load %1: %2")
936 .arg(mixer_device_tag, snd_strerror(chk)));
941 uint elcount = snd_mixer_get_count(
m_mixer.handle);
942 snd_mixer_elem_t* elx = snd_mixer_first_elem(
m_mixer.handle);
944 for (
uint ctr = 0; elx !=
nullptr && ctr < elcount; ctr++)
946 QString
tmp = QString(snd_mixer_selem_get_name(elx));
948 !snd_mixer_selem_is_enumerated(elx) &&
949 snd_mixer_selem_has_playback_volume(elx) &&
950 snd_mixer_selem_is_active(elx))
953 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"found playback control %1 on %2")
954 .arg(
m_mixer.control, mixer_device_tag));
957 elx = snd_mixer_elem_next(elx);
961 snd_mixer_close(
m_mixer.handle);
963 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"no playback control %1 found on %2")
964 .arg(
m_mixer.control, mixer_device_tag));
967 if ((snd_mixer_selem_get_playback_volume_range(
m_mixer.elem,
971 snd_mixer_close(
m_mixer.handle);
973 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"failed to get volume range on %1/%2")
974 .arg(mixer_device_tag,
m_mixer.control));
979 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"mixer volume range on %1/%2 - min %3, max %4, range %5")
980 .arg(mixer_device_tag,
m_mixer.control)
982 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"%1/%2 set up successfully")
983 .arg(mixer_device_tag,
m_mixer.control));
987 int initial_vol = 80;
1001 auto *alsadevs =
new QMap<QString, QString>();
1002 void **hints =
nullptr;
1005 if (snd_device_name_hint(-1,
type, &hints) < 0)
1009 while (*n !=
nullptr)
1011 QString name = snd_device_name_get_hint(*n,
"NAME");
1012 QString desc = snd_device_name_get_hint(*n,
"DESC");
1013 if (!name.isEmpty() && !desc.isEmpty() && (name !=
"null"))
1014 alsadevs->insert(name, desc);
1017 snd_device_name_free_hint(hints);
1020 #if SND_LIB_MAJOR == 1
1021 #if SND_LIB_MINOR == 0
1022 #if SND_LIB_SUBMINOR < 22
1023 snd_config_update_free_global();