kwave  18.07.70
PlayBack-ALSA.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  PlayBack-ALSA.cpp - playback device for ALSA
3  -------------------
4  begin : Sat Mar 05 2005
5  copyright : (C) 2005 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7 
8  much of this code is based on "aplay.c" from alsa-utils-1.0.8
9  from the ALSA project, see http://www.alsa-project.org/
10 
11  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
12  Based on vplay program by Michael Beck
13  ***************************************************************************/
14 
15 /***************************************************************************
16  * *
17  * This program is free software; you can redistribute it and/or modify *
18  * it under the terms of the GNU General Public License as published by *
19  * the Free Software Foundation; either version 2 of the License, or *
20  * (at your option) any later version. *
21  * *
22  ***************************************************************************/
23 
24 #include "config.h"
25 #ifdef HAVE_ALSA_SUPPORT
26 
27 #include <errno.h>
28 #include <math.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 
32 #include <QLatin1Char>
33 #include <QString>
34 #include <QtGlobal>
35 
36 #include <KLocalizedString>
37 
38 #include "libkwave/Compression.h"
40 #include "libkwave/SampleFormat.h"
41 #include "libkwave/String.h"
42 #include "libkwave/Utils.h"
43 #include "libkwave/memcpy.h"
44 
45 #include "PlayBack-ALSA.h"
46 
47 QMap<QString, QString> Kwave::PlayBackALSA::m_device_list;
48 
50 #define DEFAULT_DEVICE (i18n("Default device") + _("|sound_note"))
51 
53 #define NULL_DEVICE (i18n("Null device") + _("|sound_note"))
54 
55 //***************************************************************************
56 
57 /* define some endian dependent symbols that are missing in ALSA */
58 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
59 // big endian
60 #define SND_PCM_FORMAT_S18_3 SND_PCM_FORMAT_S18_3BE
61 #define SND_PCM_FORMAT_U18_3 SND_PCM_FORMAT_U18_3BE
62 #define SND_PCM_FORMAT_U20_3 SND_PCM_FORMAT_U20_3BE
63 #define SND_PCM_FORMAT_S20_3 SND_PCM_FORMAT_S20_3BE
64 #define SND_PCM_FORMAT_U24_3 SND_PCM_FORMAT_U24_3BE
65 #define SND_PCM_FORMAT_S24_3 SND_PCM_FORMAT_S24_3BE
66 
67 #else
68 // little endian
69 #define SND_PCM_FORMAT_S18_3 SND_PCM_FORMAT_S18_3LE
70 #define SND_PCM_FORMAT_U18_3 SND_PCM_FORMAT_U18_3LE
71 #define SND_PCM_FORMAT_U20_3 SND_PCM_FORMAT_U20_3LE
72 #define SND_PCM_FORMAT_S20_3 SND_PCM_FORMAT_S20_3LE
73 #define SND_PCM_FORMAT_U24_3 SND_PCM_FORMAT_U24_3LE
74 #define SND_PCM_FORMAT_S24_3 SND_PCM_FORMAT_S24_3LE
75 
76 #endif
77 
91 static const snd_pcm_format_t _known_formats[] =
92 {
93  /* 8 bit */
94  SND_PCM_FORMAT_S8,
95  SND_PCM_FORMAT_U8,
96 
97  /* 16 bit */
98  SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE,
99  SND_PCM_FORMAT_U16, SND_PCM_FORMAT_U16_LE, SND_PCM_FORMAT_U16_BE,
100 
101  /* 18 bit / 3 byte */
102  SND_PCM_FORMAT_S18_3, SND_PCM_FORMAT_S18_3LE, SND_PCM_FORMAT_S18_3BE,
103  SND_PCM_FORMAT_U18_3, SND_PCM_FORMAT_U18_3LE, SND_PCM_FORMAT_U18_3BE,
104 
105  /* 20 bit / 3 byte */
106  SND_PCM_FORMAT_S20_3, SND_PCM_FORMAT_S20_3LE, SND_PCM_FORMAT_S20_3BE,
107  SND_PCM_FORMAT_U20_3, SND_PCM_FORMAT_U20_3LE, SND_PCM_FORMAT_U20_3BE,
108 
109  /* 24 bit / 3 byte */
110  SND_PCM_FORMAT_S24_3, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3BE,
111  SND_PCM_FORMAT_U24_3, SND_PCM_FORMAT_U24_3LE, SND_PCM_FORMAT_U24_3BE,
112 
113  /* 24 bit / 4 byte */
114  SND_PCM_FORMAT_S24, SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_BE,
115  SND_PCM_FORMAT_U24, SND_PCM_FORMAT_U24_LE, SND_PCM_FORMAT_U24_BE,
116 
117  /* 32 bit */
118  SND_PCM_FORMAT_S32, SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S32_BE,
119  SND_PCM_FORMAT_U32, SND_PCM_FORMAT_U32_LE, SND_PCM_FORMAT_U32_BE,
120 
121 };
122 
123 //***************************************************************************
125 static Kwave::SampleFormat::Format sample_format_of(snd_pcm_format_t fmt)
126 {
127  if (snd_pcm_format_float(fmt)) {
128  if (snd_pcm_format_width(fmt) == 32)
130  if (snd_pcm_format_width(fmt) == 64)
132  } else if (snd_pcm_format_linear(fmt)) {
133  if (snd_pcm_format_signed(fmt) == 1)
135  else if (snd_pcm_format_unsigned(fmt) == 1)
137  }
138 
140 }
141 
142 //***************************************************************************
144 static Kwave::byte_order_t endian_of(snd_pcm_format_t fmt)
145 {
146  if (snd_pcm_format_little_endian(fmt) == 1)
147  return Kwave::LittleEndian;
148  if (snd_pcm_format_big_endian(fmt) == 1)
149  return Kwave::BigEndian;
150  return Kwave::CpuEndian;
151 }
152 
153 //***************************************************************************
159 template <class T, int (allocator)(T**), void (deleter)(T*)>
161 {
162 public:
165  :m_data(Q_NULLPTR)
166  {
167  allocator(&m_data);
168  }
169 
172  {
173  if (m_data) deleter(m_data);
174  m_data = Q_NULLPTR;
175  }
176 
178  inline operator T *&() { return m_data; }
179 
180 private:
181  T *m_data;
182 };
183 
184 //***************************************************************************
185 
187 #define ALSA_MALLOC_WRAPPER(__t__) \
188  AlsaMallocWrapper<__t__##_t, __t__##_malloc, __t__##_free>
189 
190 //***************************************************************************
192  :Kwave::PlayBackDevice(),
193  m_device_name(),
194  m_handle(Q_NULLPTR),
195  m_rate(0),
196  m_channels(0),
197  m_bits(0),
198  m_bytes_per_sample(0),
199  m_bufbase(0),
200  m_buffer(),
201  m_buffer_size(0),
202  m_buffer_used(0),
203  m_format(),
204  m_chunk_size(0),
205  m_supported_formats(),
206  m_encoder(Q_NULLPTR)
207 {
208 }
209 
210 //***************************************************************************
212 {
213  close();
214 }
215 
216 //***************************************************************************
217 int Kwave::PlayBackALSA::setFormat(snd_pcm_hw_params_t *hw_params,
218  unsigned int bits)
219 {
220  qDebug("PlayBackALSA::setFormat(..., bits=%u)", bits);
221  m_format = SND_PCM_FORMAT_UNKNOWN;
222  m_bits = 0;
223  m_bytes_per_sample = 0;
224  if (m_encoder) delete m_encoder;
225  m_encoder = Q_NULLPTR;
226 
227  // get a format that matches the number of bits
228  int format_index = mode2format(bits);
229  if (format_index < 0) {
230  qWarning("PlayBackALSA::setFormat(): %u bit is not supported", bits);
231  return -EINVAL;
232  }
233 
234  m_format = _known_formats[format_index];
235  m_bits = snd_pcm_format_width(m_format);
237  ((snd_pcm_format_physical_width(m_format) + 7) >> 3) * m_channels;
238 
241  m_bits,
243  );
244  Q_ASSERT(m_encoder);
245  if (!m_encoder) {
246  qWarning("PlayBackALSA: out of memory");
247  return -ENOMEM;
248  }
249 
250  // activate the settings
251  Q_ASSERT(m_bits);
252  Q_ASSERT(m_bytes_per_sample);
253  Q_ASSERT(m_format != SND_PCM_FORMAT_UNKNOWN);
254  int err = snd_pcm_hw_params_set_format(m_handle, hw_params, m_format);
255  Q_ASSERT(!err);
256 
257  return err;
258 }
259 
260 //***************************************************************************
262 {
263  // loop over all supported formats and keep only those that are
264  // compatible with the given compression, bits and sample format
265  foreach (int index, m_supported_formats) {
266  const snd_pcm_format_t *fmt = &_known_formats[index];
267 
268  if (snd_pcm_format_width(*fmt) != bits) continue;
269 
270  // mode is compatible
271  // As the list of known formats is already sorted so that
272  // the simplest formats come first, we don't have a lot
273  // of work -> just take the first entry ;-)
274 // qDebug("PlayBackALSA::mode2format -> %d", index);
275  return index;
276  }
277 
278  qWarning("PlayBackALSA::mode2format -> no match found !?");
279  return -1;
280 }
281 
282 //***************************************************************************
283 QList<int> Kwave::PlayBackALSA::detectSupportedFormats(const QString &device)
284 {
285  // start with an empty list
286  QList<int> supported_formats;
287 
288  ALSA_MALLOC_WRAPPER(snd_pcm_hw_params) p;
289 
290  if (!p) return supported_formats;
291 
292  snd_pcm_t *pcm = openDevice(device);
293  if (!pcm) return supported_formats;
294 
295  if (snd_pcm_hw_params_any(pcm, p) < 0) {
296  if (pcm != m_handle) snd_pcm_close(pcm);
297  return supported_formats;
298  }
299 
300  // try all known formats
301 // qDebug("--- list of supported formats --- ");
302  const unsigned int count =
303  sizeof(_known_formats) / sizeof(_known_formats[0]);
304  for (unsigned int i=0; i < count; i++) {
305  // test the sample format
306  snd_pcm_format_t format = _known_formats[i];
307  int err = snd_pcm_hw_params_test_format(pcm, p, format);
308  if (err < 0) continue;
309 
310  const snd_pcm_format_t *fmt = &(_known_formats[i]);
311 
312  // eliminate duplicate alsa sample formats (e.g. BE/LE)
313  foreach (int index, m_supported_formats) {
314  const snd_pcm_format_t *f = &_known_formats[index];
315  if (*f == *fmt) {
316  fmt = Q_NULLPTR;
317  break;
318  }
319  }
320  if (!fmt) continue;
321 
322 // Kwave::Compression t;
323 // Kwave::SampleFormat::Map sf;
324 // qDebug("#%2u, %2d, %2u bit [%u byte], %s, '%s'",
325 // i,
326 // *fmt,
327 // snd_pcm_format_width(*fmt),
328 // (snd_pcm_format_physical_width(*fmt)+7) >> 3,
329 // endian_of(*fmt) == Kwave::CpuEndian ? "CPU" :
330 // (endian_of(*fmt) == Kwave::LittleEndian ? "LE " : "BE "),
331 // sf.description(sf.findFromData(sample_format_of(
332 // *fmt), true)).local8Bit().data());
333 
334  supported_formats.append(i);
335  }
336 // qDebug("--------------------------------- ");
337 
338  if (pcm != m_handle) snd_pcm_close(pcm);
339  return supported_formats;
340 }
341 
342 //***************************************************************************
343 int Kwave::PlayBackALSA::openDevice(const QString &device, unsigned int rate,
344  unsigned int channels, unsigned int bits)
345 {
346  int err;
347  snd_output_t *output = Q_NULLPTR;
348  ALSA_MALLOC_WRAPPER(snd_pcm_hw_params) hw_params;
349  ALSA_MALLOC_WRAPPER(snd_pcm_sw_params) sw_params;
350  snd_pcm_uframes_t buffer_size;
351  unsigned period_time = 0; // period time in us
352  unsigned buffer_time = 0; // ring buffer length in us
353  snd_pcm_uframes_t period_frames = 0;
354  snd_pcm_uframes_t buffer_frames = 0;
355  snd_pcm_uframes_t start_threshold, stop_threshold;
356 
357  m_chunk_size = 0;
358  if (m_handle) snd_pcm_close(m_handle);
359  m_handle = Q_NULLPTR;
360 
361  // translate verbose name to internal ALSA name
362  QString alsa_device = alsaDeviceName(device);
363  qDebug("PlayBackALSA::openDevice() - opening ALSA device '%s', "
364  "%dHz %d channels, %u bit",
365  DBG(alsa_device.split(_("|")).at(0)), rate, channels, bits);
366 
367  // workaround for bug in ALSA
368  // if the device name ends with "," -> invalid name
369  if (alsa_device.endsWith(_(","))) return -ENODEV;
370 
371  if (!rate) return -EINVAL;
372  if (!channels) return -EINVAL;
373  if (!bits) return -EINVAL;
374 
375  err = snd_output_stdio_attach(&output, stderr, 0);
376  if (err < 0) {
377  qWarning("Output failed: %s", snd_strerror(err));
378  }
379 
380  // open a new one
381  err = snd_pcm_open(&m_handle, alsa_device.toLocal8Bit().data(),
382  SND_PCM_STREAM_PLAYBACK,
383  SND_PCM_NONBLOCK);
384  if (err < 0) return err;
385 
386 #if 0
387  // this would be very nice if it works, but currently (alsa-1.0.8)
388  // it causes only a segfault :-(
389  err = snd_spcm_init(m_handle,
390  (unsigned int)m_rate,
391  m_channels,
392  SND_PCM_FORMAT_S16_LE,
393  SND_PCM_SUBFORMAT_STD,
394  SND_SPCM_LATENCY_MEDIUM,
395  SND_PCM_ACCESS_RW_INTERLEAVED,
396  SND_SPCM_XRUN_IGNORE
397  );
398  if (err < 0) {
399  qWarning("Cannot initialize '%s': %s",
400  device.local8Bit().data(), snd_strerror(err));
401  return err;
402  }
403 #else
404  if ((err = snd_pcm_hw_params_any(m_handle, hw_params)) < 0) {
405  qWarning("Cannot initialize hardware parameters: %s",
406  snd_strerror(err));
407  snd_output_close(output);
408  return err;
409  }
410 
411  err = snd_pcm_hw_params_set_access(m_handle, hw_params,
412  SND_PCM_ACCESS_RW_INTERLEAVED);
413  if (err < 0) {
414  qWarning("Cannot set access type: %s", snd_strerror(err));
415  snd_output_close(output);
416  return err;
417  }
418 
419  err = setFormat(hw_params, bits);
420  if (err < 0) {
421  qWarning("Cannot set sample format: %s", snd_strerror(err));
422  snd_output_close(output);
423  return err;
424  }
425 
426  err = snd_pcm_hw_params_set_channels(m_handle, hw_params, channels);
427  if (err < 0) {
428  qWarning("Cannot set channel count: %s", snd_strerror(err));
429  snd_output_close(output);
430  return err;
431  }
432 
433  unsigned int rrate = rate;
434  err = snd_pcm_hw_params_set_rate_near(m_handle, hw_params, &rrate,
435  Q_NULLPTR);
436  if (err < 0) {
437  qWarning("Cannot set sample rate: %s", snd_strerror(err));
438  snd_output_close(output);
439  return err;
440  }
441  qDebug(" real rate = %u", rrate);
442  if (static_cast<float>(rate) * 1.05f < rrate ||
443  static_cast<float>(rate) * 0.95f > rrate)
444  {
445  qWarning("rate is not accurate (requested = %iHz, got = %iHz)",
446  rate, rrate);
447  qWarning(" please, try the plug plugin (-Dplug:%s)",
448  snd_pcm_name(m_handle));
449  }
450 
451  err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &buffer_time,
452  Q_NULLPTR);
453  Q_ASSERT(err >= 0);
454  if (buffer_time > 500000) buffer_time = 500000;
455  if (buffer_time > 0)
456  period_time = buffer_time / 4;
457  else
458  period_frames = buffer_frames / 4;
459 
460  if (period_time > 0) {
461  err = snd_pcm_hw_params_set_period_time_near(m_handle, hw_params,
462  &period_time, Q_NULLPTR);
463  } else {
464  err = snd_pcm_hw_params_set_period_size_near(m_handle, hw_params,
465  &period_frames, Q_NULLPTR);
466  }
467  Q_ASSERT(err >= 0);
468  if (buffer_time > 0) {
469  err = snd_pcm_hw_params_set_buffer_time_near(m_handle, hw_params,
470  &buffer_time, Q_NULLPTR);
471  } else {
472  err = snd_pcm_hw_params_set_buffer_size_near(m_handle, hw_params,
473  &buffer_frames);
474  }
475  Q_ASSERT(err >= 0);
476 
477  qDebug(" setting hw_params");
478  err = snd_pcm_hw_params(m_handle, hw_params);
479  if (err < 0) {
480  snd_pcm_dump(m_handle, output);
481  snd_output_close(output);
482  qWarning("Cannot set parameters: %s", snd_strerror(err));
483  return err;
484  }
485 
486  snd_pcm_hw_params_get_period_size(hw_params, &m_chunk_size, Q_NULLPTR);
487  snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
488  if (m_chunk_size == buffer_size) {
489  qWarning("Can't use period equal to buffer size (%lu == %lu)",
490  m_chunk_size, buffer_size);
491  snd_output_close(output);
492  return -EIO;
493  }
494 
495  /* set software parameters */
496  err = snd_pcm_sw_params_current(m_handle, sw_params);
497  if (err < 0) {
498  qWarning("Unable to determine current software parameters: %s",
499  snd_strerror(err));
500  snd_output_close(output);
501  return err;
502  }
503 
504  err = snd_pcm_sw_params_set_avail_min(m_handle, sw_params, m_chunk_size);
505  Q_ASSERT(err >= 0);
506 
507  /* round up to closest transfer boundary */
508  start_threshold = 1;
509  err = snd_pcm_sw_params_set_start_threshold(m_handle, sw_params,
510  start_threshold);
511  Q_ASSERT(err >= 0);
512  stop_threshold = buffer_size;
513 
514  err = snd_pcm_sw_params_set_stop_threshold(m_handle, sw_params,
515  stop_threshold);
516  Q_ASSERT(err >= 0);
517 
518  // write the software parameters to the playback device
519  err = snd_pcm_sw_params(m_handle, sw_params);
520  if (err < 0) {
521  qDebug(" activating snd_pcm_sw_params FAILED");
522  snd_pcm_dump(m_handle, output);
523  qWarning("Unable to set software parameters: %s", snd_strerror(err));
524  }
525 #endif
526 
527  snd_pcm_dump(m_handle, output);
528  snd_output_close(output);
529 
530  // prepare the device for playback
531  if ((err = snd_pcm_prepare(m_handle)) < 0) {
532  snd_pcm_dump(m_handle, output);
533  qWarning("cannot prepare interface for use: %s", snd_strerror(err));
534  }
535 
536  return 0;
537 }
538 
539 //***************************************************************************
540 QString Kwave::PlayBackALSA::open(const QString &device, double rate,
541  unsigned int channels, unsigned int bits,
542  unsigned int bufbase)
543 {
544  qDebug("PlayBackALSA::open(device=%s, rate=%0.1f, channels=%u, bits=%u, "
545  "bufbase=%u)", DBG(device), rate, channels, bits, bufbase);
546 
547  m_device_name = device;
548  m_rate = rate;
549  m_channels = channels;
550  m_bits = 0;
551  m_bufbase = bufbase;
552  m_buffer_size = 0;
553  m_buffer_used = 0;
554  m_handle = Q_NULLPTR;
555 
556  // close the previous device
557  if (m_handle) snd_pcm_close(m_handle);
558  m_handle = Q_NULLPTR;
559  if (m_encoder) delete m_encoder;
560  m_encoder = Q_NULLPTR;
561 
562  // initialize the list of supported formats
564 
565  int err = openDevice(device, Kwave::toUint(rate), channels, bits);
566  if (err) {
567  QString reason;
568  switch (err) {
569  case ENOENT:
570  case ENODEV:
571  case ENXIO:
572  case EIO:
573  reason = i18n("I/O error. Maybe the driver\n"\
574  "is not present in your kernel or it is not\n"\
575  "properly configured.");
576  break;
577  case EBUSY:
578  reason = i18n(
579  "The device is busy. Maybe some other application is\n"\
580  "currently using it. Please try again later.\n"\
581  "(Hint: you might find out the name and process ID of\n"\
582  "the program by calling: \"fuser -v %1\"\n"\
583  "on the command line.)",
584  m_device_name);
585  break;
586  default:
587  reason = i18n("Opening the device '%1' failed: %2",
588  device.section(QLatin1Char('|'), 0, 0),
589  QString::fromLocal8Bit(snd_strerror(err)));
590  }
591  return reason;
592  }
593 
594  // resize our buffer and reset it
595  Q_ASSERT(m_chunk_size);
596  Q_ASSERT(m_bytes_per_sample);
597  unsigned int chunk_bytes = Kwave::toUint(m_chunk_size) * m_bytes_per_sample;
598  Q_ASSERT(chunk_bytes);
599  if (!chunk_bytes) return QString();
600  unsigned int n = Kwave::toUint(ceil(
601  static_cast<double>(1 << m_bufbase) /
602  static_cast<double>(chunk_bytes)));
603  if (n < 1) n = 1;
604  m_buffer_size = n * chunk_bytes;
605  m_buffer.resize(m_buffer_size);
606  m_buffer_size = m_buffer.size();
607 
608 // qDebug("PlayBackALSA::open: OK, buffer resized to %u bytes",
609 // m_buffer_size);
610 
611  return QString();
612 }
613 
614 //***************************************************************************
616 {
617  Q_ASSERT(m_encoder);
618  if (!m_encoder) return -EIO;
619 
620  unsigned int bytes = m_bytes_per_sample;
621  Q_ASSERT (m_buffer_used + bytes <= m_buffer_size);
622  if (m_buffer_used + bytes > m_buffer_size) {
623  qWarning("PlayBackALSA::write(): buffer overflow ?! (%u/%u)",
625  m_buffer_used = 0;
626  return -EIO;
627  }
628 
629  QByteArray raw(bytes, char(0));
630  m_encoder->encode(samples, m_channels, raw);
631  MEMCPY(m_buffer.data() + m_buffer_used, raw.constData(), bytes);
632  m_buffer_used += bytes;
633 
634  // write buffer to device if full
635  if (m_buffer_used >= m_buffer_size) return flush();
636 
637  return 0;
638 }
639 
640 //***************************************************************************
642 {
643  if (!m_buffer_used) return 0; // nothing to do
644  Q_ASSERT(m_channels);
645  Q_ASSERT(m_bytes_per_sample);
646  if (!m_channels || !m_bytes_per_sample) return -EINVAL;
647 
648  if (m_handle) {
649  snd_pcm_uframes_t samples = m_buffer_used / m_bytes_per_sample;
650  unsigned int buffer_samples = m_buffer_size / m_bytes_per_sample;
651  unsigned int timeout = (m_rate > 0) ?
652  3 * ((1000 * buffer_samples) /
653  Kwave::toUint(m_rate)) : 1000U;
654  quint8 *p = reinterpret_cast<quint8 *>(m_buffer.data());
655 
656  // pad the buffer with silence if necessary
657  if (samples < m_chunk_size) {
658  snd_pcm_format_set_silence(m_format,
659  m_buffer.data() + samples * m_bytes_per_sample,
660  Kwave::toUint((m_chunk_size - samples) * m_channels));
661  samples = m_chunk_size;
662  qDebug("--- added silence ---");
663  }
664 
665  while (samples > 0) {
666  // try to write as much as the device accepts
667  int r = Kwave::toInt(snd_pcm_writei(m_handle, p, samples));
668  if ((r == -EAGAIN) || ((r >= 0) && (r < Kwave::toInt(samples))))
669  {
670  snd_pcm_wait(m_handle, timeout);
671  } else if (r == -EPIPE) {
672  // underrun -> start again
673  qWarning("PlayBackALSA::flush(), underrun");
674  r = snd_pcm_prepare(m_handle);
675  if (r < 0) {
676  qWarning("PlayBackALSA::flush(), "\
677  "resume after underrun failed: %s",
678  snd_strerror(r));
679  m_buffer_used = 0;
680  return r;
681  }
682  qWarning("PlayBackALSA::flush(), after underrun: resuming");
683  continue; // try again
684  } else if (r == -ESTRPIPE) {
685  qWarning("PlayBackALSA::flush(), suspended. "\
686  "trying to resume...");
687  while ((r = snd_pcm_resume(m_handle)) == -EAGAIN)
688  sleep(1); /* wait until suspend flag is released */
689  if (r < 0) {
690  qWarning("PlayBackALSA::flush(), resume failed, "\
691  "restarting stream.");
692  if ((r = snd_pcm_prepare(m_handle)) < 0) {
693  qWarning("PlayBackALSA::flush(), resume error: %s",
694  snd_strerror(r));
695  m_buffer_used = 0;
696  return r;
697  }
698  }
699  qWarning("PlayBackALSA::flush(), after suspend: resuming");
700  continue; // try again
701  } else if (r < 0) {
702  qWarning("write error: %s", snd_strerror(r));
703  m_buffer_used = 0;
704  return r;
705  }
706  if (r > 0) {
707  // advance in the buffer
708  Q_ASSERT(r <= Kwave::toInt(samples));
709  p += r * m_bytes_per_sample;
710  samples -= r;
711  }
712  }
713  }
714 
715  m_buffer_used = 0;
716  return 0;
717 }
718 
719 //***************************************************************************
721 {
722  flush();
723 
724  // close the device handle
725  if (m_handle) snd_pcm_close(m_handle);
726  m_handle = Q_NULLPTR;
727 
728  // get rid of the old sample encoder
729  if (m_encoder) delete m_encoder;
730  m_encoder = Q_NULLPTR;
731 
732  // clear the list of supported formats, nothing open -> nothing supported
733  m_supported_formats.clear();
734 
735  return 0;
736 }
737 
738 //***************************************************************************
740 {
741  snd_ctl_t *handle;
742  int card, err, dev;
743  int idx;
744  ALSA_MALLOC_WRAPPER(snd_ctl_card_info) info;
745  ALSA_MALLOC_WRAPPER(snd_pcm_info) pcminfo;
746 
747  m_device_list.clear();
748 
749  card = -1;
750  if (snd_card_next(&card) < 0 || card < 0) {
751  qWarning("no soundcards found...");
752  return;
753  }
754 
755 // qDebug("**** List of PLAYBACK Hardware Devices ****");
756  while (card >= 0) {
757  QString name;
758  name = _("hw:%1");
759  name = name.arg(card);
760  if ((err = snd_ctl_open(&handle, name.toLocal8Bit().data(), 0)) < 0) {
761  qWarning("control open (%i): %s", card, snd_strerror(err));
762  goto next_card;
763  }
764  if ((err = snd_ctl_card_info(handle, info)) < 0) {
765  qWarning("control hardware info (%i): %s",
766  card, snd_strerror(err));
767  snd_ctl_close(handle);
768  goto next_card;
769  }
770  dev = -1;
771  while (1) {
772  unsigned int count;
773  if (snd_ctl_pcm_next_device(handle, &dev)<0)
774  qWarning("snd_ctl_pcm_next_device");
775  if (dev < 0)
776  break;
777  snd_pcm_info_set_device(pcminfo, dev);
778  snd_pcm_info_set_subdevice(pcminfo, 0);
779  snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
780  if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
781  if (err != -ENOENT)
782  qWarning("control digital audio info (%i): %s", card,
783  snd_strerror(err));
784  continue;
785  }
786  count = snd_pcm_info_get_subdevices_count(pcminfo);
787 
788 // qDebug("card %i: %s [%s], device %i: %s [%s]",
789 // card,
790 // snd_ctl_card_info_get_id(info),
791 // snd_ctl_card_info_get_name(info),
792 // dev,
793 // snd_pcm_info_get_id(pcminfo),
794 // snd_pcm_info_get_name(pcminfo));
795 
796  // add the device to the list
797  QString hw_device;
798  hw_device = _("plughw:%1,%2");
799  hw_device = hw_device.arg(card).arg(dev);
800 
801  QString card_name = _(snd_ctl_card_info_get_name(info));
802  QString device_name = _(snd_pcm_info_get_name(pcminfo));
803 
804 // qDebug(" Subdevices: %i/%i\n",
805 // snd_pcm_info_get_subdevices_avail(pcminfo), count);
806  if (count > 1) {
807  for (idx = 0; idx < Kwave::toInt(count); idx++) {
808  snd_pcm_info_set_subdevice(pcminfo, idx);
809  if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
810  qWarning("ctrl digital audio playback info (%i): %s",
811  card, snd_strerror(err));
812  } else {
813  QString hwdev = hw_device + _(",%1").arg(idx);
814  QString subdevice_name =
815  _(snd_pcm_info_get_subdevice_name(pcminfo));
816  QString full_name = QString(
817  i18n("Card %1: ", card) + card_name +
818  _("|sound_card||") +
819  i18n("Device %1: ", dev) + device_name +
820  _("|sound_device||") +
821  i18n("Subdevice %1: ", idx) + subdevice_name +
822  _("|sound_subdevice")
823  );
824  qDebug("# '%s' -> '%s'", DBG(hwdev), DBG(full_name));
825  m_device_list.insert(full_name, hwdev);
826  }
827  }
828  } else {
829  // no sub-devices
830  QString full_name = QString(
831  i18n("Card %1: ", card) +
832  card_name + _("|sound_card||") +
833  i18n("Device %1: ", dev) +
834  device_name + _("|sound_subdevice")
835  );
836 // qDebug("# '%s' -> '%s'", DBG(hw_device), DBG(name));
837  m_device_list.insert(full_name, hw_device);
838  }
839  }
840  snd_ctl_close(handle);
841 next_card:
842  if (snd_card_next(&card) < 0) {
843  qWarning("snd_card_next failed");
844  break;
845  }
846  }
847 
848  // per default: offer the dmix plugin and the default device
849  // if slave devices exist
850  if (!m_device_list.isEmpty()) {
851  m_device_list.insert(i18n("DMIX plugin") +
852  _("|sound_note"),
853  _("plug:dmix"));
854  m_device_list.insert(DEFAULT_DEVICE, _("default"));
855  } else {
856  m_device_list.insert(NULL_DEVICE, _("null"));
857  }
858 
859 }
860 
861 //***************************************************************************
863 {
864  if (m_device_list.isEmpty() || (name.length() &&
865  !m_device_list.contains(name)))
866  {
867 // qDebug("### RESCAN ### (list.count=%d, name='%s')",
868 // m_device_list.count(), name.data());
869  scanDevices();
870  }
871 
872  if (!m_device_list.contains(name)) {
873  // maybe we already have a ALSA compatible name (like in init state)
874  foreach (QString n, m_device_list.values())
875  if (n == name) return n;
876 
877  qWarning("PlayBackALSA::alsaDeviceName('%s') - NOT FOUND", DBG(name));
878  return _("");
879  }
880  return m_device_list[name];
881 }
882 
883 //***************************************************************************
885 {
886  // re-validate the list if necessary
887  scanDevices();
888 
889  QStringList list = m_device_list.keys();
890 
891  // move "default" or "null" to the start of the list
892  if (list.contains(NULL_DEVICE))
893  list.move(list.indexOf(NULL_DEVICE), 0);
894  if (list.contains(DEFAULT_DEVICE))
895  list.move(list.indexOf(DEFAULT_DEVICE), 0);
896 
897  if (!list.isEmpty()) list.append(_("#TREE#"));
898 
899  return list;
900 }
901 
902 //***************************************************************************
904 {
905  return _("");
906 }
907 
908 //***************************************************************************
909 snd_pcm_t *Kwave::PlayBackALSA::openDevice(const QString &device)
910 {
911  snd_pcm_t *pcm = m_handle;
912 
913 // qDebug("PlayBackALSA::openDevice(%s)", device.local8Bit().data());
914 
915  // translate verbose name to internal ALSA name
916  QString alsa_device = alsaDeviceName(device);
917 
918  if (!alsa_device.length()) return Q_NULLPTR;
919 
920  // workaround for bug in ALSA
921  // if the device name ends with "," -> invalid name
922  if (alsa_device.endsWith(_(","))) return Q_NULLPTR;
923 
924  if (!pcm) {
925  // open the device in case it's not already open
926  int err = snd_pcm_open(&pcm, alsa_device.toLocal8Bit().data(),
927  SND_PCM_STREAM_PLAYBACK,
928  SND_PCM_NONBLOCK);
929  if (err < 0) {
930  pcm = Q_NULLPTR;
931  qWarning("PlayBackALSA::openDevice('%s') - failed, err=%d (%s)",
932  DBG(alsa_device), err, snd_strerror(err));
933  }
934  }
935 
936  return pcm;
937 }
938 
939 //***************************************************************************
940 QList<unsigned int> Kwave::PlayBackALSA::supportedBits(const QString &device)
941 {
942  QList<unsigned int> list;
943  QList<int> supported_formats;
944 
945  // try all known sample formats
946  supported_formats = detectSupportedFormats(device);
947  foreach (int index, supported_formats) {
948  const snd_pcm_format_t *fmt = &(_known_formats[index]);
949  const unsigned int bits = snd_pcm_format_width(*fmt);
950 
951  // 0 bits means invalid/does not apply
952  if (!bits) continue;
953 
954  // do not produce duplicates
955  if (list.contains(bits)) continue;
956 
957 // qDebug("found bits/sample %u", bits);
958  list.append(bits);
959  }
960 
961  return list;
962 }
963 
964 //***************************************************************************
965 int Kwave::PlayBackALSA::detectChannels(const QString &device,
966  unsigned int &min, unsigned int &max)
967 {
968  min = max = 0;
969  ALSA_MALLOC_WRAPPER(snd_pcm_hw_params) p;
970 
971  if (!p) return -1;
972 
973  snd_pcm_t *pcm = openDevice(device);
974  if (!pcm) return -1;
975 
976  if (snd_pcm_hw_params_any(pcm, p) >= 0) {
977  int err;
978 
979  if ((err = snd_pcm_hw_params_get_channels_min(p, &min)) < 0)
980  qWarning("PlayBackALSA::detectTracks: min: %s",
981  snd_strerror(err));
982  if ((err = snd_pcm_hw_params_get_channels_max(p, &max)) < 0)
983  qWarning("PlayBackALSA::detectTracks: max: %s",
984  snd_strerror(err));
985  }
986 
987  // close the device if *we* opened it
988  if (pcm != m_handle) snd_pcm_close(pcm);
989 
990  return 0;
991 }
992 
993 #endif /* HAVE_ALSA_SUPPORT */
994 
995 //***************************************************************************
996 //***************************************************************************
#define SND_PCM_FORMAT_U24_3
snd_pcm_t * openDevice(const QString &device)
byte_order_t
Definition: ByteOrder.h:25
Definition: App.h:33
snd_pcm_format_t m_format
virtual QString open(const QString &device, double rate, unsigned int channels, unsigned int bits, unsigned int bufbase) Q_DECL_OVERRIDE
unsigned int m_bufbase
#define SND_PCM_FORMAT_S20_3
static Kwave::SampleFormat::Format sample_format_of(snd_pcm_format_t fmt)
QList< int > detectSupportedFormats(const QString &device)
#define DEFAULT_DEVICE
snd_pcm_t * m_handle
virtual QString fileFilter() Q_DECL_OVERRIDE
#define SND_PCM_FORMAT_U18_3
#define SND_PCM_FORMAT_S24_3
unsigned int m_channels
QString alsaDeviceName(const QString &name)
#define SND_PCM_FORMAT_U20_3
#define ALSA_MALLOC_WRAPPER(__t__)
const char name[16]
Definition: memcpy.c:510
virtual int write(const Kwave::SampleArray &samples) Q_DECL_OVERRIDE
Kwave::SampleEncoder * m_encoder
virtual ~PlayBackALSA() Q_DECL_OVERRIDE
snd_pcm_uframes_t m_chunk_size
int setFormat(snd_pcm_hw_params_t *hw_params, unsigned int bits)
virtual QList< unsigned int > supportedBits(const QString &device) Q_DECL_OVERRIDE
int toInt(T x)
Definition: Utils.h:127
#define NULL_DEVICE
static QMap< QString, QString > m_device_list
virtual int detectChannels(const QString &device, unsigned int &min, unsigned int &max) Q_DECL_OVERRIDE
#define MEMCPY
Definition: memcpy.h:37
static const snd_pcm_format_t _known_formats[]
static Kwave::byte_order_t endian_of(snd_pcm_format_t fmt)
unsigned int m_buffer_size
int mode2format(int bits)
virtual QStringList supportedDevices() Q_DECL_OVERRIDE
#define _(m)
Definition: memcpy.c:66
#define SND_PCM_FORMAT_S18_3
#define DBG(qs)
Definition: String.h:55
unsigned int m_bytes_per_sample
unsigned int m_buffer_used
virtual int close() Q_DECL_OVERRIDE
unsigned int toUint(T x)
Definition: Utils.h:109
QList< int > m_supported_formats
virtual ~AlsaMallocWrapper()
virtual void encode(const Kwave::SampleArray &samples, unsigned int count, QByteArray &raw_data)=0
unsigned int m_bits