summaryrefslogtreecommitdiff
path: root/gr-audio-alsa/src
diff options
context:
space:
mode:
Diffstat (limited to 'gr-audio-alsa/src')
-rw-r--r--gr-audio-alsa/src/Makefile.am91
-rw-r--r--gr-audio-alsa/src/audio_alsa.i80
-rw-r--r--gr-audio-alsa/src/audio_alsa_sink.cc538
-rw-r--r--gr-audio-alsa/src/audio_alsa_sink.h124
-rw-r--r--gr-audio-alsa/src/audio_alsa_source.cc506
-rw-r--r--gr-audio-alsa/src/audio_alsa_source.h124
-rw-r--r--gr-audio-alsa/src/gri_alsa.cc175
-rw-r--r--gr-audio-alsa/src/gri_alsa.h44
-rwxr-xr-xgr-audio-alsa/src/qa_alsa.py40
-rw-r--r--gr-audio-alsa/src/run_tests.in47
10 files changed, 1769 insertions, 0 deletions
diff --git a/gr-audio-alsa/src/Makefile.am b/gr-audio-alsa/src/Makefile.am
new file mode 100644
index 000000000..e06fef9ae
--- /dev/null
+++ b/gr-audio-alsa/src/Makefile.am
@@ -0,0 +1,91 @@
+#
+# Copyright 2004 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+# Install this stuff so that it ends up as the gnuradio.audio_alsa module
+# This usually ends up at:
+# ${prefix}/lib/python${python_version}/site-packages/gnuradio
+
+ourpythondir = $(grpythondir)
+ourlibdir = $(grpyexecdir)
+
+LIBS += $(GNURADIO_CORE_LIBS)
+
+EXTRA_DIST = run_tests.in
+
+TESTS = run_tests
+
+LOCAL_IFILES = \
+ audio_alsa.i
+
+NON_LOCAL_IFILES = \
+ $(top_srcdir)/gnuradio-core/src/lib/swig/gnuradio.i
+
+ALL_IFILES = \
+ $(LOCAL_IFILES) \
+ $(NON_LOCAL_IFILES)
+
+BUILT_SOURCES = \
+ audio_alsa.cc \
+ audio_alsa.py
+
+ourpython_PYTHON = \
+ audio_alsa.py
+
+INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS)
+
+SWIGPYTHONARGS = $(SWIGPYTHONFLAGS) $(STD_DEFINES_AND_INCLUDES)
+
+ourlib_LTLIBRARIES = _audio_alsa.la
+
+_audio_alsa_la_SOURCES = \
+ audio_alsa.cc \
+ audio_alsa_sink.cc \
+ audio_alsa_source.cc \
+ gri_alsa.cc
+
+grinclude_HEADERS = \
+ audio_alsa_sink.h \
+ audio_alsa_source.h
+
+noinst_HEADERS = \
+ gri_alsa.h
+
+
+swiginclude_HEADERS = \
+ $(LOCAL_IFILES)
+
+_audio_alsa_la_LIBADD = \
+ $(PYTHON_LDFLAGS) \
+ -lstdc++
+
+_audio_alsa_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version
+
+audio_alsa.cc audio_alsa.py: audio_alsa.i $(NON_LOCAL_IFILES)
+ $(SWIG) $(SWIGPYTHONARGS) -module audio_alsa -o audio_alsa.cc $<
+
+
+noinst_PYTHON = \
+ qa_alsa.py
+
+MOSTLYCLEANFILES = \
+ $(BUILT_SOURCES) *~ *.pyc
diff --git a/gr-audio-alsa/src/audio_alsa.i b/gr-audio-alsa/src/audio_alsa.i
new file mode 100644
index 000000000..1cb13b838
--- /dev/null
+++ b/gr-audio-alsa/src/audio_alsa.i
@@ -0,0 +1,80 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004,2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+%feature("autodoc","1");
+
+%include "exception.i"
+%import "gnuradio.i" // the common stuff
+
+%{
+#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix
+#include "audio_alsa_sink.h"
+#include "audio_alsa_source.h"
+#include <stdexcept>
+%}
+
+// ----------------------------------------------------------------
+
+GR_SWIG_BLOCK_MAGIC(audio_alsa,source)
+
+audio_alsa_source_sptr
+audio_alsa_make_source (int sampling_rate,
+ const std::string dev = "",
+ bool ok_to_block = true
+ ) throw (std::runtime_error);
+
+class audio_alsa_source : public gr_sync_block {
+
+ protected:
+ audio_alsa_source (int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block = true
+ ) throw (std::runtime_error);
+
+ public:
+ ~audio_alsa_source ();
+
+ bool start();
+ bool stop();
+};
+
+// ----------------------------------------------------------------
+
+GR_SWIG_BLOCK_MAGIC(audio_alsa,sink)
+
+audio_alsa_sink_sptr
+audio_alsa_make_sink (int sampling_rate,
+ const std::string dev = "",
+ bool ok_to_block = true
+ ) throw (std::runtime_error);
+
+class audio_alsa_sink : public gr_sync_block {
+
+ protected:
+ audio_alsa_sink (int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block
+ ) throw (std::runtime_error);
+
+ public:
+ ~audio_alsa_sink ();
+};
diff --git a/gr-audio-alsa/src/audio_alsa_sink.cc b/gr-audio-alsa/src/audio_alsa_sink.cc
new file mode 100644
index 000000000..bdf9dbbed
--- /dev/null
+++ b/gr-audio-alsa/src/audio_alsa_sink.cc
@@ -0,0 +1,538 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <audio_alsa_sink.h>
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <stdio.h>
+#include <iostream>
+#include <stdexcept>
+#include <gri_alsa.h>
+
+
+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
+};
+
+#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_sptr
+audio_alsa_make_sink (int sampling_rate,
+ const std::string dev,
+ bool ok_to_block)
+{
+ return audio_alsa_sink_sptr (new audio_alsa_sink (sampling_rate, dev,
+ ok_to_block));
+}
+
+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)
+{
+ 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 (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_alloca (&access_mask);
+ 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;
+
+ // FIXME check_topology may be called more than once.
+ // Ensure that the pcm is in a state where we can still mess with the hw_params
+
+ 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 int NBITS = 16; // # of bits in a sample
+
+ 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] * (float) ((1L << (NBITS-1)) - 1));
+ }
+ }
+
+ // 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.
+ }
+
+ return n;
+}
+
+
+/*
+ * 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 int NBITS = 32; // # of bits in a sample
+
+ 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] * (float) ((1L << (NBITS-1)) - 1));
+ }
+ }
+
+ // 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.
+ }
+
+ return n;
+}
+
+/*
+ * 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 int NBITS = 16; // # of bits in a sample
+
+ 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] * (float) ((1L << (NBITS-1)) - 1));
+ 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.
+ }
+
+ return n;
+}
+
+/*
+ * 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 int NBITS = 32; // # of bits in a sample
+
+ 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] * (float) ((1L << (NBITS-1)) - 1));
+ 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.
+ }
+
+ return n;
+}
+
+bool
+audio_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)
+ continue; // try again
+
+ 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;
+}
+
+
+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
+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");
+}
diff --git a/gr-audio-alsa/src/audio_alsa_sink.h b/gr-audio-alsa/src/audio_alsa_sink.h
new file mode 100644
index 000000000..bb61e49ab
--- /dev/null
+++ b/gr-audio-alsa/src/audio_alsa_sink.h
@@ -0,0 +1,124 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_ALSA_SINK_H
+#define INCLUDED_AUDIO_ALSA_SINK_H
+
+// use new ALSA API
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <gr_sync_block.h>
+#include <string>
+#include <alsa/asoundlib.h>
+#include <stdexcept>
+
+
+class audio_alsa_sink;
+typedef boost::shared_ptr<audio_alsa_sink> audio_alsa_sink_sptr;
+
+/*!
+ * \brief make an alsa audio sink.
+ *
+ * \param sampling_rate sampling rate in Hz
+ * \param dev ALSA pcm device name, e.g., "hw:0,0"
+ * \param ok_to_block (currently ignored)
+ */
+audio_alsa_sink_sptr
+audio_alsa_make_sink (int sampling_rate,
+ const std::string dev = "",
+ bool ok_to_block = true);
+
+/*!
+ * \brief audio sink using ALSA
+ *
+ * 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 gr_sync_block {
+ friend audio_alsa_sink_sptr
+ audio_alsa_make_sink (int sampling_rate, const std::string device_name,
+ bool ok_to_block);
+
+ // 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
+
+ void output_error_msg (const char *msg, int err);
+ void bail (const char *msg, int err) throw (std::runtime_error);
+
+ protected:
+ audio_alsa_sink (int sampling_rate, const std::string device_name,
+ bool ok_to_block);
+
+ public:
+ ~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);
+};
+
+#endif /* INCLUDED_AUDIO_ALSA_SINK_H */
diff --git a/gr-audio-alsa/src/audio_alsa_source.cc b/gr-audio-alsa/src/audio_alsa_source.cc
new file mode 100644
index 000000000..4e7ee94b6
--- /dev/null
+++ b/gr-audio-alsa/src/audio_alsa_source.cc
@@ -0,0 +1,506 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004,2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <audio_alsa_source.h>
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <stdio.h>
+#include <iostream>
+#include <stdexcept>
+#include <gri_alsa.h>
+
+
+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
+};
+
+#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 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_sptr
+audio_alsa_make_source (int sampling_rate, const std::string dev,
+ bool ok_to_block)
+{
+ return audio_alsa_source_sptr (new audio_alsa_source (sampling_rate, dev,
+ ok_to_block));
+}
+
+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_alloca (&access_mask);
+ 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;
+
+ // FIXME check_topology may be called more than once.
+ // Ensure that the pcm is in a state where we can still mess with the hw_params
+
+ 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);
+}
+
+/*
+ * 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 int NBITS = 16; // # of bits in a sample
+
+ 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++] * (1.0 / (float) ((1L << (NBITS-1)) - 1));
+ }
+ }
+
+ return d_period_size;
+}
+
+/*
+ * 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 int NBITS = 16; // # of bits in a sample
+
+ unsigned int nchan = output_items.size ();
+ float **out = (float **) &output_items[0];
+ sample_t *buf = (sample_t *) d_buffer;
+ int bi;
+
+ assert (nchan == 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 * (1.0 / (float) ((1L << (NBITS-1)) - 1));
+ }
+
+ return d_period_size;
+}
+
+/*
+ * 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 int NBITS = 32; // # of bits in a sample
+
+ 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++] * (1.0 / (float) ((1L << (NBITS-1)) - 1));
+ }
+ }
+
+ return d_period_size;
+}
+
+/*
+ * 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 int NBITS = 32; // # of bits in a sample
+
+ unsigned int nchan = output_items.size ();
+ float **out = (float **) &output_items[0];
+ sample_t *buf = (sample_t *) d_buffer;
+ int bi;
+
+ assert (nchan == 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 * (1.0 / (float) ((1L << (NBITS-1)) - 1));
+ }
+
+ 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;
+ }
+ 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;
+ }
+
+ return true;
+}
+
+
+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));
+}
+
+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");
+}
diff --git a/gr-audio-alsa/src/audio_alsa_source.h b/gr-audio-alsa/src/audio_alsa_source.h
new file mode 100644
index 000000000..cc1ae1167
--- /dev/null
+++ b/gr-audio-alsa/src/audio_alsa_source.h
@@ -0,0 +1,124 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004,2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_ALSA_SOURCE_H
+#define INCLUDED_AUDIO_ALSA_SOURCE_H
+
+// use new ALSA API
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <gr_sync_block.h>
+#include <string>
+#include <alsa/asoundlib.h>
+#include <stdexcept>
+
+class audio_alsa_source;
+typedef boost::shared_ptr<audio_alsa_source> audio_alsa_source_sptr;
+
+/*!
+ * \brief Make an ALSA audio source.
+ *
+ * \param sampling_rate sampling rate
+ * \param dev ALSA pcm device name, e.g., "hw:0,0"
+ * \param ok_to_block (currently ignored)
+ */
+audio_alsa_source_sptr
+audio_alsa_make_source (int sampling_rate,
+ const std::string dev = "",
+ bool ok_to_block = true);
+
+/*!
+ * \brief audio source using ALSA
+ *
+ * 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 gr_sync_block {
+ friend audio_alsa_source_sptr
+ audio_alsa_make_source (int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block);
+
+ // 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);
+
+ protected:
+ audio_alsa_source (int sampling_rate, const std::string device_name,
+ bool ok_to_block);
+
+ public:
+ ~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);
+};
+
+#endif /* INCLUDED_AUDIO_ALSA_SOURCE_H */
diff --git a/gr-audio-alsa/src/gri_alsa.cc b/gr-audio-alsa/src/gri_alsa.cc
new file mode 100644
index 000000000..25ff7f652
--- /dev/null
+++ b/gr-audio-alsa/src/gri_alsa.cc
@@ -0,0 +1,175 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gri_alsa.h>
+#include <algorithm>
+
+static snd_pcm_access_t access_types[] = {
+ SND_PCM_ACCESS_MMAP_INTERLEAVED,
+ SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
+ SND_PCM_ACCESS_MMAP_COMPLEX,
+ SND_PCM_ACCESS_RW_INTERLEAVED,
+ SND_PCM_ACCESS_RW_NONINTERLEAVED
+};
+
+static snd_pcm_format_t format_types[] = {
+ // SND_PCM_FORMAT_UNKNOWN,
+ SND_PCM_FORMAT_S8,
+ SND_PCM_FORMAT_U8,
+ SND_PCM_FORMAT_S16_LE,
+ SND_PCM_FORMAT_S16_BE,
+ SND_PCM_FORMAT_U16_LE,
+ SND_PCM_FORMAT_U16_BE,
+ SND_PCM_FORMAT_S24_LE,
+ SND_PCM_FORMAT_S24_BE,
+ SND_PCM_FORMAT_U24_LE,
+ SND_PCM_FORMAT_U24_BE,
+ SND_PCM_FORMAT_S32_LE,
+ SND_PCM_FORMAT_S32_BE,
+ SND_PCM_FORMAT_U32_LE,
+ SND_PCM_FORMAT_U32_BE,
+ SND_PCM_FORMAT_FLOAT_LE,
+ SND_PCM_FORMAT_FLOAT_BE,
+ SND_PCM_FORMAT_FLOAT64_LE,
+ SND_PCM_FORMAT_FLOAT64_BE,
+ SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
+ SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
+ SND_PCM_FORMAT_MU_LAW,
+ SND_PCM_FORMAT_A_LAW,
+ SND_PCM_FORMAT_IMA_ADPCM,
+ SND_PCM_FORMAT_MPEG,
+ SND_PCM_FORMAT_GSM,
+ SND_PCM_FORMAT_SPECIAL,
+ SND_PCM_FORMAT_S24_3LE,
+ SND_PCM_FORMAT_S24_3BE,
+ SND_PCM_FORMAT_U24_3LE,
+ SND_PCM_FORMAT_U24_3BE,
+ SND_PCM_FORMAT_S20_3LE,
+ SND_PCM_FORMAT_S20_3BE,
+ SND_PCM_FORMAT_U20_3LE,
+ SND_PCM_FORMAT_U20_3BE,
+ SND_PCM_FORMAT_S18_3LE,
+ SND_PCM_FORMAT_S18_3BE,
+ SND_PCM_FORMAT_U18_3LE,
+ SND_PCM_FORMAT_U18_3BE
+};
+
+static unsigned int test_rates[] = {
+ 8000, 16000, 22050, 32000, 44100, 48000, 96000, 192000
+};
+
+#define NELEMS(x) (sizeof(x)/sizeof(x[0]))
+
+void
+gri_alsa_dump_hw_params (snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams, FILE *fp)
+{
+ fprintf (fp, "PCM name: %s\n", snd_pcm_name (pcm));
+
+ fprintf (fp, "Access types:\n");
+ for (unsigned i = 0; i < NELEMS (access_types); i++){
+ snd_pcm_access_t at = access_types[i];
+ fprintf (fp, " %-20s %s\n",
+ snd_pcm_access_name (at),
+ snd_pcm_hw_params_test_access (pcm, hwparams, at) == 0 ? "YES" : "NO");
+ }
+
+ fprintf (fp, "Formats:\n");
+ for (unsigned i = 0; i < NELEMS (format_types); i++){
+ snd_pcm_format_t ft = format_types[i];
+ if (0)
+ fprintf (fp, " %-20s %s\n",
+ snd_pcm_format_name (ft),
+ snd_pcm_hw_params_test_format (pcm, hwparams, ft) == 0 ? "YES" : "NO");
+ else {
+ if (snd_pcm_hw_params_test_format (pcm, hwparams, ft) == 0)
+ fprintf (fp, " %-20s YES\n", snd_pcm_format_name (ft));
+ }
+ }
+
+ fprintf (fp, "Number of channels\n");
+ unsigned int min_chan, max_chan;
+ snd_pcm_hw_params_get_channels_min (hwparams, &min_chan);
+ snd_pcm_hw_params_get_channels_max (hwparams, &max_chan);
+ fprintf (fp, " min channels: %d\n", min_chan);
+ fprintf (fp, " max channels: %d\n", max_chan);
+ unsigned int chan;
+ max_chan = std::min (max_chan, 16U); // truncate display...
+ for (chan = min_chan; chan <= max_chan; chan++){
+ fprintf (fp, " %d channels\t%s\n", chan,
+ snd_pcm_hw_params_test_channels (pcm, hwparams, chan) == 0 ? "YES" : "NO");
+ }
+
+ fprintf (fp, "Sample Rates:\n");
+ unsigned int min_rate, max_rate;
+ int min_dir, max_dir;
+
+ snd_pcm_hw_params_get_rate_min (hwparams, &min_rate, &min_dir);
+ snd_pcm_hw_params_get_rate_max (hwparams, &max_rate, &max_dir);
+ fprintf (fp, " min rate: %7d (dir = %d)\n", min_rate, min_dir);
+ fprintf (fp, " max rate: %7d (dir = %d)\n", max_rate, max_dir);
+ for (unsigned i = 0; i < NELEMS (test_rates); i++){
+ unsigned int rate = test_rates[i];
+ fprintf (fp, " %6u %s\n", rate,
+ snd_pcm_hw_params_test_rate (pcm, hwparams, rate, 0) == 0 ? "YES" : "NO");
+ }
+
+ fflush (fp);
+}
+
+bool
+gri_alsa_pick_acceptable_format (snd_pcm_t *pcm,
+ snd_pcm_hw_params_t *hwparams,
+ snd_pcm_format_t acceptable_formats[],
+ unsigned nacceptable_formats,
+ snd_pcm_format_t *selected_format,
+ const char *error_msg_tag,
+ bool verbose)
+{
+ int err;
+
+ // pick a format that we like...
+ for (unsigned i = 0; i < nacceptable_formats; i++){
+ if (snd_pcm_hw_params_test_format (pcm, hwparams,
+ acceptable_formats[i]) == 0){
+ err = snd_pcm_hw_params_set_format (pcm, hwparams, acceptable_formats[i]);
+ if (err < 0){
+ fprintf (stderr, "%s[%s]: failed to set format: %s\n",
+ error_msg_tag, snd_pcm_name (pcm), snd_strerror (err));
+ return false;
+ }
+ if (verbose)
+ fprintf (stdout, "%s[%s]: using %s\n",
+ error_msg_tag, snd_pcm_name (pcm),
+ snd_pcm_format_name (acceptable_formats[i]));
+ *selected_format = acceptable_formats[i];
+ return true;
+ }
+ }
+
+ fprintf (stderr, "%s[%s]: failed to find acceptable format",
+ error_msg_tag, snd_pcm_name (pcm));
+ return false;
+}
diff --git a/gr-audio-alsa/src/gri_alsa.h b/gr-audio-alsa/src/gri_alsa.h
new file mode 100644
index 000000000..841c54d4b
--- /dev/null
+++ b/gr-audio-alsa/src/gri_alsa.h
@@ -0,0 +1,44 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef INCLUDED_GRI_ALSA_H
+#define INCLUDED_GRI_ALSA_H
+
+#include <stdio.h>
+#include <alsa/asoundlib.h>
+
+void
+gri_alsa_dump_hw_params (snd_pcm_t *pcm,
+ snd_pcm_hw_params_t *hwparams,
+ FILE *fp);
+
+bool
+gri_alsa_pick_acceptable_format (snd_pcm_t *pcm,
+ snd_pcm_hw_params_t *hwparams,
+ snd_pcm_format_t acceptable_formats[],
+ unsigned nacceptable_formats,
+ snd_pcm_format_t *selected_format,
+ const char *error_msg_tag,
+ bool verbose);
+
+
+#endif /* INCLUDED_GRI_ALSA_H */
diff --git a/gr-audio-alsa/src/qa_alsa.py b/gr-audio-alsa/src/qa_alsa.py
new file mode 100755
index 000000000..2994573d1
--- /dev/null
+++ b/gr-audio-alsa/src/qa_alsa.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#
+# Copyright 2005 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import audio_alsa
+
+class qa_alsa (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_000_nop (self):
+ """Just see if we can import the module...
+ They may not have ALSA drivers, etc. Don't try to run anything"""
+ pass
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gr-audio-alsa/src/run_tests.in b/gr-audio-alsa/src/run_tests.in
new file mode 100644
index 000000000..4bfb076e8
--- /dev/null
+++ b/gr-audio-alsa/src/run_tests.in
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# All this strange PYTHONPATH manipulation is required to run our
+# tests using our just built shared library and swig-generated python
+# code prior to installation.
+
+# build tree == src tree unless you're doing a VPATH build.
+# If you don't know what a VPATH build is, you're not doing one. Relax...
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+# Where to look in the build tree for our shared library
+libbld=@abs_top_builddir@/gr-audio-alsa/src
+# Where to look in the src tree for swig generated python code
+libsrc=@abs_top_srcdir@/gr-audio-alsa/src
+# Where to look in the src tree for hand written python code
+py=@abs_top_srcdir@/gr-audio-alsa/src
+
+# Where to look for GNU Radio python modules in current build tree
+# FIXME this is wrong on a distcheck. We really need to ask gnuradio-core
+# where it put its python files.
+grpythonbld=@abs_top_builddir@/gnuradio-core/src/python/:@abs_top_builddir@/gnuradio-core/src/lib/swig/:@abs_top_builddir@/gnuradio-core/src/lib/swig/.libs
+
+PYTHONPATH="$grpythonbld:$libbld:$libbld/.libs:$libsrc:$py:$PYTHONPATH"
+export PYTHONPATH
+
+#
+# This is the simple part...
+# Run everything that matches qa_*.py and return the final result.
+#
+
+ok=yes
+for file in @srcdir@/qa_*.py
+do
+ if ! $file
+ then
+ ok=no
+ fi
+done
+
+if [ $ok = yes ]
+then
+ exit 0
+else
+ exit 1
+fi