diff options
author | Tom Rondeau <trondeau@vt.edu> | 2013-02-11 21:20:27 -0500 |
---|---|---|
committer | Tom Rondeau <trondeau@vt.edu> | 2013-02-11 21:20:27 -0500 |
commit | 354c1219a22e13422b14d9bc14645e1296776d24 (patch) | |
tree | e432361a66c5ecb8c6fd47c16e2f96722690f0c4 | |
parent | d28b2f2bdd4a2961bb29076085a129299aeec157 (diff) |
audio: wip: conversion cont. Alsa building.
-rw-r--r-- | gr-audio/include/audio/sink.h | 60 | ||||
-rw-r--r-- | gr-audio/include/audio/source.h | 60 | ||||
-rw-r--r-- | gr-audio/lib/alsa/audio_alsa_sink.cc | 940 | ||||
-rw-r--r-- | gr-audio/lib/alsa/audio_alsa_sink.h | 146 | ||||
-rw-r--r-- | gr-audio/lib/alsa/audio_alsa_source.cc | 889 | ||||
-rw-r--r-- | gr-audio/lib/alsa/audio_alsa_source.h | 151 | ||||
-rw-r--r-- | gr-audio/lib/gr_audio_registry.cc | 212 | ||||
-rw-r--r-- | gr-audio/lib/gr_audio_registry.h | 46 |
8 files changed, 1279 insertions, 1225 deletions
diff --git a/gr-audio/include/audio/sink.h b/gr-audio/include/audio/sink.h index a66db7b0f4..d53ff6f6f1 100644 --- a/gr-audio/include/audio/sink.h +++ b/gr-audio/include/audio/sink.h @@ -1,3 +1,4 @@ +/* -*- c++ -*- */ /* * Copyright 2011,2013 Free Software Foundation, Inc. * @@ -25,33 +26,38 @@ #include <audio/api.h> #include <gr_sync_block.h> -/*! - * \brief Creates a sink from an audio device. - * \ingroup audio_blk - */ -class GR_AUDIO_API audio_sink : virtual public gr_sync_block{ -public: - typedef boost::shared_ptr<audio_sink> sptr; -}; +namespace gr { + namespace audio { -/*! - * Creates a sink from an audio device at a specified - * sample_rate. The specific audio device to use can be specified as - * the device_name parameter. Typical choices are: - * \li pulse - * \li hw:0,0 - * \li plughw:0,0 - * \li surround51 - * \li /dev/dsp - * - * \xmlonly - * - pulse, hw:0,0, plughw:0,0, surround51, /dev/dsp - * \endxmlonly - */ -GR_AUDIO_API audio_sink::sptr audio_make_sink( - int sampling_rate, - const std::string device_name = "", - bool ok_to_block = true -); + /*! + * \brief Creates a sink from an audio device. + * \ingroup audio_blk + */ + class GR_AUDIO_API sink : virtual public gr_sync_block + { + public: + typedef boost::shared_ptr<sink> sptr; + + /*! + * Creates a sink from an audio device at a specified + * sample_rate. The specific audio device to use can be + * specified as the device_name parameter. Typical choices are: + * \li pulse + * \li hw:0,0 + * \li plughw:0,0 + * \li surround51 + * \li /dev/dsp + * + * \xmlonly + * - pulse, hw:0,0, plughw:0,0, surround51, /dev/dsp + * \endxmlonly + */ + static sptr make(int sampling_rate, + const std::string device_name = "", + bool ok_to_block = true); + }; + + } /* namespace audio */ +} /* namespace gr */ #endif /* INCLUDED_GR_AUDIO_SINK_H */ diff --git a/gr-audio/include/audio/source.h b/gr-audio/include/audio/source.h index e920c5143e..f8e21f1567 100644 --- a/gr-audio/include/audio/source.h +++ b/gr-audio/include/audio/source.h @@ -1,3 +1,4 @@ +/* -*- c++ -*- */ /* * Copyright 2011,2013 Free Software Foundation, Inc. * @@ -25,33 +26,38 @@ #include <audio/api.h> #include <gr_sync_block.h> -/*! - * \brief Creates a source from an audio device. - * \ingroup audio_blk - */ -class GR_AUDIO_API audio_source : virtual public gr_sync_block{ -public: - typedef boost::shared_ptr<audio_source> sptr; -}; +namespace gr { + namespace audio { -/*! - * Creates a source from an audio device at a specified - * sample_rate. The specific audio device to use can be specified as - * the device_name parameter. Typical choices are: - * \li pulse - * \li hw:0,0 - * \li plughw:0,0 - * \li surround51 - * \li /dev/dsp - * - * \xmlonly - * - pulse, hw:0,0, plughw:0,0, surround51, /dev/dsp - * \endxmlonly - */ -GR_AUDIO_API audio_source::sptr audio_make_source( - int sampling_rate, - const std::string device_name = "", - bool ok_to_block = true -); + /*! + * \brief Creates a source from an audio device. + * \ingroup audio_blk + */ + class GR_AUDIO_API source : virtual public gr_sync_block + { + public: + typedef boost::shared_ptr<source> sptr; + + /*! + * Creates a source from an audio device at a specified + * sample_rate. The specific audio device to use can be + * specified as the device_name parameter. Typical choices are: + * \li pulse + * \li hw:0,0 + * \li plughw:0,0 + * \li surround51 + * \li /dev/dsp + * + * \xmlonly + * - pulse, hw:0,0, plughw:0,0, surround51, /dev/dsp + * \endxmlonly + */ + static sptr make(int sampling_rate, + const std::string device_name = "", + bool ok_to_block = true); + }; + + } /* namespace audio */ +} /* namespace gr */ #endif /* INCLUDED_GR_AUDIO_SOURCE_H */ diff --git a/gr-audio/lib/alsa/audio_alsa_sink.cc b/gr-audio/lib/alsa/audio_alsa_sink.cc index 687f24bde2..65f4379310 100644 --- a/gr-audio/lib/alsa/audio_alsa_sink.cc +++ b/gr-audio/lib/alsa/audio_alsa_sink.cc @@ -33,516 +33,510 @@ #include <stdexcept> #include <gri_alsa.h> -AUDIO_REGISTER_SINK(REG_PRIO_HIGH, alsa)( - int sampling_rate, const std::string &device_name, bool ok_to_block -){ - return audio_sink::sptr(new audio_alsa_sink(sampling_rate, device_name, ok_to_block)); -} +namespace gr { + namespace audio { -static bool CHATTY_DEBUG = false; + AUDIO_REGISTER_SINK(REG_PRIO_HIGH, alsa)(int sampling_rate, + const std::string &device_name, + bool ok_to_block) + { + return sink::sptr + (new alsa_sink(sampling_rate, device_name, ok_to_block)); + } + static bool CHATTY_DEBUG = false; -static snd_pcm_format_t acceptable_formats[] = { - // these are in our preferred order... - SND_PCM_FORMAT_S32, - SND_PCM_FORMAT_S16 -}; + static snd_pcm_format_t acceptable_formats[] = { + // these are in our preferred order... + SND_PCM_FORMAT_S32, + SND_PCM_FORMAT_S16 + }; #define NELEMS(x) (sizeof(x)/sizeof(x[0])) - -static std::string -default_device_name () -{ - return gr_prefs::singleton()->get_string("audio_alsa", "default_output_device", "hw:0,0"); -} - -static double -default_period_time () -{ - return std::max(0.001, gr_prefs::singleton()->get_double("audio_alsa", "period_time", 0.010)); -} - -static int -default_nperiods () -{ - return std::max(2L, gr_prefs::singleton()->get_long("audio_alsa", "nperiods", 4)); -} - -// ---------------------------------------------------------------- - -audio_alsa_sink::audio_alsa_sink (int sampling_rate, - const std::string device_name, - bool ok_to_block) - : gr_sync_block ("audio_alsa_sink", - gr_make_io_signature (0, 0, 0), - gr_make_io_signature (0, 0, 0)), - d_sampling_rate (sampling_rate), - d_device_name (device_name.empty() ? default_device_name() : device_name), - d_pcm_handle (0), - d_hw_params ((snd_pcm_hw_params_t *)(new char[snd_pcm_hw_params_sizeof()])), - d_sw_params ((snd_pcm_sw_params_t *)(new char[snd_pcm_sw_params_sizeof()])), - d_nperiods (default_nperiods()), - d_period_time_us ((unsigned int) (default_period_time() * 1e6)), - d_period_size (0), - d_buffer_size_bytes (0), d_buffer (0), - d_worker (0), d_special_case_mono_to_stereo (false), - d_nunderuns (0), d_nsuspends (0), d_ok_to_block(ok_to_block) -{ - CHATTY_DEBUG = gr_prefs::singleton()->get_bool("audio_alsa", "verbose", false); - - int error; - int dir; - - // open the device for playback - error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str (), - SND_PCM_STREAM_PLAYBACK, 0); - if (ok_to_block == false) - snd_pcm_nonblock(d_pcm_handle, !ok_to_block); - if (error < 0){ - fprintf (stderr, "audio_alsa_sink[%s]: %s\n", - d_device_name.c_str(), snd_strerror(error)); - throw std::runtime_error ("audio_alsa_sink"); - } - - // Fill params with a full configuration space for a PCM. - error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); - if (error < 0) - bail ("broken configuration for playback", error); - - - if (CHATTY_DEBUG) - gri_alsa_dump_hw_params (d_pcm_handle, d_hw_params, stdout); - - - // now that we know how many channels the h/w can handle, set input signature - unsigned int umin_chan, umax_chan; - snd_pcm_hw_params_get_channels_min (d_hw_params, &umin_chan); - snd_pcm_hw_params_get_channels_max (d_hw_params, &umax_chan); - int min_chan = std::min (umin_chan, 1000U); - int max_chan = std::min (umax_chan, 1000U); - - // As a special case, if the hw's min_chan is two, we'll accept - // a single input and handle the duplication ourselves. - - if (min_chan == 2){ - min_chan = 1; - d_special_case_mono_to_stereo = true; - } - set_input_signature (gr_make_io_signature (min_chan, max_chan, - sizeof (float))); - - // fill in portions of the d_hw_params that we know now... - - // Specify the access methods we implement - // For now, we only handle RW_INTERLEAVED... - snd_pcm_access_mask_t *access_mask; - snd_pcm_access_mask_t **access_mask_ptr = &access_mask; // FIXME: workaround for compiler warning - snd_pcm_access_mask_alloca (access_mask_ptr); - snd_pcm_access_mask_none (access_mask); - snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_INTERLEAVED); - // snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); - - if ((error = snd_pcm_hw_params_set_access_mask (d_pcm_handle, - d_hw_params, access_mask)) < 0) - bail ("failed to set access mask", error); - - - // set sample format - if (!gri_alsa_pick_acceptable_format (d_pcm_handle, d_hw_params, - acceptable_formats, - NELEMS (acceptable_formats), - &d_format, - "audio_alsa_sink", - CHATTY_DEBUG)) - throw std::runtime_error ("audio_alsa_sink"); - - - // sampling rate - unsigned int orig_sampling_rate = d_sampling_rate; - if ((error = snd_pcm_hw_params_set_rate_near (d_pcm_handle, d_hw_params, - &d_sampling_rate, 0)) < 0) - bail ("failed to set rate near", error); - - if (orig_sampling_rate != d_sampling_rate){ - fprintf (stderr, "audio_alsa_sink[%s]: unable to support sampling rate %d\n", - snd_pcm_name (d_pcm_handle), orig_sampling_rate); - fprintf (stderr, " card requested %d instead.\n", d_sampling_rate); - } - - /* - * ALSA transfers data in units of "periods". - * We indirectly determine the underlying buffersize by specifying - * the number of periods we want (typically 4) and the length of each - * period in units of time (typically 1ms). - */ - unsigned int min_nperiods, max_nperiods; - snd_pcm_hw_params_get_periods_min (d_hw_params, &min_nperiods, &dir); - snd_pcm_hw_params_get_periods_max (d_hw_params, &max_nperiods, &dir); - //fprintf (stderr, "alsa_sink: min_nperiods = %d, max_nperiods = %d\n", - // min_nperiods, max_nperiods); - - unsigned int orig_nperiods = d_nperiods; - d_nperiods = std::min (std::max (min_nperiods, d_nperiods), max_nperiods); - - // adjust period time so that total buffering remains more-or-less constant - d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; - - error = snd_pcm_hw_params_set_periods (d_pcm_handle, d_hw_params, - d_nperiods, 0); - if (error < 0) - bail ("set_periods failed", error); - - dir = 0; - error = snd_pcm_hw_params_set_period_time_near (d_pcm_handle, d_hw_params, - &d_period_time_us, &dir); - if (error < 0) - bail ("set_period_time_near failed", error); - - dir = 0; - error = snd_pcm_hw_params_get_period_size (d_hw_params, - &d_period_size, &dir); - if (error < 0) - bail ("get_period_size failed", error); - - set_output_multiple (d_period_size); -} - - -bool -audio_alsa_sink::check_topology (int ninputs, int noutputs) -{ - // ninputs is how many channels the user has connected. - // Now we can finish up setting up the hw params... - - int nchan = ninputs; - int err; - - // Check the state of the stream - // Ensure that the pcm is in a state where we can still mess with the hw_params - snd_pcm_state_t state; - state=snd_pcm_state(d_pcm_handle); - if ( state== SND_PCM_STATE_RUNNING) - return true; // If stream is running, don't change any parameters - else if(state == SND_PCM_STATE_XRUN ) - snd_pcm_prepare ( d_pcm_handle ); // Prepare stream on underrun, and we can set parameters; - - bool special_case = nchan == 1 && d_special_case_mono_to_stereo; - if (special_case) - nchan = 2; - - err = snd_pcm_hw_params_set_channels (d_pcm_handle, d_hw_params, nchan); - - if (err < 0){ - output_error_msg ("set_channels failed", err); - return false; - } - - // set the parameters into the driver... - err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); - if (err < 0){ - output_error_msg ("snd_pcm_hw_params failed", err); - return false; - } - - // get current s/w params - err = snd_pcm_sw_params_current (d_pcm_handle, d_sw_params); - if (err < 0) - bail ("snd_pcm_sw_params_current", err); - - // Tell the PCM device to wait to start until we've filled - // it's buffers half way full. This helps avoid audio underruns. - - err = snd_pcm_sw_params_set_start_threshold(d_pcm_handle, - d_sw_params, - d_nperiods * d_period_size / 2); - if (err < 0) - bail ("snd_pcm_sw_params_set_start_threshold", err); - - // store the s/w params - err = snd_pcm_sw_params (d_pcm_handle, d_sw_params); - if (err < 0) - bail ("snd_pcm_sw_params", err); - - d_buffer_size_bytes = - d_period_size * nchan * snd_pcm_format_size (d_format, 1); - - d_buffer = new char [d_buffer_size_bytes]; - - if (CHATTY_DEBUG) - fprintf (stdout, "audio_alsa_sink[%s]: sample resolution = %d bits\n", - snd_pcm_name (d_pcm_handle), - snd_pcm_hw_params_get_sbits (d_hw_params)); - - switch (d_format){ - case SND_PCM_FORMAT_S16: - if (special_case) - d_worker = &audio_alsa_sink::work_s16_1x2; - else - d_worker = &audio_alsa_sink::work_s16; - break; - - case SND_PCM_FORMAT_S32: - if (special_case) - d_worker = &audio_alsa_sink::work_s32_1x2; - else - d_worker = &audio_alsa_sink::work_s32; - break; - - default: - assert (0); - } - return true; -} - -audio_alsa_sink::~audio_alsa_sink () -{ - if (snd_pcm_state (d_pcm_handle) == SND_PCM_STATE_RUNNING) - snd_pcm_drop (d_pcm_handle); - - snd_pcm_close(d_pcm_handle); - delete [] ((char *) d_hw_params); - delete [] ((char *) d_sw_params); - delete [] d_buffer; -} - -int -audio_alsa_sink::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - assert ((noutput_items % d_period_size) == 0); - - // this is a call through a pointer to a method... - return (this->*d_worker)(noutput_items, input_items, output_items); -} - -/* - * Work function that deals with float to S16 conversion - */ -int -audio_alsa_sink::work_s16 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - typedef gr_int16 sample_t; // the type of samples we're creating - static const float scale_factor = std::pow(2.0f, 16-1) - 1; - - unsigned int nchan = input_items.size (); - const float **in = (const float **) &input_items[0]; - sample_t *buf = (sample_t *) d_buffer; - int bi; - int n; - - unsigned int sizeof_frame = nchan * sizeof (sample_t); - assert (d_buffer_size_bytes == d_period_size * sizeof_frame); - - for (n = 0; n < noutput_items; n += d_period_size){ - - // process one period of data - bi = 0; - for (unsigned int i = 0; i < d_period_size; i++){ - for (unsigned int chan = 0; chan < nchan; chan++){ - buf[bi++] = (sample_t) (in[chan][i] * scale_factor); - } + static std::string + default_device_name() + { + return gr_prefs::singleton()->get_string("audio_alsa", "default_output_device", "hw:0,0"); } - // update src pointers - for (unsigned int chan = 0; chan < nchan; chan++) - in[chan] += d_period_size; - - if (!write_buffer (buf, d_period_size, sizeof_frame)) - return -1; // No fixing this problem. Say we're done. - } + static double + default_period_time() + { + return std::max(0.001, + gr_prefs::singleton()->get_double("audio_alsa", "period_time", 0.010)); + } - return n; -} + static int + default_nperiods() + { + return std::max(2L, + gr_prefs::singleton()->get_long("audio_alsa", "nperiods", 4)); + } + // ---------------------------------------------------------------- + + alsa_sink::alsa_sink(int sampling_rate, + const std::string device_name, + bool ok_to_block) + : gr_sync_block("audio_alsa_sink", + gr_make_io_signature(0, 0, 0), + gr_make_io_signature(0, 0, 0)), + d_sampling_rate(sampling_rate), + d_device_name(device_name.empty() ? default_device_name() : device_name), + d_pcm_handle(0), + d_hw_params((snd_pcm_hw_params_t*)(new char[snd_pcm_hw_params_sizeof()])), + d_sw_params((snd_pcm_sw_params_t*)(new char[snd_pcm_sw_params_sizeof()])), + d_nperiods(default_nperiods()), + d_period_time_us((unsigned int)(default_period_time() * 1e6)), + d_period_size(0), + d_buffer_size_bytes(0), d_buffer(0), + d_worker(0), d_special_case_mono_to_stereo(false), + d_nunderuns(0), d_nsuspends(0), d_ok_to_block(ok_to_block) + { + CHATTY_DEBUG = gr_prefs::singleton()->get_bool("audio_alsa", "verbose", false); + + int error; + int dir; + + // open the device for playback + error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str(), + SND_PCM_STREAM_PLAYBACK, 0); + if(ok_to_block == false) + snd_pcm_nonblock(d_pcm_handle, !ok_to_block); + if(error < 0){ + fprintf(stderr, "audio_alsa_sink[%s]: %s\n", + d_device_name.c_str(), snd_strerror(error)); + throw std::runtime_error("audio_alsa_sink"); + } -/* - * Work function that deals with float to S32 conversion - */ -int -audio_alsa_sink::work_s32 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - typedef gr_int32 sample_t; // the type of samples we're creating - static const float scale_factor = std::pow(2.0f, 32-1) - 1; - - unsigned int nchan = input_items.size (); - const float **in = (const float **) &input_items[0]; - sample_t *buf = (sample_t *) d_buffer; - int bi; - int n; - - unsigned int sizeof_frame = nchan * sizeof (sample_t); - assert (d_buffer_size_bytes == d_period_size * sizeof_frame); - - for (n = 0; n < noutput_items; n += d_period_size){ - - // process one period of data - bi = 0; - for (unsigned int i = 0; i < d_period_size; i++){ - for (unsigned int chan = 0; chan < nchan; chan++){ - buf[bi++] = (sample_t) (in[chan][i] * scale_factor); + // Fill params with a full configuration space for a PCM. + error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); + if(error < 0) + bail("broken configuration for playback", error); + + if(CHATTY_DEBUG) + gri_alsa_dump_hw_params(d_pcm_handle, d_hw_params, stdout); + + // now that we know how many channels the h/w can handle, set input signature + unsigned int umin_chan, umax_chan; + snd_pcm_hw_params_get_channels_min(d_hw_params, &umin_chan); + snd_pcm_hw_params_get_channels_max(d_hw_params, &umax_chan); + int min_chan = std::min(umin_chan, 1000U); + int max_chan = std::min(umax_chan, 1000U); + + // As a special case, if the hw's min_chan is two, we'll accept + // a single input and handle the duplication ourselves. + if(min_chan == 2) { + min_chan = 1; + d_special_case_mono_to_stereo = true; + } + set_input_signature(gr_make_io_signature(min_chan, max_chan, + sizeof(float))); + + // fill in portions of the d_hw_params that we know now... + + // Specify the access methods we implement + // For now, we only handle RW_INTERLEAVED... + snd_pcm_access_mask_t *access_mask; + snd_pcm_access_mask_t **access_mask_ptr = &access_mask; // FIXME: workaround for compiler warning + snd_pcm_access_mask_alloca(access_mask_ptr); + snd_pcm_access_mask_none(access_mask); + snd_pcm_access_mask_set(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED); + // snd_pcm_access_mask_set(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); + + if((error = snd_pcm_hw_params_set_access_mask(d_pcm_handle, + d_hw_params, access_mask)) < 0) + bail("failed to set access mask", error); + + // set sample format + if(!gri_alsa_pick_acceptable_format(d_pcm_handle, d_hw_params, + acceptable_formats, + NELEMS(acceptable_formats), + &d_format, + "audio_alsa_sink", + CHATTY_DEBUG)) + throw std::runtime_error("audio_alsa_sink"); + + // sampling rate + unsigned int orig_sampling_rate = d_sampling_rate; + if((error = snd_pcm_hw_params_set_rate_near(d_pcm_handle, d_hw_params, + &d_sampling_rate, 0)) < 0) + bail("failed to set rate near", error); + + if(orig_sampling_rate != d_sampling_rate) { + fprintf(stderr, "audio_alsa_sink[%s]: unable to support sampling rate %d\n", + snd_pcm_name(d_pcm_handle), orig_sampling_rate); + fprintf(stderr, " card requested %d instead.\n", d_sampling_rate); } - } - // update src pointers - for (unsigned int chan = 0; chan < nchan; chan++) - in[chan] += d_period_size; + /* + * ALSA transfers data in units of "periods". + * We indirectly determine the underlying buffersize by specifying + * the number of periods we want (typically 4) and the length of each + * period in units of time (typically 1ms). + */ + unsigned int min_nperiods, max_nperiods; + snd_pcm_hw_params_get_periods_min(d_hw_params, &min_nperiods, &dir); + snd_pcm_hw_params_get_periods_max(d_hw_params, &max_nperiods, &dir); + //fprintf(stderr, "alsa_sink: min_nperiods = %d, max_nperiods = %d\n", + // min_nperiods, max_nperiods); + + unsigned int orig_nperiods = d_nperiods; + d_nperiods = std::min (std::max (min_nperiods, d_nperiods), max_nperiods); + + // adjust period time so that total buffering remains more-or-less constant + d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; + + error = snd_pcm_hw_params_set_periods(d_pcm_handle, d_hw_params, + d_nperiods, 0); + if(error < 0) + bail("set_periods failed", error); + + dir = 0; + error = snd_pcm_hw_params_set_period_time_near(d_pcm_handle, d_hw_params, + &d_period_time_us, &dir); + if(error < 0) + bail("set_period_time_near failed", error); + + dir = 0; + error = snd_pcm_hw_params_get_period_size(d_hw_params, + &d_period_size, &dir); + if(error < 0) + bail("get_period_size failed", error); + + set_output_multiple(d_period_size); + } - if (!write_buffer (buf, d_period_size, sizeof_frame)) - return -1; // No fixing this problem. Say we're done. - } + bool + alsa_sink::check_topology(int ninputs, int noutputs) + { + // ninputs is how many channels the user has connected. + // Now we can finish up setting up the hw params... + + int nchan = ninputs; + int err; + + // Check the state of the stream + // Ensure that the pcm is in a state where we can still mess with the hw_params + snd_pcm_state_t state; + state = snd_pcm_state(d_pcm_handle); + if(state == SND_PCM_STATE_RUNNING) + return true; // If stream is running, don't change any parameters + else if(state == SND_PCM_STATE_XRUN) + snd_pcm_prepare(d_pcm_handle); // Prepare stream on underrun, and we can set parameters; + + bool special_case = nchan == 1 && d_special_case_mono_to_stereo; + if(special_case) + nchan = 2; + + err = snd_pcm_hw_params_set_channels(d_pcm_handle, d_hw_params, nchan); + + if(err < 0) { + output_error_msg("set_channels failed", err); + return false; + } - return n; -} + // set the parameters into the driver... + err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); + if(err < 0) { + output_error_msg("snd_pcm_hw_params failed", err); + return false; + } -/* - * Work function that deals with float to S16 conversion and - * mono to stereo kludge. - */ -int -audio_alsa_sink::work_s16_1x2 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - typedef gr_int16 sample_t; // the type of samples we're creating - static const float scale_factor = std::pow(2.0f, 16-1) - 1; - - assert (input_items.size () == 1); - static const unsigned int nchan = 2; - const float **in = (const float **) &input_items[0]; - sample_t *buf = (sample_t *) d_buffer; - int bi; - int n; - - unsigned int sizeof_frame = nchan * sizeof (sample_t); - assert (d_buffer_size_bytes == d_period_size * sizeof_frame); - - for (n = 0; n < noutput_items; n += d_period_size){ - - // process one period of data - bi = 0; - for (unsigned int i = 0; i < d_period_size; i++){ - sample_t t = (sample_t) (in[0][i] * scale_factor); - buf[bi++] = t; - buf[bi++] = t; + // get current s/w params + err = snd_pcm_sw_params_current(d_pcm_handle, d_sw_params); + if(err < 0) + bail("snd_pcm_sw_params_current", err); + + // Tell the PCM device to wait to start until we've filled + // it's buffers half way full. This helps avoid audio underruns. + + err = snd_pcm_sw_params_set_start_threshold(d_pcm_handle, + d_sw_params, + d_nperiods * d_period_size / 2); + if(err < 0) + bail("snd_pcm_sw_params_set_start_threshold", err); + + // store the s/w params + err = snd_pcm_sw_params(d_pcm_handle, d_sw_params); + if(err < 0) + bail("snd_pcm_sw_params", err); + + d_buffer_size_bytes = + d_period_size * nchan * snd_pcm_format_size(d_format, 1); + + d_buffer = new char[d_buffer_size_bytes]; + + if(CHATTY_DEBUG) + fprintf(stdout, "audio_alsa_sink[%s]: sample resolution = %d bits\n", + snd_pcm_name(d_pcm_handle), + snd_pcm_hw_params_get_sbits(d_hw_params)); + + switch(d_format) { + case SND_PCM_FORMAT_S16: + if(special_case) + d_worker = &alsa_sink::work_s16_1x2; + else + d_worker = &alsa_sink::work_s16; + break; + + case SND_PCM_FORMAT_S32: + if(special_case) + d_worker = &alsa_sink::work_s32_1x2; + else + d_worker = &alsa_sink::work_s32; + break; + + default: + assert(0); + } + return true; } - // update src pointers - in[0] += d_period_size; - - if (!write_buffer (buf, d_period_size, sizeof_frame)) - return -1; // No fixing this problem. Say we're done. - } - - return n; -} + alsa_sink::~alsa_sink() + { + if(snd_pcm_state(d_pcm_handle) == SND_PCM_STATE_RUNNING) + snd_pcm_drop(d_pcm_handle); -/* - * Work function that deals with float to S32 conversion and - * mono to stereo kludge. - */ -int -audio_alsa_sink::work_s32_1x2 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - typedef gr_int32 sample_t; // the type of samples we're creating - static const float scale_factor = std::pow(2.0f, 32-1) - 1; - - assert (input_items.size () == 1); - static unsigned int nchan = 2; - const float **in = (const float **) &input_items[0]; - sample_t *buf = (sample_t *) d_buffer; - int bi; - int n; - - unsigned int sizeof_frame = nchan * sizeof (sample_t); - assert (d_buffer_size_bytes == d_period_size * sizeof_frame); - - for (n = 0; n < noutput_items; n += d_period_size){ - - // process one period of data - bi = 0; - for (unsigned int i = 0; i < d_period_size; i++){ - sample_t t = (sample_t) (in[0][i] * scale_factor); - buf[bi++] = t; - buf[bi++] = t; + snd_pcm_close(d_pcm_handle); + delete [] ((char*)d_hw_params); + delete [] ((char*)d_sw_params); + delete [] d_buffer; } - // update src pointers - in[0] += d_period_size; + int + alsa_sink::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + assert((noutput_items % d_period_size) == 0); - if (!write_buffer (buf, d_period_size, sizeof_frame)) - return -1; // No fixing this problem. Say we're done. - } + // this is a call through a pointer to a method... + return (this->*d_worker)(noutput_items, input_items, output_items); + } - return n; -} + /* + * Work function that deals with float to S16 conversion + */ + int + alsa_sink::work_s16(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + typedef gr_int16 sample_t; // the type of samples we're creating + static const float scale_factor = std::pow(2.0f, 16-1) - 1; + + unsigned int nchan = input_items.size(); + const float **in = (const float **)&input_items[0]; + sample_t *buf = (sample_t *)d_buffer; + int bi; + int n; + + unsigned int sizeof_frame = nchan * sizeof(sample_t); + assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + + for(n = 0; n < noutput_items; n += d_period_size) { + // process one period of data + bi = 0; + for(unsigned int i = 0; i < d_period_size; i++) { + for (unsigned int chan = 0; chan < nchan; chan++) { + buf[bi++] = (sample_t) (in[chan][i] * scale_factor); + } + } + + // update src pointers + for(unsigned int chan = 0; chan < nchan; chan++) + in[chan] += d_period_size; + + if(!write_buffer (buf, d_period_size, sizeof_frame)) + return -1; // No fixing this problem. Say we're done. + } -bool -audio_alsa_sink::write_buffer (const void *vbuffer, - unsigned nframes, unsigned sizeof_frame) -{ - const unsigned char *buffer = (const unsigned char *) vbuffer; + return n; + } - while (nframes > 0){ - int r = snd_pcm_writei (d_pcm_handle, buffer, nframes); - if (r == -EAGAIN) + /* + * Work function that deals with float to S32 conversion + */ + int + alsa_sink::work_s32(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) { - if (d_ok_to_block == true) - continue; // try again + typedef gr_int32 sample_t; // the type of samples we're creating + static const float scale_factor = std::pow(2.0f, 32-1) - 1; + + unsigned int nchan = input_items.size(); + const float **in = (const float **)&input_items[0]; + sample_t *buf = (sample_t *)d_buffer; + int bi; + int n; + + unsigned int sizeof_frame = nchan * sizeof (sample_t); + assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + + for(n = 0; n < noutput_items; n += d_period_size) { + // process one period of data + bi = 0; + for(unsigned int i = 0; i < d_period_size; i++) { + for(unsigned int chan = 0; chan < nchan; chan++) { + buf[bi++] = (sample_t)(in[chan][i] * scale_factor); + } + } + + // update src pointers + for(unsigned int chan = 0; chan < nchan; chan++) + in[chan] += d_period_size; + + if(!write_buffer (buf, d_period_size, sizeof_frame)) + return -1; // No fixing this problem. Say we're done. + } - break; + return n; } - else if (r == -EPIPE){ // underrun - d_nunderuns++; - fputs ("aU", stderr); - if ((r = snd_pcm_prepare (d_pcm_handle)) < 0){ - output_error_msg ("snd_pcm_prepare failed. Can't recover from underrun", r); - return false; + /* + * Work function that deals with float to S16 conversion and + * mono to stereo kludge. + */ + int + alsa_sink::work_s16_1x2(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + typedef gr_int16 sample_t; // the type of samples we're creating + static const float scale_factor = std::pow(2.0f, 16-1) - 1; + + assert(input_items.size () == 1); + static const unsigned int nchan = 2; + const float **in = (const float **)&input_items[0]; + sample_t *buf = (sample_t *)d_buffer; + int bi; + int n; + + unsigned int sizeof_frame = nchan * sizeof(sample_t); + assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + + for(n = 0; n < noutput_items; n += d_period_size) { + // process one period of data + bi = 0; + for(unsigned int i = 0; i < d_period_size; i++) { + sample_t t = (sample_t) (in[0][i] * scale_factor); + buf[bi++] = t; + buf[bi++] = t; + } + + // update src pointers + in[0] += d_period_size; + + if(!write_buffer (buf, d_period_size, sizeof_frame)) + return -1; // No fixing this problem. Say we're done. } - continue; // try again + + return n; } - else if (r == -ESTRPIPE){ // h/w is suspended (whatever that means) - // This is apparently related to power management - d_nsuspends++; - if ((r = snd_pcm_resume (d_pcm_handle)) < 0){ - output_error_msg ("failed to resume from suspend", r); - return false; + /* + * Work function that deals with float to S32 conversion and + * mono to stereo kludge. + */ + int + alsa_sink::work_s32_1x2(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + typedef gr_int32 sample_t; // the type of samples we're creating + static const float scale_factor = std::pow(2.0f, 32-1) - 1; + + assert(input_items.size () == 1); + static unsigned int nchan = 2; + const float **in = (const float **)&input_items[0]; + sample_t *buf = (sample_t*)d_buffer; + int bi; + int n; + + unsigned int sizeof_frame = nchan * sizeof(sample_t); + assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + + for(n = 0; n < noutput_items; n += d_period_size) { + // process one period of data + bi = 0; + for(unsigned int i = 0; i < d_period_size; i++) { + sample_t t = (sample_t)(in[0][i] * scale_factor); + buf[bi++] = t; + buf[bi++] = t; + } + + // update src pointers + in[0] += d_period_size; + + if(!write_buffer (buf, d_period_size, sizeof_frame)) + return -1; // No fixing this problem. Say we're done. } - continue; // try again - } - else if (r < 0){ - output_error_msg ("snd_pcm_writei failed", r); - return false; + return n; } - nframes -= r; - buffer += r * sizeof_frame; - } + bool + alsa_sink::write_buffer(const void *vbuffer, + unsigned nframes, unsigned sizeof_frame) + { + const unsigned char *buffer = (const unsigned char *)vbuffer; + + while(nframes > 0){ + int r = snd_pcm_writei(d_pcm_handle, buffer, nframes); + if(r == -EAGAIN) { + if(d_ok_to_block == true) + continue; // try again + break; + } + + else if(r == -EPIPE) { // underrun + d_nunderuns++; + fputs("aU", stderr); + if((r = snd_pcm_prepare (d_pcm_handle)) < 0){ + output_error_msg("snd_pcm_prepare failed. Can't recover from underrun", r); + return false; + } + continue; // try again + } + + else if(r == -ESTRPIPE) { // h/w is suspended (whatever that means) + // This is apparently related to power management + d_nsuspends++; + if((r = snd_pcm_resume (d_pcm_handle)) < 0) { + output_error_msg("failed to resume from suspend", r); + return false; + } + continue; // try again + } + + else if (r < 0) { + output_error_msg("snd_pcm_writei failed", r); + return false; + } + + nframes -= r; + buffer += r * sizeof_frame; + } - return true; -} + return true; + } + void + alsa_sink::output_error_msg (const char *msg, int err) + { + fprintf(stderr, "audio_alsa_sink[%s]: %s: %s\n", + snd_pcm_name(d_pcm_handle), msg, snd_strerror(err)); + } -void -audio_alsa_sink::output_error_msg (const char *msg, int err) -{ - fprintf (stderr, "audio_alsa_sink[%s]: %s: %s\n", - snd_pcm_name (d_pcm_handle), msg, snd_strerror (err)); -} + void + alsa_sink::bail(const char *msg, int err) throw (std::runtime_error) + { + output_error_msg(msg, err); + throw std::runtime_error("audio_alsa_sink"); + } -void -audio_alsa_sink::bail (const char *msg, int err) throw (std::runtime_error) -{ - output_error_msg (msg, err); - throw std::runtime_error ("audio_alsa_sink"); -} + } /* namespace audio */ +} /* namespace gr */ diff --git a/gr-audio/lib/alsa/audio_alsa_sink.h b/gr-audio/lib/alsa/audio_alsa_sink.h index 979a00c8f8..1dea62f56e 100644 --- a/gr-audio/lib/alsa/audio_alsa_sink.h +++ b/gr-audio/lib/alsa/audio_alsa_sink.h @@ -28,78 +28,84 @@ #define ALSA_PCM_NEW_SW_PARAMS_API #include <audio/sink.h> -#include <string> #include <alsa/asoundlib.h> +#include <string> #include <stdexcept> -/*! - * \brief audio sink using ALSA - * \ingroup audio_blk - * - * The sink has N input streams of floats, where N depends - * on the hardware characteristics of the selected device. - * - * Input samples must be in the range [-1,1]. - */ -class audio_alsa_sink : public audio_sink { - // typedef for pointer to class work method - typedef int (audio_alsa_sink::*work_t)(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - unsigned int d_sampling_rate; - std::string d_device_name; - snd_pcm_t *d_pcm_handle; - snd_pcm_hw_params_t *d_hw_params; - snd_pcm_sw_params_t *d_sw_params; - snd_pcm_format_t d_format; - unsigned int d_nperiods; - unsigned int d_period_time_us; // microseconds - snd_pcm_uframes_t d_period_size; // in frames - unsigned int d_buffer_size_bytes; // sizeof of d_buffer - char *d_buffer; - work_t d_worker; // the work method to use - bool d_special_case_mono_to_stereo; - - // random stats - int d_nunderuns; // count of underruns - int d_nsuspends; // count of suspends - bool d_ok_to_block; // defaults to "true", controls blocking/non-block I/O - - void output_error_msg (const char *msg, int err); - void bail (const char *msg, int err) throw (std::runtime_error); - -public: - audio_alsa_sink (int sampling_rate, const std::string device_name, - bool ok_to_block); - - ~audio_alsa_sink (); - - bool check_topology (int ninputs, int noutputs); - - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - -protected: - bool write_buffer (const void *buffer, unsigned nframes, unsigned sizeof_frame); - - int work_s16 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - int work_s16_1x2 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - int work_s32 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - int work_s32_1x2 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; +namespace gr { + namespace audio { + + /*! + * \brief audio sink using ALSA + * \ingroup audio_blk + * + * The sink has N input streams of floats, where N depends + * on the hardware characteristics of the selected device. + * + * Input samples must be in the range [-1,1]. + */ + class alsa_sink : public sink + { + // typedef for pointer to class work method + typedef int(alsa_sink::*work_t)(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + unsigned int d_sampling_rate; + std::string d_device_name; + snd_pcm_t *d_pcm_handle; + snd_pcm_hw_params_t *d_hw_params; + snd_pcm_sw_params_t *d_sw_params; + snd_pcm_format_t d_format; + unsigned int d_nperiods; + unsigned int d_period_time_us; // microseconds + snd_pcm_uframes_t d_period_size; // in frames + unsigned int d_buffer_size_bytes; // sizeof of d_buffer + char *d_buffer; + work_t d_worker; // the work method to use + bool d_special_case_mono_to_stereo; + + // random stats + int d_nunderuns; // count of underruns + int d_nsuspends; // count of suspends + bool d_ok_to_block; // defaults to "true", controls blocking/non-block I/O + + void output_error_msg(const char *msg, int err); + void bail(const char *msg, int err) throw (std::runtime_error); + + public: + alsa_sink(int sampling_rate, + const std::string device_name, + bool ok_to_block); + ~alsa_sink(); + + bool check_topology(int ninputs, int noutputs); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + protected: + bool write_buffer(const void *buffer, unsigned nframes, unsigned sizeof_frame); + + int work_s16(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + int work_s16_1x2(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + int work_s32(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + int work_s32_1x2(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace audio */ +} /* namespace gr */ #endif /* INCLUDED_AUDIO_ALSA_SINK_H */ diff --git a/gr-audio/lib/alsa/audio_alsa_source.cc b/gr-audio/lib/alsa/audio_alsa_source.cc index 9fdf80b43f..64238fdd8b 100644 --- a/gr-audio/lib/alsa/audio_alsa_source.cc +++ b/gr-audio/lib/alsa/audio_alsa_source.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004-2011 Free Software Foundation, Inc. + * Copyright 2004-2011,2013 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -33,477 +33,482 @@ #include <stdexcept> #include <gri_alsa.h> -AUDIO_REGISTER_SOURCE(REG_PRIO_HIGH, alsa)( - int sampling_rate, const std::string &device_name, bool ok_to_block -){ - return audio_source::sptr(new audio_alsa_source(sampling_rate, device_name, ok_to_block)); -} +namespace gr { + namespace audio { -static bool CHATTY_DEBUG = false; + AUDIO_REGISTER_SOURCE(REG_PRIO_HIGH, alsa)(int sampling_rate, + const std::string &device_name, + bool ok_to_block) + { + return source::sptr + (new alsa_source(sampling_rate, device_name, ok_to_block)); + } + + static bool CHATTY_DEBUG = false; -static snd_pcm_format_t acceptable_formats[] = { - // these are in our preferred order... - SND_PCM_FORMAT_S32, - SND_PCM_FORMAT_S16 -}; + static snd_pcm_format_t acceptable_formats[] = { + // these are in our preferred order... + SND_PCM_FORMAT_S32, + SND_PCM_FORMAT_S16 + }; #define NELEMS(x) (sizeof(x)/sizeof(x[0])) + static std::string + default_device_name() + { + return gr_prefs::singleton()->get_string("audio_alsa", + "default_input_device", + "hw:0,0"); + } -static std::string -default_device_name () -{ - return gr_prefs::singleton()->get_string("audio_alsa", "default_input_device", "hw:0,0"); -} - -static double -default_period_time () -{ - return std::max(0.001, gr_prefs::singleton()->get_double("audio_alsa", "period_time", 0.010)); -} - -static int -default_nperiods () -{ - return std::max(2L, gr_prefs::singleton()->get_long("audio_alsa", "nperiods", 4)); -} - -// ---------------------------------------------------------------- - -audio_alsa_source::audio_alsa_source (int sampling_rate, - const std::string device_name, - bool ok_to_block) - : gr_sync_block ("audio_alsa_source", - gr_make_io_signature (0, 0, 0), - gr_make_io_signature (0, 0, 0)), - d_sampling_rate (sampling_rate), - d_device_name (device_name.empty() ? default_device_name() : device_name), - d_pcm_handle (0), - d_hw_params ((snd_pcm_hw_params_t *)(new char[snd_pcm_hw_params_sizeof()])), - d_sw_params ((snd_pcm_sw_params_t *)(new char[snd_pcm_sw_params_sizeof()])), - d_nperiods (default_nperiods()), - d_period_time_us ((unsigned int) (default_period_time() * 1e6)), - d_period_size (0), - d_buffer_size_bytes (0), d_buffer (0), - d_worker (0), d_hw_nchan (0), - d_special_case_stereo_to_mono (false), - d_noverruns (0), d_nsuspends (0) -{ - - CHATTY_DEBUG = gr_prefs::singleton()->get_bool("audio_alsa", "verbose", false); - - int error; - int dir; - - // open the device for capture - error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str (), - SND_PCM_STREAM_CAPTURE, 0); - if (error < 0){ - fprintf (stderr, "audio_alsa_source[%s]: %s\n", - d_device_name.c_str(), snd_strerror(error)); - throw std::runtime_error ("audio_alsa_source"); - } - - // Fill params with a full configuration space for a PCM. - error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); - if (error < 0) - bail ("broken configuration for playback", error); - - if (CHATTY_DEBUG) - gri_alsa_dump_hw_params (d_pcm_handle, d_hw_params, stdout); - - // now that we know how many channels the h/w can handle, set output signature - unsigned int umax_chan; - unsigned int umin_chan; - snd_pcm_hw_params_get_channels_min (d_hw_params, &umin_chan); - snd_pcm_hw_params_get_channels_max (d_hw_params, &umax_chan); - int min_chan = std::min (umin_chan, 1000U); - int max_chan = std::min (umax_chan, 1000U); - - // As a special case, if the hw's min_chan is two, we'll accept - // a single output and handle the demux ourselves. - - if (min_chan == 2){ - min_chan = 1; - d_special_case_stereo_to_mono = true; - } - - set_output_signature (gr_make_io_signature (min_chan, max_chan, - sizeof (float))); - - // fill in portions of the d_hw_params that we know now... - - // Specify the access methods we implement - // For now, we only handle RW_INTERLEAVED... - snd_pcm_access_mask_t *access_mask; - snd_pcm_access_mask_t **access_mask_ptr = &access_mask; // FIXME: workaround for compiler warning - snd_pcm_access_mask_alloca (access_mask_ptr); - snd_pcm_access_mask_none (access_mask); - snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_INTERLEAVED); - // snd_pcm_access_mask_set (access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); - - if ((error = snd_pcm_hw_params_set_access_mask (d_pcm_handle, - d_hw_params, access_mask)) < 0) - bail ("failed to set access mask", error); - - - // set sample format - if (!gri_alsa_pick_acceptable_format (d_pcm_handle, d_hw_params, - acceptable_formats, - NELEMS (acceptable_formats), - &d_format, - "audio_alsa_source", - CHATTY_DEBUG)) - throw std::runtime_error ("audio_alsa_source"); - - - // sampling rate - unsigned int orig_sampling_rate = d_sampling_rate; - if ((error = snd_pcm_hw_params_set_rate_near (d_pcm_handle, d_hw_params, - &d_sampling_rate, 0)) < 0) - bail ("failed to set rate near", error); - - if (orig_sampling_rate != d_sampling_rate){ - fprintf (stderr, "audio_alsa_source[%s]: unable to support sampling rate %d\n", - snd_pcm_name (d_pcm_handle), orig_sampling_rate); - fprintf (stderr, " card requested %d instead.\n", d_sampling_rate); - } - - /* - * ALSA transfers data in units of "periods". - * We indirectly determine the underlying buffersize by specifying - * the number of periods we want (typically 4) and the length of each - * period in units of time (typically 1ms). - */ - unsigned int min_nperiods, max_nperiods; - snd_pcm_hw_params_get_periods_min (d_hw_params, &min_nperiods, &dir); - snd_pcm_hw_params_get_periods_max (d_hw_params, &max_nperiods, &dir); - //fprintf (stderr, "alsa_source: min_nperiods = %d, max_nperiods = %d\n", - // min_nperiods, max_nperiods); - - - unsigned int orig_nperiods = d_nperiods; - d_nperiods = std::min (std::max (min_nperiods, d_nperiods), max_nperiods); - - // adjust period time so that total buffering remains more-or-less constant - d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; - - error = snd_pcm_hw_params_set_periods (d_pcm_handle, d_hw_params, - d_nperiods, 0); - if (error < 0) - bail ("set_periods failed", error); - - dir = 0; - error = snd_pcm_hw_params_set_period_time_near (d_pcm_handle, d_hw_params, - &d_period_time_us, &dir); - if (error < 0) - bail ("set_period_time_near failed", error); - - dir = 0; - error = snd_pcm_hw_params_get_period_size (d_hw_params, - &d_period_size, &dir); - if (error < 0) - bail ("get_period_size failed", error); - - set_output_multiple (d_period_size); -} - -bool -audio_alsa_source::check_topology (int ninputs, int noutputs) -{ - // noutputs is how many channels the user has connected. - // Now we can finish up setting up the hw params... - - unsigned int nchan = noutputs; - int err; - - // Check the state of the stream - // Ensure that the pcm is in a state where we can still mess with the hw_params - snd_pcm_state_t state; - state=snd_pcm_state(d_pcm_handle); - if ( state== SND_PCM_STATE_RUNNING) - return true; // If stream is running, don't change any parameters - else if(state == SND_PCM_STATE_XRUN ) - snd_pcm_prepare ( d_pcm_handle ); // Prepare stream on underrun, and we can set parameters; - - bool special_case = nchan == 1 && d_special_case_stereo_to_mono; - if (special_case) - nchan = 2; - - d_hw_nchan = nchan; - err = snd_pcm_hw_params_set_channels (d_pcm_handle, d_hw_params, d_hw_nchan); - if (err < 0){ - output_error_msg ("set_channels failed", err); - return false; - } - - // set the parameters into the driver... - err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); - if (err < 0){ - output_error_msg ("snd_pcm_hw_params failed", err); - return false; - } - - d_buffer_size_bytes = - d_period_size * d_hw_nchan * snd_pcm_format_size (d_format, 1); - - d_buffer = new char [d_buffer_size_bytes]; - - if (CHATTY_DEBUG) - fprintf (stdout, "audio_alsa_source[%s]: sample resolution = %d bits\n", - snd_pcm_name (d_pcm_handle), - snd_pcm_hw_params_get_sbits (d_hw_params)); - - switch (d_format){ - case SND_PCM_FORMAT_S16: - if (special_case) - d_worker = &audio_alsa_source::work_s16_2x1; - else - d_worker = &audio_alsa_source::work_s16; - break; - - case SND_PCM_FORMAT_S32: - if (special_case) - d_worker = &audio_alsa_source::work_s32_2x1; - else - d_worker = &audio_alsa_source::work_s32; - break; - - default: - assert (0); - } - - return true; -} - -audio_alsa_source::~audio_alsa_source () -{ - if (snd_pcm_state (d_pcm_handle) == SND_PCM_STATE_RUNNING) - snd_pcm_drop (d_pcm_handle); - - snd_pcm_close(d_pcm_handle); - delete [] ((char *) d_hw_params); - delete [] ((char *) d_sw_params); - delete [] d_buffer; -} - -int -audio_alsa_source::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - assert ((noutput_items % d_period_size) == 0); - assert (noutput_items != 0); - - // this is a call through a pointer to a method... - return (this->*d_worker)(noutput_items, input_items, output_items); -} + static double + default_period_time() + { + return std::max(0.001, + gr_prefs::singleton()->get_double("audio_alsa", "period_time", 0.010)); + } -/* - * Work function that deals with float to S16 conversion - */ -int -audio_alsa_source::work_s16 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - typedef gr_int16 sample_t; // the type of samples we're creating - static const float scale_factor = 1.0 / std::pow(2.0f, 16-1); - - unsigned int nchan = output_items.size (); - float **out = (float **) &output_items[0]; - sample_t *buf = (sample_t *) d_buffer; - int bi; - - unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); - assert (d_buffer_size_bytes == d_period_size * sizeof_frame); - - // To minimize latency, return at most a single period's worth of samples. - // [We could also read the first one in a blocking mode and subsequent - // ones in non-blocking mode, but we'll leave that for later (or never).] - - if (!read_buffer (buf, d_period_size, sizeof_frame)) - return -1; // No fixing this problem. Say we're done. - - // process one period of data - bi = 0; - for (unsigned int i = 0; i < d_period_size; i++){ - for (unsigned int chan = 0; chan < nchan; chan++){ - out[chan][i] = (float) buf[bi++] * scale_factor; + static int + default_nperiods() + { + return std::max(2L, + gr_prefs::singleton()->get_long("audio_alsa", "nperiods", 4)); } - } - return d_period_size; -} + // ---------------------------------------------------------------- + + alsa_source::alsa_source(int sampling_rate, + const std::string device_name, + bool ok_to_block) + : gr_sync_block("audio_alsa_source", + gr_make_io_signature(0, 0, 0), + gr_make_io_signature(0, 0, 0)), + d_sampling_rate(sampling_rate), + d_device_name(device_name.empty() ? default_device_name() : device_name), + d_pcm_handle(0), + d_hw_params((snd_pcm_hw_params_t*)(new char[snd_pcm_hw_params_sizeof()])), + d_sw_params((snd_pcm_sw_params_t*)(new char[snd_pcm_sw_params_sizeof()])), + d_nperiods(default_nperiods()), + d_period_time_us((unsigned int)(default_period_time() * 1e6)), + d_period_size(0), + d_buffer_size_bytes(0), d_buffer(0), + d_worker(0), d_hw_nchan(0), + d_special_case_stereo_to_mono(false), + d_noverruns(0), d_nsuspends(0) + { + CHATTY_DEBUG = gr_prefs::singleton()->get_bool("audio_alsa", "verbose", false); + + int error; + int dir; + + // open the device for capture + error = snd_pcm_open(&d_pcm_handle, d_device_name.c_str(), + SND_PCM_STREAM_CAPTURE, 0); + if(error < 0){ + fprintf(stderr, "audio_alsa_source[%s]: %s\n", + d_device_name.c_str(), snd_strerror(error)); + throw std::runtime_error("audio_alsa_source"); + } -/* - * Work function that deals with float to S16 conversion - * and stereo to mono kludge... - */ -int -audio_alsa_source::work_s16_2x1 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - typedef gr_int16 sample_t; // the type of samples we're creating - static const float scale_factor = 1.0 / std::pow(2.0f, 16-1); + // Fill params with a full configuration space for a PCM. + error = snd_pcm_hw_params_any(d_pcm_handle, d_hw_params); + if(error < 0) + bail("broken configuration for playback", error); + + if(CHATTY_DEBUG) + gri_alsa_dump_hw_params(d_pcm_handle, d_hw_params, stdout); + + // now that we know how many channels the h/w can handle, set output signature + unsigned int umax_chan; + unsigned int umin_chan; + snd_pcm_hw_params_get_channels_min(d_hw_params, &umin_chan); + snd_pcm_hw_params_get_channels_max(d_hw_params, &umax_chan); + int min_chan = std::min(umin_chan, 1000U); + int max_chan = std::min(umax_chan, 1000U); + + // As a special case, if the hw's min_chan is two, we'll accept + // a single output and handle the demux ourselves. + if(min_chan == 2) { + min_chan = 1; + d_special_case_stereo_to_mono = true; + } - float **out = (float **) &output_items[0]; - sample_t *buf = (sample_t *) d_buffer; - int bi; + set_output_signature(gr_make_io_signature(min_chan, max_chan, + sizeof(float))); + + // fill in portions of the d_hw_params that we know now... + + // Specify the access methods we implement + // For now, we only handle RW_INTERLEAVED... + snd_pcm_access_mask_t *access_mask; + snd_pcm_access_mask_t **access_mask_ptr = &access_mask; // FIXME: workaround for compiler warning + snd_pcm_access_mask_alloca(access_mask_ptr); + snd_pcm_access_mask_none(access_mask); + snd_pcm_access_mask_set(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED); + // snd_pcm_access_mask_set(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); + + if((error = snd_pcm_hw_params_set_access_mask(d_pcm_handle, + d_hw_params, access_mask)) < 0) + bail("failed to set access mask", error); + + // set sample format + if(!gri_alsa_pick_acceptable_format(d_pcm_handle, d_hw_params, + acceptable_formats, + NELEMS(acceptable_formats), + &d_format, + "audio_alsa_source", + CHATTY_DEBUG)) + throw std::runtime_error("audio_alsa_source"); + + // sampling rate + unsigned int orig_sampling_rate = d_sampling_rate; + if((error = snd_pcm_hw_params_set_rate_near(d_pcm_handle, d_hw_params, + &d_sampling_rate, 0)) < 0) + bail("failed to set rate near", error); + + if(orig_sampling_rate != d_sampling_rate){ + fprintf(stderr, "audio_alsa_source[%s]: unable to support sampling rate %d\n", + snd_pcm_name (d_pcm_handle), orig_sampling_rate); + fprintf(stderr, " card requested %d instead.\n", d_sampling_rate); + } - assert (output_items.size () == 1); + /* + * ALSA transfers data in units of "periods". + * We indirectly determine the underlying buffersize by specifying + * the number of periods we want (typically 4) and the length of each + * period in units of time (typically 1ms). + */ + unsigned int min_nperiods, max_nperiods; + snd_pcm_hw_params_get_periods_min(d_hw_params, &min_nperiods, &dir); + snd_pcm_hw_params_get_periods_max(d_hw_params, &max_nperiods, &dir); + //fprintf (stderr, "alsa_source: min_nperiods = %d, max_nperiods = %d\n", + // min_nperiods, max_nperiods); + + unsigned int orig_nperiods = d_nperiods; + d_nperiods = std::min(std::max (min_nperiods, d_nperiods), max_nperiods); + + // adjust period time so that total buffering remains more-or-less constant + d_period_time_us = (d_period_time_us * orig_nperiods) / d_nperiods; + + error = snd_pcm_hw_params_set_periods(d_pcm_handle, d_hw_params, + d_nperiods, 0); + if(error < 0) + bail("set_periods failed", error); + + dir = 0; + error = snd_pcm_hw_params_set_period_time_near(d_pcm_handle, d_hw_params, + &d_period_time_us, &dir); + if(error < 0) + bail("set_period_time_near failed", error); + + dir = 0; + error = snd_pcm_hw_params_get_period_size(d_hw_params, + &d_period_size, &dir); + if(error < 0) + bail("get_period_size failed", error); + + set_output_multiple(d_period_size); + } - unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); - assert (d_buffer_size_bytes == d_period_size * sizeof_frame); + bool + alsa_source::check_topology(int ninputs, int noutputs) + { + // noutputs is how many channels the user has connected. + // Now we can finish up setting up the hw params... + + unsigned int nchan = noutputs; + int err; + + // Check the state of the stream + // Ensure that the pcm is in a state where we can still mess with the hw_params + snd_pcm_state_t state; + state=snd_pcm_state(d_pcm_handle); + if(state== SND_PCM_STATE_RUNNING) + return true; // If stream is running, don't change any parameters + else if(state == SND_PCM_STATE_XRUN) + snd_pcm_prepare(d_pcm_handle); // Prepare stream on underrun, and we can set parameters; + + bool special_case = nchan == 1 && d_special_case_stereo_to_mono; + if(special_case) + nchan = 2; + + d_hw_nchan = nchan; + err = snd_pcm_hw_params_set_channels(d_pcm_handle, d_hw_params, d_hw_nchan); + if(err < 0) { + output_error_msg("set_channels failed", err); + return false; + } - // To minimize latency, return at most a single period's worth of samples. - // [We could also read the first one in a blocking mode and subsequent - // ones in non-blocking mode, but we'll leave that for later (or never).] + // set the parameters into the driver... + err = snd_pcm_hw_params(d_pcm_handle, d_hw_params); + if(err < 0) { + output_error_msg("snd_pcm_hw_params failed", err); + return false; + } - if (!read_buffer (buf, d_period_size, sizeof_frame)) - return -1; // No fixing this problem. Say we're done. + d_buffer_size_bytes = + d_period_size * d_hw_nchan * snd_pcm_format_size(d_format, 1); - // process one period of data - bi = 0; - for (unsigned int i = 0; i < d_period_size; i++){ - int t = (buf[bi] + buf[bi+1]) / 2; - bi += 2; - out[0][i] = (float) t * scale_factor; - } + d_buffer = new char[d_buffer_size_bytes]; - return d_period_size; -} + if(CHATTY_DEBUG) { + fprintf(stdout, "audio_alsa_source[%s]: sample resolution = %d bits\n", + snd_pcm_name(d_pcm_handle), + snd_pcm_hw_params_get_sbits(d_hw_params)); + } -/* - * Work function that deals with float to S32 conversion - */ -int -audio_alsa_source::work_s32 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - typedef gr_int32 sample_t; // the type of samples we're creating - static const float scale_factor = 1.0 / std::pow(2.0f, 32-1); - - unsigned int nchan = output_items.size (); - float **out = (float **) &output_items[0]; - sample_t *buf = (sample_t *) d_buffer; - int bi; - - unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); - assert (d_buffer_size_bytes == d_period_size * sizeof_frame); - - // To minimize latency, return at most a single period's worth of samples. - // [We could also read the first one in a blocking mode and subsequent - // ones in non-blocking mode, but we'll leave that for later (or never).] - - if (!read_buffer (buf, d_period_size, sizeof_frame)) - return -1; // No fixing this problem. Say we're done. - - // process one period of data - bi = 0; - for (unsigned int i = 0; i < d_period_size; i++){ - for (unsigned int chan = 0; chan < nchan; chan++){ - out[chan][i] = (float) buf[bi++] * scale_factor; + switch(d_format) { + case SND_PCM_FORMAT_S16: + if(special_case) + d_worker = &alsa_source::work_s16_2x1; + else + d_worker = &alsa_source::work_s16; + break; + + case SND_PCM_FORMAT_S32: + if(special_case) + d_worker = &alsa_source::work_s32_2x1; + else + d_worker = &alsa_source::work_s32; + break; + + default: + assert(0); + } + + return true; } - } - return d_period_size; -} + alsa_source::~alsa_source() + { + if(snd_pcm_state(d_pcm_handle) == SND_PCM_STATE_RUNNING) + snd_pcm_drop(d_pcm_handle); -/* - * Work function that deals with float to S32 conversion - * and stereo to mono kludge... - */ -int -audio_alsa_source::work_s32_2x1 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - typedef gr_int32 sample_t; // the type of samples we're creating - static const float scale_factor = 1.0 / std::pow(2.0f, 32-1); - - float **out = (float **) &output_items[0]; - sample_t *buf = (sample_t *) d_buffer; - int bi; - - assert (output_items.size () == 1); - - unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); - assert (d_buffer_size_bytes == d_period_size * sizeof_frame); - - // To minimize latency, return at most a single period's worth of samples. - // [We could also read the first one in a blocking mode and subsequent - // ones in non-blocking mode, but we'll leave that for later (or never).] - - if (!read_buffer (buf, d_period_size, sizeof_frame)) - return -1; // No fixing this problem. Say we're done. - - // process one period of data - bi = 0; - for (unsigned int i = 0; i < d_period_size; i++){ - int t = (buf[bi] + buf[bi+1]) / 2; - bi += 2; - out[0][i] = (float) t * scale_factor; - } - - return d_period_size; -} - -bool -audio_alsa_source::read_buffer (void *vbuffer, unsigned nframes, unsigned sizeof_frame) -{ - unsigned char *buffer = (unsigned char *) vbuffer; - - while (nframes > 0){ - int r = snd_pcm_readi (d_pcm_handle, buffer, nframes); - if (r == -EAGAIN) - continue; // try again - - else if (r == -EPIPE){ // overrun - d_noverruns++; - fputs ("aO", stderr); - if ((r = snd_pcm_prepare (d_pcm_handle)) < 0){ - output_error_msg ("snd_pcm_prepare failed. Can't recover from overrun", r); - return false; + snd_pcm_close(d_pcm_handle); + delete [] ((char*)d_hw_params); + delete [] ((char*)d_sw_params); + delete [] d_buffer; + } + + int + alsa_source::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + assert((noutput_items % d_period_size) == 0); + assert(noutput_items != 0); + + // this is a call through a pointer to a method... + return (this->*d_worker)(noutput_items, input_items, output_items); + } + + /* + * Work function that deals with float to S16 conversion + */ + int + alsa_source::work_s16(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + typedef gr_int16 sample_t; // the type of samples we're creating + static const float scale_factor = 1.0 / std::pow(2.0f, 16-1); + + unsigned int nchan = output_items.size (); + float **out = (float **)&output_items[0]; + sample_t *buf = (sample_t *)d_buffer; + int bi; + + unsigned int sizeof_frame = d_hw_nchan * sizeof (sample_t); + assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + + // To minimize latency, return at most a single period's worth of samples. + // [We could also read the first one in a blocking mode and subsequent + // ones in non-blocking mode, but we'll leave that for later (or never).] + + if(!read_buffer(buf, d_period_size, sizeof_frame)) + return -1; // No fixing this problem. Say we're done. + + // process one period of data + bi = 0; + for(unsigned int i = 0; i < d_period_size; i++) { + for(unsigned int chan = 0; chan < nchan; chan++) { + out[chan][i] = (float) buf[bi++] * scale_factor; + } } - continue; // try again + + return d_period_size; } - else if (r == -ESTRPIPE){ // h/w is suspended (whatever that means) - // This is apparently related to power management - d_nsuspends++; - if ((r = snd_pcm_resume (d_pcm_handle)) < 0){ - output_error_msg ("failed to resume from suspend", r); - return false; + /* + * Work function that deals with float to S16 conversion + * and stereo to mono kludge... + */ + int + alsa_source::work_s16_2x1(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + typedef gr_int16 sample_t; // the type of samples we're creating + static const float scale_factor = 1.0 / std::pow(2.0f, 16-1); + + float **out = (float**)&output_items[0]; + sample_t *buf = (sample_t*)d_buffer; + int bi; + + assert(output_items.size () == 1); + + unsigned int sizeof_frame = d_hw_nchan * sizeof(sample_t); + assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + + // To minimize latency, return at most a single period's worth of samples. + // [We could also read the first one in a blocking mode and subsequent + // ones in non-blocking mode, but we'll leave that for later (or never).] + if(!read_buffer (buf, d_period_size, sizeof_frame)) + return -1; // No fixing this problem. Say we're done. + + // process one period of data + bi = 0; + for(unsigned int i = 0; i < d_period_size; i++) { + int t = (buf[bi] + buf[bi+1]) / 2; + bi += 2; + out[0][i] = (float) t * scale_factor; } - continue; // try again + + return d_period_size; } - else if (r < 0){ - output_error_msg ("snd_pcm_readi failed", r); - return false; + /* + * Work function that deals with float to S32 conversion + */ + int + alsa_source::work_s32(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + typedef gr_int32 sample_t; // the type of samples we're creating + static const float scale_factor = 1.0 / std::pow(2.0f, 32-1); + + unsigned int nchan = output_items.size (); + float **out = (float**)&output_items[0]; + sample_t *buf = (sample_t*)d_buffer; + int bi; + + unsigned int sizeof_frame = d_hw_nchan * sizeof(sample_t); + assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + + // To minimize latency, return at most a single period's worth of samples. + // [We could also read the first one in a blocking mode and subsequent + // ones in non-blocking mode, but we'll leave that for later (or never).] + + if(!read_buffer(buf, d_period_size, sizeof_frame)) + return -1; // No fixing this problem. Say we're done. + + // process one period of data + bi = 0; + for(unsigned int i = 0; i < d_period_size; i++) { + for(unsigned int chan = 0; chan < nchan; chan++) { + out[chan][i] = (float) buf[bi++] * scale_factor; + } + } + + return d_period_size; } - nframes -= r; - buffer += r * sizeof_frame; - } + /* + * Work function that deals with float to S32 conversion + * and stereo to mono kludge... + */ + int + alsa_source::work_s32_2x1(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + typedef gr_int32 sample_t; // the type of samples we're creating + static const float scale_factor = 1.0 / std::pow(2.0f, 32-1); + + float **out = (float**)&output_items[0]; + sample_t *buf = (sample_t*)d_buffer; + int bi; + + assert(output_items.size () == 1); + + unsigned int sizeof_frame = d_hw_nchan * sizeof(sample_t); + assert(d_buffer_size_bytes == d_period_size * sizeof_frame); + + // To minimize latency, return at most a single period's worth of samples. + // [We could also read the first one in a blocking mode and subsequent + // ones in non-blocking mode, but we'll leave that for later (or never).] + + if(!read_buffer(buf, d_period_size, sizeof_frame)) + return -1; // No fixing this problem. Say we're done. + + // process one period of data + bi = 0; + for(unsigned int i = 0; i < d_period_size; i++) { + int t = (buf[bi] + buf[bi+1]) / 2; + bi += 2; + out[0][i] = (float)t * scale_factor; + } - return true; -} + return d_period_size; + } + bool + alsa_source::read_buffer(void *vbuffer, unsigned nframes, unsigned sizeof_frame) + { + unsigned char *buffer = (unsigned char*)vbuffer; + + while(nframes > 0) { + int r = snd_pcm_readi (d_pcm_handle, buffer, nframes); + if(r == -EAGAIN) + continue; // try again + + else if(r == -EPIPE) { // overrun + d_noverruns++; + fputs("aO", stderr); + if((r = snd_pcm_prepare (d_pcm_handle)) < 0) { + output_error_msg("snd_pcm_prepare failed. Can't recover from overrun", r); + return false; + } + continue; // try again + } + + else if(r == -ESTRPIPE) { // h/w is suspended (whatever that means) + // This is apparently related to power management + d_nsuspends++; + if((r = snd_pcm_resume (d_pcm_handle)) < 0) { + output_error_msg ("failed to resume from suspend", r); + return false; + } + continue; // try again + } + + else if(r < 0) { + output_error_msg("snd_pcm_readi failed", r); + return false; + } + + nframes -= r; + buffer += r * sizeof_frame; + } -void -audio_alsa_source::output_error_msg (const char *msg, int err) -{ - fprintf (stderr, "audio_alsa_source[%s]: %s: %s\n", - snd_pcm_name (d_pcm_handle), msg, snd_strerror (err)); -} + return true; + } + + void + alsa_source::output_error_msg(const char *msg, int err) + { + fprintf(stderr, "audio_alsa_source[%s]: %s: %s\n", + snd_pcm_name(d_pcm_handle), msg, snd_strerror (err)); + } + + void + alsa_source::bail(const char *msg, int err) throw (std::runtime_error) + { + output_error_msg(msg, err); + throw std::runtime_error("audio_alsa_source"); + } -void -audio_alsa_source::bail (const char *msg, int err) throw (std::runtime_error) -{ - output_error_msg (msg, err); - throw std::runtime_error ("audio_alsa_source"); -} + } /* namespace audio */ +} /* namespace gr */ diff --git a/gr-audio/lib/alsa/audio_alsa_source.h b/gr-audio/lib/alsa/audio_alsa_source.h index fd33fd89ad..6314fc1376 100644 --- a/gr-audio/lib/alsa/audio_alsa_source.h +++ b/gr-audio/lib/alsa/audio_alsa_source.h @@ -28,80 +28,87 @@ #define ALSA_PCM_NEW_SW_PARAMS_API #include <audio/source.h> -#include <string> #include <alsa/asoundlib.h> +#include <string> #include <stdexcept> -class audio_alsa_source; -typedef boost::shared_ptr<audio_alsa_source> audio_alsa_source_sptr; - -/*! - * \brief audio source using ALSA - * \ingroup audio_blk - * - * The source has between 1 and N input streams of floats, where N is - * depends on the hardware characteristics of the selected device. - * - * Output samples will be in the range [-1,1]. - */ -class audio_alsa_source : public audio_source { - // typedef for pointer to class work method - typedef int (audio_alsa_source::*work_t)(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - unsigned int d_sampling_rate; - std::string d_device_name; - snd_pcm_t *d_pcm_handle; - snd_pcm_hw_params_t *d_hw_params; - snd_pcm_sw_params_t *d_sw_params; - snd_pcm_format_t d_format; - unsigned int d_nperiods; - unsigned int d_period_time_us; // microseconds - snd_pcm_uframes_t d_period_size; // in frames - unsigned int d_buffer_size_bytes; // sizeof of d_buffer - char *d_buffer; - work_t d_worker; // the work method to use - unsigned int d_hw_nchan; // # of configured h/w channels - bool d_special_case_stereo_to_mono; - - // random stats - int d_noverruns; // count of overruns - int d_nsuspends; // count of suspends - - void output_error_msg (const char *msg, int err); - void bail (const char *msg, int err) throw (std::runtime_error); - -public: - audio_alsa_source (int sampling_rate, const std::string device_name, - bool ok_to_block); - - ~audio_alsa_source (); - - bool check_topology (int ninputs, int noutputs); - - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - -protected: - bool read_buffer (void *buffer, unsigned nframes, unsigned sizeof_frame); - - int work_s16 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - int work_s16_2x1 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - int work_s32 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - int work_s32_2x1 (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; +namespace gr { + namespace audio { + + class alsa_source; + typedef boost::shared_ptr<alsa_source> alsa_source_sptr; + + /*! + * \brief audio source using ALSA + * \ingroup audio_blk + * + * The source has between 1 and N input streams of floats, where N is + * depends on the hardware characteristics of the selected device. + * + * Output samples will be in the range [-1,1]. + */ + class alsa_source : public source + { + // typedef for pointer to class work method + typedef int(alsa_source::*work_t)(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + unsigned int d_sampling_rate; + std::string d_device_name; + snd_pcm_t *d_pcm_handle; + snd_pcm_hw_params_t *d_hw_params; + snd_pcm_sw_params_t *d_sw_params; + snd_pcm_format_t d_format; + unsigned int d_nperiods; + unsigned int d_period_time_us; // microseconds + snd_pcm_uframes_t d_period_size; // in frames + unsigned int d_buffer_size_bytes; // sizeof of d_buffer + char *d_buffer; + work_t d_worker; // the work method to use + unsigned int d_hw_nchan; // # of configured h/w channels + bool d_special_case_stereo_to_mono; + + // random stats + int d_noverruns; // count of overruns + int d_nsuspends; // count of suspends + + void output_error_msg(const char *msg, int err); + void bail(const char *msg, int err) throw (std::runtime_error); + + public: + alsa_source(int sampling_rate, + const std::string device_name, + bool ok_to_block); + ~alsa_source(); + + bool check_topology(int ninputs, int noutputs); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + protected: + bool read_buffer(void *buffer, unsigned nframes, unsigned sizeof_frame); + + int work_s16(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + int work_s16_2x1(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + int work_s32(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + int work_s32_2x1(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace audio */ +} /* namespace gr */ #endif /* INCLUDED_AUDIO_ALSA_SOURCE_H */ diff --git a/gr-audio/lib/gr_audio_registry.cc b/gr-audio/lib/gr_audio_registry.cc index e07bf844ac..5446eb09bb 100644 --- a/gr-audio/lib/gr_audio_registry.cc +++ b/gr-audio/lib/gr_audio_registry.cc @@ -1,5 +1,5 @@ /* - * Copyright 2011 Free Software Foundation, Inc. + * Copyright 2011,2013 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -26,107 +26,129 @@ #include <vector> #include <iostream> -/*********************************************************************** - * Create registries - **********************************************************************/ - -struct source_entry_t{ - reg_prio_type prio; - std::string arch; - source_factory_t source; -}; - -static std::vector<source_entry_t> &get_source_registry(void){ - static std::vector<source_entry_t> _registry; - return _registry; -} - -struct sink_entry_t{ - reg_prio_type prio; - std::string arch; - sink_factory_t sink; -}; - -static std::vector<sink_entry_t> &get_sink_registry(void){ - static std::vector<sink_entry_t> _registry; - return _registry; -} - -/*********************************************************************** - * Register functions - **********************************************************************/ -void audio_register_source( - reg_prio_type prio, const std::string &arch, source_factory_t source -){ - source_entry_t entry; - entry.prio = prio; - entry.arch = arch; - entry.source = source; - get_source_registry().push_back(entry); -} - -void audio_register_sink( - reg_prio_type prio, const std::string &arch, sink_factory_t sink -){ - sink_entry_t entry; - entry.prio = prio; - entry.arch = arch; - entry.sink = sink; - get_sink_registry().push_back(entry); -} - -/*********************************************************************** - * Factory functions - **********************************************************************/ -static std::string default_arch_name(void){ - return gr_prefs::singleton()->get_string("audio", "audio_module", "auto"); -} - -static void do_arch_warning(const std::string &arch){ - if (arch == "auto") return; //no warning when arch not specified - std::cerr << "Could not find audio architecture \"" << arch << "\" in registry." << std::endl; - std::cerr << " Defaulting to the first available architecture..." << std::endl; -} - -audio_source::sptr audio_make_source( - int sampling_rate, - const std::string device_name, - bool ok_to_block -){ - if (get_source_registry().empty()){ - throw std::runtime_error("no available audio source factories"); +namespace gr { + namespace audio { + + /*********************************************************************** + * Create registries + **********************************************************************/ + + struct source_entry_t { + reg_prio_type prio; + std::string arch; + source_factory_t source; + }; + + static std::vector<source_entry_t> &get_source_registry(void) + { + static std::vector<source_entry_t> d_registry; + return d_registry; + } + + struct sink_entry_t + { + reg_prio_type prio; + std::string arch; + sink_factory_t sink; + }; + + static std::vector<sink_entry_t> &get_sink_registry(void) + { + static std::vector<sink_entry_t> d_registry; + return d_registry; } - std::string arch = default_arch_name(); - source_entry_t entry = get_source_registry().front(); + /*********************************************************************** + * Register functions + **********************************************************************/ + void + register_source(reg_prio_type prio, + const std::string &arch, + source_factory_t source) + { + source_entry_t entry; + entry.prio = prio; + entry.arch = arch; + entry.source = source; + get_source_registry().push_back(entry); + } + + void register_sink(reg_prio_type prio, + const std::string &arch, + sink_factory_t sink) + { + sink_entry_t entry; + entry.prio = prio; + entry.arch = arch; + entry.sink = sink; + get_sink_registry().push_back(entry); + } + + /*********************************************************************** + * Factory functions + **********************************************************************/ + static std::string default_arch_name(void) + { + return gr_prefs::singleton()->get_string("audio", "audio_module", "auto"); + } - BOOST_FOREACH(const source_entry_t &e, get_source_registry()){ - if (e.prio > entry.prio) entry = e; //entry is highest prio - if (arch != e.arch) continue; //continue when no match + static void do_arch_warning(const std::string &arch) + { + if(arch == "auto") + return; //no warning when arch not specified + std::cerr << "Could not find audio architecture \"" << arch + << "\" in registry." << std::endl; + std::cerr << " Defaulting to the first available architecture..." << std::endl; + } + + source::sptr + source::make(int sampling_rate, + const std::string device_name, + bool ok_to_block) + { + if(get_source_registry().empty()) { + throw std::runtime_error("no available audio source factories"); + } + + std::string arch = default_arch_name(); + source_entry_t entry = get_source_registry().front(); + + BOOST_FOREACH(const source_entry_t &e, get_source_registry()) { + if(e.prio > entry.prio) + entry = e; //entry is highest prio + if(arch != e.arch) + continue; //continue when no match return e.source(sampling_rate, device_name, ok_to_block); + } + + //std::cout << "Audio source arch: " << entry.name << std::endl; + return entry.source(sampling_rate, device_name, ok_to_block); } - //std::cout << "Audio source arch: " << entry.name << std::endl; - return entry.source(sampling_rate, device_name, ok_to_block); -} - -audio_sink::sptr audio_make_sink( - int sampling_rate, - const std::string device_name, - bool ok_to_block -){ - if (get_sink_registry().empty()){ + + sink::sptr + sink::make(int sampling_rate, + const std::string device_name, + bool ok_to_block) + { + if(get_sink_registry().empty()) { throw std::runtime_error("no available audio sink factories"); - } + } - std::string arch = default_arch_name(); - sink_entry_t entry = get_sink_registry().front(); + std::string arch = default_arch_name(); + sink_entry_t entry = get_sink_registry().front(); - BOOST_FOREACH(const sink_entry_t &e, get_sink_registry()){ - if (e.prio > entry.prio) entry = e; //entry is highest prio - if (arch != e.arch) continue; //continue when no match + BOOST_FOREACH(const sink_entry_t &e, get_sink_registry()) { + if(e.prio > entry.prio) + entry = e; //entry is highest prio + if(arch != e.arch) + continue; //continue when no match return e.sink(sampling_rate, device_name, ok_to_block); + } + + do_arch_warning(arch); + //std::cout << "Audio sink arch: " << entry.name << std::endl; + return entry.sink(sampling_rate, device_name, ok_to_block); } - do_arch_warning(arch); - //std::cout << "Audio sink arch: " << entry.name << std::endl; - return entry.sink(sampling_rate, device_name, ok_to_block); -} + + } /* namespace audio */ +} /* namespace gr */ diff --git a/gr-audio/lib/gr_audio_registry.h b/gr-audio/lib/gr_audio_registry.h index 59a4c0fa67..70612de574 100644 --- a/gr-audio/lib/gr_audio_registry.h +++ b/gr-audio/lib/gr_audio_registry.h @@ -26,30 +26,38 @@ #include <audio/source.h> #include <string> -typedef audio_source::sptr(*source_factory_t)(int, const std::string &, bool); -typedef audio_sink::sptr(*sink_factory_t)(int, const std::string &, bool); +namespace gr { + namespace audio { -enum reg_prio_type{ - REG_PRIO_LOW = 100, - REG_PRIO_MED = 200, - REG_PRIO_HIGH = 300 -}; + typedef source::sptr(*source_factory_t)(int, const std::string &, bool); + typedef sink::sptr(*sink_factory_t)(int, const std::string &, bool); -void audio_register_source(reg_prio_type prio, const std::string &arch, source_factory_t source); -void audio_register_sink(reg_prio_type prio, const std::string &arch, sink_factory_t sink); + enum reg_prio_type { + REG_PRIO_LOW = 100, + REG_PRIO_MED = 200, + REG_PRIO_HIGH = 300 + }; + + void register_source(reg_prio_type prio, const std::string &arch, + source_factory_t source); + void register_sink(reg_prio_type prio, const std::string &arch, + sink_factory_t sink); #define AUDIO_REGISTER_FIXTURE(x) static struct x{x();}x;x::x() #define AUDIO_REGISTER_SOURCE(prio, arch) \ - static audio_source::sptr arch##_source_fcn(int, const std::string &, bool); \ - AUDIO_REGISTER_FIXTURE(arch##_source_reg){ \ - audio_register_source(prio, #arch, &arch##_source_fcn); \ - } static audio_source::sptr arch##_source_fcn - -#define AUDIO_REGISTER_SINK(prio, arch) \ - static audio_sink::sptr arch##_sink_fcn(int, const std::string &, bool); \ - AUDIO_REGISTER_FIXTURE(arch##_sink_reg){ \ - audio_register_sink(prio, #arch, &arch##_sink_fcn); \ - } static audio_sink::sptr arch##_sink_fcn + static source::sptr arch##_source_fcn(int, const std::string &, bool); \ + AUDIO_REGISTER_FIXTURE(arch##_source_reg) { \ + register_source(prio, #arch, &arch##_source_fcn); \ + } static source::sptr arch##_source_fcn + +#define AUDIO_REGISTER_SINK(prio, arch) \ + static sink::sptr arch##_sink_fcn(int, const std::string &, bool); \ + AUDIO_REGISTER_FIXTURE(arch##_sink_reg) { \ + register_sink(prio, #arch, &arch##_sink_fcn); \ + } static sink::sptr arch##_sink_fcn + + } /* namespace audio */ +} /* namespace gr */ #endif /* INCLUDED_GR_AUDIO_REGISTRY_H */ |