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