Nsound  0.9.4
AudioPlayback.cc
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 //
3 // $Id: AudioPlayback.cc 912 2015-07-26 00:50:29Z weegreenblobbie $
4 //
5 // Nsound is a C++ library and Python module for audio synthesis featuring
6 // dynamic digital filters. Nsound lets you easily shape waveforms and write
7 // to disk or plot them. Nsound aims to be as powerful as Csound but easy to
8 // use.
9 //
10 // Copyright (c) 2011-Present Nick Hilton
11 //
12 // weegreenblobbie_yahoo_com (replace '_' with '@' and '.')
13 //
14 //-----------------------------------------------------------------------------
15 
16 //-----------------------------------------------------------------------------
17 //
18 // This program is free software; you can redistribute it and/or modify
19 // it under the terms of the GNU General Public License as published by
20 // the Free Software Foundation; either version 2 of the License, or
21 // (at your option) any later version.
22 //
23 // This program is distributed in the hope that it will be useful,
24 // but WITHOUT ANY WARRANTY; without even the implied warranty of
25 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 // GNU Library General Public License for more details.
27 //
28 // You should have received a copy of the GNU General Public License
29 // along with this program; if not, write to the Free Software
30 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 //
32 //-----------------------------------------------------------------------------
33 
34 #include <Nsound/Nsound.h>
35 #include <Nsound/AudioBackend.h>
39 #include <Nsound/AudioPlayback.h>
40 #include <Nsound/AudioStream.h>
41 #include <Nsound/Buffer.h>
42 #include <Nsound/GuitarBass.h>
43 
44 #include <algorithm>
45 #include <iostream>
46 #include <sstream>
47 #include <string>
48 
49 using namespace Nsound;
50 using std::cout;
51 using std::cerr;
52 using std::endl;
53 
54 #ifdef NSOUND_LIBPORTAUDIO
56 #elif defined(NSOUND_LIBAO)
58 #else
60 #endif
61 
62 // A common function to create the backend pointer, but doesn't call the
63 // backend initialize() function.
66  const AudioBackendType & type,
67  const uint32 sample_rate,
68  const uint32 channels,
69  const uint32 bits_per_sample)
70 {
71  AudioBackend * backend = NULL;
72 
73  switch(type)
74  {
75  case BACKEND_TYPE_LIBAO:
76  #ifdef NSOUND_LIBAO
77  backend = new AudioBackendLibao(
78  sample_rate,
79  channels,
80  bits_per_sample);
81  #endif
82  break;
83 
85  #ifdef NSOUND_LIBPORTAUDIO
86  backend = new AudioBackendLibportaudio(
87  sample_rate,
88  channels,
89  bits_per_sample);
90  #endif
91  break;
92 
93  case BACKEND_TYPE_NONE:
94  break;
95  }
96 
97  return backend;
98 }
99 
100 void
103 {
104  backend_type_ = ab;
105 }
106 
110 {
111  return backend_type_;
112 }
113 
116  const float64 & sample_rate,
117  uint32 channels,
118  uint32 bits_per_sample)
119  :
120  sample_rate_(static_cast<uint32>(sample_rate)),
121  channels_(channels),
122  bits_per_sample_(bits_per_sample),
123  backend_(NULL)
124 {
127  sample_rate_,
128  channels_,
130 
131  if(BACKEND_TYPE_NONE != backend_type_ && backend_ == NULL)
132  {
133  M_THROW("Nsound::AudioPlayback::AudioPlayback():"
134  << ": failed to create AudioBackend!");
135  }
136 };
137 
140 {
141  if(backend_ != NULL)
142  {
143  backend_->shutdown();
144  delete backend_;
145  }
146 }
147 
148 uint32
151 {
152  if(backend_ != NULL)
153  {
154  return backend_->getBitsPerSample();
155  }
156 
157  return bits_per_sample_;
158 }
159 
160 uint32
163 {
164  if(backend_ != NULL)
165  {
166  return backend_->getChannels();
167  }
168 
169  return channels_;
170 }
171 
172 std::string
175 {
176  if(backend_ != NULL)
177  {
178  return backend_->getError();
179  }
180 
181  std::stringstream ss;
182 
183  ss << "Nsound::AudioPlayback::getError():"
184  << __LINE__
185  << ": backend not initialized"
186  << endl;
187 
188  return ss.str();
189 }
190 
191 std::string
194 {
195  if(backend_ != NULL)
196  {
197  return backend_->getInfo();
198  }
199 
200  std::stringstream ss;
201 
202  ss << "Nsound::AudioPlayback::getInfo():"
203  << __LINE__
204  << ": backend not initialized"
205  << endl;
206 
207  return ss.str();
208 }
209 
210 uint32
213 {
214  if(backend_ != NULL)
215  {
216  return backend_->getSampleRate();
217  }
218 
219  return sample_rate_;
220 }
221 
225 {
226  if(backend_ != NULL)
227  {
228  return backend_->getState();
229  }
230 
232 }
233 
234 std::string
237 {
238  if(backend_ != NULL)
239  {
240  return backend_->getStateString();
241  }
242 
244 }
245 
246 std::string
249 {
250  return AudioBackend::getStateString(state);
251 }
252 
253 void
256 {
258  {
259  M_THROW("Nsound::AudioPlayback::initialize():"
260  << ": no backend selected or available");
261  return;
262  }
263 
264  // If the backend is currently NULL, recreate it with current settings
265  if(backend_ == NULL)
266  {
269  sample_rate_,
270  channels_,
272  }
273 
274  if(backend_ == NULL)
275  {
276  M_THROW("Nsound::AudioPlayback::initialize():"
277  << ": failed to initialize AudioBackend!");
278 
279  return;
280  }
281 
282  backend_->initialize();
283 
285  {
286  M_THROW("Nsound::AudioPlayback::initialize():"
287  << ": backend faild to initialize\n"
288  << "Backend error: "
289  << backend_->getError());
290  }
291 }
292 
293 // Templates, these are declared here not in a header, nobody else needs access
294 // to these templates, they are also not class members. I did this to avoid
295 // including <iostream> in the header and keep the header a little cleaner.
296 
297 // forward delcare
298 
299 template <typename T>
300 void
301 play_int(
302  AudioBackend * backend,
303  const AudioStream & a,
304  const float64 & scale);
305 
306 // forward delcare
307 
308 template <typename T>
309 void
310 play_int(
311  AudioBackend * backend,
312  const Buffer & b,
313  const float64 & scale);
314 
315 template <typename T>
316 void
318  AudioBackend * backend,
319  const AudioStream & a,
320  const float64 & scale)
321 {
322  uint32 n_channels = a.getNChannels();
323 
324  // If the AudioStream is mono, just call play() for the buffer method and
325  // return.
326  if(n_channels == 1)
327  {
328  play_int<T>(backend, a[0], scale);
329  return;
330  }
331 
332  // If the number of channels in the AudioStream doesn't agree with the
333  // number of channels of the backend, print error.
334  if(n_channels != backend->getChannels())
335  {
336  M_THROW("Nsound::AudioPlayback::play():"
337  << ": AudioStream channels do not match backend channels ("
338  << n_channels
339  << " != "
340  << backend->getChannels()
341  << ")");
342  return;
343  }
344 
345  T * array = NULL;
346 
347  uint32 n_samples = a.getLength();
348 
349  array = new T [n_samples * n_channels];
350 
351  if(array == NULL)
352  {
353  M_THROW("Nsound::AudioPlayback::play():"
354  << ": failed allocate memory");
355  return;
356  }
357 
358  uint32 k = 0;
359  for(uint32 i = 0; i < n_samples; ++i)
360  {
361  for(uint32 j = 0; j < n_channels; ++j)
362  {
363  array[k] = static_cast<T>(a[j][i] * scale);
364  ++k;
365  }
366  }
367 
368  backend->play(
369  reinterpret_cast<void *>(array),
370  n_samples * n_channels * sizeof(T));
371 
372  delete array;
373 }
374 
375 template <typename T>
376 void
378  AudioBackend * backend,
379  const Buffer & b,
380  const float64 & scale)
381 {
382  uint32 n_samples = b.getLength();
383  uint32 n_channels = backend->getChannels();
384 
385  std::vector<T> array;
386 
387  array.reserve(n_samples * n_channels);
388 
389  uint32 k = 0;
390  for(uint32 i = 0; i < n_samples; ++i)
391  {
392  for(uint32 j = 0; j < n_channels; ++j)
393  {
394  array.push_back(static_cast<T>(b[i] * scale));
395  ++k;
396  }
397  }
398 
399  backend->play(
400  reinterpret_cast<void *>(array.data()),
401  n_samples * n_channels * sizeof(T));
402 }
403 
404 
405 template <typename T>
406 void
407 _play(AudioPlayback * pb, AudioBackend ** be, const T & audio)
408 {
409  pb->initialize();
410 
411  if(*be == NULL) return;
412 
413  uint32 bits = (*be)->getBitsPerSample();
414 
415  switch(bits)
416  {
417  case 16:
418  play_int<int16>(*be, audio, 32768.0);
419  break;
420  case 32:
421  play_int<int32>(*be, audio, 2147483648.0);
422  break;
423 
424  default:
425  M_THROW("Nsound::AudioPlayback::play():"
426  << ": Support for "
427  << bits
428  << "-bit playback not yet implemented");
429  break;
430  }
431 }
432 
433 void
435 play(const AudioStream & a)
436 {
437  _play<AudioStream>(this, &backend_, a);
438 }
439 
440 void
442 play(const Buffer & b)
443 {
444  _play<Buffer>(this, &backend_, b);
445 }
446 
447 void
450 {
451  GuitarBass guitar(sample_rate_);
452 
453  AudioStream test_clip(sample_rate_, channels_);
454 
455  test_clip << guitar.play();
456 
457  initialize();
458 
459  std::vector< std::string > names = Nsound::getBackends();
460  std::vector< AudioBackendType > types = Nsound::getBackendTypes();
461 
462  cout << "Nsound::AudioPlayback::scanDevices(): starting\n";
463  cout.flush();
464 
465  if(types.size() == 0)
466  {
467  cout << "No backends available\n";
468  cout.flush();
469  }
470 
471  for(uint32 i = 0; i < types.size(); ++i)
472  {
473  cout << "Selecting backend '"
474  << names[i]
475  << "'\n";
476  cout.flush();
477 
478  AudioBackend * orig = backend_;
479 
481  types[i],
482  sample_rate_,
483  channels_,
485 
487  {
488  backend_->scanDevices(*this, test_clip);
489  }
490  else
491  {
492  cout << "Backend '"
493  << names[i]
494  << "' failed to initialize\n"
495  << backend_->getError();
496  cout.flush();
497  }
498 
499  delete backend_;
500 
501  backend_ = orig;
502  }
503 
504  cout << "Nsound::AudioPlayback::scanDevices(): finished\n";
505  cout.flush();
506 }
507 
508 void
510 setOption(const std::string & key, const std::string & value)
511 {
512  if(backend_ == NULL)
513  {
514  M_THROW("Nsound::AudioPlayback::setOption():"
515  << ": backend is NULL");
516  return;
517  }
519  {
520  M_THROW("Nsound::AudioPlayback::setOption():"
521  << ": backend already initialized");
522  return;
523  }
524 
525  backend_->setOption(key, value);
526 }
527 
528 void
531 {
532  if(backend_ != NULL)
533  {
534  backend_->shutdown();
535  delete backend_;
536  backend_ = NULL;
537  }
538 }
539 
540 void
541 Nsound::
543 {
544  ap.play(lhs);
545 }
546 
547 void
548 Nsound::
549 operator>>(const Buffer & lhs, AudioPlayback & ap)
550 {
551  ap.play(lhs);
552 }
553 
554 std::string
555 mylower(const std::string & x)
556 {
557  std::string y = x;
558  std::transform(y.begin(), y.end(), y.begin(), ::tolower);
559  return y;
560 }
561 
562 void
563 Nsound::
564 use(const std::string & backend)
565 {
566  std::string be = mylower(backend);
567 
568  if(be == "ao" || be == "libao")
569  {
571  }
572  else
573  if(be == "portaudio" || be == "libportaudio")
574  {
576  }
577  else
578  {
579  std::stringstream ss_buffer;
580  ss_buffer << "Nsound::use(): "
581  << "Unrecognized AudioBackend '"
582  << backend
583  << "'"
584  << endl;
585 
586  std::vector< std::string > vec = getBackends();
587 
588  ss_buffer << "Available backends are:" << endl;
589 
590  for(uint32 i = 0; i < vec.size(); ++i)
591  {
592  ss_buffer << " "
593  << vec[i]
594  << endl;
595  }
596 
597  M_THROW(ss_buffer.str());
598  }
599 }
600 
601 void
602 Nsound::
603 use(const AudioBackendType & type)
604 {
605  switch(type)
606  {
607  case BACKEND_TYPE_LIBAO:
608  use("ao");
609  break;
610 
612  use("portaudio");
613  break;
614 
615  default:
616  M_THROW("Nsound::use(): "
617  << "Unrecognized AudioBackendType "
618  << static_cast<uint32>(type));
619  }
620 }
621 
622 std::vector< std::string >
623 Nsound::
625 {
626  std::vector< std::string > vec;
627 
628  #if defined(NSOUND_LIBPORTAUDIO)
629  vec.push_back("portaudio");
630  #endif
631 
632  #if defined(NSOUND_LIBAO)
633  vec.push_back("ao");
634  #endif
635 
636  return vec;
637 }
638 
639 std::vector< AudioBackendType >
640 Nsound::
642 {
643  std::vector< AudioBackendType > vec;
644 
645  #if defined(NSOUND_LIBPORTAUDIO)
646  vec.push_back(BACKEND_TYPE_LIBPORTAUDIO);
647  #endif
648 
649  #if defined(NSOUND_LIBAO)
650  vec.push_back(BACKEND_TYPE_LIBAO);
651  #endif
652 
653  return vec;
654 }
655 
656 // :mode=c++:
std::string getError()
Returns an error string describing any backend error.
unsigned int uint32
Definition: Nsound.h:153
static AudioBackendType getBackendType()
Gets the AudioBackendType that is currently set.
AudioBackend * allocate_backend(const AudioBackendType &type, const uint32 sample_rate, const uint32 channels, const uint32 bits_per_sample)
void initialize()
Initializes the backend and transitions to the BACKEND_READY state on success.
virtual void play(void *data, uint32 n_bytes)=0
virtual void scanDevices(AudioPlayback &pb, const AudioStream &test_clip)=0
void scanDevices()
Scans for devices and tries to play a test sound.
void setOption(const std::string &key, const std::string &value)
Sets an options, must be called before initialize().
AudioBackend::State getState()
Returns the backend state.
uint32 getLength() const
Returns the number of samples of audio data in the stream.
Definition: AudioStream.cc:197
void _play(AudioPlayback *pb, AudioBackend **be, const T &audio)
virtual std::string getInfo()=0
virtual void setOption(const std::string &key, const std::string &value)=0
void use(const std::string &backend)
Selects the AudioBackend to use by name.
virtual void shutdown()=0
AudioPlayback(const float64 &sample_rate=44100.0, const uint32 channels=1, const uint32 bits_per_sample=16)
double float64
Definition: Nsound.h:146
AudioBackend * backend_
std::string getStateString()
Returns the backend state.
Definition: AudioBackend.cc:43
uint32 getLength() const
Returns the number of samples in the Buffer.
Definition: Buffer.h:587
std::string mylower(const std::string &x)
void play_int(AudioBackend *backend, const AudioStream &a, const float64 &scale)
virtual std::string getError()=0
void shutdown()
Shuts down the backend.
static AudioBackendType backend_type_
uint32 getNChannels(void) const
Returns the number of audio channels in the stream.
Definition: AudioStream.h:212
void operator>>(const AudioStream &lhs, AudioPlayback &rhs)
static void setBackendType(const AudioBackendType ab)
Sets the AudioBackendType.
std::string getStateString()
Returns the backend state string.
AudioStream play()
Plays a demo for this instrument.
Definition: GuitarBass.cc:56
#define M_THROW(message)
Definition: Macros.h:108
std::vector< AudioBackendType > getBackendTypes()
Returns a list of the available audio backends types.
std::string getInfo()
Returns information about the backend driver.
A Buffer for storing audio samples.
Definition: Buffer.h:60
virtual void initialize()=0
uint32 getBitsPerSample()
Definition: AudioBackend.h:77
void play(const AudioStream &a)
Plays the AudioStream throuh the backend.
std::vector< std::string > getBackends()
Returns a list of the available audio backends by name.
Class Drum.
Definition: GuitarBass.h:50