kwave  18.07.70
PlayBack-OSS.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  PlayBack-OSS.cpp - playback device for standard linux OSS
3  -------------------
4  begin : Sat May 19 2001
5  copyright : (C) 2001 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 #ifdef HAVE_OSS_SUPPORT
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <math.h>
24 #include <stdlib.h>
25 #include <sys/ioctl.h>
26 #include <sys/soundcard.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 
31 #include <QDir>
32 #include <QFile>
33 #include <QLatin1Char>
34 
35 #include <KLocalizedString>
36 
37 #include "libkwave/ByteOrder.h"
38 #include "libkwave/Compression.h"
40 #include "libkwave/String.h"
41 #include "libkwave/Utils.h"
42 #include "libkwave/memcpy.h"
43 
44 #include "PlayBack-OSS.h"
45 
47 #define MIN_PLAYBACK_BUFFER 8
48 
50 #define MAX_PLAYBACK_BUFFER 16
51 
53 #define MAX_CHANNELS 7
54 
55 // Linux 2.6.24 and above's OSS emulation supports 24 and 32 bit formats
56 // but did not declare them in <soundcard.h>
57 #ifndef AFMT_S24_LE
58 #define AFMT_S24_LE 0x00008000
59 #endif
60 #ifndef AFMT_S24_BE
61 #define AFMT_S24_BE 0x00010000
62 #endif
63 #ifndef AFMT_S32_LE
64 #define AFMT_S32_LE 0x00001000
65 #endif
66 #ifndef AFMT_S32_BE
67 #define AFMT_S32_BE 0x00002000
68 #endif
69 
70 #ifndef SNDCTL_DSP_SPEED
71 #define SNDCTL_DSP_SPEED SOUND_PCM_WRITE_RATE
72 #endif
73 
74 #ifndef SNDCTL_DSP_CHANNELS
75 #define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS
76 #endif
77 
78 #ifndef SOUND_PCM_SETFMT
79 #define SOUND_PCM_SETFMT SOUND_PCM_WRITE_BITS
80 #endif
81 
82 #ifndef SNDCTL_DSP_SETFMT
83 #define SNDCTL_DSP_SETFMT SOUND_PCM_SETFMT
84 #endif
85 
86 //***************************************************************************
89  m_device_name(),
90  m_handle(-1),
91  m_rate(0),
92  m_channels(0),
93  m_bits(0),
94  m_bufbase(0),
95  m_buffer(),
96  m_raw_buffer(),
97  m_buffer_size(0),
98  m_buffer_used(0),
99  m_encoder(Q_NULLPTR),
100  m_oss_version(-1)
101 {
102 }
103 
104 //***************************************************************************
106 {
107  close();
108 }
109 
110 //***************************************************************************
111 QString Kwave::PlayBackOSS::open(const QString &device, double rate,
112  unsigned int channels, unsigned int bits,
113  unsigned int bufbase)
114 {
115  qDebug("PlayBackOSS::open(device=%s,rate=%0.1f,channels=%u,"
116  "bits=%u, bufbase=%u)", DBG(device.split(_("|")).at(0)),
117  rate, channels, bits, bufbase);
118 
119  m_device_name = device;
120  m_rate = rate;
121  m_channels = channels;
122  m_bits = bits;
123  m_bufbase = bufbase;
124  m_buffer_size = 0;
125  m_buffer_used = 0;
126  m_handle = 0;
127 
128  // prepeare for playback by opening the sound device
129  // and initializing with the proper settings
130  m_handle = ::open(m_device_name.toLocal8Bit(), O_WRONLY | O_NONBLOCK);
131  if (m_handle == -1) {
132  QString reason;
133  switch (errno) {
134  case ENOENT:
135  case ENODEV:
136  case ENXIO:
137  case EIO:
138  reason = i18n("I/O error. Maybe the driver\n"\
139  "is not present in your kernel or it is not\n"\
140  "properly configured.");
141  break;
142  case EBUSY:
143  reason = i18n(
144  "The device is busy. Maybe some other application is\n"\
145  "currently using it. Please try again later.\n"\
146  "(Hint: you might find out the name and process ID of\n"\
147  "the program by calling: \"fuser -v %1\"\n"\
148  "on the command line.)",
149  m_device_name.section(QLatin1Char('|'), 0, 0));
150  break;
151  default:
152  reason = QString::fromLocal8Bit(strerror(errno));
153  }
154  return reason;
155  }
156 
157  // the device was opened in non-blocking mode to detect if it is
158  // busy or not - but from now on we need blocking mode again
159  ::fcntl(m_handle, F_SETFL, fcntl(m_handle, F_GETFL) & ~O_NONBLOCK);
160  if (fcntl(m_handle, F_GETFL) & O_NONBLOCK) {
161  // resetting O:NONBLOCK failed
162  return i18n("The device '%1' cannot be opened "\
163  "in the correct mode.",
164  m_device_name.section(QLatin1Char('|'), 0, 0));
165  }
166 
167  // query OSS driver version
168  m_oss_version = 0x030000;
169 #ifdef OSS_GETVERSION
170  ioctl(m_handle, OSS_GETVERSION, &m_oss_version);
171 #endif
172 
173  int format;
174  switch (m_bits) {
175  case 8: format = AFMT_U8; break;
176  case 24: format = AFMT_S24_LE; break;
177  case 32: format = AFMT_S32_LE; break;
178  default: format = AFMT_S16_LE;
179  }
180 
181  // number of bits per sample
182  int oldformat = format;
183  if ((ioctl(m_handle, SNDCTL_DSP_SETFMT, &format) == -1) ||
184  (format != oldformat)) {
185  return i18n("%1 bits per sample are not supported", m_bits);
186  }
187 
188  // channels selection
189  if ((ioctl(m_handle, SNDCTL_DSP_CHANNELS, &m_channels) == -1) ||
190  (format != oldformat)) {
191  return i18n("%1 channels playback is not supported", m_channels);
192  }
193 
194  // playback rate, we allow up to 10% variation
195  int int_rate = Kwave::toInt(m_rate);
196  if ((ioctl(m_handle, SNDCTL_DSP_SPEED, &int_rate) == -1) ||
197  (int_rate < 0.9 * m_rate) || (int_rate > 1.1 * m_rate)) {
198  return i18n("Playback rate %1 Hz is not supported", int_rate);
199  }
200  m_rate = int_rate;
201 
202  // buffer size
203  Q_ASSERT(bufbase >= MIN_PLAYBACK_BUFFER);
204  Q_ASSERT(bufbase <= MAX_PLAYBACK_BUFFER);
205  if (bufbase < MIN_PLAYBACK_BUFFER) bufbase = MIN_PLAYBACK_BUFFER;
206  if (bufbase > MAX_PLAYBACK_BUFFER) bufbase = MAX_PLAYBACK_BUFFER;
207  if (ioctl(m_handle, SNDCTL_DSP_SETFRAGMENT, &bufbase) == -1) {
208  return i18n("Unusable buffer size: %1", 1 << bufbase);
209  }
210 
211  // get the real buffer size in bytes
212  ioctl(m_handle, SNDCTL_DSP_GETBLKSIZE, &m_buffer_size);
213 
214  // create the sample encoder
215  // we assume that OSS is always little endian
216  if (m_encoder) delete m_encoder;
217 
218  switch (m_bits) {
219  case 8:
222  break;
223  case 24:
224  if (m_oss_version >= 0x040000) {
227  break;
228  } // else:
229  /* FALLTHROUGH */
230  case 32:
231  if (m_oss_version >= 0x040000) {
234  break;
235  }
236  // else:
237  /* FALLTHROUGH */
238  default:
241  break;
242  }
243 
244  Q_ASSERT(m_encoder);
245  if (!m_encoder) return i18n("Out of memory");
246 
247  // resize the raw buffer
248  m_raw_buffer.resize(m_buffer_size);
249 
250  // resize our buffer (size in samples) and reset it
253  return i18n("Out of memory");
254 
255  return QString();
256 }
257 
258 //***************************************************************************
260 {
261  Q_ASSERT (m_buffer_used <= m_buffer_size);
263  qWarning("PlayBackOSS::write(): buffer overflow ?!");
265  flush();
266  return -EIO;
267  }
268 
269  // number of samples left in the buffer
270  unsigned int remaining = samples.size();
271  unsigned int offset = 0;
272  while (remaining) {
273  unsigned int length = remaining;
274  if (m_buffer_used + length > m_buffer_size)
275  length = m_buffer_size - m_buffer_used;
276 
277  MEMCPY(&(m_buffer[m_buffer_used]),
278  &(samples[offset]),
279  length * sizeof(sample_t));
280  m_buffer_used += length;
281  offset += length;
282  remaining -= length;
283 
284  // write buffer to device if it has become full
285  if (m_buffer_used >= m_buffer_size)
286  flush();
287  }
288 
289  return 0;
290 }
291 
292 //***************************************************************************
294 {
295  if (!m_buffer_used || !m_encoder) return; // nothing to do
296 
297  // convert into byte stream
298  unsigned int bytes = m_buffer_used * m_encoder->rawBytesPerSample();
300 
301  ssize_t res = 0;
302  if (m_handle)
303  res = ::write(m_handle, m_raw_buffer.data(), bytes);
304  if (res < 0) perror(__FUNCTION__);
305 
306  m_buffer_used = 0;
307 }
308 
309 //***************************************************************************
311 {
312  flush();
313 
314  // close the device handle
315  if (m_handle) ::close(m_handle);
316 
317  // get rid of the old encoder
318  if (m_encoder) delete m_encoder;
319  m_encoder = Q_NULLPTR;
320 
321  return 0;
322 }
323 
324 //***************************************************************************
325 static bool addIfExists(QStringList &list, const QString &name)
326 {
327  QFile file;
328 
329  if (name.contains(_("%1"))) {
330  // test for the name without suffix first
331  addIfExists(list, name.arg(_("")));
332 
333  // loop over the list and try until a suffix does not exist
334  for (unsigned int index=0; index < 64; index++)
335  addIfExists(list, name.arg(index));
336  } else {
337  // check a single name
338  file.setFileName(name);
339  if (!file.exists())
340  return false;
341 
342  if (!list.contains(name))
343  list.append(name);
344  }
345 
346  return true;
347 }
348 
349 //***************************************************************************
350 static void scanFiles(QStringList &list, const QString &dirname,
351  const QString &mask)
352 {
353  QStringList files;
354  QDir dir;
355 
356  dir.setPath(dirname);
357  dir.setNameFilters(mask.split(QLatin1Char(' ')));
358  dir.setFilter(QDir::Files | QDir::Writable | QDir::System);
359  dir.setSorting(QDir::Name);
360  files = dir.entryList();
361 
362  for (QStringList::Iterator it=files.begin(); it != files.end(); ++it) {
363  QString devicename = dirname + QDir::separator() + (*it);
364  addIfExists(list, devicename);
365  }
366 }
367 
368 //***************************************************************************
369 static void scanDirectory(QStringList &list, const QString &dir)
370 {
371  scanFiles(list, dir, _("dsp*"));
372  scanFiles(list, dir, _("*audio*"));
373  scanFiles(list, dir, _("adsp*"));
374  scanFiles(list, dir, _("dio*"));
375  scanFiles(list, dir, _("pcm*"));
376 }
377 
378 //***************************************************************************
380 {
381  QStringList list, dirlist;
382 
383  scanDirectory(list, _("/dev"));
384  scanDirectory(list, _("/dev/snd"));
385  scanDirectory(list, _("/dev/sound"));
386  scanFiles(dirlist, _("/dev/oss"), _("[^.]*"));
387  foreach (QString dir, dirlist)
388  scanDirectory(list, dir);
389  list.append(_("#EDIT#"));
390  list.append(_("#SELECT#"));
391 
392  return list;
393 }
394 
395 //***************************************************************************
397 {
398  QString filter;
399 
400  if (filter.length()) filter += _("\n");
401  filter += _("dsp*|") + i18n("OSS playback device (dsp*)");
402 
403  if (filter.length()) filter += _("\n");
404  filter += _("adsp*|") + i18n("ALSA playback device (adsp*)");
405 
406  if (filter.length()) filter += _("\n");
407  filter += _("*|") + i18n("Any device (*)");
408 
409  return filter;
410 }
411 
412 //***************************************************************************
413 void Kwave::PlayBackOSS::format2mode(int format, int &compression,
414  int &bits, Kwave::SampleFormat::Format &sample_format) const
415 {
416  switch (format) {
417  case AFMT_MU_LAW:
418  compression = Kwave::Compression::G711_ULAW;
419  sample_format = Kwave::SampleFormat::Signed;
420  bits = 16;
421  break;
422  case AFMT_A_LAW:
423  compression = Kwave::Compression::G711_ALAW;
424  sample_format = Kwave::SampleFormat::Unsigned;
425  bits = 16;
426  break;
427  case AFMT_IMA_ADPCM:
428  compression = Kwave::Compression::MS_ADPCM;
429  sample_format = Kwave::SampleFormat::Signed;
430  bits = 16;
431  break;
432  case AFMT_U8:
433  compression = Kwave::Compression::NONE;
434  sample_format = Kwave::SampleFormat::Unsigned;
435  bits = 8;
436  break;
437  case AFMT_S16_LE:
438  case AFMT_S16_BE:
439  compression = Kwave::Compression::NONE;
440  sample_format = Kwave::SampleFormat::Signed;
441  bits = 16;
442  break;
443  case AFMT_S8:
444  compression = Kwave::Compression::NONE;
445  sample_format = Kwave::SampleFormat::Signed;
446  bits = 8;
447  break;
448  case AFMT_U16_LE:
449  case AFMT_U16_BE:
450  compression = Kwave::Compression::NONE;
451  sample_format = Kwave::SampleFormat::Unsigned;
452  bits = 16;
453  break;
454  case AFMT_MPEG:
455  compression = static_cast<int>(
457  sample_format = Kwave::SampleFormat::Signed;
458  bits = 16;
459  break;
460 #if 0
461  case AFMT_AC3: /* Dolby Digital AC3 */
462  compression = Kwave::SampleFormat::Unknown;
463  sample_format = 0;
464  bits = 16;
465  break;
466 #endif
467  case AFMT_S24_LE:
468  case AFMT_S24_BE:
469  if (m_oss_version >= 0x040000) {
470  compression = Kwave::Compression::NONE;
471  sample_format = Kwave::SampleFormat::Signed;
472  bits = 24;
473  break;
474  } /* FALLTHROUGH */
475  case AFMT_S32_LE:
476  case AFMT_S32_BE:
477  if (m_oss_version >= 0x040000) {
478  compression = Kwave::Compression::NONE;
479  sample_format = Kwave::SampleFormat::Signed;
480  bits = 32;
481  break;
482  } /* FALLTHROUGH */
483  default:
484  compression = -1;
485  sample_format = Kwave::SampleFormat::Unknown;
486  bits = -1;
487  }
488 
489 }
490 
491 //***************************************************************************
492 int Kwave::PlayBackOSS::openDevice(const QString &device)
493 {
494  int fd = m_handle;
495 
496 // qDebug("PlayBackOSS::openDevice(%s)", device.local8Bit().data());
497  if (!device.length()) return -1;
498 
499  if (fd <= 0) {
500  // open the device in case it's not already open
501  fd = ::open(device.toLocal8Bit(), O_WRONLY | O_NONBLOCK);
502  if (fd <= 0) {
503  qWarning("PlayBackOSS::openDevice('%s') - failed, errno=%d (%s)",
504  DBG(device),
505  errno, strerror(errno));
506  } else {
507  // we use blocking mode
508  ::fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
509  // query OSS driver version
510  m_oss_version = -1;
511 #ifdef OSS_GETVERSION
512  ioctl(fd, OSS_GETVERSION, &m_oss_version);
513 #endif
514  }
515  }
516  if (fd <= 0) {
517  qWarning("PlayBackOSS::openDevice('%s') - failed, errno=%d (%s)",
518  DBG(device), errno, strerror(errno));
519  }
520 
521  return fd;
522 }
523 
524 //***************************************************************************
525 QList<unsigned int> Kwave::PlayBackOSS::supportedBits(const QString &device)
526 {
527  QList<unsigned int> bits;
528  bits.clear();
529  int err = -1;
530  int mask = AFMT_QUERY;
531  int fd;
532 
533 // qDebug("PlayBackOSS::supportedBits(%s)", device.local8Bit().data());
534 
535  fd = openDevice(device);
536  if (fd >= 0) {
537  err = ::ioctl(fd, SNDCTL_DSP_GETFMTS, &mask);
538  if (err < 0) {
539  qWarning("PlayBackOSS::supportedBits() - "\
540  "SNDCTL_DSP_GETFMTS failed, "\
541  "fd=%d, result=%d, error=%d (%s)",
542  fd, err, errno, strerror(errno));
543  }
544  }
545 
546  // close the device if *we* opened it
547  if ((fd != m_handle) && (fd >= 0)) ::close(fd);
548 
549  if (err < 0) return bits;
550 
551  // mask out all modes that do not match the current compression
552  const int compression = Kwave::Compression::NONE;
553  for (unsigned int bit=0; bit < (sizeof(mask) << 3); bit++) {
554  if (!(mask & (1 << bit))) continue;
555 
556  // format is supported, split into compression, bits, sample format
557  int c, b;
559  format2mode(1 << bit, c, b, s);
560  if (b < 0) continue; // unknown -> skip
561 
562  // take the mode if compression matches and it is not already known
563  if ((c == compression) && !(bits.contains(b))) {
564  bits += b;
565  }
566  }
567 
568  return bits;
569 }
570 
571 //***************************************************************************
572 int Kwave::PlayBackOSS::detectChannels(const QString &device,
573  unsigned int &min, unsigned int &max)
574 {
575  int fd, t, err = -1;
576 
577  min = 0;
578  max = 0;
579 
580  fd = openDevice(device);
581  if (fd < 0) return -1;
582 
583  // find the smalles number of tracks, limit to MAX_CHANNELS
584  for (t=1; t < MAX_CHANNELS; t++) {
585  int real_tracks = t;
586  err = ioctl(fd, SNDCTL_DSP_CHANNELS, &real_tracks);
587  Q_ASSERT(real_tracks == t);
588  if (err >= 0) {
589  min = real_tracks;
590  break;
591  }
592  }
593  if (t >= MAX_CHANNELS) {
594  // no minimum track number found :-o
595  qWarning("no minimum track number found, err=%d", err);
596  // close the device if *we* opened it
597  if ((fd != m_handle) && (fd >= 0)) ::close(fd);
598  return err;
599  }
600 
601  // find the highest number of tracks, start from MAX_CHANNELS downwards
602  for (t = MAX_CHANNELS; t >= Kwave::toInt(min); t--) {
603  int real_tracks = t;
604  err = ioctl(fd, SNDCTL_DSP_CHANNELS, &real_tracks);
605  Q_ASSERT(real_tracks == t);
606  if (err >= 0) {
607  max = real_tracks;
608  break;
609  }
610  }
611  max = t;
612 // qDebug("PlayBackOSS::detectTracks, min=%u, max=%u", min, max);
613 
614  // close the device if *we* opened it
615  if ((fd != m_handle) && (fd >= 0)) ::close(fd);
616  return 0;
617 }
618 
619 #endif /* HAVE_OSS_SUPPORT */
620 
621 //***************************************************************************
622 //***************************************************************************
Definition: App.h:33
static void scanFiles(QStringList &list, const QString &dirname, const QString &mask)
virtual unsigned int rawBytesPerSample()=0
#define SNDCTL_DSP_SPEED
#define SNDCTL_DSP_SETFMT
virtual QString fileFilter() Q_DECL_OVERRIDE
unsigned int m_buffer_size
Definition: PlayBack-OSS.h:151
virtual int detectChannels(const QString &device, unsigned int &min, unsigned int &max) Q_DECL_OVERRIDE
virtual int write(const Kwave::SampleArray &samples) Q_DECL_OVERRIDE
QByteArray m_raw_buffer
Definition: PlayBack-OSS.h:148
unsigned int m_channels
Definition: PlayBack-OSS.h:136
#define MAX_CHANNELS
const char name[16]
Definition: memcpy.c:510
#define MIN_PLAYBACK_BUFFER
#define AFMT_S32_LE
Kwave::SampleArray m_buffer
Definition: PlayBack-OSS.h:145
unsigned int m_bufbase
Definition: PlayBack-OSS.h:142
#define AFMT_S24_LE
int toInt(T x)
Definition: Utils.h:127
static void scanDirectory(QStringList &list, const QString &dir)
#define MEMCPY
Definition: memcpy.h:37
#define AFMT_S32_BE
int openDevice(const QString &device)
virtual ~PlayBackOSS() Q_DECL_OVERRIDE
unsigned int m_bits
Definition: PlayBack-OSS.h:139
virtual QStringList supportedDevices() Q_DECL_OVERRIDE
#define _(m)
Definition: memcpy.c:66
unsigned int size() const
#define DBG(qs)
Definition: String.h:55
#define MAX_PLAYBACK_BUFFER
static bool addIfExists(QStringList &list, const QString &name)
#define AFMT_S24_BE
virtual int close() Q_DECL_OVERRIDE
virtual QList< unsigned int > supportedBits(const QString &device) Q_DECL_OVERRIDE
unsigned int m_buffer_used
Definition: PlayBack-OSS.h:154
virtual void encode(const Kwave::SampleArray &samples, unsigned int count, QByteArray &raw_data)=0
bool resize(unsigned int size) Q_REQUIRED_RESULT
Kwave::SampleEncoder * m_encoder
Definition: PlayBack-OSS.h:157
void format2mode(int format, int &compression, int &bits, Kwave::SampleFormat::Format &sample_format) const
#define SNDCTL_DSP_CHANNELS
virtual QString open(const QString &device, double rate, unsigned int channels, unsigned int bits, unsigned int bufbase) Q_DECL_OVERRIDE
qint32 sample_t
Definition: Sample.h:37