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