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