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