Nsound  0.9.4
AudioBackendLibao.cc
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 //
3 // $Id: AudioBackendLibao.cc 874 2014-09-08 02:21:29Z weegreenblobbie $
4 //
5 // Copyright (c) 2005-2006 Nick Hilton
6 //
7 // weegreenblobbie_yahoo_com (replace '_' with '@' and '.')
8 //
9 //-----------------------------------------------------------------------------
10 
11 //-----------------------------------------------------------------------------
12 //
13 // This program is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU Library General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 //
27 //-----------------------------------------------------------------------------
28 
29 #include <Nsound/Nsound.h>
31 #include <Nsound/AudioPlayback.h>
32 
33 #include <algorithm>
34 #include <iostream>
35 #include <string>
36 
37 #include <string.h> // for memset
38 #include <stdio.h>
39 
40 #include <ao/ao.h>
41 
42 using namespace Nsound;
43 
44 using std::cerr;
45 using std::cout;
46 using std::endl;
47 
48 //-----------------------------------------------------------------------------
49 // Stupid windows
50 extern "C"
51 {
52  const char * KSDATAFORMAT_SUBTYPE_PCM = "00000001-0000-0010-8000-00aa00389b71";
53 }
54 
56 
58 {
59  "alsa",
60  "arts",
61  "axis",
62  "esd",
63  "irix",
64  "macosx",
65  "nas",
66  "oss",
67  "pulse",
68  "roar",
69  "sndio",
70  "sun",
71  "wmm",
72 };
73 
76  uint32 sample_rate,
77  uint32 channels,
78  uint32 bits_per_sample)
79  :
80  AudioBackend(sample_rate, channels, bits_per_sample),
81  options_(),
82  error_buffer_(""),
83  driver_id_(-1),
84  device_ptr_(NULL)
85 {
86  initialize();
87 };
88 
91 {
92  shutdown();
93 }
94 
98 {
99  return BACKEND_TYPE_LIBAO;
100 }
101 
102 std::string
105 {
106  return error_buffer_.str();
107 }
108 
109 std::string
110 print_info(ao_info * info)
111 {
112  std::stringstream ss;
113 
114  ss << "libao Driver Info:" << endl
115  << " type: " << info->type << endl
116  << " name: " << info->name << endl
117  << " short_name: " << info->short_name << endl
118  << " comment: " << info->comment << endl
119  << " preferred_byte_format: ";
120 
121  switch(info->preferred_byte_format)
122  {
123  case AO_FMT_LITTLE:
124  ss << "Little Endian" << endl;
125  break;
126 
127  case AO_FMT_BIG:
128  ss << "Big Endian" << endl;
129  break;
130 
131  case AO_FMT_NATIVE:
132 
133  if(ao_is_big_endian())
134  {
135  ss << "Native Big Endian" << endl;
136  }
137  else
138  {
139  ss << "Native Little Endian" << endl;
140  }
141  break;
142 
143  default:
144  ss << "Unknown!" << endl;
145  break;
146  }
147 
148  ss << " priority: " << info->priority << endl
149  << " option count: " << info->option_count << endl;
150 
151  for(int32 i = 0; i < info->option_count; ++i)
152  {
153  ss << " options[" << i << "]: " << info->options[i] << endl;
154  }
155 
156  return ss.str();
157 }
158 
159 std::string
162 {
163  #ifndef NSOUND_LIBAO
165  M_THROW("Nsound::AudioBackendLibao::getInfo():"
166  << ": Nsound was not compiled with libao support.\n");
167  return;
168  #else
170  {
171  return "Nsound::AudioBackendLibao::getInfo(): "
172  "Backend not initialized yet";
173  }
174  else if(state_ == BACKEND_ERROR)
175  {
176  return getError();
177  }
178 
179  ao_info * info = ao_driver_info(driver_id_);
180 
181  if(info == NULL)
182  {
183  return "Nsound::AudioBackendLibao::getInfo(): "
184  "ao_driver_info() failed";
185  }
186 
187  return print_info(info);
188  #endif
189 }
190 
191 void
194 {
195  #ifndef NSOUND_LIBAO
197  M_THROW("Nsound::AudioBackendLibao::initialize():"
198  << ": Nsound was not compiled with libao support.\n");
199  return;
200  #else
201 
202  // Only initialize if the back end has not been initialized yet.
204  {
205  return;
206  }
207 
208  ao_initialize();
209 
210  if(driver_id_ < 0)
211  {
212  driver_id_ = ao_default_driver_id();
213  }
214 
215  if(driver_id_ < 0)
216  {
217  ao_shutdown();
218 
219  error_buffer_ << "ao_default_driver_id() failed" << endl;
220 
222 
223  return;
224  }
225 
226  // Setup the sample format.
227  ao_sample_format format;
228 
229  memset(&format, 0, sizeof(ao_sample_format));
230 
231  format.bits = bits_per_sample_;
232  format.rate = sample_rate_;
233  format.channels = channels_;
234 
235  #ifdef NSOUND_LITTLE_ENDIAN
236  format.byte_format = AO_FMT_LITTLE;
237  #else
238  format.byte_format = AO_FMT_BIG;
239  #endif
240 
241  // Need to add config check for the matrix field, for now, just
242  // disable.
243 
244 //~ char matrix[16];
245 //~ format.matrix = matrix;
246 
247 //~ switch(channels_)
248 //~ {
249 //~ case 1:
250 //~ sprintf(matrix, "M");
251 //~ break;
252 
253 //~ case 2:
254 //~ sprintf(matrix, "L,R");
255 //~ break;
256 
257 //~ default:
258 //~ error_buffer_
259 //~ << "Nsound::AudioBackendLibao::initialize(): "
260 //~ << "Don't know how to map "
261 //~ << channels_
262 //~ << " channels to the ao_sample_format.matrix"
263 //~ << endl;
264 
265 //~ shutdown();
266 //~ state_ = BACKEND_ERROR;
267 
268 //~ return;
269 //~ }
270 
271  // Setup any options.
272 
273  ao_option * options = NULL;
274 
275  uint32 n_options = static_cast<uint32>(options_.size());
276 
277  int32 ecode = 0;
278 
279  if(n_options >= 2)
280  {
281  for(uint32 i = 0; i < n_options; i += 2)
282  {
283  ecode = ao_append_option(
284  &options,
285  options_[i ].c_str(),
286  options_[i + 1].c_str());
287 
288  if(ecode != 1)
289  {
290  ao_shutdown();
291 
293  << "Nsound::AudioBackendLibao::initialize():"
294  << __LINE__
295  << ": error appending libao '"
296  << options_[i]
297  << "' : '"
298  << options_[i + 1]
299  << "' device option"
300  << endl;
301 
303 
304  return;
305  }
306  }
307  }
308 
309  device_ptr_ = ao_open_live(driver_id_, &format, options);
310 
311  ao_free_options(options);
312 
313  if(device_ptr_ == NULL)
314  {
315  ao_shutdown();
316 
318  << "Nsound::AudioBackendLibao::initialize():"
319  << __LINE__
320  << ": ao_open_live() failed:"
321  << endl;
322 
323  switch(errno)
324  {
325  case AO_ENODRIVER:
327  << "No driver corresponds to driver_id ("
328  << driver_id_
329  << ")"
330  << endl;
331  break;
332 
333  case AO_ENOTLIVE:
335  << "This driver is not a live output device"
336  << endl;
337  break;
338 
339  case AO_EBADOPTION:
341  << "A valid option key has an invalid value"
342  << endl;
343  break;
344 
345  case AO_EOPENDEVICE:
347  << "Cannot open the device (for example, if "
348  << "/dev/dsp cannot be opened for writing)"
349  << endl;
350  break;
351 
352  case AO_EFAIL:
354  << "Any other cause of failure"
355  << endl;
356  break;
357 
358  default:
360  << "Reason unknown"
361  << endl;
362  break;
363  }
364 
366 
367  return;
368  }
369 
371  #endif
372 }
373 
374 void
376 play(void * data, uint32 n_bytes)
377 {
378  #ifndef NSOUND_LIBAO
380  M_THROW("Nsound::AudioBackendLibao::play():"
381  << ": Nsound was not compiled with libao support.\n");
382  return;
383  #else
384 
385  if(state_ != BACKEND_READY)
386  {
387  return;
388  }
389 
390  if(n_bytes == 0)
391  {
392  return;
393  }
394 
395  if(data == NULL)
396  {
398  << "AudioBackendLibao::play():"
399  << __LINE__
400  << ": data == NULL"
401  << endl;
402 
404 
405  return;
406  }
407 
408  int32 ecode = 0;
409 
410  ecode = ao_play(
411  device_ptr_,
412  reinterpret_cast<char *>(data),
413  n_bytes);
414 
415  if(ecode == 0)
416  {
418 
419  ao_close(device_ptr_);
420  ao_shutdown();
421 
423  << "AudioBackendLibao::play():"
424  << __LINE__
425  << ": ao_play() failed"
426  << endl;
427 
428  return;
429  }
430 
431  #endif
432 }
433 
434 int32
435 static
436 integer(const std::string & x, Nsound::AudioBackend::State * state)
437 {
438  std::stringstream ss(x);
439  int32 i = 0;
440 
441  ss >> i;
442 
443  if(ss.fail())
444  {
446  M_THROW("Nsound::AudioBackendLibao::setOption():"
447  << ": could not convert '"
448  << x
449  << "' to an integer.\n");
450  return -1;
451  }
452 
453  return i;
454 }
455 
456 std::string
457 static
458 lower(const std::string & x)
459 {
460  std::string y = x;
461  std::transform(y.begin(), y.end(), y.begin(), ::tolower);
462  return y;
463 }
464 
465 void
467 scanDevices(AudioPlayback & pb, const AudioStream & test_clip)
468 {
469  for(uint32 i = 0; i < N_DRIVER_TYPES; ++i)
470  {
471  int32 id = ao_driver_id(driver_types[i].c_str());
472 
473  if(id >= 0)
474  {
475  shutdown();
476  driver_id_ = id;
477  initialize();
478 
479  cout << "Libao: found driver '"
480  << driver_types[i].c_str()
481  << "', id = "
482  << id
483  << "\nPLAYBACK STARTING ...";
484  cout.flush();
485 
486  pb.play(test_clip);
487 
488  cout << " STOPPED\n";
489  cout.flush();
490 
491  shutdown();
492  }
493  }
494 }
495 
496 void
498 setOption(const std::string & key, const std::string & value)
499 {
500  #ifndef NSOUND_LIBAO
502  M_THROW("Nsound::AudioBackendLibao::setOption():"
503  << ": Nsound was not compiled with libao support.\n");
504  return;
505  #else
506 
507  std::string k = lower(key);
508 
509  // Special handeling if key == "id"
510  if(k == "id")
511  {
512  int32 id = integer(value, &state_);
513  if(id >= 0) driver_id_ = id;
514  }
515  else
516  if(k == "driver")
517  {
518  int32 id = ao_driver_id(value.c_str());
519 
520  cerr << "key = 'driver', value = '"
521  << value.c_str()
522  << "', id = "
523  << id
524  << "\n";
525  cerr.flush();
526 
527  if(id >= 0)
528  {
529  driver_id_ = id;
530  }
531  else
532  {
534  M_THROW("Nsound::AudioBackendLibao::setOption():"
535  << ": failed to select driver '"
536  << value
537  << "'\n");
538  return;
539  }
540  }
541  else
542  {
543  options_.push_back(key);
544  options_.push_back(value);
545  }
546  #endif
547 }
548 
549 void
552 {
553  #ifndef NSOUND_LIBAO
555  M_THROW("Nsound::AudioBackendLibao::shutdown():"
556  << ": Nsound was not compiled with libao support.\n");
557  return;
558  #else
559  if(device_ptr_ != NULL)
560  {
561  ao_close(device_ptr_);
562  device_ptr_ = NULL;
563  driver_id_ = -1;
564  }
565 
566  ao_shutdown();
568  #endif
569 }
unsigned int uint32
Definition: Nsound.h:153
AudioBackendLibao(uint32 sample_rate=44100, uint32 channels=1, uint32 bits_per_sample=16)
static int32 integer(const std::string &x, Nsound::AudioBackend::State *state)
AudioBackendType getBackendType()
static std::string lower(const std::string &x)
std::vector< std::string > options_
const char * KSDATAFORMAT_SUBTYPE_PCM
void setOption(const std::string &key, const std::string &value)
Set libao options.
#define M_THROW(message)
Definition: Macros.h:108
void scanDevices(AudioPlayback &pb, const AudioStream &test_clip)
std::string print_info(ao_info *info)
signed int int32
Definition: Nsound.h:142
const uint32 N_DRIVER_TYPES
void play(void *data, uint32 n_bytes)
std::string driver_types[N_DRIVER_TYPES]
std::stringstream error_buffer_
void play(const AudioStream &a)
Plays the AudioStream throuh the backend.