MythTV  master
audiooutputalsa.cpp
Go to the documentation of this file.
1 #include <cstdint>
2 #include <cstdio>
3 #include <cstdlib>
4 #include <ctime>
5 #include <sys/time.h>
6 
7 #include <QFile>
9 #include "audiooutputalsa.h"
10 
11 #define LOC QString("ALSA: ")
12 
13 // redefine assert as no-op to quiet some compiler warnings
14 // about assert always evaluating true in alsa headers.
15 #undef assert
16 #define assert(x)
17 
18 static constexpr uint8_t CHANNELS_MIN { 1 };
19 static constexpr uint8_t CHANNELS_MAX { 8 };
20 
21 static constexpr int OPEN_FLAGS
22  { SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_FORMAT | SND_PCM_NO_AUTO_CHANNELS };
23 
24 static constexpr int FILTER_FLAGS { ~(SND_PCM_NO_AUTO_FORMAT) };
25 
26 // NOLINTBEGIN(cppcoreguidelines-macro-usage)
27 #define AERROR(str) VBERROR((str) + QString(": %1").arg(snd_strerror(err)))
28 #define CHECKERR(str) { if (err < 0) { AERROR(str); return err; } }
29 // NOLINTEND(cppcoreguidelines-macro-usage)
30 
32  AudioOutputBase(settings)
33 {
34  // Set everything up
35  if (m_passthruDevice == "auto")
36  {
38 
39  int len = m_passthruDevice.length();
40  int args = m_passthruDevice.indexOf(":");
41 
42  /*
43  * AES description:
44  * AES0=6 AES1=0x82 AES2=0x00 AES3=0x01.
45  * AES0 = NON_AUDIO | PRO_MODE
46  * AES1 = original stream, original PCM coder
47  * AES2 = source and channel unspecified
48  * AES3 = sample rate unspecified
49  */
50  bool s48k = gCoreContext->GetBoolSetting("SPDIFRateOverride", false);
51  QString iecarg = QString("AES0=6,AES1=0x82,AES2=0x00") +
52  (s48k ? QString() : QString(",AES3=0x01"));
53  QString iecarg2 = QString("AES0=6 AES1=0x82 AES2=0x00") +
54  (s48k ? QString() : QString(" AES3=0x01"));
55 
56  if (args < 0)
57  {
58  /* no existing parameters: add it behind device name */
59  m_passthruDevice += ":" + iecarg;
60  }
61  else
62  {
63  do
64  ++args;
65  while (args < m_passthruDevice.length() &&
66  m_passthruDevice[args].isSpace());
67  if (args == m_passthruDevice.length())
68  {
69  /* ":" but no parameters */
70  m_passthruDevice += iecarg;
71  }
72  else if (m_passthruDevice[args] != '{')
73  {
74  /* a simple list of parameters: add it at the end of the list */
75  m_passthruDevice += "," + iecarg;
76  }
77  else
78  {
79  /* parameters in config syntax: add it inside the { } block */
80  do
81  --len;
82  while (len > 0 && m_passthruDevice[len].isSpace());
83  if (m_passthruDevice[len] == '}')
85  m_passthruDevice.insert(len, " " + iecarg2);
86  }
87  }
88  }
89  else if (m_passthruDevice.toLower() == "default")
90  {
92  }
93  else
94  {
95  m_discreteDigital = true;
96  }
97 
98  InitSettings(settings);
99  if (settings.m_init)
100  Reconfigure(settings);
101 }
102 
104 {
105  KillAudio();
106 }
107 
108 int AudioOutputALSA::TryOpenDevice(int open_mode, bool try_ac3)
109 {
110  QByteArray dev_ba;
111  int err = -1;
112 
113  if (try_ac3)
114  {
115  dev_ba = m_passthruDevice.toLatin1();
116  VBAUDIO(QString("OpenDevice %1 for passthrough").arg(m_passthruDevice));
117  err = snd_pcm_open(&m_pcmHandle, dev_ba.constData(),
118  SND_PCM_STREAM_PLAYBACK, open_mode);
119 
121 
122  if (m_discreteDigital)
123  return err;
124 
125  if (err < 0)
126  {
127  VBAUDIO(QString("Auto setting passthrough failed (%1), defaulting "
128  "to main device").arg(snd_strerror(err)));
129  }
130  }
131  if (!try_ac3 || err < 0)
132  {
133  // passthru open failed, retry default device
134  VBAUDIO(QString("OpenDevice %1").arg(m_mainDevice));
135  dev_ba = m_mainDevice.toLatin1();
136  err = snd_pcm_open(&m_pcmHandle, dev_ba.constData(),
137  SND_PCM_STREAM_PLAYBACK, open_mode);
139  }
140  return err;
141 }
142 
143 int AudioOutputALSA::GetPCMInfo(int &card, int &device, int &subdevice)
144 {
145  // Check for saved values
146  if (m_card != -1 && m_device != -1 && m_subdevice != -1)
147  {
148  card = m_card;
149  device = m_device;
150  subdevice = m_subdevice;
151  return 0;
152  }
153 
154  if (!m_pcmHandle)
155  return -1;
156 
157  snd_pcm_info_t *pcm_info = nullptr;
158 
159  snd_pcm_info_alloca(&pcm_info);
160 
161  int err = snd_pcm_info(m_pcmHandle, pcm_info);
162  CHECKERR("snd_pcm_info");
163 
164  err = snd_pcm_info_get_card(pcm_info);
165  CHECKERR("snd_pcm_info_get_card");
166  int tcard = err;
167 
168  err = snd_pcm_info_get_device(pcm_info);
169  CHECKERR("snd_pcm_info_get_device");
170  int tdevice = err;
171 
172  err = snd_pcm_info_get_subdevice(pcm_info);
173  CHECKERR("snd_pcm_info_get_subdevice");
174  int tsubdevice = err;
175 
176  m_card = card = tcard;
177  m_device = device = tdevice;
178  m_subdevice = subdevice = tsubdevice;
179 
180  return 0;
181  }
182 
183 bool AudioOutputALSA::IncPreallocBufferSize(int requested, int buffer_time)
184 {
185  int card = 0;
186  int device = 0;
187  int subdevice = 0;
188 
189  m_pbufSize = 0;
190 
191  if (GetPCMInfo(card, device, subdevice) < 0)
192  return false;
193 
194  const QString pf = QString("/proc/asound/card%1/pcm%2p/sub%3/prealloc")
195  .arg(card).arg(device).arg(subdevice);
196 
197  QFile pfile(pf);
198  QFile mfile(pf + "_max");
199 
200  if (!pfile.open(QIODevice::ReadOnly))
201  {
202  VBERROR(QString("Error opening %1. Fix reading permissions.").arg(pf));
203  return false;
204  }
205 
206  if (!mfile.open(QIODevice::ReadOnly))
207  {
208  VBERROR(QString("Error opening %1").arg(pf + "_max"));
209  return false;
210  }
211 
212  int cur = pfile.readAll().trimmed().toInt();
213  int max = mfile.readAll().trimmed().toInt();
214 
215  int size = ((int)(cur * (float)requested / (float)buffer_time)
216  / 64 + 1) * 64;
217 
218  VBAUDIO(QString("Hardware audio buffer cur: %1 need: %2 max allowed: %3")
219  .arg(cur).arg(size).arg(max));
220 
221  if (cur == max)
222  {
223  // It's already the maximum it can be, no point trying further
224  pfile.close();
225  mfile.close();
226  return false;
227  }
228  if (size > max || !size)
229  {
230  size = max;
231  }
232 
233  pfile.close();
234  mfile.close();
235 
236  VBWARN(QString("Try to manually increase audio buffer with: echo %1 "
237  "| sudo tee %2").arg(size).arg(pf));
238  return false;
239 }
240 
241 QByteArray *AudioOutputALSA::GetELD(int card, int device, int subdevice)
242 {
243  QByteArray *result = nullptr;
244  snd_hctl_t *hctl = nullptr;
245  snd_ctl_elem_info_t *info = nullptr;
246  snd_ctl_elem_id_t *id = nullptr;
247  snd_ctl_elem_value_t *control = nullptr;
248 
249  snd_ctl_elem_info_alloca(&info);
250  snd_ctl_elem_id_alloca(&id);
251  snd_ctl_elem_value_alloca(&control);
252 
253  snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
254  snd_ctl_elem_id_set_name(id, "ELD");
255  snd_ctl_elem_id_set_device(id, device);
256  snd_ctl_elem_id_set_subdevice(id, subdevice);
257 
258  int err = snd_hctl_open(&hctl,
259  QString("hw:%1").arg(card).toLatin1().constData(),
260  0);
261  if (err < 0)
262  {
263  VBAUDIO(QString("Control %1 open error: %2")
264  .arg(card)
265  .arg(snd_strerror(err)));
266  return nullptr;
267  }
268  err = snd_hctl_load(hctl);
269  if (err < 0)
270  {
271  VBAUDIO(QString("Control %1 load error: %2")
272  .arg(card)
273  .arg(snd_strerror(err)));
274  /* frees automatically the control which cannot be added. */
275  return nullptr;
276  }
277  snd_hctl_elem_t *elem = snd_hctl_find_elem(hctl, id);
278  if (elem)
279  {
280  err = snd_hctl_elem_info(elem, info);
281  if (err < 0)
282  {
283  VBAUDIO(QString("Control %1 snd_hctl_elem_info error: %2")
284  .arg(card)
285  .arg(snd_strerror(err)));
286  snd_hctl_close(hctl);
287  return nullptr;
288  }
289  unsigned int count = snd_ctl_elem_info_get_count(info);
290  snd_ctl_elem_type_t type = snd_ctl_elem_info_get_type(info);
291  if (!snd_ctl_elem_info_is_readable(info))
292  {
293  VBAUDIO(QString("Control %1 element info is not readable")
294  .arg(card));
295  snd_hctl_close(hctl);
296  return nullptr;
297  }
298  err = snd_hctl_elem_read(elem, control);
299  if (err < 0)
300  {
301  VBAUDIO(QString("Control %1 element read error: %2")
302  .arg(card)
303  .arg(snd_strerror(err)));
304  snd_hctl_close(hctl);
305  return nullptr;
306  }
307  if (type != SND_CTL_ELEM_TYPE_BYTES)
308  {
309  VBAUDIO(QString("Control %1 element is of the wrong type")
310  .arg(card));
311  snd_hctl_close(hctl);
312  return nullptr;
313  }
314  result = new QByteArray((char *)snd_ctl_elem_value_get_bytes(control),
315  count);
316  }
317  snd_hctl_close(hctl);
318  return result;
319 }
320 
322 {
323  snd_pcm_hw_params_t *params = nullptr;
324  snd_pcm_format_t afmt = SND_PCM_FORMAT_UNKNOWN;
325  AudioFormat fmt = FORMAT_NONE;
326 
327  auto *settings = new AudioOutputSettings();
328 
329  if (m_pcmHandle)
330  {
331  snd_pcm_close(m_pcmHandle);
332  m_pcmHandle = nullptr;
333  }
334 
335  int err = TryOpenDevice(OPEN_FLAGS, passthrough);
336  if (err < 0)
337  {
338  AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastDevice));
339  delete settings;
340  return nullptr;
341  }
342 
343  snd_pcm_hw_params_alloca(&params);
344 
345  if (snd_pcm_hw_params_any(m_pcmHandle, params) < 0)
346  {
347  snd_pcm_close(m_pcmHandle);
348  err = TryOpenDevice(OPEN_FLAGS&FILTER_FLAGS, passthrough);
349  if (err < 0)
350  {
351  AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastDevice));
352  delete settings;
353  return nullptr;
354  }
355  err = snd_pcm_hw_params_any(m_pcmHandle, params);
356  if (err < 0)
357  {
358  AERROR("No playback configurations available");
359  snd_pcm_close(m_pcmHandle);
360  m_pcmHandle = nullptr;
361  delete settings;
362  return nullptr;
363  }
364  Warn("Supported audio format detection will be inacurrate "
365  "(using plugin?)");
366  }
367 
368  // NOLINTNEXTLINE(bugprone-infinite-loop)
369  while (int rate = settings->GetNextRate())
370  if(snd_pcm_hw_params_test_rate(m_pcmHandle, params, rate, 0) >= 0)
371  settings->AddSupportedRate(rate);
372 
373  while ((fmt = settings->GetNextFormat()))
374  {
375  switch (fmt)
376  {
377  case FORMAT_U8: afmt = SND_PCM_FORMAT_U8; break;
378  case FORMAT_S16: afmt = SND_PCM_FORMAT_S16; break;
379  // NOLINTNEXTLINE(bugprone-branch-clone)
380  case FORMAT_S24LSB: afmt = SND_PCM_FORMAT_S24; break;
381  case FORMAT_S24: afmt = SND_PCM_FORMAT_S24; break;
382  case FORMAT_S32: afmt = SND_PCM_FORMAT_S32; break;
383  case FORMAT_FLT: afmt = SND_PCM_FORMAT_FLOAT; break;
384  default: continue;
385  }
386  if (snd_pcm_hw_params_test_format(m_pcmHandle, params, afmt) >= 0)
387  settings->AddSupportedFormat(fmt);
388  }
389 
390  for (uint channels = CHANNELS_MIN; channels <= CHANNELS_MAX; channels++)
391  if (snd_pcm_hw_params_test_channels(m_pcmHandle, params, channels) >= 0)
392  settings->AddSupportedChannels(channels);
393 
394  int card = 0;
395  int device = 0;
396  int subdevice = 0;
397  if (GetPCMInfo(card, device, subdevice) >= 0)
398  {
399  // Check if we can retrieve ELD for this device
400  QByteArray *eld = GetELD(card, device, subdevice);
401  if (eld != nullptr)
402  {
403  VBAUDIO(QString("Successfully retrieved ELD data"));
404  settings->setELD(eld);
405  delete eld;
406  }
407  }
408  else
409  {
410  VBAUDIO("Can't get card and device number");
411  }
412 
413  snd_pcm_close(m_pcmHandle);
414  m_pcmHandle = nullptr;
415 
416  /* Check if name or description contains information
417  to know if device can accept passthrough or not */
418  QMap<QString, QString> *alsadevs = GetDevices("pcm");
419  while (true)
420  {
421  QString real_device = ((passthrough && m_discreteDigital) ?
423 
424  QString desc = alsadevs->value(real_device);
425 
426  settings->setPassthrough(1); // yes passthrough
427  if (real_device.contains("digital", Qt::CaseInsensitive) ||
428  desc.contains("digital", Qt::CaseInsensitive))
429  break;
430  if (real_device.contains("iec958", Qt::CaseInsensitive))
431  break;
432  if (real_device.contains("spdif", Qt::CaseInsensitive))
433  break;
434  if (real_device.contains("hdmi", Qt::CaseInsensitive))
435  break;
436 
437  settings->setPassthrough(-1); // no passthrough
438  // PulseAudio does not support passthrough
439  if (real_device.contains("pulse", Qt::CaseInsensitive) ||
440  desc.contains("pulse", Qt::CaseInsensitive))
441  break;
442  if (real_device.contains("analog", Qt::CaseInsensitive) ||
443  desc.contains("analog", Qt::CaseInsensitive))
444  break;
445  if (real_device.contains("surround", Qt::CaseInsensitive) ||
446  desc.contains("surround", Qt::CaseInsensitive))
447  break;
448 
449  settings->setPassthrough(0); // maybe passthrough
450  break;
451  }
452  delete alsadevs;
453  return settings;
454 }
455 
457 {
458  snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
459 
460  if (m_pcmHandle != nullptr)
461  CloseDevice();
462 
463  int err = TryOpenDevice(0, m_passthru || m_enc);
464  if (err < 0)
465  {
466  AERROR(QString("snd_pcm_open(\"%1\")").arg(m_lastDevice));
467  if (m_pcmHandle)
468  CloseDevice();
469  return false;
470  }
471 
472  switch (m_outputFormat)
473  {
474  case FORMAT_U8: format = SND_PCM_FORMAT_U8; break;
475  case FORMAT_S16: format = SND_PCM_FORMAT_S16; break;
476  // NOLINTNEXTLINE(bugprone-branch-clone)
477  case FORMAT_S24LSB: format = SND_PCM_FORMAT_S24; break;
478  case FORMAT_S24: format = SND_PCM_FORMAT_S24; break;
479  case FORMAT_S32: format = SND_PCM_FORMAT_S32; break;
480  case FORMAT_FLT: format = SND_PCM_FORMAT_FLOAT; break;
481  default:
482  Error(QObject::tr("Unknown sample format: %1").arg(m_outputFormat));
483  return false;
484  }
485 
486  // buffer 0.5s worth of samples
487  uint buffer_time = gCoreContext->GetNumSetting("ALSABufferOverride", 500) * 1000;
488 
489  uint period_time = 4; // aim for an interrupt every (1/4th of buffer_time)
490 
492  buffer_time, period_time);
493  if (err < 0)
494  {
495  AERROR("Unable to set ALSA parameters");
496  CloseDevice();
497  return false;
498  }
499 
500  if (m_internalVol && !OpenMixer())
501  VBERROR("Unable to open audio mixer. Volume control disabled");
502 
503  // Device opened successfully
504  return true;
505 }
506 
508 {
509  if (m_mixer.handle)
510  snd_mixer_close(m_mixer.handle);
511  m_mixer.handle = nullptr;
512  if (m_pcmHandle)
513  {
514  snd_pcm_drain(m_pcmHandle);
515  snd_pcm_close(m_pcmHandle);
516  m_pcmHandle = nullptr;
517  }
518 }
519 
520 template <class AudioDataType>
521 static inline void tReorderSmpteToAlsa(AudioDataType *buf, uint frames,
522  uint extrach)
523 {
524  AudioDataType tmpC;
525  AudioDataType tmpLFE;
526  AudioDataType *buf2 = nullptr;
527 
528  for (uint i = 0; i < frames; i++)
529  {
530  buf = buf2 = buf + 2;
531 
532  tmpC = *buf++;
533  tmpLFE = *buf++;
534  *buf2++ = *buf++;
535  *buf2++ = *buf++;
536  *buf2++ = tmpC;
537  *buf2++ = tmpLFE;
538  buf += extrach;
539  }
540 }
541 
542 static inline void ReorderSmpteToAlsa(void *buf, uint frames,
543  AudioFormat format, uint extrach)
544 {
545  switch(AudioOutputSettings::FormatToBits(format))
546  {
547  case 8: tReorderSmpteToAlsa((uchar *)buf, frames, extrach); break;
548  case 16: tReorderSmpteToAlsa((short *)buf, frames, extrach); break;
549  default: tReorderSmpteToAlsa((int *)buf, frames, extrach); break;
550  }
551 }
552 
553 void AudioOutputALSA::WriteAudio(uchar *aubuf, int size)
554 {
555  uchar *tmpbuf = aubuf;
556  uint frames = size / m_outputBytesPerFrame;
557 
558  if (m_pcmHandle == nullptr)
559  {
560  Error("WriteAudio() called with pcm_handle == nullptr!");
561  return;
562  }
563 
564  //Audio received is in SMPTE channel order, reorder to ALSA unless passthru
565  if (!m_passthru && (m_channels == 6 || m_channels == 8))
566  {
567  ReorderSmpteToAlsa(aubuf, frames, m_outputFormat, m_channels - 6);
568  }
569 
570  LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
571  QString("WriteAudio: Preparing %1 bytes (%2 frames)")
572  .arg(size).arg(frames));
573 
574  while (frames > 0)
575  {
576  int lw = snd_pcm_writei(m_pcmHandle, tmpbuf, frames);
577 
578  if (lw >= 0)
579  {
580  if ((uint)lw < frames)
581  VBAUDIO(QString("WriteAudio: short write %1 bytes (ok)")
582  .arg(lw * m_outputBytesPerFrame));
583 
584  frames -= lw;
585  tmpbuf += static_cast<ptrdiff_t>(lw) * m_outputBytesPerFrame; // bytes
586  continue;
587  }
588 
589  int err = lw;
590 
591  switch (err)
592  {
593  case -EPIPE:
594  if (snd_pcm_state(m_pcmHandle) == SND_PCM_STATE_XRUN)
595  {
596  VBAUDIO("WriteAudio: buffer underrun");
597  err = snd_pcm_prepare(m_pcmHandle);
598  if (err < 0)
599  {
600  AERROR("WriteAudio: unable to recover from xrun");
601  return;
602  }
603  }
604  break;
605 
606 #if ESTRPIPE != EPIPE
607  case -ESTRPIPE:
608  VBAUDIO("WriteAudio: device is suspended");
609  while ((err = snd_pcm_resume(m_pcmHandle)) == -EAGAIN)
610  usleep(200us);
611 
612  if (err < 0)
613  {
614  VBERROR("WriteAudio: resume failed");
615  err = snd_pcm_prepare(m_pcmHandle);
616  if (err < 0)
617  {
618  AERROR("WriteAudio: unable to recover from suspend");
619  return;
620  }
621  }
622  break;
623 #endif
624 
625  case -EBADFD:
626  Error(
627  QString("WriteAudio: device is in a bad state (state = %1)")
628  .arg(snd_pcm_state(m_pcmHandle)));
629  return;
630 
631  default:
632  AERROR(QString("WriteAudio: Write failed, state: %1, err")
633  .arg(snd_pcm_state(m_pcmHandle)));
634  return;
635  }
636  }
637 }
638 
640 {
641  if (m_pcmHandle == nullptr)
642  {
643  VBERROR("getBufferedOnSoundcard() called with pcm_handle == nullptr!");
644  return 0;
645  }
646 
647  snd_pcm_sframes_t delay = 0;
648 
649  /* Delay is the total delay from writing to the pcm until the samples
650  hit the DAC - includes buffered samples and any fixed latencies */
651  if (snd_pcm_delay(m_pcmHandle, &delay) < 0)
652  return 0;
653 
654  // BUG: calling snd_pcm_state causes noise and repeats on the Raspberry Pi
655  return delay * m_outputBytesPerFrame;
656 }
657 
665 int AudioOutputALSA::SetParameters(snd_pcm_t *handle, snd_pcm_format_t format,
666  uint channels, uint rate, uint buffer_time,
667  uint period_time)
668 {
669  snd_pcm_hw_params_t *params = nullptr;
670  snd_pcm_sw_params_t *swparams = nullptr;
671  snd_pcm_uframes_t period_size = 0;
672  snd_pcm_uframes_t period_size_min = 0;
673  snd_pcm_uframes_t period_size_max = 0;
674  snd_pcm_uframes_t buffer_size = 0;
675  snd_pcm_uframes_t buffer_size_min = 0;
676  snd_pcm_uframes_t buffer_size_max = 0;
677 
678  VBAUDIO(QString("SetParameters(format=%1, channels=%2, rate=%3, "
679  "buffer_time=%4, period_time=%5)")
680  .arg(format).arg(channels).arg(rate).arg(buffer_time)
681  .arg(period_time));
682 
683  if (handle == nullptr)
684  {
685  Error(QObject::tr("SetParameters() called with handle == nullptr!"));
686  return -1;
687  }
688 
689  snd_pcm_hw_params_alloca(&params);
690  snd_pcm_sw_params_alloca(&swparams);
691 
692  /* choose all parameters */
693  int err = snd_pcm_hw_params_any(handle, params);
694  CHECKERR("No playback configurations available");
695 
696  /* set the interleaved read/write format */
697  err = snd_pcm_hw_params_set_access(handle, params,
698  SND_PCM_ACCESS_RW_INTERLEAVED);
699  CHECKERR(QString("Interleaved RW audio not available"));
700 
701  /* set the sample format */
702  err = snd_pcm_hw_params_set_format(handle, params, format);
703  CHECKERR(QString("Sample format %1 not available").arg(format));
704 
705  /* set the count of channels */
706  err = snd_pcm_hw_params_set_channels(handle, params, channels);
707  CHECKERR(QString("Channels count %1 not available").arg(channels));
708 
709  /* set the stream rate */
711  {
712  err = snd_pcm_hw_params_set_rate_resample(handle, params, 1);
713  CHECKERR(QString("Resampling setup failed").arg(rate));
714 
715  uint rrate = rate;
716  err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, nullptr);
717  CHECKERR(QString("Rate %1Hz not available for playback: %s").arg(rate));
718 
719  if (rrate != rate)
720  {
721  VBERROR(QString("Rate doesn't match (requested %1Hz, got %2Hz)")
722  .arg(rate).arg(err));
723  return err;
724  }
725  }
726  else
727  {
728  err = snd_pcm_hw_params_set_rate(handle, params, rate, 0);
729  CHECKERR(QString("Samplerate %1 Hz not available").arg(rate));
730  }
731 
732  /* get the buffer parameters */
733  (void) snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min);
734  (void) snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max);
735  (void) snd_pcm_hw_params_get_period_size_min(params, &period_size_min, nullptr);
736  (void) snd_pcm_hw_params_get_period_size_max(params, &period_size_max, nullptr);
737  VBAUDIO(QString("Buffer size range from %1 to %2")
738  .arg(buffer_size_min)
739  .arg(buffer_size_max));
740  VBAUDIO(QString("Period size range from %1 to %2")
741  .arg(period_size_min)
742  .arg(period_size_max));
743 
744  /* set the buffer time */
745  uint original_buffer_time = buffer_time;
746  bool canincrease = true;
747  err = snd_pcm_hw_params_set_buffer_time_near(handle, params,
748  &buffer_time, nullptr);
749  if (err < 0)
750  {
751  int dir = -1;
752  uint buftmp = buffer_time;
753  int attempt = 0;
754  do
755  {
756  err = snd_pcm_hw_params_set_buffer_time_near(handle, params,
757  &buffer_time, &dir);
758  if (err < 0)
759  {
760  AERROR(QString("Unable to set buffer time to %1us, retrying")
761  .arg(buffer_time));
762  /*
763  * with some drivers, snd_pcm_hw_params_set_buffer_time_near
764  * only works once, if that's the case no point trying with
765  * different values
766  */
767  if ((buffer_time <= 100000) ||
768  (attempt > 0 && buffer_time == buftmp))
769  {
770  VBERROR("Couldn't set buffer time, giving up");
771  return err;
772  }
773  buffer_time -= 100000;
774  canincrease = false;
775  attempt++;
776  }
777  }
778  while (err < 0);
779  }
780 
781  /* See if we need to increase the prealloc'd buffer size
782  If buffer_time is too small we could underrun - make 10% difference ok */
783  if (buffer_time * 1.10F < (float)original_buffer_time)
784  {
785  VBWARN(QString("Requested %1us got %2 buffer time")
786  .arg(original_buffer_time).arg(buffer_time));
787  // We need to increase preallocated buffer size in the driver
788  if (canincrease && m_pbufSize < 0)
789  {
790  IncPreallocBufferSize(original_buffer_time, buffer_time);
791  }
792  }
793 
794  VBAUDIO(QString("Buffer time = %1 us").arg(buffer_time));
795 
796  /* set the period time */
797  err = snd_pcm_hw_params_set_periods_near(handle, params,
798  &period_time, nullptr);
799  CHECKERR(QString("Unable to set period time %1").arg(period_time));
800  VBAUDIO(QString("Period time = %1 periods").arg(period_time));
801 
802  /* write the parameters to device */
803  err = snd_pcm_hw_params(handle, params);
804  CHECKERR("Unable to set hw params for playback");
805 
806  err = snd_pcm_get_params(handle, &buffer_size, &period_size);
807  CHECKERR("Unable to get PCM params");
808  VBAUDIO(QString("Buffer size = %1 | Period size = %2")
809  .arg(buffer_size).arg(period_size));
810 
811  /* set member variables */
813  m_fragmentSize = (period_size >> 1) * m_outputBytesPerFrame;
814 
815  /* get the current swparams */
816  err = snd_pcm_sw_params_current(handle, swparams);
817  CHECKERR("Unable to get current swparams");
818 
819  /* start the transfer after period_size */
820  err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period_size);
821  CHECKERR("Unable to set start threshold");
822 
823  /* allow the transfer when at least period_size samples can be processed */
824  err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
825  CHECKERR("Unable to set avail min");
826 
827  /* write the parameters to the playback device */
828  err = snd_pcm_sw_params(handle, swparams);
829  CHECKERR("Unable to set sw params");
830 
831  err = snd_pcm_prepare(handle);
832  CHECKERR("Unable to prepare the PCM");
833 
834  return 0;
835 }
836 
837 int AudioOutputALSA::GetVolumeChannel(int channel) const
838 {
839  int retvol = 0;
840 
841  if (!m_mixer.elem)
842  return retvol;
843 
844  auto chan = (snd_mixer_selem_channel_id_t) channel;
845  if (!snd_mixer_selem_has_playback_channel(m_mixer.elem, chan))
846  return retvol;
847 
848  long mixervol = 0;
849  int chk = snd_mixer_selem_get_playback_volume(m_mixer.elem, chan,
850  &mixervol);
851  if (chk < 0)
852  {
853  VBERROR(QString("failed to get channel %1 volume, mixer %2/%3: %4")
854  .arg(QString::number(channel), m_mixer.device,
855  m_mixer.control, snd_strerror(chk)));
856  }
857  else
858  {
859  retvol = (m_mixer.volrange != 0L)
860  ? ((mixervol - m_mixer.volmin) * 100.0F / m_mixer.volrange) + 0.5F
861  : 0;
862  retvol = std::max(retvol, 0);
863  retvol = std::min(retvol, 100);
864  VBAUDIO(QString("get volume channel %1: %2")
865  .arg(channel).arg(retvol));
866  }
867  return retvol;
868 }
869 
870 void AudioOutputALSA::SetVolumeChannel(int channel, int volume)
871 {
872  if (!(m_internalVol && m_mixer.elem))
873  return;
874 
875  long mixervol = ((int64_t(volume) * m_mixer.volrange) / 100) + m_mixer.volmin;
876  mixervol = std::max(mixervol, m_mixer.volmin);
877  mixervol = std::min(mixervol, m_mixer.volmax);
878 
879  auto chan = (snd_mixer_selem_channel_id_t) channel;
880 
881  if (snd_mixer_selem_has_playback_switch(m_mixer.elem))
882  snd_mixer_selem_set_playback_switch(m_mixer.elem, chan, static_cast<int>(volume > 0));
883 
884  if (snd_mixer_selem_set_playback_volume(m_mixer.elem, chan, mixervol) < 0)
885  VBERROR(QString("failed to set channel %1 volume").arg(channel));
886  else
887  VBAUDIO(QString("channel %1 volume set %2 => %3")
888  .arg(channel).arg(volume).arg(mixervol));
889 }
890 
892 {
893  if (!m_pcmHandle)
894  {
895  VBERROR("mixer setup without a pcm");
896  return false;
897  }
898  m_mixer.device = gCoreContext->GetSetting("MixerDevice", "default");
899  m_mixer.device = m_mixer.device.remove(QString("ALSA:"));
900  if (m_mixer.device.toLower() == "software")
901  return true;
902 
903  m_mixer.control = gCoreContext->GetSetting("MixerControl", "PCM");
904 
905  QString mixer_device_tag = QString("mixer device %1").arg(m_mixer.device);
906 
907  int chk = snd_mixer_open(&m_mixer.handle, 0);
908  if (chk < 0)
909  {
910  VBERROR(QString("failed to open mixer device %1: %2")
911  .arg(mixer_device_tag, snd_strerror(chk)));
912  return false;
913  }
914 
915  QByteArray dev_ba = m_mixer.device.toLatin1();
916  struct snd_mixer_selem_regopt regopts =
917  {1, SND_MIXER_SABSTRACT_NONE, dev_ba.constData(), nullptr, nullptr};
918 
919  chk = snd_mixer_selem_register(m_mixer.handle, &regopts, nullptr);
920  if (chk < 0)
921  {
922  snd_mixer_close(m_mixer.handle);
923  m_mixer.handle = nullptr;
924  VBERROR(QString("failed to register %1: %2")
925  .arg(mixer_device_tag, snd_strerror(chk)));
926  return false;
927  }
928 
929  chk = snd_mixer_load(m_mixer.handle);
930  if (chk < 0)
931  {
932  snd_mixer_close(m_mixer.handle);
933  m_mixer.handle = nullptr;
934  VBERROR(QString("failed to load %1: %2")
935  .arg(mixer_device_tag, snd_strerror(chk)));
936  return false;
937  }
938 
939  m_mixer.elem = nullptr;
940  uint elcount = snd_mixer_get_count(m_mixer.handle);
941  snd_mixer_elem_t* elx = snd_mixer_first_elem(m_mixer.handle);
942 
943  for (uint ctr = 0; elx != nullptr && ctr < elcount; ctr++)
944  {
945  QString tmp = QString(snd_mixer_selem_get_name(elx));
946  if (m_mixer.control == tmp &&
947  !snd_mixer_selem_is_enumerated(elx) &&
948  snd_mixer_selem_has_playback_volume(elx) &&
949  snd_mixer_selem_is_active(elx))
950  {
951  m_mixer.elem = elx;
952  VBAUDIO(QString("found playback control %1 on %2")
953  .arg(m_mixer.control, mixer_device_tag));
954  break;
955  }
956  elx = snd_mixer_elem_next(elx);
957  }
958  if (!m_mixer.elem)
959  {
960  snd_mixer_close(m_mixer.handle);
961  m_mixer.handle = nullptr;
962  VBERROR(QString("no playback control %1 found on %2")
963  .arg(m_mixer.control, mixer_device_tag));
964  return false;
965  }
966  if ((snd_mixer_selem_get_playback_volume_range(m_mixer.elem,
967  &m_mixer.volmin,
968  &m_mixer.volmax) < 0))
969  {
970  snd_mixer_close(m_mixer.handle);
971  m_mixer.handle = nullptr;
972  VBERROR(QString("failed to get volume range on %1/%2")
973  .arg(mixer_device_tag, m_mixer.control));
974  return false;
975  }
976 
977  m_mixer.volrange = m_mixer.volmax - m_mixer.volmin;
978  VBAUDIO(QString("mixer volume range on %1/%2 - min %3, max %4, range %5")
979  .arg(mixer_device_tag, m_mixer.control)
980  .arg(m_mixer.volmin).arg(m_mixer.volmax).arg(m_mixer.volrange));
981  VBAUDIO(QString("%1/%2 set up successfully")
982  .arg(mixer_device_tag, m_mixer.control));
983 
984  if (m_setInitialVol)
985  {
986  int initial_vol = 80;
987  if (m_mixer.control == "PCM")
988  initial_vol = gCoreContext->GetNumSetting("PCMMixerVolume", 80);
989  else
990  initial_vol = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
991  for (int ch = 0; ch < m_channels; ++ch)
992  SetVolumeChannel(ch, initial_vol);
993  }
994 
995  return true;
996 }
997 
998 QMap<QString, QString> *AudioOutputALSA::GetDevices(const char *type)
999 {
1000  auto *alsadevs = new QMap<QString, QString>();
1001  void **hints = nullptr;
1002  void **n = nullptr;
1003 
1004  if (snd_device_name_hint(-1, type, &hints) < 0)
1005  return alsadevs;
1006  n = hints;
1007 
1008  while (*n != nullptr)
1009  {
1010  QString name = snd_device_name_get_hint(*n, "NAME");
1011  QString desc = snd_device_name_get_hint(*n, "DESC");
1012  if (!name.isEmpty() && !desc.isEmpty() && (name != "null"))
1013  alsadevs->insert(name, desc);
1014  n++;
1015  }
1016  snd_device_name_free_hint(hints);
1017  // Work around ALSA bug < 1.0.22 ; where snd_device_name_hint can corrupt
1018  // global ALSA memory context
1019 #if SND_LIB_MAJOR == 1
1020 #if SND_LIB_MINOR == 0
1021 #if SND_LIB_SUBMINOR < 22
1022  snd_config_update_free_global();
1023 #endif
1024 #endif
1025 #endif
1026  return alsadevs;
1027 }
AudioOutputBase::m_enc
bool m_enc
Definition: audiooutputbase.h:190
AudioOutputALSA::elem
snd_mixer_elem_t * elem
Definition: audiooutputalsa.h:55
build_compdb.args
args
Definition: build_compdb.py:11
AudioOutputBase::m_discreteDigital
bool m_discreteDigital
Definition: audiooutputbase.h:187
AudioOutputALSA::WriteAudio
void WriteAudio(unsigned char *aubuf, int size) override
Definition: audiooutputalsa.cpp:553
AudioOutputALSA::~AudioOutputALSA
~AudioOutputALSA() override
Definition: audiooutputalsa.cpp:103
VBAUDIO
#define VBAUDIO(str)
Definition: audiooutputbase.h:20
AudioOutputALSA::SetParameters
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.
Definition: audiooutputalsa.cpp:665
AudioOutputALSA::GetOutputSettings
AudioOutputSettings * GetOutputSettings(bool passthrough) override
Definition: audiooutputalsa.cpp:321
FORMAT_S16
@ FORMAT_S16
Definition: audiooutputsettings.h:27
FORMAT_S24
@ FORMAT_S24
Definition: audiooutputsettings.h:29
AudioOutput::Error
void Error(const QString &msg)
Definition: audiooutput.cpp:277
AudioOutputALSA::CloseDevice
void CloseDevice(void) override
Definition: audiooutputalsa.cpp:507
eld
Definition: eldutils.h:37
FORMAT_S32
@ FORMAT_S32
Definition: audiooutputsettings.h:30
VolumeBase::m_internalVol
bool m_internalVol
Definition: volumebase.h:43
AudioOutputBase::KillAudio
void KillAudio(void)
Kill the output thread and cleanup.
Definition: audiooutputbase.cpp:870
AudioOutputSettings::FormatToBits
static int FormatToBits(AudioFormat format)
Definition: audiooutputsettings.cpp:150
MThread::usleep
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
AudioOutput::Warn
void Warn(const QString &msg)
Definition: audiooutput.cpp:288
VBERROR
#define VBERROR(str)
Definition: audiooutputbase.h:23
AudioOutputALSA::GetPCMInfo
int GetPCMInfo(int &card, int &device, int &subdevice)
Definition: audiooutputalsa.cpp:143
AudioOutputALSA::OpenDevice
bool OpenDevice(void) override
Definition: audiooutputalsa.cpp:456
FILTER_FLAGS
static constexpr int FILTER_FLAGS
Definition: audiooutputalsa.cpp:24
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
AudioOutputALSA::GetDevices
static QMap< QString, QString > * GetDevices(const char *type)
Definition: audiooutputalsa.cpp:998
audiooutputalsa.h
AudioOutputALSA::IncPreallocBufferSize
bool IncPreallocBufferSize(int requested, int buffer_time)
Definition: audiooutputalsa.cpp:183
AudioOutputBase::m_sampleRate
int m_sampleRate
Definition: audiooutputbase.h:180
CHANNELS_MAX
static constexpr uint8_t CHANNELS_MAX
Definition: audiooutputalsa.cpp:19
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
ReorderSmpteToAlsa
static void ReorderSmpteToAlsa(void *buf, uint frames, AudioFormat format, uint extrach)
Definition: audiooutputalsa.cpp:542
FORMAT_U8
@ FORMAT_U8
Definition: audiooutputsettings.h:26
FORMAT_S24LSB
@ FORMAT_S24LSB
Definition: audiooutputsettings.h:28
AudioSettings
Definition: audiosettings.h:28
AudioOutputBase::m_mainDevice
QString m_mainDevice
Definition: audiooutputbase.h:185
AudioOutputALSA::m_mixer
struct AudioOutputALSA::@0 m_mixer
AudioOutputBase::m_soundcardBufferSize
long m_soundcardBufferSize
Definition: audiooutputbase.h:183
AudioOutputBase
Definition: audiooutputbase.h:51
AudioOutputALSA::GetBufferedOnSoundcard
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
Definition: audiooutputalsa.cpp:639
AudioSettings::m_init
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:85
tReorderSmpteToAlsa
static void tReorderSmpteToAlsa(AudioDataType *buf, uint frames, uint extrach)
Definition: audiooutputalsa.cpp:521
AudioOutputBase::m_srcQuality
int m_srcQuality
Definition: audiooutputbase.h:215
CHANNELS_MIN
static constexpr uint8_t CHANNELS_MIN
Definition: audiooutputalsa.cpp:18
FORMAT_NONE
@ FORMAT_NONE
Definition: audiooutputsettings.h:25
AudioOutputBase::Reconfigure
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
Definition: audiooutputbase.cpp:472
AudioOutputALSA::SetVolumeChannel
void SetVolumeChannel(int channel, int volume) override
Definition: audiooutputalsa.cpp:870
OPEN_FLAGS
static constexpr int OPEN_FLAGS
Definition: audiooutputalsa.cpp:22
AudioOutputALSA::OpenMixer
bool OpenMixer(void)
Definition: audiooutputalsa.cpp:891
CHECKERR
#define CHECKERR(str)
Definition: audiooutputalsa.cpp:28
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
AudioOutputBase::m_passthru
bool m_passthru
Definition: audiooutputbase.h:189
AudioOutputALSA::handle
snd_mixer_t * handle
Definition: audiooutputalsa.h:54
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:916
AudioOutputBase::m_outputFormat
AudioFormat m_outputFormat
Definition: audiooutputbase.h:179
AudioOutputALSA::device
QString device
Definition: audiooutputalsa.h:52
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:910
AudioOutputALSA::m_device
int m_device
Definition: audiooutputalsa.h:46
AudioOutputBase::m_channels
int m_channels
Definition: audiooutputbase.h:174
AudioOutputBase::m_setInitialVol
bool m_setInitialVol
Definition: audiooutputbase.h:203
AudioOutputBase::m_passthruDevice
QString m_passthruDevice
Definition: audiooutputbase.h:186
AudioOutputALSA::TryOpenDevice
int TryOpenDevice(int open_mode, bool try_ac3)
Definition: audiooutputalsa.cpp:108
AudioOutputBase::m_outputBytesPerFrame
int m_outputBytesPerFrame
Definition: audiooutputbase.h:177
AudioOutputALSA::control
QString control
Definition: audiooutputalsa.h:53
AudioOutputALSA::AudioOutputALSA
AudioOutputALSA(const AudioSettings &settings)
Definition: audiooutputalsa.cpp:31
mythcorecontext.h
AudioOutputALSA::m_pcmHandle
snd_pcm_t * m_pcmHandle
Definition: audiooutputalsa.h:43
AudioOutputALSA::m_pbufSize
int m_pbufSize
Definition: audiooutputalsa.h:44
AudioOutputALSA::m_subdevice
int m_subdevice
Definition: audiooutputalsa.h:47
AudioOutputALSA::GetVolumeChannel
int GetVolumeChannel(int channel) const override
Definition: audiooutputalsa.cpp:837
AudioOutputSettings
Definition: audiooutputsettings.h:48
AudioOutputALSA::m_lastDevice
QString m_lastDevice
Definition: audiooutputalsa.h:49
AudioOutputALSA::m_card
int m_card
Definition: audiooutputalsa.h:45
AudioOutputBase::InitSettings
void InitSettings(const AudioSettings &settings)
Definition: audiooutputbase.cpp:119
AudioOutputALSA::GetELD
static QByteArray * GetELD(int card, int device, int subdevice)
Definition: audiooutputalsa.cpp:241
AudioFormat
AudioFormat
Definition: audiooutputsettings.h:24
AERROR
#define AERROR(str)
Definition: audiooutputalsa.cpp:27
AudioOutputBase::m_fragmentSize
int m_fragmentSize
Definition: audiooutputbase.h:182
azlyrics.info
dictionary info
Definition: azlyrics.py:7
FORMAT_FLT
@ FORMAT_FLT
Definition: audiooutputsettings.h:31
AudioOutputBase::QUALITY_DISABLED
@ QUALITY_DISABLED
Definition: audiooutputbase.h:210
VBWARN
#define VBWARN(str)
Definition: audiooutputbase.h:24
uint
unsigned int uint
Definition: freesurround.h:24
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:902