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