SoDaRadio-5.0.3-master:8901fb5
AudioALSA.cxx
Go to the documentation of this file.
1 /*
2  Copyright (c) 2012, Matthew H. Reilly (kb1vc)
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are
7  met:
8 
9  Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in
13  the documentation and/or other materials provided with the
14  distribution.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 #include "AudioALSA.hxx"
30 #if HAVE_ASOUNDLIB
31 #include <alsa/asoundlib.h>
32 #endif
33 
34 #include <boost/format.hpp>
35 
36 // Use the simple setup, as recent (March 2015) changes to the
37 // ALSA/pulseaudio interactions have made explicit selection
38 // of parameters somewhat harder to figure out. In particular,
39 // buffer management has become far more important.
40 #define ALSA_USE_SIMPLE_SETUP
41 
42 namespace SoDa {
43 #if HAVE_LIBASOUND
44  AudioALSA::AudioALSA(unsigned int _sample_rate,
45  DataFormat _fmt,
46  unsigned int _sample_count_hint,
47  std::string audio_port_name) :
48  AudioIfc(_sample_rate, _fmt, _sample_count_hint, "AudioALSA ALSA Interface") {
49 
50  // code is largely borrowed from equalarea.com/paul/alsa-audio.html
51  setupPlayback(audio_port_name);
52 
53  setupCapture(audio_port_name);
54  }
55 
56  void AudioALSA::setupPlayback(std::string audio_port_name)
57  {
58  (void) audio_port_name;
59  // setup the playback (output) stream
60  char pcm_name[] = "default";
61 
62  // const char *pcm_name = audio_port_name.c_str();
63 
64  if(snd_pcm_open(&pcm_out, pcm_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
65  std::cerr << boost::format("can't open Alsa PCM device [%s] for output ... Crap.\n") % pcm_name;
66  exit(-1);
67  }
68 
69 #ifdef ALSA_USE_SIMPLE_SETUP
70  checkStatus(snd_pcm_set_params(pcm_out,
71  SND_PCM_FORMAT_FLOAT,
72  SND_PCM_ACCESS_RW_INTERLEAVED,
73  1,
75  1,
76  500000), // 100000),
77  "Failed to do simple set params for output.", true);
78 #else
79  setupParams(pcm_out, hw_out_params);
80 #endif
81  }
82 
83  void AudioALSA::setupCapture(std::string audio_port_name)
84  {
85  // char pcm_cap_name[] = "default"; // "hw:0,2";
86  const char *pcm_cap_name = audio_port_name.c_str();
87  snd_pcm_stream_t instream = SND_PCM_STREAM_CAPTURE;
88  if(snd_pcm_open(&pcm_in, pcm_cap_name, instream, 0) < 0) {
89  std::cerr << boost::format("can't open Alsa PCM device [%s] for input... Crap.\n") % pcm_cap_name;
90  exit(-1);
91  }
92 
93 #ifdef ALSA_USE_SIMPLE_SETUP
94  checkStatus(snd_pcm_set_params(pcm_in,
95  SND_PCM_FORMAT_FLOAT,
96  SND_PCM_ACCESS_RW_INTERLEAVED,
97  1,
99  1,
100  500000), // 100000),
101  "Failed to do simple set params for output.", true);
102 #else
103  setupParams(pcm_in, hw_in_params);
104 #endif
105  }
106 
107  void AudioALSA::setupParams(snd_pcm_t * dev, snd_pcm_hw_params_t * & hw_params_ptr)
108  {
109  snd_pcm_hw_params_t * hw_paramsp;
110 
111  snd_pcm_hw_params_alloca(&hw_paramsp);
112 
113  hw_params_ptr = hw_paramsp;
114 
115  checkStatus(snd_pcm_hw_params_any (dev, hw_paramsp), "setupParams init parm block", true);
116 
117  checkStatus(snd_pcm_hw_params_set_access (dev, hw_paramsp, SND_PCM_ACCESS_RW_INTERLEAVED),
118  "setupParams set access", true);
119 
120  checkStatus(snd_pcm_hw_params_set_format (dev, hw_paramsp, translateFormat(format)),
121  "setupParams set format", true);
122 
123  checkStatus(snd_pcm_hw_params_set_rate_near (dev, hw_paramsp, &sample_rate, 0),
124  "setupParams set sample rate", true);
125 
126  checkStatus(snd_pcm_hw_params_set_channels (dev, hw_paramsp, 1),
127  "setupParams set number of channels", true);
128 
129  checkStatus(snd_pcm_hw_params_set_buffer_size (dev, hw_paramsp,
131  "setupParams set buffer size", true);
132 
133  checkStatus(snd_pcm_hw_params (dev, hw_paramsp),
134  "setupParams set parameter block", true);
135 
136  checkStatus(snd_pcm_prepare (dev),
137  "setupParams prepare audio interface", true);
138  }
139 
140 
141  bool AudioALSA::recvBufferReady(unsigned int len) {
142  snd_pcm_sframes_t sframes_ready = snd_pcm_avail(pcm_in);
143  if(sframes_ready == -EPIPE) {
144  // we got an under-run... just ignore it.
145  int err;
146  if((err = snd_pcm_recover(pcm_in, sframes_ready, 1)) < 0) {
147  checkStatus(err, "recvBufferReady got EPIPE, tried recovery", false);
148  }
149  sframes_ready = snd_pcm_avail(pcm_in);
150  }
151 
152  if(sframes_ready < 0) {
153  checkStatus(sframes_ready, "recvBufferReady", false);
154  }
155  return sframes_ready >= len;
156  }
157 
158  bool AudioALSA::sendBufferReady(unsigned int len) {
159  snd_pcm_sframes_t sframes_ready;
160 
161  while(1) {
162  sframes_ready= snd_pcm_avail(pcm_out);
163 
164  if(sframes_ready == -EPIPE) {
165  // we got an under-run... we can't just ignore it.
166  // if pcm_avail returns -EPIPE we need to recover and restart the pipe... sigh.
167  // std::cerr << boost::format("snd_pcm_avail returns EPIPE -- current state is %s\n") % currentPlaybackState();
168  int err;
169  if((err = snd_pcm_recover(pcm_out, sframes_ready, 1)) < 0) {
170  checkStatus(err, "sendBufferReady got EPIPE, tried recovery", false);
171  }
172  if((err = snd_pcm_start(pcm_out)) < 0) {
173  throw
174  SoDaException((boost::format("AudioALSA::wakeOut() Failed to wake after sleepOut() -- %s")
175  % snd_strerror(err)).str(), this);
176  }
177  }
178  else {
179  checkStatus(sframes_ready, "sendBufferReady", false);
180  break;
181  }
182  }
183  return sframes_ready >= len;
184  }
185 
186  int AudioALSA::send(void * buf, unsigned int len) {
187  int err;
188  int olen = len;
189 
190  char * cbuf = (char *) buf;
191  while(1) {
192  err = snd_pcm_writei(pcm_out, cbuf, len);
193 
194  if(err == (int) len) return len;
195  else if(err == -EAGAIN) continue;
196  else if(err == -EPIPE) {
197  // we got an under-run... just ignore it.
198  if((err = snd_pcm_recover(pcm_out, err, 1)) < 0) {
199  checkStatus(err, "send got EPIPE, tried recovery", false);
200  }
201  }
202  else if(err < 0) {
203  checkStatus(err, "send", true);
204  }
205  else if(err != (int)len) {
206  len -= err;
207  cbuf += (err * datatype_size);
208  }
209  }
210 
211  return olen;
212  }
213 
214  int AudioALSA::recv(void * buf, unsigned int len, bool block) {
215  (void) block;
216  int err;
217  int olen = len;
218 
219  char * cbuf = (char *) buf;
220  while(1) {
221  err = snd_pcm_readi(pcm_in, cbuf, len);
222 
223  if(err == (int)len) return len;
224  else if(err == -EAGAIN) continue;
225  else if(err < 0) {
226  checkStatus(err, "recv", true);
227  }
228  else if(err != (int)len) {
229  len -= err;
230  cbuf += (err * datatype_size);
231  }
232  }
233 
234  return olen;
235  }
236 
237 
238  snd_pcm_format_t AudioALSA::translateFormat(AudioIfc::DataFormat fmt) {
239  switch(fmt) {
240  case FLOAT: return SND_PCM_FORMAT_FLOAT;
241  break;
242  case DFLOAT: return SND_PCM_FORMAT_FLOAT64;
243  break;
244  case INT32: return SND_PCM_FORMAT_S32;
245  break;
246  case INT16: return SND_PCM_FORMAT_S16;
247  break;
248  case INT8: return SND_PCM_FORMAT_S8;
249  break;
250  }
251  return SND_PCM_FORMAT_S16_LE;
252  }
253 #else
254  AudioALSA::AudioALSA(unsigned int _sample_rate,
255  DataFormat _fmt,
256  unsigned int _sample_count_hint,
257  std::string audio_port_name) :
258  AudioIfc(_sample_rate, _fmt, _sample_count_hint, "AudioALSA ALSA Interface")
259  {
260  std::cerr << "ALSA Sound Library is not enabled in this build version.";
261  throw SoDa::SoDaException("ALSA Sound Library is not enabled in this build version.");
262  }
263 #endif // HAVE_LIBASOUND
264 }
int send(void *buf, unsigned int len)
send – send a buffer to the audio output
Definition: AudioALSA.hxx:90
bool sendBufferReady(unsigned int len)
sendBufferReady – is there enough space in the audio device send buffer for a call from send...
Definition: AudioALSA.hxx:98
unsigned int sample_rate
Definition: AudioIfc.hxx:169
The SoDa Exception class.
Definition: SoDaBase.hxx:217
unsigned int sample_count_hint
Definition: AudioIfc.hxx:171
bool recvBufferReady(unsigned int len)
recvBufferReady – are there samples waiting in the audio device?
Definition: AudioALSA.hxx:115
Generic Audio Interface Class.
Definition: AudioIfc.hxx:44
int recv(void *buf, unsigned int len, bool block=true)
recv – get a buffer of data from the audio input
Definition: AudioALSA.hxx:107
DataFormat format
Definition: AudioIfc.hxx:170
AudioALSA(unsigned int _sample_rate, AudioIfc::DataFormat _fmt, unsigned int _sample_count_hint=1024, std::string audio_port_name=std::string("default"))
constructor
Definition: AudioALSA.cxx:254