kwave  18.07.70
Kwave::PlayBackALSA Class Reference

#include <PlayBack-ALSA.h>

Inheritance diagram for Kwave::PlayBackALSA:
Inheritance graph
Collaboration diagram for Kwave::PlayBackALSA:
Collaboration graph

Public Member Functions

 PlayBackALSA ()
 
virtual ~PlayBackALSA () Q_DECL_OVERRIDE
 
virtual QString open (const QString &device, double rate, unsigned int channels, unsigned int bits, unsigned int bufbase) Q_DECL_OVERRIDE
 
virtual int write (const Kwave::SampleArray &samples) Q_DECL_OVERRIDE
 
virtual int close () Q_DECL_OVERRIDE
 
virtual QStringList supportedDevices () Q_DECL_OVERRIDE
 
virtual QString fileFilter () Q_DECL_OVERRIDE
 
virtual QList< unsigned int > supportedBits (const QString &device) Q_DECL_OVERRIDE
 
virtual int detectChannels (const QString &device, unsigned int &min, unsigned int &max) Q_DECL_OVERRIDE
 
- Public Member Functions inherited from Kwave::PlayBackDevice
virtual ~PlayBackDevice ()
 

Protected Member Functions

snd_pcm_t * openDevice (const QString &device)
 
int openDevice (const QString &device, unsigned int rate, unsigned int channels, unsigned int bits)
 
int setFormat (snd_pcm_hw_params_t *hw_params, unsigned int bits)
 
int flush ()
 
void scanDevices ()
 
QString alsaDeviceName (const QString &name)
 

Private Member Functions

QList< int > detectSupportedFormats (const QString &device)
 
int mode2format (int bits)
 

Private Attributes

QString m_device_name
 
snd_pcm_t * m_handle
 
double m_rate
 
unsigned int m_channels
 
unsigned int m_bits
 
unsigned int m_bytes_per_sample
 
unsigned int m_bufbase
 
QByteArray m_buffer
 
unsigned int m_buffer_size
 
unsigned int m_buffer_used
 
snd_pcm_format_t m_format
 
snd_pcm_uframes_t m_chunk_size
 
QList< int > m_supported_formats
 
Kwave::SampleEncoderm_encoder
 

Static Private Attributes

static QMap< QString, QString > m_device_list
 

Detailed Description

Definition at line 45 of file PlayBack-ALSA.h.

Constructor & Destructor Documentation

◆ PlayBackALSA()

Kwave::PlayBackALSA::PlayBackALSA ( )

Default constructor

Definition at line 191 of file PlayBack-ALSA.cpp.

193  m_device_name(),
194  m_handle(Q_NULLPTR),
195  m_rate(0),
196  m_channels(0),
197  m_bits(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),
206  m_encoder(Q_NULLPTR)
207 {
208 }
snd_pcm_format_t m_format
unsigned int m_bufbase
snd_pcm_t * m_handle
unsigned int m_channels
Kwave::SampleEncoder * m_encoder
snd_pcm_uframes_t m_chunk_size
unsigned int m_buffer_size
unsigned int m_bytes_per_sample
unsigned int m_buffer_used
QList< int > m_supported_formats
unsigned int m_bits

◆ ~PlayBackALSA()

Kwave::PlayBackALSA::~PlayBackALSA ( )
virtual

Destructor

Definition at line 211 of file PlayBack-ALSA.cpp.

References close().

212 {
213  close();
214 }
virtual int close() Q_DECL_OVERRIDE
Here is the call graph for this function:

Member Function Documentation

◆ alsaDeviceName()

QString Kwave::PlayBackALSA::alsaDeviceName ( const QString &  name)
protected

Translate a verbose device name into a ALSA hardware device name.

Parameters
nameverbose name of the device
Returns
device name that can be used for snd_pcm_open()

Definition at line 862 of file PlayBack-ALSA.cpp.

References _, DBG, m_device_list, name, and scanDevices().

Referenced by openDevice().

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 }
const char name[16]
Definition: memcpy.c:510
static QMap< QString, QString > m_device_list
#define _(m)
Definition: memcpy.c:66
#define DBG(qs)
Definition: String.h:55
Here is the call graph for this function:
Here is the caller graph for this function:

◆ close()

int Kwave::PlayBackALSA::close ( )
virtual

Closes the output device.

See also
PlayBackDevice::close

Implements Kwave::PlayBackDevice.

Definition at line 720 of file PlayBack-ALSA.cpp.

References flush(), m_encoder, m_handle, and m_supported_formats.

Referenced by ~PlayBackALSA().

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 }
snd_pcm_t * m_handle
Kwave::SampleEncoder * m_encoder
QList< int > m_supported_formats
Here is the call graph for this function:
Here is the caller graph for this function:

◆ detectChannels()

int Kwave::PlayBackALSA::detectChannels ( const QString &  device,
unsigned int &  min,
unsigned int &  max 
)
virtual

Detect the minimum and maximum number of channels. If the detection fails, minimum and maximum are set to zero.

Parameters
devicefilename of the device
minreceives the lowest supported number of channels
maxreceives the highest supported number of channels
Returns
zero or positive number if ok, negative error number if failed

Reimplemented from Kwave::PlayBackDevice.

Definition at line 965 of file PlayBack-ALSA.cpp.

References ALSA_MALLOC_WRAPPER, m_handle, and openDevice().

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 }
snd_pcm_t * openDevice(const QString &device)
snd_pcm_t * m_handle
#define ALSA_MALLOC_WRAPPER(__t__)
Here is the call graph for this function:

◆ detectSupportedFormats()

QList< int > Kwave::PlayBackALSA::detectSupportedFormats ( const QString &  device)
private

Walk through the list of all known formats and collect the ones that are supported into "m_supported_formats".

Definition at line 283 of file PlayBack-ALSA.cpp.

References _known_formats, ALSA_MALLOC_WRAPPER, m_handle, m_supported_formats, and openDevice().

Referenced by open(), and supportedBits().

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 }
snd_pcm_t * openDevice(const QString &device)
snd_pcm_t * m_handle
#define ALSA_MALLOC_WRAPPER(__t__)
static const snd_pcm_format_t _known_formats[]
QList< int > m_supported_formats
Here is the call graph for this function:
Here is the caller graph for this function:

◆ fileFilter()

QString Kwave::PlayBackALSA::fileFilter ( )
virtual

return a string suitable for a "File Open..." dialog

Reimplemented from Kwave::PlayBackDevice.

Definition at line 903 of file PlayBack-ALSA.cpp.

References _.

904 {
905  return _("");
906 }
#define _(m)
Definition: memcpy.c:66

◆ flush()

int Kwave::PlayBackALSA::flush ( )
protected

Writes the output buffer to the device

Definition at line 641 of file PlayBack-ALSA.cpp.

References m_buffer, m_buffer_size, m_buffer_used, m_bytes_per_sample, m_channels, m_chunk_size, m_format, m_handle, m_rate, Kwave::toInt(), and Kwave::toUint().

Referenced by close(), and write().

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 }
snd_pcm_format_t m_format
snd_pcm_t * m_handle
unsigned int m_channels
snd_pcm_uframes_t m_chunk_size
int toInt(T x)
Definition: Utils.h:127
unsigned int m_buffer_size
unsigned int m_bytes_per_sample
unsigned int m_buffer_used
unsigned int toUint(T x)
Definition: Utils.h:109
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mode2format()

int Kwave::PlayBackALSA::mode2format ( int  bits)
private

create a ALSA device format (enum) from parameters.

Parameters
bitsthe number of bits per sample, related to the decoded stream
Returns
the index of the best matching format within the list of known formats, or -1 if no match was found

Definition at line 261 of file PlayBack-ALSA.cpp.

References _known_formats, and m_supported_formats.

Referenced by setFormat().

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 }
static const snd_pcm_format_t _known_formats[]
QList< int > m_supported_formats
Here is the caller graph for this function:

◆ open()

QString Kwave::PlayBackALSA::open ( const QString &  device,
double  rate,
unsigned int  channels,
unsigned int  bits,
unsigned int  bufbase 
)
virtual

Opens the device for playback.

See also
PlayBackDevice::open

Implements Kwave::PlayBackDevice.

Definition at line 540 of file PlayBack-ALSA.cpp.

References DBG, detectSupportedFormats(), m_bits, m_bufbase, m_buffer, m_buffer_size, m_buffer_used, m_bytes_per_sample, m_channels, m_chunk_size, m_device_name, m_encoder, m_handle, m_rate, m_supported_formats, openDevice(), and Kwave::toUint().

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 }
snd_pcm_t * openDevice(const QString &device)
unsigned int m_bufbase
QList< int > detectSupportedFormats(const QString &device)
snd_pcm_t * m_handle
unsigned int m_channels
Kwave::SampleEncoder * m_encoder
snd_pcm_uframes_t m_chunk_size
unsigned int m_buffer_size
#define DBG(qs)
Definition: String.h:55
unsigned int m_bytes_per_sample
unsigned int m_buffer_used
unsigned int toUint(T x)
Definition: Utils.h:109
QList< int > m_supported_formats
unsigned int m_bits
Here is the call graph for this function:

◆ openDevice() [1/2]

snd_pcm_t * Kwave::PlayBackALSA::openDevice ( const QString &  device)
protected

Opens a physical device and returns its file descriptor (short version, used for probing / detecting)

Parameters
devicefilename of the device
Returns
pcm stream or null pointer on errors

Definition at line 909 of file PlayBack-ALSA.cpp.

References _, alsaDeviceName(), DBG, and m_handle.

Referenced by detectChannels(), detectSupportedFormats(), and open().

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 }
snd_pcm_t * m_handle
QString alsaDeviceName(const QString &name)
#define _(m)
Definition: memcpy.c:66
#define DBG(qs)
Definition: String.h:55
Here is the call graph for this function:
Here is the caller graph for this function:

◆ openDevice() [2/2]

int Kwave::PlayBackALSA::openDevice ( const QString &  device,
unsigned int  rate,
unsigned int  channels,
unsigned int  bits 
)
protected

Open the device and set all internal member variables that need to be initialized for playback.

Parameters
devicename of the ALSA device
ratesample rate, rounded as unsigned int
channelsnumber of tracks/channels to use
bitsnumber of bits per sample
Returns
zero or positive if succeeded, or negative error code if failed

Definition at line 343 of file PlayBack-ALSA.cpp.

References _, ALSA_MALLOC_WRAPPER, alsaDeviceName(), DBG, m_channels, m_chunk_size, m_handle, m_rate, and setFormat().

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 }
snd_pcm_t * m_handle
unsigned int m_channels
QString alsaDeviceName(const QString &name)
#define ALSA_MALLOC_WRAPPER(__t__)
snd_pcm_uframes_t m_chunk_size
int setFormat(snd_pcm_hw_params_t *hw_params, unsigned int bits)
#define _(m)
Definition: memcpy.c:66
#define DBG(qs)
Definition: String.h:55
Here is the call graph for this function:

◆ scanDevices()

void Kwave::PlayBackALSA::scanDevices ( )
protected

scan all ALSA devices, re-creates m_device_list

Definition at line 739 of file PlayBack-ALSA.cpp.

References _, ALSA_MALLOC_WRAPPER, DBG, DEFAULT_DEVICE, m_device_list, name, NULL_DEVICE, and Kwave::toInt().

Referenced by alsaDeviceName(), and supportedDevices().

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 }
#define DEFAULT_DEVICE
#define ALSA_MALLOC_WRAPPER(__t__)
const char name[16]
Definition: memcpy.c:510
int toInt(T x)
Definition: Utils.h:127
#define NULL_DEVICE
static QMap< QString, QString > m_device_list
#define _(m)
Definition: memcpy.c:66
#define DBG(qs)
Definition: String.h:55
Here is the call graph for this function:
Here is the caller graph for this function:

◆ setFormat()

int Kwave::PlayBackALSA::setFormat ( snd_pcm_hw_params_t *  hw_params,
unsigned int  bits 
)
protected

Used in "openDevice()" to set the member variables m_format, m_bytes_per_sample and m_bits according the given resolution in bits per sample. The number of bits per sample will be rounded up to the next multiple of 8. m_handle must already be an opened device.

Parameters
hw_paramsvalid ALSA hardware parameters
bitsnumber of bits per sample [1...32]
Returns
zero or positive if succeeded, or negative error code if failed

Definition at line 217 of file PlayBack-ALSA.cpp.

References _known_formats, endian_of(), m_bits, m_bytes_per_sample, m_channels, m_encoder, m_format, m_handle, mode2format(), and sample_format_of().

Referenced by openDevice().

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 }
snd_pcm_format_t m_format
static Kwave::SampleFormat::Format sample_format_of(snd_pcm_format_t fmt)
snd_pcm_t * m_handle
unsigned int m_channels
Kwave::SampleEncoder * m_encoder
static const snd_pcm_format_t _known_formats[]
static Kwave::byte_order_t endian_of(snd_pcm_format_t fmt)
int mode2format(int bits)
unsigned int m_bytes_per_sample
unsigned int m_bits
Here is the call graph for this function:
Here is the caller graph for this function:

◆ supportedBits()

QList< unsigned int > Kwave::PlayBackALSA::supportedBits ( const QString &  device)
virtual

returns a list of supported bits per sample resolutions of a given device.

Parameters
devicefilename of the device
Returns
list of supported bits per sample, or empty on errors

Implements Kwave::PlayBackDevice.

Definition at line 940 of file PlayBack-ALSA.cpp.

References _known_formats, and detectSupportedFormats().

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 }
QList< int > detectSupportedFormats(const QString &device)
static const snd_pcm_format_t _known_formats[]
Here is the call graph for this function:

◆ supportedDevices()

QStringList Kwave::PlayBackALSA::supportedDevices ( )
virtual

return a string list with supported device names

Reimplemented from Kwave::PlayBackDevice.

Definition at line 884 of file PlayBack-ALSA.cpp.

References _, DEFAULT_DEVICE, m_device_list, NULL_DEVICE, and scanDevices().

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 }
#define DEFAULT_DEVICE
#define NULL_DEVICE
static QMap< QString, QString > m_device_list
#define _(m)
Definition: memcpy.c:66
Here is the call graph for this function:

◆ write()

int Kwave::PlayBackALSA::write ( const Kwave::SampleArray samples)
virtual

Writes an array of samples to the output device.

See also
PlayBackDevice::write

Implements Kwave::PlayBackDevice.

Definition at line 615 of file PlayBack-ALSA.cpp.

References Kwave::SampleEncoder::encode(), flush(), m_buffer, m_buffer_size, m_buffer_used, m_bytes_per_sample, m_channels, m_encoder, and MEMCPY.

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 }
unsigned int m_channels
Kwave::SampleEncoder * m_encoder
#define MEMCPY
Definition: memcpy.h:37
unsigned int m_buffer_size
unsigned int m_bytes_per_sample
unsigned int m_buffer_used
virtual void encode(const Kwave::SampleArray &samples, unsigned int count, QByteArray &raw_data)=0
Here is the call graph for this function:

Member Data Documentation

◆ m_bits

unsigned int Kwave::PlayBackALSA::m_bits
private

Resolution in bits per sample

Definition at line 190 of file PlayBack-ALSA.h.

Referenced by open(), and setFormat().

◆ m_bufbase

unsigned int Kwave::PlayBackALSA::m_bufbase
private

Exponent of the buffer size

Definition at line 199 of file PlayBack-ALSA.h.

Referenced by open().

◆ m_buffer

QByteArray Kwave::PlayBackALSA::m_buffer
private

buffer with raw device data

Definition at line 202 of file PlayBack-ALSA.h.

Referenced by flush(), open(), and write().

◆ m_buffer_size

unsigned int Kwave::PlayBackALSA::m_buffer_size
private

Buffer size on bytes

Definition at line 205 of file PlayBack-ALSA.h.

Referenced by flush(), open(), and write().

◆ m_buffer_used

unsigned int Kwave::PlayBackALSA::m_buffer_used
private

number of bytes in the buffer

Definition at line 208 of file PlayBack-ALSA.h.

Referenced by flush(), open(), and write().

◆ m_bytes_per_sample

unsigned int Kwave::PlayBackALSA::m_bytes_per_sample
private

Number of bytes per sample, already multiplied with the number of channels (m_channels)

Definition at line 196 of file PlayBack-ALSA.h.

Referenced by flush(), open(), setFormat(), and write().

◆ m_channels

unsigned int Kwave::PlayBackALSA::m_channels
private

Number of channels

Definition at line 187 of file PlayBack-ALSA.h.

Referenced by flush(), open(), openDevice(), setFormat(), and write().

◆ m_chunk_size

snd_pcm_uframes_t Kwave::PlayBackALSA::m_chunk_size
private

number of samples per period

Definition at line 214 of file PlayBack-ALSA.h.

Referenced by flush(), open(), and openDevice().

◆ m_device_list

QMap< QString, QString > Kwave::PlayBackALSA::m_device_list
staticprivate

dictionary for translating verbose device names into ALSA hardware device names (key = verbose name, data = ALSA hardware device name)

Definition at line 221 of file PlayBack-ALSA.h.

Referenced by alsaDeviceName(), scanDevices(), and supportedDevices().

◆ m_device_name

QString Kwave::PlayBackALSA::m_device_name
private

Name of the output device

Definition at line 178 of file PlayBack-ALSA.h.

Referenced by open().

◆ m_encoder

Kwave::SampleEncoder* Kwave::PlayBackALSA::m_encoder
private

encoder for conversion from samples to raw

Definition at line 232 of file PlayBack-ALSA.h.

Referenced by close(), open(), setFormat(), and write().

◆ m_format

snd_pcm_format_t Kwave::PlayBackALSA::m_format
private

sample format, used for ALSA

Definition at line 211 of file PlayBack-ALSA.h.

Referenced by flush(), and setFormat().

◆ m_handle

snd_pcm_t* Kwave::PlayBackALSA::m_handle
private

Handle of the output device

Definition at line 181 of file PlayBack-ALSA.h.

Referenced by close(), detectChannels(), detectSupportedFormats(), flush(), open(), openDevice(), and setFormat().

◆ m_rate

double Kwave::PlayBackALSA::m_rate
private

Playback rate [samples/second]

Definition at line 184 of file PlayBack-ALSA.h.

Referenced by flush(), open(), and openDevice().

◆ m_supported_formats

QList<int> Kwave::PlayBackALSA::m_supported_formats
private

list of supported formats of the current device, indices in the global list of known formats. Only valid after a successful call to "open()", otherwise empty

Definition at line 229 of file PlayBack-ALSA.h.

Referenced by close(), detectSupportedFormats(), mode2format(), and open().


The documentation for this class was generated from the following files: