3 * Copyright 2004,2010 Free Software Foundation, Inc.
5 * This file is part of GNU Radio
7 * GNU Radio is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
12 * GNU Radio is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Radio; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street,
20 * Boston, MA 02110-1301, USA.
27 #include <audio_alsa_sink.h>
28 #include <gr_io_signature.h>
36 static bool CHATTY_DEBUG = false;
39 static snd_pcm_format_t acceptable_formats[] = {
40 // these are in our preferred order...
45 #define NELEMS(x) (sizeof(x)/sizeof(x[0]))
49 default_device_name ()
51 return gr_prefs::singleton()->get_string("audio_alsa", "default_output_device", "hw:0,0");
55 default_period_time ()
57 return std::max(0.001, gr_prefs::singleton()->get_double("audio_alsa", "period_time", 0.010));
63 return std::max(2L, gr_prefs::singleton()->get_long("audio_alsa", "nperiods", 4));
66 // ----------------------------------------------------------------
69 audio_alsa_make_sink (int sampling_rate,
70 const std::string dev,
73 return gnuradio::get_initial_sptr(new audio_alsa_sink (sampling_rate, dev,
77 audio_alsa_sink::audio_alsa_sink (int sampling_rate,
78 const std::string device_name,
80 : gr_sync_block ("audio_alsa_sink",
81 gr_make_io_signature (0, 0, 0),
82 gr_make_io_signature (0, 0, 0)),
83 d_sampling_rate (sampling_rate),
84 d_device_name (device_name.empty() ? default_device_name() : device_name),
86 d_hw_params ((snd_pcm_hw_params_t *)(new char[snd_pcm_hw_params_sizeof()])),
87 d_sw_params ((snd_pcm_sw_params_t *)(new char[snd_pcm_sw_params_sizeof()])),
88 d_nperiods (default_nperiods()),
89 d_period_time_us ((unsigned int) (default_period_time() * 1e6)),
91 d_buffer_size_bytes (0), d_buffer (0),
92 d_worker (0), d_special_case_mono_to_stereo (false),
93 d_nunderuns (0), d_nsuspends (0), d_ok_to_block(ok_to_block),
94 d_change_in_progress(false)
96 CHATTY_DEBUG = gr_prefs::singleton()->get_bool("audio_alsa", "verbose", false);
101 // open the device for playback
102 error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str (),
103 SND_PCM_STREAM_PLAYBACK, 0);
104 if (ok_to_block == false)
105 snd_pcm_nonblock(d_pcm_handle, !ok_to_block);
107 fprintf (stderr, "audio_alsa_sink[%s]: %s\n",
108 d_device_name.c_str(), snd_strerror(error));
109 throw std::runtime_error ("audio_alsa_sink");
112 // Fill params with a full configuration space for a PCM.
113 error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params);
115 bail ("broken configuration for playback", error);
119 gri_alsa_dump_hw_params (d_pcm_handle, d_hw_params, stdout);
122 // now that we know how many channels the h/w can handle, set input signature
123 unsigned int umin_chan, umax_chan;
124 snd_pcm_hw_params_get_channels_min (d_hw_params, &umin_chan);
125 snd_pcm_hw_params_get_channels_max (d_hw_params, &umax_chan);
126 int min_chan = std::min (umin_chan, 1000U);
127 int max_chan = std::min (umax_chan, 1000U);
129 // As a special case, if the hw's min_chan is two, we'll accept
130 // a single input and handle the duplication ourselves.
134 d_special_case_mono_to_stereo = true;
136 set_input_signature (gr_make_io_signature (min_chan, max_chan,
139 // fill in portions of the d_hw_params that we know now...
141 // Specify the access methods we implement
142 // For now, we only handle RW_INTERLEAVED...
143 snd_pcm_access_mask_t *access_mask;
144 snd_pcm_access_mask_t **access_mask_ptr = &access_mask; // FIXME: workaround for compiler warning
145 snd_pcm_access_mask_alloca (access_mask_ptr);
146 snd_pcm_access_mask_none (access_mask);
147 snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_INTERLEAVED);
148 // snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
150 if ((error = snd_pcm_hw_params_set_access_mask (d_pcm_handle,
151 d_hw_params, access_mask)) < 0)
152 bail ("failed to set access mask", error);
156 if (!gri_alsa_pick_acceptable_format (d_pcm_handle, d_hw_params,
158 NELEMS (acceptable_formats),
162 throw std::runtime_error ("audio_alsa_sink");
166 unsigned int orig_sampling_rate = d_sampling_rate;
167 if ((error = snd_pcm_hw_params_set_rate_near (d_pcm_handle, d_hw_params,
168 &d_sampling_rate, 0)) < 0)
169 bail ("failed to set rate near", error);
171 if (orig_sampling_rate != d_sampling_rate){
172 fprintf (stderr, "audio_alsa_sink[%s]: unable to support sampling rate %d\n",
173 snd_pcm_name (d_pcm_handle), orig_sampling_rate);
174 fprintf (stderr, " card requested %d instead.\n", d_sampling_rate);
178 * ALSA transfers data in units of "periods".
179 * We indirectly determine the underlying buffersize by specifying
180 * the number of periods we want (typically 4) and the length of each
181 * period in units of time (typically 1ms).
183 unsigned int min_nperiods, max_nperiods;
184 snd_pcm_hw_params_get_periods_min (d_hw_params, &min_nperiods, &dir);
185 snd_pcm_hw_params_get_periods_max (d_hw_params, &max_nperiods, &dir);
186 //fprintf (stderr, "alsa_sink: min_nperiods = %d, max_nperiods = %d\n",
187 // min_nperiods, max_nperiods);
189 unsigned int orig_nperiods = d_nperiods;
190 d_nperiods = std::min (std::max (min_nperiods, d_nperiods), max_nperiods);
192 // adjust period time so that total buffering remains more-or-less constant
193 d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods;
195 error = snd_pcm_hw_params_set_periods (d_pcm_handle, d_hw_params,
198 bail ("set_periods failed", error);
201 error = snd_pcm_hw_params_set_period_time_near (d_pcm_handle, d_hw_params,
202 &d_period_time_us, &dir);
204 bail ("set_period_time_near failed", error);
207 error = snd_pcm_hw_params_get_period_size (d_hw_params,
208 &d_period_size, &dir);
210 bail ("get_period_size failed", error);
212 set_output_multiple (d_period_size);
217 audio_alsa_sink::check_topology (int ninputs, int noutputs)
219 // ninputs is how many channels the user has connected.
220 // Now we can finish up setting up the hw params...
225 // FIXME check_topology may be called more than once.
226 // Ensure that the pcm is in a state where we can still mess with the hw_params
228 d_change_in_progress = true;
230 if (snd_pcm_state (d_pcm_handle) == SND_PCM_STATE_RUNNING)
231 snd_pcm_drop (d_pcm_handle);
233 bool special_case = nchan == 1 && d_special_case_mono_to_stereo;
237 err = snd_pcm_hw_params_set_channels (d_pcm_handle, d_hw_params, nchan);
240 output_error_msg ("set_channels failed", err);
241 d_change_in_progress = false;
245 // set the parameters into the driver...
246 err = snd_pcm_hw_params(d_pcm_handle, d_hw_params);
248 output_error_msg ("snd_pcm_hw_params failed", err);
249 d_change_in_progress = false;
253 // get current s/w params
254 err = snd_pcm_sw_params_current (d_pcm_handle, d_sw_params);
256 bail ("snd_pcm_sw_params_current", err);
258 // Tell the PCM device to wait to start until we've filled
259 // it's buffers half way full. This helps avoid audio underruns.
261 err = snd_pcm_sw_params_set_start_threshold(d_pcm_handle,
263 d_nperiods * d_period_size / 2);
265 bail ("snd_pcm_sw_params_set_start_threshold", err);
267 // store the s/w params
268 err = snd_pcm_sw_params (d_pcm_handle, d_sw_params);
270 bail ("snd_pcm_sw_params", err);
272 d_buffer_size_bytes =
273 d_period_size * nchan * snd_pcm_format_size (d_format, 1);
275 d_buffer = new char [d_buffer_size_bytes];
278 fprintf (stdout, "audio_alsa_sink[%s]: sample resolution = %d bits\n",
279 snd_pcm_name (d_pcm_handle),
280 snd_pcm_hw_params_get_sbits (d_hw_params));
283 case SND_PCM_FORMAT_S16:
285 d_worker = &audio_alsa_sink::work_s16_1x2;
287 d_worker = &audio_alsa_sink::work_s16;
290 case SND_PCM_FORMAT_S32:
292 d_worker = &audio_alsa_sink::work_s32_1x2;
294 d_worker = &audio_alsa_sink::work_s32;
300 d_change_in_progress = false;
304 audio_alsa_sink::~audio_alsa_sink ()
306 if (snd_pcm_state (d_pcm_handle) == SND_PCM_STATE_RUNNING)
307 snd_pcm_drop (d_pcm_handle);
309 snd_pcm_close(d_pcm_handle);
310 delete [] ((char *) d_hw_params);
311 delete [] ((char *) d_sw_params);
316 audio_alsa_sink::work (int noutput_items,
317 gr_vector_const_void_star &input_items,
318 gr_vector_void_star &output_items)
320 assert ((noutput_items % d_period_size) == 0);
322 // this is a call through a pointer to a method...
323 return (this->*d_worker)(noutput_items, input_items, output_items);
327 * Work function that deals with float to S16 conversion
330 audio_alsa_sink::work_s16 (int noutput_items,
331 gr_vector_const_void_star &input_items,
332 gr_vector_void_star &output_items)
334 typedef gr_int16 sample_t; // the type of samples we're creating
335 static const int NBITS = 16; // # of bits in a sample
337 unsigned int nchan = input_items.size ();
338 const float **in = (const float **) &input_items[0];
339 sample_t *buf = (sample_t *) d_buffer;
343 unsigned int sizeof_frame = nchan * sizeof (sample_t);
344 assert (d_buffer_size_bytes == d_period_size * sizeof_frame);
346 for (n = 0; n < noutput_items; n += d_period_size){
348 // process one period of data
350 for (unsigned int i = 0; i < d_period_size; i++){
351 for (unsigned int chan = 0; chan < nchan; chan++){
352 buf[bi++] = (sample_t) (in[chan][i] * (float) ((1L << (NBITS-1)) - 1));
356 // update src pointers
357 for (unsigned int chan = 0; chan < nchan; chan++)
358 in[chan] += d_period_size;
360 if (!write_buffer (buf, d_period_size, sizeof_frame))
361 return -1; // No fixing this problem. Say we're done.
369 * Work function that deals with float to S32 conversion
372 audio_alsa_sink::work_s32 (int noutput_items,
373 gr_vector_const_void_star &input_items,
374 gr_vector_void_star &output_items)
376 typedef gr_int32 sample_t; // the type of samples we're creating
377 static const int NBITS = 32; // # of bits in a sample
379 unsigned int nchan = input_items.size ();
380 const float **in = (const float **) &input_items[0];
381 sample_t *buf = (sample_t *) d_buffer;
385 unsigned int sizeof_frame = nchan * sizeof (sample_t);
386 assert (d_buffer_size_bytes == d_period_size * sizeof_frame);
388 for (n = 0; n < noutput_items; n += d_period_size){
390 // process one period of data
392 for (unsigned int i = 0; i < d_period_size; i++){
393 for (unsigned int chan = 0; chan < nchan; chan++){
394 buf[bi++] = (sample_t) (in[chan][i] * (float) ((1L << (NBITS-1)) - 1));
398 // update src pointers
399 for (unsigned int chan = 0; chan < nchan; chan++)
400 in[chan] += d_period_size;
402 if (!write_buffer (buf, d_period_size, sizeof_frame))
403 return -1; // No fixing this problem. Say we're done.
410 * Work function that deals with float to S16 conversion and
411 * mono to stereo kludge.
414 audio_alsa_sink::work_s16_1x2 (int noutput_items,
415 gr_vector_const_void_star &input_items,
416 gr_vector_void_star &output_items)
418 typedef gr_int16 sample_t; // the type of samples we're creating
419 static const int NBITS = 16; // # of bits in a sample
421 assert (input_items.size () == 1);
422 static const unsigned int nchan = 2;
423 const float **in = (const float **) &input_items[0];
424 sample_t *buf = (sample_t *) d_buffer;
428 unsigned int sizeof_frame = nchan * sizeof (sample_t);
429 assert (d_buffer_size_bytes == d_period_size * sizeof_frame);
431 for (n = 0; n < noutput_items; n += d_period_size){
433 // process one period of data
435 for (unsigned int i = 0; i < d_period_size; i++){
436 sample_t t = (sample_t) (in[0][i] * (float) ((1L << (NBITS-1)) - 1));
441 // update src pointers
442 in[0] += d_period_size;
444 if (!write_buffer (buf, d_period_size, sizeof_frame))
445 return -1; // No fixing this problem. Say we're done.
452 * Work function that deals with float to S32 conversion and
453 * mono to stereo kludge.
456 audio_alsa_sink::work_s32_1x2 (int noutput_items,
457 gr_vector_const_void_star &input_items,
458 gr_vector_void_star &output_items)
460 typedef gr_int32 sample_t; // the type of samples we're creating
461 static const int NBITS = 32; // # of bits in a sample
463 assert (input_items.size () == 1);
464 static unsigned int nchan = 2;
465 const float **in = (const float **) &input_items[0];
466 sample_t *buf = (sample_t *) d_buffer;
470 unsigned int sizeof_frame = nchan * sizeof (sample_t);
471 assert (d_buffer_size_bytes == d_period_size * sizeof_frame);
473 for (n = 0; n < noutput_items; n += d_period_size){
475 // process one period of data
477 for (unsigned int i = 0; i < d_period_size; i++){
478 sample_t t = (sample_t) (in[0][i] * (float) ((1L << (NBITS-1)) - 1));
483 // update src pointers
484 in[0] += d_period_size;
486 if (!write_buffer (buf, d_period_size, sizeof_frame))
487 return -1; // No fixing this problem. Say we're done.
494 audio_alsa_sink::write_buffer (const void *vbuffer,
495 unsigned nframes, unsigned sizeof_frame)
497 const unsigned char *buffer = (const unsigned char *) vbuffer;
499 int change_counter = 10;
500 while (d_change_in_progress == true && change_counter >= 0) {
504 d_change_in_progress = false;
507 int r = snd_pcm_writei (d_pcm_handle, buffer, nframes);
510 if (d_ok_to_block == true)
511 continue; // try again
516 else if (r == -EPIPE){ // underrun
518 fputs ("aU", stderr);
519 if ((r = snd_pcm_prepare (d_pcm_handle)) < 0){
520 output_error_msg ("snd_pcm_prepare failed. Can't recover from underrun", r);
523 continue; // try again
526 else if (r == -ESTRPIPE){ // h/w is suspended (whatever that means)
527 // This is apparently related to power management
529 if ((r = snd_pcm_resume (d_pcm_handle)) < 0){
530 output_error_msg ("failed to resume from suspend", r);
533 continue; // try again
537 output_error_msg ("snd_pcm_writei failed", r);
542 buffer += r * sizeof_frame;
550 audio_alsa_sink::output_error_msg (const char *msg, int err)
552 fprintf (stderr, "audio_alsa_sink[%s]: %s: %s\n",
553 snd_pcm_name (d_pcm_handle), msg, snd_strerror (err));
557 audio_alsa_sink::bail (const char *msg, int err) throw (std::runtime_error)
559 output_error_msg (msg, err);
560 throw std::runtime_error ("audio_alsa_sink");