summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rondeau <trondeau@vt.edu>2013-02-11 21:20:27 -0500
committerTom Rondeau <trondeau@vt.edu>2013-02-11 21:20:27 -0500
commit354c1219a22e13422b14d9bc14645e1296776d24 (patch)
treee432361a66c5ecb8c6fd47c16e2f96722690f0c4
parentd28b2f2bdd4a2961bb29076085a129299aeec157 (diff)
audio: wip: conversion cont. Alsa building.
-rw-r--r--gr-audio/include/audio/sink.h60
-rw-r--r--gr-audio/include/audio/source.h60
-rw-r--r--gr-audio/lib/alsa/audio_alsa_sink.cc940
-rw-r--r--gr-audio/lib/alsa/audio_alsa_sink.h146
-rw-r--r--gr-audio/lib/alsa/audio_alsa_source.cc889
-rw-r--r--gr-audio/lib/alsa/audio_alsa_source.h151
-rw-r--r--gr-audio/lib/gr_audio_registry.cc212
-rw-r--r--gr-audio/lib/gr_audio_registry.h46
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 */