summaryrefslogtreecommitdiff
path: root/gr-audio/lib
diff options
context:
space:
mode:
authorTom Rondeau2011-04-08 15:37:29 -0400
committerTom Rondeau2011-04-08 15:37:29 -0400
commit21b64f070a3eb38ab044529a6ddd9cd6b6d0a2cb (patch)
tree4e72909a051b0a4ac428252d15f34b53bcc986b6 /gr-audio/lib
parentd7093fd06d0ec37f6ba2841d202fe90f4fa3661e (diff)
parent5c358afd00347f5da7a59dd73ea79cd3df26659d (diff)
downloadgnuradio-21b64f070a3eb38ab044529a6ddd9cd6b6d0a2cb.tar.gz
gnuradio-21b64f070a3eb38ab044529a6ddd9cd6b6d0a2cb.tar.bz2
gnuradio-21b64f070a3eb38ab044529a6ddd9cd6b6d0a2cb.zip
Merge branch 'master' into constell_obj
Conflicts: gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h gr-audio-oss/src/Makefile.swig.gen gr-audio/swig/Makefile.swig.gen
Diffstat (limited to 'gr-audio/lib')
-rw-r--r--gr-audio/lib/.gitignore2
-rw-r--r--gr-audio/lib/Makefile.am180
-rw-r--r--gr-audio/lib/alsa/audio_alsa_sink.cc548
-rw-r--r--gr-audio/lib/alsa/audio_alsa_sink.h104
-rw-r--r--gr-audio/lib/alsa/audio_alsa_source.cc505
-rw-r--r--gr-audio/lib/alsa/audio_alsa_source.h106
-rw-r--r--gr-audio/lib/alsa/gr-audio-alsa.conf11
-rw-r--r--gr-audio/lib/alsa/gri_alsa.cc175
-rw-r--r--gr-audio/lib/alsa/gri_alsa.h44
-rw-r--r--gr-audio/lib/gr-audio.conf12
-rw-r--r--gr-audio/lib/gr_audio_registry.cc132
-rw-r--r--gr-audio/lib/gr_audio_registry.h55
-rw-r--r--gr-audio/lib/jack/audio_jack_sink.cc236
-rw-r--r--gr-audio/lib/jack/audio_jack_sink.h79
-rw-r--r--gr-audio/lib/jack/audio_jack_source.cc237
-rw-r--r--gr-audio/lib/jack/audio_jack_source.h79
-rw-r--r--gr-audio/lib/jack/gr-audio-jack.conf8
-rw-r--r--gr-audio/lib/jack/gri_jack.cc30
-rw-r--r--gr-audio/lib/jack/gri_jack.h28
-rw-r--r--gr-audio/lib/oss/audio_oss_sink.cc161
-rw-r--r--gr-audio/lib/oss/audio_oss_sink.h54
-rw-r--r--gr-audio/lib/oss/audio_oss_source.cc177
-rw-r--r--gr-audio/lib/oss/audio_oss_source.h58
-rw-r--r--gr-audio/lib/oss/gr-audio-oss.conf9
-rw-r--r--gr-audio/lib/osx/audio_osx.h71
-rw-r--r--gr-audio/lib/osx/audio_osx_sink.cc404
-rw-r--r--gr-audio/lib/osx/audio_osx_sink.h79
-rw-r--r--gr-audio/lib/osx/audio_osx_source.cc1016
-rw-r--r--gr-audio/lib/osx/audio_osx_source.h115
-rw-r--r--gr-audio/lib/osx/circular_buffer.h315
-rw-r--r--gr-audio/lib/portaudio/audio_portaudio_sink.cc362
-rw-r--r--gr-audio/lib/portaudio/audio_portaudio_sink.h85
-rw-r--r--gr-audio/lib/portaudio/audio_portaudio_source.cc374
-rw-r--r--gr-audio/lib/portaudio/audio_portaudio_source.h83
-rw-r--r--gr-audio/lib/portaudio/gr-audio-portaudio.conf10
-rw-r--r--gr-audio/lib/portaudio/gri_portaudio.cc111
-rw-r--r--gr-audio/lib/portaudio/gri_portaudio.h32
-rw-r--r--gr-audio/lib/windows/audio_windows_sink.cc323
-rw-r--r--gr-audio/lib/windows/audio_windows_sink.h72
-rw-r--r--gr-audio/lib/windows/audio_windows_source.cc205
-rw-r--r--gr-audio/lib/windows/audio_windows_source.h56
41 files changed, 6743 insertions, 0 deletions
diff --git a/gr-audio/lib/.gitignore b/gr-audio/lib/.gitignore
new file mode 100644
index 000000000..b336cc7ce
--- /dev/null
+++ b/gr-audio/lib/.gitignore
@@ -0,0 +1,2 @@
+/Makefile
+/Makefile.in
diff --git a/gr-audio/lib/Makefile.am b/gr-audio/lib/Makefile.am
new file mode 100644
index 000000000..2bec73ff0
--- /dev/null
+++ b/gr-audio/lib/Makefile.am
@@ -0,0 +1,180 @@
+#
+# Copyright 2011 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 3, 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., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = \
+ $(STD_DEFINES_AND_INCLUDES) \
+ $(WITH_INCLUDES) \
+ -I$(abs_top_srcdir)/gr-audio/include \
+ -Dgnuradio_audio_EXPORTS
+
+lib_LTLIBRARIES = libgnuradio-audio.la
+
+libgnuradio_audio_la_SOURCES = \
+ gr_audio_registry.cc
+
+libgnuradio_audio_la_LIBADD = \
+ $(GNURADIO_CORE_LA)
+
+libgnuradio_audio_la_LDFLAGS = $(NO_UNDEFINED) $(LTVERSIONFLAGS)
+
+noinst_HEADERS = gr_audio_registry.h
+
+etcdir = $(gr_prefsdir)
+dist_etc_DATA = gr-audio.conf
+
+########################################################################
+## ALSA Support
+########################################################################
+if GR_AUDIO_ALSA_SUPPORT
+
+AM_CPPFLAGS += \
+ -I$(srcdir)/alsa \
+ $(ALSA_CPPFLAGS)
+
+libgnuradio_audio_la_LIBADD += $(ALSA_LIBS)
+
+libgnuradio_audio_la_SOURCES += \
+ alsa/gri_alsa.cc \
+ alsa/audio_alsa_source.cc \
+ alsa/audio_alsa_sink.cc
+
+noinst_HEADERS += \
+ alsa/gri_alsa.h \
+ alsa/audio_alsa_source.h \
+ alsa/audio_alsa_sink.h
+
+dist_etc_DATA += alsa/gr-audio-alsa.conf
+
+endif
+
+########################################################################
+## OSS Support
+########################################################################
+if GR_AUDIO_OSS_SUPPORT
+
+AM_CPPFLAGS += \
+ -I$(srcdir)/oss
+
+libgnuradio_audio_la_LIBADD += $(OSS_LIBS)
+
+libgnuradio_audio_la_SOURCES += \
+ oss/audio_oss_source.cc \
+ oss/audio_oss_sink.cc
+
+noinst_HEADERS += \
+ oss/audio_oss_source.h \
+ oss/audio_oss_sink.h
+
+dist_etc_DATA += oss/gr-audio-oss.conf
+
+endif
+
+########################################################################
+## Jack Support
+########################################################################
+if GR_AUDIO_JACK_SUPPORT
+
+AM_CPPFLAGS += \
+ -I$(srcdir)/jack \
+ $(JACK_CPPFLAGS)
+
+libgnuradio_audio_la_LIBADD += $(JACK_LIBS)
+
+libgnuradio_audio_la_SOURCES += \
+ jack/gri_jack.cc \
+ jack/audio_jack_source.cc \
+ jack/audio_jack_sink.cc
+
+noinst_HEADERS += \
+ jack/gri_jack.h \
+ jack/audio_jack_source.h \
+ jack/audio_jack_sink.h
+
+dist_etc_DATA += jack/gr-audio-jack.conf
+
+endif
+
+########################################################################
+## OSX Support
+########################################################################
+if GR_AUDIO_OSX_SUPPORT
+
+AM_CPPFLAGS += \
+ -I$(srcdir)/osx
+
+libgnuradio_audio_la_SOURCES += \
+ osx/audio_osx_source.cc \
+ osx/audio_osx_sink.cc
+
+noinst_HEADERS += \
+ osx/audio_osx.h \
+ osx/audio_osx_source.h \
+ osx/audio_osx_sink.h \
+ osx/circular_buffer.h
+
+endif
+
+########################################################################
+## PortAudio Support
+########################################################################
+if GR_AUDIO_PORTAUDIO_SUPPORT
+
+AM_CPPFLAGS += \
+ -I$(srcdir)/portaudio \
+ $(PORTAUDIO_CPPFLAGS)
+
+libgnuradio_audio_la_LIBADD += $(PORTAUDIO_LIBS)
+
+libgnuradio_audio_la_SOURCES += \
+ portaudio/gri_portaudio.cc \
+ portaudio/audio_portaudio_source.cc \
+ portaudio/audio_portaudio_sink.cc
+
+noinst_HEADERS += \
+ portaudio/gri_portaudio.h \
+ portaudio/audio_portaudio_source.h \
+ portaudio/audio_portaudio_sink.h
+
+dist_etc_DATA += portaudio/gr-audio-portaudio.conf
+
+endif
+
+########################################################################
+## Windows Support
+########################################################################
+if GR_AUDIO_WINDOWS_SUPPORT
+
+AM_CPPFLAGS += \
+ -I$(srcdir)/windows
+
+libgnuradio_audio_la_LIBADD += $(WINAUDIO_LIBS)
+
+libgnuradio_audio_la_SOURCES += \
+ windows/audio_windows_source.cc \
+ windows/audio_windows_sink.cc
+
+noinst_HEADERS += \
+ windows/audio_windows_source.h \
+ windows/audio_windows_sink.h
+
+endif
diff --git a/gr-audio/lib/alsa/audio_alsa_sink.cc b/gr-audio/lib/alsa/audio_alsa_sink.cc
new file mode 100644
index 000000000..5fd197ec7
--- /dev/null
+++ b/gr-audio/lib/alsa/audio_alsa_sink.cc
@@ -0,0 +1,548 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#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>
+
+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));
+}
+
+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::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 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)
+ {
+ 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;
+}
+
+
+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/lib/alsa/audio_alsa_sink.h b/gr-audio/lib/alsa/audio_alsa_sink.h
new file mode 100644
index 000000000..23e406d6b
--- /dev/null
+++ b/gr-audio/lib/alsa/audio_alsa_sink.h
@@ -0,0 +1,104 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, 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_audio_sink.h>
+#include <string>
+#include <alsa/asoundlib.h>
+#include <stdexcept>
+
+/*!
+ * \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 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);
+};
+
+#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
new file mode 100644
index 000000000..4f0042b22
--- /dev/null
+++ b/gr-audio/lib/alsa/audio_alsa_source.cc
@@ -0,0 +1,505 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#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>
+
+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));
+}
+
+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::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;
+
+ // 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/lib/alsa/audio_alsa_source.h b/gr-audio/lib/alsa/audio_alsa_source.h
new file mode 100644
index 000000000..e38af3872
--- /dev/null
+++ b/gr-audio/lib/alsa/audio_alsa_source.h
@@ -0,0 +1,106 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, 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_audio_source.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 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 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);
+};
+
+#endif /* INCLUDED_AUDIO_ALSA_SOURCE_H */
diff --git a/gr-audio/lib/alsa/gr-audio-alsa.conf b/gr-audio/lib/alsa/gr-audio-alsa.conf
new file mode 100644
index 000000000..5cec63e7a
--- /dev/null
+++ b/gr-audio/lib/alsa/gr-audio-alsa.conf
@@ -0,0 +1,11 @@
+# This file contains system wide configuration data for GNU Radio.
+# You may override any setting on a per-user basis by editing
+# ~/.gnuradio/config.conf
+
+[audio_alsa]
+
+default_input_device = hw:0,0
+default_output_device = hw:0,0
+period_time = 0.010 # in seconds
+nperiods = 4 # total buffering = period_time * nperiods
+verbose = false
diff --git a/gr-audio/lib/alsa/gri_alsa.cc b/gr-audio/lib/alsa/gri_alsa.cc
new file mode 100644
index 000000000..d9fda0f7d
--- /dev/null
+++ b/gr-audio/lib/alsa/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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, 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/lib/alsa/gri_alsa.h b/gr-audio/lib/alsa/gri_alsa.h
new file mode 100644
index 000000000..3d72fd950
--- /dev/null
+++ b/gr-audio/lib/alsa/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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, 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/lib/gr-audio.conf b/gr-audio/lib/gr-audio.conf
new file mode 100644
index 000000000..e0d9221c3
--- /dev/null
+++ b/gr-audio/lib/gr-audio.conf
@@ -0,0 +1,12 @@
+# This file contains system wide configuration data for GNU Radio.
+# You may override any setting on a per-user basis by editing
+# ~/.gnuradio/config.conf
+
+# specify which audio module to load, or use "auto" to have the system
+# select one. Valid choices depend on your OS and which modules
+# you've installed, but typically include:
+# auto, alsa, oss, portaudio, jack, osx, windows
+
+[audio]
+
+audio_module = auto
diff --git a/gr-audio/lib/gr_audio_registry.cc b/gr-audio/lib/gr_audio_registry.cc
new file mode 100644
index 000000000..da4b16b35
--- /dev/null
+++ b/gr-audio/lib/gr_audio_registry.cc
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "gr_audio_registry.h"
+#include <boost/foreach.hpp>
+#include <gr_prefs.h>
+#include <stdexcept>
+#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");
+ }
+
+ 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);
+}
+
+audio_sink::sptr audio_make_sink(
+ 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();
+
+ 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);
+}
diff --git a/gr-audio/lib/gr_audio_registry.h b/gr-audio/lib/gr_audio_registry.h
new file mode 100644
index 000000000..ec341e95e
--- /dev/null
+++ b/gr-audio/lib/gr_audio_registry.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_GR_AUDIO_REGISTRY_H
+#define INCLUDED_GR_AUDIO_REGISTRY_H
+
+#include <gr_audio_sink.h>
+#include <gr_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);
+
+enum reg_prio_type{
+ REG_PRIO_LOW = 100,
+ REG_PRIO_MED = 200,
+ REG_PRIO_HIGH = 300
+};
+
+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);
+
+#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
+
+#endif /* INCLUDED_GR_AUDIO_REGISTRY_H */
diff --git a/gr-audio/lib/jack/audio_jack_sink.cc b/gr-audio/lib/jack/audio_jack_sink.cc
new file mode 100644
index 000000000..f06e77bd5
--- /dev/null
+++ b/gr-audio/lib/jack/audio_jack_sink.cc
@@ -0,0 +1,236 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_jack_sink.h>
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <stdio.h>
+#include <iostream>
+#include <stdexcept>
+#include <gri_jack.h>
+
+#ifndef NO_PTHREAD
+#include <pthread.h>
+#endif
+
+AUDIO_REGISTER_SINK(REG_PRIO_MED, jack)(
+ int sampling_rate, const std::string &device_name, bool ok_to_block
+){
+ return audio_sink::sptr(new audio_jack_sink(sampling_rate, device_name, ok_to_block));
+}
+
+typedef jack_default_audio_sample_t sample_t;
+
+
+// Number of jack buffers in the ringbuffer
+// TODO: make it to match at least the quantity of items passed by work()
+static const unsigned int N_BUFFERS = 16;
+
+static std::string
+default_device_name ()
+{
+ return gr_prefs::singleton()->get_string("audio_jack", "default_output_device", "gr_sink");
+}
+
+int
+jack_sink_process (jack_nframes_t nframes, void *arg)
+{
+ audio_jack_sink *self = (audio_jack_sink *)arg;
+ unsigned int read_size = nframes*sizeof(sample_t);
+
+ if (jack_ringbuffer_read_space (self->d_ringbuffer) < read_size) {
+ self->d_nunderuns++;
+ // FIXME: move this fputs out, we shouldn't use blocking calls in process()
+ fputs ("jU", stderr);
+ return 0;
+ }
+
+ char *buffer = (char *) jack_port_get_buffer (self->d_jack_output_port, nframes);
+
+ jack_ringbuffer_read (self->d_ringbuffer, buffer, read_size);
+
+#ifndef NO_PTHREAD
+ // Tell the sink thread there is room in the ringbuffer.
+ // If it is already running, the lock will not be available.
+ // We can't wait here in the process() thread, but we don't
+ // need to signal in that case, because the sink thread will
+ // check for room availability.
+
+ if (pthread_mutex_trylock (&self->d_jack_process_lock) == 0) {
+ pthread_cond_signal (&self->d_ringbuffer_ready);
+ pthread_mutex_unlock (&self->d_jack_process_lock);
+ }
+#endif
+
+ return 0;
+}
+
+// ----------------------------------------------------------------
+
+audio_jack_sink::audio_jack_sink (int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block)
+ : gr_sync_block ("audio_jack_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_ok_to_block (ok_to_block),
+ d_jack_client (0),
+ d_ringbuffer (0),
+ d_nunderuns (0)
+{
+#ifndef NO_PTHREAD
+ pthread_cond_init(&d_ringbuffer_ready, NULL);;
+ pthread_mutex_init(&d_jack_process_lock, NULL);
+#endif
+
+ // try to become a client of the JACK server
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+ const char *server_name = NULL;
+ if ((d_jack_client = jack_client_open (d_device_name.c_str (),
+ options, &status,
+ server_name)) == NULL) {
+ fprintf (stderr, "audio_jack_sink[%s]: jack server not running?\n",
+ d_device_name.c_str());
+ throw std::runtime_error ("audio_jack_sink");
+ }
+
+ // tell the JACK server to call `jack_sink_process()' whenever
+ // there is work to be done.
+ jack_set_process_callback (d_jack_client, &jack_sink_process, (void*)this);
+
+ // tell the JACK server to call `jack_shutdown()' if
+ // it ever shuts down, either entirely, or if it
+ // just decides to stop calling us.
+
+ //jack_on_shutdown (d_jack_client, &jack_shutdown, (void*)this);
+
+ d_jack_output_port =
+ jack_port_register (d_jack_client, "out",
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+
+ d_jack_buffer_size = jack_get_buffer_size (d_jack_client);
+
+ set_output_multiple (d_jack_buffer_size);
+
+ d_ringbuffer =
+ jack_ringbuffer_create (N_BUFFERS*d_jack_buffer_size*sizeof(sample_t));
+ if (d_ringbuffer == NULL)
+ bail ("jack_ringbuffer_create failed", 0);
+
+ assert(sizeof(float)==sizeof(sample_t));
+ set_input_signature (gr_make_io_signature (1, 1, sizeof (sample_t)));
+
+
+ jack_nframes_t sample_rate = jack_get_sample_rate (d_jack_client);
+
+ if ((jack_nframes_t)sampling_rate != sample_rate){
+ fprintf (stderr, "audio_jack_sink[%s]: unable to support sampling rate %d\n",
+ d_device_name.c_str (), sampling_rate);
+ fprintf (stderr, " card requested %d instead.\n", sample_rate);
+ }
+}
+
+
+bool
+audio_jack_sink::check_topology (int ninputs, int noutputs)
+{
+ if (ninputs != 1)
+ return false;
+
+ // tell the JACK server that we are ready to roll
+ if (jack_activate (d_jack_client))
+ throw std::runtime_error ("audio_jack_sink");
+
+ return true;
+}
+
+audio_jack_sink::~audio_jack_sink ()
+{
+ jack_client_close (d_jack_client);
+ jack_ringbuffer_free (d_ringbuffer);
+}
+
+int
+audio_jack_sink::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ // write_size and work_size are in bytes
+ int work_size = noutput_items*sizeof(sample_t);
+ unsigned int write_size;
+
+ while (work_size > 0) {
+ unsigned int write_space; // bytes
+
+#ifdef NO_PTHREAD
+ while ((write_space=jack_ringbuffer_write_space (d_ringbuffer)) <
+ d_jack_buffer_size*sizeof(sample_t)) {
+ usleep(1000000*((d_jack_buffer_size-write_space/sizeof(sample_t))/d_sampling_rate));
+ }
+#else
+ // JACK actually requires POSIX
+
+ pthread_mutex_lock (&d_jack_process_lock);
+ while ((write_space=jack_ringbuffer_write_space (d_ringbuffer)) <
+ d_jack_buffer_size*sizeof(sample_t)) {
+
+ // wait until jack_sink_process() signals more room
+ pthread_cond_wait (&d_ringbuffer_ready, &d_jack_process_lock);
+ }
+ pthread_mutex_unlock (&d_jack_process_lock);
+#endif
+
+ write_space -= write_space%(d_jack_buffer_size*sizeof(sample_t));
+ write_size = std::min(write_space, (unsigned int)work_size);
+
+ if (jack_ringbuffer_write (d_ringbuffer, (char *) input_items[0],
+ write_size) < write_size) {
+ bail ("jack_ringbuffer_write failed", 0);
+ }
+ work_size -= write_size;
+ }
+
+ return noutput_items;
+}
+
+void
+audio_jack_sink::output_error_msg (const char *msg, int err)
+{
+ fprintf (stderr, "audio_jack_sink[%s]: %s: %d\n",
+ d_device_name.c_str (), msg, err);
+}
+
+void
+audio_jack_sink::bail (const char *msg, int err) throw (std::runtime_error)
+{
+ output_error_msg (msg, err);
+ throw std::runtime_error ("audio_jack_sink");
+}
diff --git a/gr-audio/lib/jack/audio_jack_sink.h b/gr-audio/lib/jack/audio_jack_sink.h
new file mode 100644
index 000000000..a11863ee0
--- /dev/null
+++ b/gr-audio/lib/jack/audio_jack_sink.h
@@ -0,0 +1,79 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_AUDIO_JACK_SINK_H
+#define INCLUDED_AUDIO_JACK_SINK_H
+
+#include <gr_audio_sink.h>
+#include <string>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <stdexcept>
+
+int jack_sink_process (jack_nframes_t nframes, void *arg);
+
+/*!
+ * \brief audio sink using JACK
+ *
+ * The sink has one input stream of floats.
+ *
+ * Input samples must be in the range [-1,1].
+ */
+class audio_jack_sink : public audio_sink {
+
+ friend int jack_sink_process (jack_nframes_t nframes, void *arg);
+
+ // typedef for pointer to class work method
+ typedef int (audio_jack_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;
+ bool d_ok_to_block;
+
+ jack_client_t *d_jack_client;
+ jack_port_t *d_jack_output_port;
+ jack_ringbuffer_t *d_ringbuffer;
+ jack_nframes_t d_jack_buffer_size;
+ pthread_cond_t d_ringbuffer_ready;
+ pthread_mutex_t d_jack_process_lock;
+
+ // random stats
+ int d_nunderuns; // count of underruns
+
+ void output_error_msg (const char *msg, int err);
+ void bail (const char *msg, int err) throw (std::runtime_error);
+
+
+public:
+ audio_jack_sink (int sampling_rate, const std::string device_name, bool ok_to_block);
+
+ ~audio_jack_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);
+};
+
+#endif /* INCLUDED_AUDIO_JACK_SINK_H */
diff --git a/gr-audio/lib/jack/audio_jack_source.cc b/gr-audio/lib/jack/audio_jack_source.cc
new file mode 100644
index 000000000..dcd41c3ac
--- /dev/null
+++ b/gr-audio/lib/jack/audio_jack_source.cc
@@ -0,0 +1,237 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005,2006,2010 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_jack_source.h>
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <stdio.h>
+#include <iostream>
+#include <stdexcept>
+#include <gri_jack.h>
+
+#ifndef NO_PTHREAD
+#include <pthread.h>
+#endif
+
+AUDIO_REGISTER_SOURCE(REG_PRIO_MED, jack)(
+ int sampling_rate, const std::string &device_name, bool ok_to_block
+){
+ return audio_source::sptr(new audio_jack_source(sampling_rate, device_name, ok_to_block));
+}
+
+typedef jack_default_audio_sample_t sample_t;
+
+
+// Number of jack buffers in the ringbuffer
+// TODO: make it to match at least the quantity of items passed to work()
+static const unsigned int N_BUFFERS = 16;
+
+static std::string
+default_device_name ()
+{
+ return gr_prefs::singleton()->get_string("audio_jack", "default_input_device", "gr_source");
+}
+
+
+int
+jack_source_process (jack_nframes_t nframes, void *arg)
+{
+ audio_jack_source *self = (audio_jack_source *)arg;
+ unsigned int write_size = nframes*sizeof(sample_t);
+
+ if (jack_ringbuffer_write_space (self->d_ringbuffer) < write_size) {
+ self->d_noverruns++;
+ // FIXME: move this fputs out, we shouldn't use blocking calls in process()
+ fputs ("jO", stderr);
+ return 0;
+ }
+
+ char *buffer = (char *) jack_port_get_buffer (self->d_jack_input_port, nframes);
+
+ jack_ringbuffer_write (self->d_ringbuffer, buffer, write_size);
+
+#ifndef NO_PTHREAD
+ // Tell the source thread there is data in the ringbuffer.
+ // If it is already running, the lock will not be available.
+ // We can't wait here in the process() thread, but we don't
+ // need to signal in that case, because the source thread will
+ // check for data availability.
+
+ if (pthread_mutex_trylock (&self->d_jack_process_lock) == 0) {
+ pthread_cond_signal (&self->d_ringbuffer_ready);
+ pthread_mutex_unlock (&self->d_jack_process_lock);
+ }
+#endif
+
+ return 0;
+}
+
+// ----------------------------------------------------------------
+
+audio_jack_source::audio_jack_source (int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block)
+ : gr_sync_block ("audio_jack_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_ok_to_block(ok_to_block),
+ d_jack_client (0),
+ d_ringbuffer (0),
+ d_noverruns (0)
+{
+#ifndef NO_PTHREAD
+ pthread_cond_init(&d_ringbuffer_ready, NULL);;
+ pthread_mutex_init(&d_jack_process_lock, NULL);
+#endif
+
+ // try to become a client of the JACK server
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+ const char *server_name = NULL;
+ if ((d_jack_client = jack_client_open (d_device_name.c_str (),
+ options, &status,
+ server_name)) == NULL) {
+ fprintf (stderr, "audio_jack_source[%s]: jack server not running?\n",
+ d_device_name.c_str());
+ throw std::runtime_error ("audio_jack_source");
+ }
+
+ // tell the JACK server to call `jack_source_process()' whenever
+ // there is work to be done.
+ jack_set_process_callback (d_jack_client, &jack_source_process, (void*)this);
+
+ // tell the JACK server to call `jack_shutdown()' if
+ // it ever shuts down, either entirely, or if it
+ // just decides to stop calling us.
+
+ //jack_on_shutdown (d_jack_client, &jack_shutdown, (void*)this);
+
+ d_jack_input_port = jack_port_register (d_jack_client, "in",
+ JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsInput, 0);
+
+
+ d_jack_buffer_size = jack_get_buffer_size (d_jack_client);
+
+ set_output_multiple (d_jack_buffer_size);
+
+ d_ringbuffer = jack_ringbuffer_create (N_BUFFERS*d_jack_buffer_size*sizeof(sample_t));
+ if (d_ringbuffer == NULL)
+ bail ("jack_ringbuffer_create failed", 0);
+
+ assert(sizeof(float)==sizeof(sample_t));
+ set_output_signature (gr_make_io_signature (1, 1, sizeof (sample_t)));
+
+
+ jack_nframes_t sample_rate = jack_get_sample_rate (d_jack_client);
+
+ if ((jack_nframes_t)sampling_rate != sample_rate){
+ fprintf (stderr, "audio_jack_source[%s]: unable to support sampling rate %d\n",
+ d_device_name.c_str (), sampling_rate);
+ fprintf (stderr, " card requested %d instead.\n", sample_rate);
+ }
+}
+
+
+bool
+audio_jack_source::check_topology (int ninputs, int noutputs)
+{
+ // tell the JACK server that we are ready to roll
+ if (jack_activate (d_jack_client))
+ throw std::runtime_error ("audio_jack_source");
+
+ return true;
+}
+
+audio_jack_source::~audio_jack_source ()
+{
+ jack_client_close (d_jack_client);
+ jack_ringbuffer_free (d_ringbuffer);
+}
+
+int
+audio_jack_source::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ // read_size and work_size are in bytes
+ unsigned int read_size;
+
+ // Minimize latency
+ noutput_items = std::min (noutput_items, (int)d_jack_buffer_size);
+
+ int work_size = noutput_items*sizeof(sample_t);
+
+ while (work_size > 0) {
+ unsigned int read_space; // bytes
+
+#ifdef NO_PTHREAD
+ while ((read_space=jack_ringbuffer_read_space (d_ringbuffer)) <
+ d_jack_buffer_size*sizeof(sample_t)) {
+ usleep(1000000*((d_jack_buffer_size-read_space/sizeof(sample_t))/d_sampling_rate));
+ }
+#else
+ // JACK actually requires POSIX
+
+ pthread_mutex_lock (&d_jack_process_lock);
+ while ((read_space=jack_ringbuffer_read_space (d_ringbuffer)) <
+ d_jack_buffer_size*sizeof(sample_t)) {
+
+ // wait until jack_source_process() signals more data
+ pthread_cond_wait (&d_ringbuffer_ready, &d_jack_process_lock);
+ }
+ pthread_mutex_unlock (&d_jack_process_lock);
+#endif
+
+ read_space -= read_space%(d_jack_buffer_size*sizeof(sample_t));
+ read_size = std::min(read_space, (unsigned int)work_size);
+
+ if (jack_ringbuffer_read (d_ringbuffer, (char *) output_items[0],
+ read_size) < read_size) {
+ bail ("jack_ringbuffer_read failed", 0);
+ }
+ work_size -= read_size;
+ }
+
+ return noutput_items;
+}
+
+void
+audio_jack_source::output_error_msg (const char *msg, int err)
+{
+ fprintf (stderr, "audio_jack_source[%s]: %s: %d\n",
+ d_device_name.c_str (), msg, err);
+}
+
+void
+audio_jack_source::bail (const char *msg, int err) throw (std::runtime_error)
+{
+ output_error_msg (msg, err);
+ throw std::runtime_error ("audio_jack_source");
+}
diff --git a/gr-audio/lib/jack/audio_jack_source.h b/gr-audio/lib/jack/audio_jack_source.h
new file mode 100644
index 000000000..858f34528
--- /dev/null
+++ b/gr-audio/lib/jack/audio_jack_source.h
@@ -0,0 +1,79 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_AUDIO_JACK_SOURCE_H
+#define INCLUDED_AUDIO_JACK_SOURCE_H
+
+#include <gr_audio_source.h>
+#include <string>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <stdexcept>
+
+int jack_source_process (jack_nframes_t nframes, void *arg);
+
+/*!
+ * \brief audio source using JACK
+ *
+ * The source has one input stream of floats.
+ *
+ * Output samples will be in the range [-1,1].
+ */
+class audio_jack_source : public audio_source {
+
+ friend int jack_source_process (jack_nframes_t nframes, void *arg);
+
+ // typedef for pointer to class work method
+ typedef int (audio_jack_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;
+ bool d_ok_to_block;
+
+ jack_client_t *d_jack_client;
+ jack_port_t *d_jack_input_port;
+ jack_ringbuffer_t *d_ringbuffer;
+ jack_nframes_t d_jack_buffer_size;
+ pthread_cond_t d_ringbuffer_ready;
+ pthread_mutex_t d_jack_process_lock;
+
+ // random stats
+ int d_noverruns; // count of overruns
+
+ void output_error_msg (const char *msg, int err);
+ void bail (const char *msg, int err) throw (std::runtime_error);
+
+
+public:
+ audio_jack_source (int sampling_rate, const std::string device_name, bool ok_to_block);
+
+ ~audio_jack_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);
+};
+
+#endif /* INCLUDED_AUDIO_JACK_SOURCE_H */
diff --git a/gr-audio/lib/jack/gr-audio-jack.conf b/gr-audio/lib/jack/gr-audio-jack.conf
new file mode 100644
index 000000000..bdbc1fd1d
--- /dev/null
+++ b/gr-audio/lib/jack/gr-audio-jack.conf
@@ -0,0 +1,8 @@
+# This file contains system wide configuration data for GNU Radio.
+# You may override any setting on a per-user basis by editing
+# ~/.gnuradio/config.conf
+
+[audio_jack]
+
+default_input_device = gr_source
+default_output_device = gr_sink
diff --git a/gr-audio/lib/jack/gri_jack.cc b/gr-audio/lib/jack/gri_jack.cc
new file mode 100644
index 000000000..fef1c58a6
--- /dev/null
+++ b/gr-audio/lib/jack/gri_jack.cc
@@ -0,0 +1,30 @@
+/* -*- c++ -*- */
+/*
+ * 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gri_jack.h>
+#include <algorithm>
+
+
diff --git a/gr-audio/lib/jack/gri_jack.h b/gr-audio/lib/jack/gri_jack.h
new file mode 100644
index 000000000..ddc0b744d
--- /dev/null
+++ b/gr-audio/lib/jack/gri_jack.h
@@ -0,0 +1,28 @@
+/* -*- c++ -*- */
+/*
+ * 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_GRI_JACK_H
+#define INCLUDED_GRI_JACK_H
+
+#include <stdio.h>
+
+#endif /* INCLUDED_GRI_JACK_H */
diff --git a/gr-audio/lib/oss/audio_oss_sink.cc b/gr-audio/lib/oss/audio_oss_sink.cc
new file mode 100644
index 000000000..34af16cce
--- /dev/null
+++ b/gr-audio/lib/oss/audio_oss_sink.cc
@@ -0,0 +1,161 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_oss_sink.h>
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <iostream>
+#include <stdexcept>
+
+AUDIO_REGISTER_SINK(REG_PRIO_LOW, oss)(
+ int sampling_rate, const std::string &device_name, bool ok_to_block
+){
+ return audio_sink::sptr(new audio_oss_sink(sampling_rate, device_name, ok_to_block));
+}
+
+static std::string
+default_device_name ()
+{
+ return gr_prefs::singleton()->get_string("audio_oss", "default_output_device", "/dev/dsp");
+}
+
+audio_oss_sink::audio_oss_sink (int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block)
+ : gr_sync_block ("audio_oss_sink",
+ gr_make_io_signature (1, 2, sizeof (float)),
+ gr_make_io_signature (0, 0, 0)),
+ d_sampling_rate (sampling_rate),
+ d_device_name (device_name.empty() ? default_device_name() : device_name),
+ d_fd (-1), d_buffer (0), d_chunk_size (0)
+{
+ if ((d_fd = open (d_device_name.c_str (), O_WRONLY)) < 0){
+ fprintf (stderr, "audio_oss_sink: ");
+ perror (d_device_name.c_str ());
+ throw std::runtime_error ("audio_oss_sink");
+ }
+
+ double CHUNK_TIME =
+ std::max(0.001, gr_prefs::singleton()->get_double("audio_oss", "latency", 0.005));
+
+ d_chunk_size = (int) (d_sampling_rate * CHUNK_TIME);
+ set_output_multiple (d_chunk_size);
+
+ d_buffer = new short [d_chunk_size * 2];
+
+ int format = AFMT_S16_NE;
+ int orig_format = format;
+ if (ioctl (d_fd, SNDCTL_DSP_SETFMT, &format) < 0){
+ std::cerr << "audio_oss_sink: " << d_device_name << " ioctl failed\n";
+ perror (d_device_name.c_str ());
+ throw std::runtime_error ("audio_oss_sink");
+ }
+
+ if (format != orig_format){
+ fprintf (stderr, "audio_oss_sink: unable to support format %d\n", orig_format);
+ fprintf (stderr, " card requested %d instead.\n", format);
+ }
+
+ // set to stereo no matter what. Some hardware only does stereo
+ int channels = 2;
+ if (ioctl (d_fd, SNDCTL_DSP_CHANNELS, &channels) < 0 || channels != 2){
+ perror ("audio_oss_sink: could not set STEREO mode");
+ throw std::runtime_error ("audio_oss_sink");
+ }
+
+ // set sampling freq
+ int sf = sampling_rate;
+ if (ioctl (d_fd, SNDCTL_DSP_SPEED, &sf) < 0){
+ std::cerr << "audio_oss_sink: "
+ << d_device_name << ": invalid sampling_rate "
+ << sampling_rate << "\n";
+ sampling_rate = 8000;
+ if (ioctl (d_fd, SNDCTL_DSP_SPEED, &sf) < 0){
+ std::cerr << "audio_oss_sink: failed to set sampling_rate to 8000\n";
+ throw std::runtime_error ("audio_oss_sink");
+ }
+ }
+}
+
+audio_oss_sink::~audio_oss_sink ()
+{
+ close (d_fd);
+ delete [] d_buffer;
+}
+
+
+int
+audio_oss_sink::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ const float *f0, *f1;
+
+ switch (input_items.size ()){
+
+ case 1: // mono input
+
+ f0 = (const float *) input_items[0];
+
+ for (int i = 0; i < noutput_items; i += d_chunk_size){
+ for (int j = 0; j < d_chunk_size; j++){
+ d_buffer[2*j+0] = (short) (f0[j] * 32767);
+ d_buffer[2*j+1] = (short) (f0[j] * 32767);
+ }
+ f0 += d_chunk_size;
+ if (write (d_fd, d_buffer, 2 * d_chunk_size * sizeof (short)) < 0)
+ perror ("audio_oss_sink: write");
+ }
+ break;
+
+ case 2: // stereo input
+
+ f0 = (const float *) input_items[0];
+ f1 = (const float *) input_items[1];
+
+ for (int i = 0; i < noutput_items; i += d_chunk_size){
+ for (int j = 0; j < d_chunk_size; j++){
+ d_buffer[2*j+0] = (short) (f0[j] * 32767);
+ d_buffer[2*j+1] = (short) (f1[j] * 32767);
+ }
+ f0 += d_chunk_size;
+ f1 += d_chunk_size;
+ if (write (d_fd, d_buffer, 2 * d_chunk_size * sizeof (short)) < 0)
+ perror ("audio_oss_sink: write");
+ }
+ break;
+ }
+
+ return noutput_items;
+}
diff --git a/gr-audio/lib/oss/audio_oss_sink.h b/gr-audio/lib/oss/audio_oss_sink.h
new file mode 100644
index 000000000..0d7280c2f
--- /dev/null
+++ b/gr-audio/lib/oss/audio_oss_sink.h
@@ -0,0 +1,54 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_OSS_SINK_H
+#define INCLUDED_AUDIO_OSS_SINK_H
+
+#include <gr_audio_sink.h>
+#include <string>
+
+/*!
+ * \brief audio sink using OSS
+ *
+ * input signature is one or two streams of floats.
+ * Input samples must be in the range [-1,1].
+ */
+
+class audio_oss_sink : public audio_sink {
+
+ int d_sampling_rate;
+ std::string d_device_name;
+ int d_fd;
+ short *d_buffer;
+ int d_chunk_size;
+
+public:
+ audio_oss_sink (int sampling_rate, const std::string device_name = "", bool ok_to_block = true);
+
+ ~audio_oss_sink ();
+
+ int work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+#endif /* INCLUDED_AUDIO_OSS_SINK_H */
diff --git a/gr-audio/lib/oss/audio_oss_source.cc b/gr-audio/lib/oss/audio_oss_source.cc
new file mode 100644
index 000000000..eefe4dd04
--- /dev/null
+++ b/gr-audio/lib/oss/audio_oss_source.cc
@@ -0,0 +1,177 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_oss_source.h>
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <iostream>
+#include <stdexcept>
+
+AUDIO_REGISTER_SOURCE(REG_PRIO_LOW, oss)(
+ int sampling_rate, const std::string &device_name, bool ok_to_block
+){
+ return audio_source::sptr(new audio_oss_source(sampling_rate, device_name, ok_to_block));
+}
+
+static std::string
+default_device_name ()
+{
+ return gr_prefs::singleton()->get_string("audio_oss", "default_input_device", "/dev/dsp");
+}
+
+audio_oss_source::audio_oss_source (int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block)
+ : gr_sync_block ("audio_oss_source",
+ gr_make_io_signature (0, 0, 0),
+ gr_make_io_signature (1, 2, sizeof (float))),
+ d_sampling_rate (sampling_rate),
+ d_device_name (device_name.empty() ? default_device_name() : device_name),
+ d_fd (-1), d_buffer (0), d_chunk_size (0)
+{
+ if ((d_fd = open (d_device_name.c_str (), O_RDONLY)) < 0){
+ fprintf (stderr, "audio_oss_source: ");
+ perror (d_device_name.c_str ());
+ throw std::runtime_error ("audio_oss_source");
+ }
+
+ double CHUNK_TIME =
+ std::max(0.001, gr_prefs::singleton()->get_double("audio_oss", "latency", 0.005));
+
+ d_chunk_size = (int) (d_sampling_rate * CHUNK_TIME);
+ set_output_multiple (d_chunk_size);
+
+ d_buffer = new short [d_chunk_size * 2];
+
+ int format = AFMT_S16_NE;
+ int orig_format = format;
+ if (ioctl (d_fd, SNDCTL_DSP_SETFMT, &format) < 0){
+ std::cerr << "audio_oss_source: " << d_device_name << " ioctl failed\n";
+ perror (d_device_name.c_str ());
+ throw std::runtime_error ("audio_oss_source");
+ }
+
+ if (format != orig_format){
+ fprintf (stderr, "audio_oss_source: unable to support format %d\n", orig_format);
+ fprintf (stderr, " card requested %d instead.\n", format);
+ }
+
+ // set to stereo no matter what. Some hardware only does stereo
+ int channels = 2;
+ if (ioctl (d_fd, SNDCTL_DSP_CHANNELS, &channels) < 0 || channels != 2){
+ perror ("audio_oss_source: could not set STEREO mode");
+ throw std::runtime_error ("audio_oss_source");
+ }
+
+ // set sampling freq
+ int sf = sampling_rate;
+ if (ioctl (d_fd, SNDCTL_DSP_SPEED, &sf) < 0){
+ std::cerr << "audio_oss_source: "
+ << d_device_name << ": invalid sampling_rate "
+ << sampling_rate << "\n";
+ sampling_rate = 8000;
+ if (ioctl (d_fd, SNDCTL_DSP_SPEED, &sf) < 0){
+ std::cerr << "audio_oss_source: failed to set sampling_rate to 8000\n";
+ throw std::runtime_error ("audio_oss_source");
+ }
+ }
+}
+
+audio_oss_source::~audio_oss_source ()
+{
+ close (d_fd);
+ delete [] d_buffer;
+}
+
+int
+audio_oss_source::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ float *f0 = (float *) output_items[0];
+ float *f1 = (float *) output_items[1]; // will be invalid if this is mono output
+
+ const int shorts_per_item = 2; // L + R
+ const int bytes_per_item = shorts_per_item * sizeof (short);
+
+ // To minimize latency, never return more than CHUNK_TIME
+ // worth of samples per call to work.
+
+ noutput_items = std::min (noutput_items, d_chunk_size);
+
+ int base = 0;
+ int ntogo = noutput_items;
+
+ while (ntogo > 0){
+ int nbytes = std::min (ntogo, d_chunk_size) * bytes_per_item;
+ int result_nbytes = read (d_fd, d_buffer, nbytes);
+
+ if (result_nbytes < 0){
+ perror ("audio_oss_source");
+ return -1; // say we're done
+ }
+
+ if ((result_nbytes & (bytes_per_item - 1)) != 0){
+ fprintf (stderr, "audio_oss_source: internal error.\n");
+ throw std::runtime_error ("internal error");
+ }
+
+ int result_nitems = result_nbytes / bytes_per_item;
+
+ // now unpack samples into output streams
+
+ switch (output_items.size ()){
+ case 1: // mono output
+ for (int i = 0; i < result_nitems; i++){
+ f0[base+i] = d_buffer[2*i+0] * (1.0 / 32767);
+ }
+ break;
+
+ case 2: // stereo output
+ for (int i = 0; i < result_nitems; i++){
+ f0[base+i] = d_buffer[2*i+0] * (1.0 / 32767);
+ f1[base+i] = d_buffer[2*i+1] * (1.0 / 32767);
+ }
+ break;
+
+ default:
+ assert (0);
+ }
+
+ ntogo -= result_nitems;
+ base += result_nitems;
+ }
+
+ return noutput_items - ntogo;
+}
diff --git a/gr-audio/lib/oss/audio_oss_source.h b/gr-audio/lib/oss/audio_oss_source.h
new file mode 100644
index 000000000..b20ef5c05
--- /dev/null
+++ b/gr-audio/lib/oss/audio_oss_source.h
@@ -0,0 +1,58 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_OSS_SOURCE_H
+#define INCLUDED_AUDIO_OSS_SOURCE_H
+
+#include <gr_audio_source.h>
+#include <string>
+
+/*!
+ * \brief audio source using OSS
+ *
+ * Output signature is one or two streams of floats.
+ * Output samples will be in the range [-1,1].
+ */
+
+class audio_oss_source : public audio_source {
+
+ int d_sampling_rate;
+ std::string d_device_name;
+ int d_fd;
+ short *d_buffer;
+ int d_chunk_size;
+
+public:
+ audio_oss_source (int sampling_rate,
+ const std::string device_name = "",
+ bool ok_to_block = true);
+
+ ~audio_oss_source ();
+
+ int work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+
+
+#endif /* INCLUDED_AUDIO_OSS_SOURCE_H */
diff --git a/gr-audio/lib/oss/gr-audio-oss.conf b/gr-audio/lib/oss/gr-audio-oss.conf
new file mode 100644
index 000000000..6ea14d67e
--- /dev/null
+++ b/gr-audio/lib/oss/gr-audio-oss.conf
@@ -0,0 +1,9 @@
+# This file contains system wide configuration data for GNU Radio.
+# You may override any setting on a per-user basis by editing
+# ~/.gnuradio/config.conf
+
+[audio_oss]
+
+default_input_device = /dev/dsp
+default_output_device = /dev/dsp
+latency = 0.005 # in seconds
diff --git a/gr-audio/lib/osx/audio_osx.h b/gr-audio/lib/osx/audio_osx.h
new file mode 100644
index 000000000..79e79e36c
--- /dev/null
+++ b/gr-audio/lib/osx/audio_osx.h
@@ -0,0 +1,71 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_OSX_H
+#define INCLUDED_AUDIO_OSX_H
+
+#include <iostream>
+#include <string.h>
+
+#define CheckErrorAndThrow(err,what,throw_str) \
+ if (err) { \
+ OSStatus error = static_cast<OSStatus>(err); \
+ char err_str[4]; \
+ strncpy (err_str, (char*)(&err), 4); \
+ std::cerr << what << std::endl; \
+ std::cerr << " Error# " << error << " ('" << err_str \
+ << "')" << std::endl; \
+ std::cerr << " " << __FILE__ << ":" << __LINE__ << std::endl; \
+ fflush (stderr); \
+ throw std::runtime_error (throw_str); \
+ }
+
+#define CheckError(err,what) \
+ if (err) { \
+ OSStatus error = static_cast<OSStatus>(err); \
+ char err_str[4]; \
+ strncpy (err_str, (char*)(&err), 4); \
+ std::cerr << what << std::endl; \
+ std::cerr << " Error# " << error << " ('" << err_str \
+ << "')" << std::endl; \
+ std::cerr << " " << __FILE__ << ":" << __LINE__ << std::endl; \
+ fflush (stderr); \
+ }
+
+#ifdef WORDS_BIGENDIAN
+#define GR_PCM_ENDIANNESS kLinearPCMFormatFlagIsBigEndian
+#else
+#define GR_PCM_ENDIANNESS 0
+#endif
+
+// Check the version of MacOSX being used
+#ifdef __APPLE_CC__
+#include <AvailabilityMacros.h>
+#ifndef MAC_OS_X_VERSION_10_6
+#define MAC_OS_X_VERSION_10_6 1060
+#endif
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
+#define GR_USE_OLD_AUDIO_UNIT
+#endif
+#endif
+
+#endif /* INCLUDED_AUDIO_OSX_H */
diff --git a/gr-audio/lib/osx/audio_osx_sink.cc b/gr-audio/lib/osx/audio_osx_sink.cc
new file mode 100644
index 000000000..901881da6
--- /dev/null
+++ b/gr-audio/lib/osx/audio_osx_sink.cc
@@ -0,0 +1,404 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_osx_sink.h>
+#include <gr_io_signature.h>
+#include <stdexcept>
+#include <audio_osx.h>
+
+#define _OSX_AU_DEBUG_ 0
+
+AUDIO_REGISTER_SINK(REG_PRIO_HIGH, osx)(
+ int sampling_rate, const std::string &device_name, bool ok_to_block
+){
+ return audio_sink::sptr(new audio_osx_sink(sampling_rate, device_name, ok_to_block));
+}
+
+audio_osx_sink::audio_osx_sink (int sample_rate,
+ const std::string device_name,
+ bool do_block,
+ int channel_config,
+ int max_sample_count)
+ : gr_sync_block ("audio_osx_sink",
+ gr_make_io_signature (0, 0, 0),
+ gr_make_io_signature (0, 0, 0)),
+ d_sample_rate (0.0), d_channel_config (0), d_n_channels (0),
+ d_queueSampleCount (0), d_max_sample_count (0),
+ d_do_block (do_block), d_internal (0), d_cond_data (0),
+ d_OutputAU (0)
+{
+ if (sample_rate <= 0) {
+ std::cerr << "Invalid Sample Rate: " << sample_rate << std::endl;
+ throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
+ } else
+ d_sample_rate = (Float64) sample_rate;
+
+ if (channel_config <= 0 & channel_config != -1) {
+ std::cerr << "Invalid Channel Config: " << channel_config << std::endl;
+ throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
+ } else if (channel_config == -1) {
+// no user input; try "device name" instead
+ int l_n_channels = (int) strtol (device_name.data(), (char **)NULL, 10);
+ if (l_n_channels == 0 & errno) {
+ std::cerr << "Error Converting Device Name: " << errno << std::endl;
+ throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
+ }
+ if (l_n_channels <= 0)
+ channel_config = 2;
+ else
+ channel_config = l_n_channels;
+ }
+
+ d_n_channels = d_channel_config = channel_config;
+
+// set the input signature
+
+ set_input_signature (gr_make_io_signature (1, d_n_channels, sizeof (float)));
+
+// check that the max # of samples to store is valid
+
+ if (max_sample_count == -1)
+ max_sample_count = sample_rate;
+ else if (max_sample_count <= 0) {
+ std::cerr << "Invalid Max Sample Count: " << max_sample_count << std::endl;
+ throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
+ }
+
+ d_max_sample_count = max_sample_count;
+
+// allocate the output circular buffer(s), one per channel
+
+ d_buffers = (circular_buffer<float>**) new
+ circular_buffer<float>* [d_n_channels];
+ UInt32 n_alloc = (UInt32) ceil ((double) d_max_sample_count);
+ for (UInt32 n = 0; n < d_n_channels; n++) {
+ d_buffers[n] = new circular_buffer<float> (n_alloc, false, false);
+ }
+
+// create the default AudioUnit for output
+ OSStatus err = noErr;
+
+// Open the default output unit
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ AudioComponentDescription desc;
+#else
+ ComponentDescription desc;
+#endif
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ AudioComponent comp = AudioComponentFindNext(NULL, &desc);
+ if (comp == NULL) {
+ std::cerr << "AudioComponentFindNext Error" << std::endl;
+ throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
+ }
+#else
+ Component comp = FindNextComponent (NULL, &desc);
+ if (comp == NULL) {
+ std::cerr << "FindNextComponent Error" << std::endl;
+ throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
+ }
+#endif
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ err = AudioComponentInstanceNew (comp, &d_OutputAU);
+ CheckErrorAndThrow (err, "AudioComponentInstanceNew", "audio_osx_sink::audio_osx_sink");
+#else
+ err = OpenAComponent (comp, &d_OutputAU);
+ CheckErrorAndThrow (err, "OpenAComponent", "audio_osx_sink::audio_osx_sink");
+#endif
+
+// Set up a callback function to generate output to the output unit
+
+ AURenderCallbackStruct input;
+ input.inputProc = (AURenderCallback)(audio_osx_sink::AUOutputCallback);
+ input.inputProcRefCon = this;
+
+ err = AudioUnitSetProperty (d_OutputAU,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input,
+ 0,
+ &input,
+ sizeof (input));
+ CheckErrorAndThrow (err, "AudioUnitSetProperty Render Callback", "audio_osx_sink::audio_osx_sink");
+
+// tell the Output Unit what format data will be supplied to it
+// so that it handles any format conversions
+
+ AudioStreamBasicDescription streamFormat;
+ streamFormat.mSampleRate = (Float64)(sample_rate);
+ streamFormat.mFormatID = kAudioFormatLinearPCM;
+ streamFormat.mFormatFlags = (kLinearPCMFormatFlagIsFloat |
+ GR_PCM_ENDIANNESS |
+ kLinearPCMFormatFlagIsPacked |
+ kAudioFormatFlagIsNonInterleaved);
+ streamFormat.mBytesPerPacket = 4;
+ streamFormat.mFramesPerPacket = 1;
+ streamFormat.mBytesPerFrame = 4;
+ streamFormat.mChannelsPerFrame = d_n_channels;
+ streamFormat.mBitsPerChannel = 32;
+
+ err = AudioUnitSetProperty (d_OutputAU,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ 0,
+ &streamFormat,
+ sizeof (AudioStreamBasicDescription));
+ CheckErrorAndThrow (err, "AudioUnitSetProperty StreamFormat", "audio_osx_sink::audio_osx_sink");
+
+// create the stuff to regulate I/O
+
+ d_cond_data = new gruel::condition_variable ();
+ if (d_cond_data == NULL)
+ CheckErrorAndThrow (errno, "new condition (data)",
+ "audio_osx_sink::audio_osx_sink");
+
+ d_internal = new gruel::mutex ();
+ if (d_internal == NULL)
+ CheckErrorAndThrow (errno, "new mutex (internal)",
+ "audio_osx_sink::audio_osx_sink");
+
+// initialize the AU for output
+
+ err = AudioUnitInitialize (d_OutputAU);
+ CheckErrorAndThrow (err, "AudioUnitInitialize",
+ "audio_osx_sink::audio_osx_sink");
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "audio_osx_sink Parameters:" << std::endl;
+ std::cerr << " Sample Rate is " << d_sample_rate << std::endl;
+ std::cerr << " Number of Channels is " << d_n_channels << std::endl;
+ std::cerr << " Max # samples to store per channel is " << d_max_sample_count << std::endl;
+#endif
+}
+
+bool audio_osx_sink::IsRunning ()
+{
+ UInt32 AURunning = 0, AUSize = sizeof (UInt32);
+
+ OSStatus err = AudioUnitGetProperty (d_OutputAU,
+ kAudioOutputUnitProperty_IsRunning,
+ kAudioUnitScope_Global,
+ 0,
+ &AURunning,
+ &AUSize);
+ CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning",
+ "audio_osx_sink::IsRunning");
+
+ return (AURunning);
+}
+
+bool audio_osx_sink::start ()
+{
+ if (! IsRunning ()) {
+ OSStatus err = AudioOutputUnitStart (d_OutputAU);
+ CheckErrorAndThrow (err, "AudioOutputUnitStart", "audio_osx_sink::start");
+ }
+
+ return (true);
+}
+
+bool audio_osx_sink::stop ()
+{
+ if (IsRunning ()) {
+ OSStatus err = AudioOutputUnitStop (d_OutputAU);
+ CheckErrorAndThrow (err, "AudioOutputUnitStop", "audio_osx_sink::stop");
+
+ for (UInt32 n = 0; n < d_n_channels; n++) {
+ d_buffers[n]->abort ();
+ }
+ }
+
+ return (true);
+}
+
+audio_osx_sink::~audio_osx_sink ()
+{
+// stop and close the AudioUnit
+ stop ();
+ AudioUnitUninitialize (d_OutputAU);
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ AudioComponentInstanceDispose (d_OutputAU);
+#else
+ CloseComponent (d_OutputAU);
+#endif
+
+// empty and delete the queues
+ for (UInt32 n = 0; n < d_n_channels; n++) {
+ delete d_buffers[n];
+ d_buffers[n] = 0;
+ }
+ delete [] d_buffers;
+ d_buffers = 0;
+
+// close and delete control stuff
+ delete d_cond_data;
+ d_cond_data = 0;
+ delete d_internal;
+ d_internal = 0;
+}
+
+int
+audio_osx_sink::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ gruel::scoped_lock l (*d_internal);
+
+ /* take the input data, copy it, and push it to the bottom of the queue
+ mono input are pushed onto queue[0];
+ stereo input are pushed onto queue[1].
+ Start the AudioUnit if necessary. */
+
+ UInt32 l_max_count;
+ int diff_count = d_max_sample_count - noutput_items;
+ if (diff_count < 0)
+ l_max_count = 0;
+ else
+ l_max_count = (UInt32) diff_count;
+
+#if 0
+ if (l_max_count < d_queueItemLength->back()) {
+// allow 2 buffers at a time, regardless of length
+ l_max_count = d_queueItemLength->back();
+ }
+#endif
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "work1: qSC = " << d_queueSampleCount << ", lMC = "<< l_max_count
+ << ", dmSC = " << d_max_sample_count << ", nOI = " << noutput_items << std::endl;
+#endif
+
+ if (d_queueSampleCount > l_max_count) {
+// data coming in too fast; do_block decides what to do
+ if (d_do_block == true) {
+// block until there is data to return
+ while (d_queueSampleCount > l_max_count) {
+// release control so-as to allow data to be retrieved;
+// block until there is data to return
+ d_cond_data->wait (l);
+// the condition's 'notify' was called; acquire control
+// to keep thread safe
+ }
+ }
+ }
+// not blocking case and overflow is handled by the circular buffer
+
+// add the input frames to the buffers' queue, checking for overflow
+
+ UInt32 l_counter;
+ int res = 0;
+ float* inBuffer = (float*) input_items[0];
+ const UInt32 l_size = input_items.size();
+ for (l_counter = 0; l_counter < l_size; l_counter++) {
+ inBuffer = (float*) input_items[l_counter];
+ int l_res = d_buffers[l_counter]->enqueue (inBuffer,
+ noutput_items);
+ if (l_res == -1)
+ res = -1;
+ }
+ while (l_counter < d_n_channels) {
+// for extra channels, copy the last input's data
+ int l_res = d_buffers[l_counter++]->enqueue (inBuffer,
+ noutput_items);
+ if (l_res == -1)
+ res = -1;
+ }
+
+ if (res == -1) {
+// data coming in too fast
+// drop oldest buffer
+ fputs ("aO", stderr);
+ fflush (stderr);
+// set the local number of samples available to the max
+ d_queueSampleCount = d_buffers[0]->buffer_length_items ();
+ } else {
+// keep up the local sample count
+ d_queueSampleCount += noutput_items;
+ }
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "work2: #OI = " << noutput_items << ", #Cnt = "
+ << d_queueSampleCount << ", mSC = " << d_max_sample_count << std::endl;
+#endif
+
+ return (noutput_items);
+}
+
+OSStatus audio_osx_sink::AUOutputCallback
+(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData)
+{
+ audio_osx_sink* This = (audio_osx_sink*) inRefCon;
+ OSStatus err = noErr;
+
+ gruel::scoped_lock l (*This->d_internal);
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cb_in: SC = " << This->d_queueSampleCount
+ << ", in#F = " << inNumberFrames << std::endl;
+#endif
+
+ if (This->d_queueSampleCount < inNumberFrames) {
+// not enough data to fill request
+ err = -1;
+ } else {
+// enough data; remove data from our buffers into the AU's buffers
+ int l_counter = This->d_n_channels;
+
+ while (--l_counter >= 0) {
+ size_t t_n_output_items = inNumberFrames;
+ float* outBuffer = (float*) ioData->mBuffers[l_counter].mData;
+ This->d_buffers[l_counter]->dequeue (outBuffer, &t_n_output_items);
+ if (t_n_output_items != inNumberFrames) {
+ throw std::runtime_error ("audio_osx_sink::AUOutputCallback(): "
+ "number of available items changing "
+ "unexpectedly.\n");
+ }
+ }
+
+ This->d_queueSampleCount -= inNumberFrames;
+ }
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cb_out: SC = " << This->d_queueSampleCount << std::endl;
+#endif
+
+// signal that data is available
+ This->d_cond_data->notify_one ();
+
+ return (err);
+}
diff --git a/gr-audio/lib/osx/audio_osx_sink.h b/gr-audio/lib/osx/audio_osx_sink.h
new file mode 100644
index 000000000..13bd95d53
--- /dev/null
+++ b/gr-audio/lib/osx/audio_osx_sink.h
@@ -0,0 +1,79 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_OSX_SINK_H
+#define INCLUDED_AUDIO_OSX_SINK_H
+
+#include <gr_audio_sink.h>
+#include <string>
+#include <list>
+#include <AudioUnit/AudioUnit.h>
+#include <circular_buffer.h>
+
+/*!
+ * \brief audio sink using OSX
+ *
+ * input signature is one or two streams of floats.
+ * Input samples must be in the range [-1,1].
+ */
+
+class audio_osx_sink : public audio_sink {
+
+ Float64 d_sample_rate;
+ int d_channel_config;
+ UInt32 d_n_channels;
+ UInt32 d_queueSampleCount, d_max_sample_count;
+ bool d_do_block;
+ gruel::mutex* d_internal;
+ gruel::condition_variable* d_cond_data;
+ circular_buffer<float>** d_buffers;
+
+// AudioUnits and Such
+ AudioUnit d_OutputAU;
+
+public:
+ audio_osx_sink (int sample_rate = 44100,
+ const std::string device_name = "2",
+ bool do_block = true,
+ int channel_config = -1,
+ int max_sample_count = -1);
+
+ ~audio_osx_sink ();
+
+ bool IsRunning ();
+ bool start ();
+ bool stop ();
+
+ int work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+
+private:
+ static OSStatus AUOutputCallback (void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData);
+};
+
+#endif /* INCLUDED_AUDIO_OSX_SINK_H */
diff --git a/gr-audio/lib/osx/audio_osx_source.cc b/gr-audio/lib/osx/audio_osx_source.cc
new file mode 100644
index 000000000..61f8eb4a6
--- /dev/null
+++ b/gr-audio/lib/osx/audio_osx_source.cc
@@ -0,0 +1,1016 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_osx_source.h>
+#include <gr_io_signature.h>
+#include <stdexcept>
+#include <audio_osx.h>
+
+#define _OSX_AU_DEBUG_ 0
+#define _OSX_DO_LISTENERS_ 0
+
+AUDIO_REGISTER_SOURCE(REG_PRIO_HIGH, osx)(
+ int sampling_rate, const std::string &device_name, bool ok_to_block
+){
+ return audio_source::sptr(new audio_osx_source(sampling_rate, device_name, ok_to_block));
+}
+
+void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
+{
+ if (inDesc == NULL) {
+ std::cerr << "PrintStreamDesc: Can't print a NULL desc!" << std::endl;
+ return;
+ }
+
+ std::cerr << " Sample Rate : " << inDesc->mSampleRate << std::endl;
+ char format_id[4];
+ strncpy (format_id, (char*)(&inDesc->mFormatID), 4);
+ std::cerr << " Format ID : " << format_id << std::endl;
+ std::cerr << " Format Flags : " << inDesc->mFormatFlags << std::endl;
+ std::cerr << " Bytes per Packet : " << inDesc->mBytesPerPacket << std::endl;
+ std::cerr << " Frames per Packet : " << inDesc->mFramesPerPacket << std::endl;
+ std::cerr << " Bytes per Frame : " << inDesc->mBytesPerFrame << std::endl;
+ std::cerr << " Channels per Frame : " << inDesc->mChannelsPerFrame << std::endl;
+ std::cerr << " Bits per Channel : " << inDesc->mBitsPerChannel << std::endl;
+}
+
+// FIXME these should query some kind of user preference
+
+audio_osx_source::audio_osx_source (int sample_rate,
+ const std::string device_name,
+ bool do_block,
+ int channel_config,
+ int max_sample_count)
+ : gr_sync_block ("audio_osx_source",
+ gr_make_io_signature (0, 0, 0),
+ gr_make_io_signature (0, 0, 0)),
+ d_deviceSampleRate (0.0), d_outputSampleRate (0.0),
+ d_channel_config (0),
+ d_inputBufferSizeFrames (0), d_inputBufferSizeBytes (0),
+ d_outputBufferSizeFrames (0), d_outputBufferSizeBytes (0),
+ d_deviceBufferSizeFrames (0), d_deviceBufferSizeBytes (0),
+ d_leadSizeFrames (0), d_leadSizeBytes (0),
+ d_trailSizeFrames (0), d_trailSizeBytes (0),
+ d_extraBufferSizeFrames (0), d_extraBufferSizeBytes (0),
+ d_queueSampleCount (0), d_max_sample_count (0),
+ d_n_AvailableInputFrames (0), d_n_ActualInputFrames (0),
+ d_n_user_channels (0), d_n_max_channels (0), d_n_deviceChannels (0),
+ d_do_block (do_block), d_passThrough (false),
+ d_internal (0), d_cond_data (0),
+ d_buffers (0),
+ d_InputAU (0), d_InputBuffer (0), d_OutputBuffer (0),
+ d_AudioConverter (0)
+{
+ if (sample_rate <= 0) {
+ std::cerr << "Invalid Sample Rate: " << sample_rate << std::endl;
+ throw std::invalid_argument ("audio_osx_source::audio_osx_source");
+ } else
+ d_outputSampleRate = (Float64) sample_rate;
+
+ if (channel_config <= 0 & channel_config != -1) {
+ std::cerr << "Invalid Channel Config: " << channel_config << std::endl;
+ throw std::invalid_argument ("audio_osx_source::audio_osx_source");
+ } else if (channel_config == -1) {
+// no user input; try "device name" instead
+ int l_n_channels = (int) strtol (device_name.data(), (char **)NULL, 10);
+ if (l_n_channels == 0 & errno) {
+ std::cerr << "Error Converting Device Name: " << errno << std::endl;
+ throw std::invalid_argument ("audio_osx_source::audio_osx_source");
+ }
+ if (l_n_channels <= 0)
+ channel_config = 2;
+ else
+ channel_config = l_n_channels;
+ }
+
+ d_channel_config = channel_config;
+
+// check that the max # of samples to store is valid
+
+ if (max_sample_count == -1)
+ max_sample_count = sample_rate;
+ else if (max_sample_count <= 0) {
+ std::cerr << "Invalid Max Sample Count: " << max_sample_count << std::endl;
+ throw std::invalid_argument ("audio_osx_source::audio_osx_source");
+ }
+
+ d_max_sample_count = max_sample_count;
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "source(): max # samples = " << d_max_sample_count << std::endl;
+#endif
+
+ OSStatus err = noErr;
+
+// create the default AudioUnit for input
+
+// Open the default input unit
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ AudioComponentDescription InputDesc;
+#else
+ ComponentDescription InputDesc;
+#endif
+
+
+ InputDesc.componentType = kAudioUnitType_Output;
+ InputDesc.componentSubType = kAudioUnitSubType_HALOutput;
+ InputDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ InputDesc.componentFlags = 0;
+ InputDesc.componentFlagsMask = 0;
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ AudioComponent comp = AudioComponentFindNext (NULL, &InputDesc);
+#else
+ Component comp = FindNextComponent (NULL, &InputDesc);
+#endif
+
+ if (comp == NULL) {
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ std::cerr << "AudioComponentFindNext Error" << std::endl;
+#else
+ std::cerr << "FindNextComponent Error" << std::endl;
+#endif
+ throw std::runtime_error ("audio_osx_source::audio_osx_source");
+ }
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ err = AudioComponentInstanceNew (comp, &d_InputAU);
+ CheckErrorAndThrow (err, "AudioComponentInstanceNew",
+ "audio_osx_source::audio_osx_source");
+#else
+ err = OpenAComponent (comp, &d_InputAU);
+ CheckErrorAndThrow (err, "OpenAComponent",
+ "audio_osx_source::audio_osx_source");
+#endif
+
+
+ UInt32 enableIO;
+
+// must enable the AUHAL for input and disable output
+// before setting the AUHAL's current device
+
+// Enable input on the AUHAL
+ enableIO = 1;
+ err = AudioUnitSetProperty (d_InputAU,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Input,
+ 1, // input element
+ &enableIO,
+ sizeof (UInt32));
+ CheckErrorAndThrow (err, "AudioUnitSetProperty Input Enable",
+ "audio_osx_source::audio_osx_source");
+
+// Disable output on the AUHAL
+ enableIO = 0;
+ err = AudioUnitSetProperty (d_InputAU,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Output,
+ 0, // output element
+ &enableIO,
+ sizeof (UInt32));
+ CheckErrorAndThrow (err, "AudioUnitSetProperty Output Disable",
+ "audio_osx_source::audio_osx_source");
+
+// set the default input device for our input AU
+
+ SetDefaultInputDeviceAsCurrent ();
+
+#if _OSX_DO_LISTENERS_
+// set up a listener if default hardware input device changes
+
+ err = AudioHardwareAddPropertyListener
+ (kAudioHardwarePropertyDefaultInputDevice,
+ (AudioHardwarePropertyListenerProc) HardwareListener,
+ this);
+
+ CheckErrorAndThrow (err, "AudioHardwareAddPropertyListener",
+ "audio_osx_source::audio_osx_source");
+
+// Add a listener for any changes in the input AU's output stream
+// the function "UnitListener" will be called if the stream format
+// changes for whatever reason
+
+ err = AudioUnitAddPropertyListener
+ (d_InputAU,
+ kAudioUnitProperty_StreamFormat,
+ (AudioUnitPropertyListenerProc) UnitListener,
+ this);
+ CheckErrorAndThrow (err, "Adding Unit Property Listener",
+ "audio_osx_source::audio_osx_source");
+#endif
+
+// Now find out if it actually can do input.
+
+ UInt32 hasInput = 0;
+ UInt32 dataSize = sizeof (hasInput);
+ err = AudioUnitGetProperty (d_InputAU,
+ kAudioOutputUnitProperty_HasIO,
+ kAudioUnitScope_Input,
+ 1,
+ &hasInput,
+ &dataSize);
+ CheckErrorAndThrow (err, "AudioUnitGetProperty HasIO",
+ "audio_osx_source::audio_osx_source");
+ if (hasInput == 0) {
+ std::cerr << "Selected Audio Device does not support Input." << std::endl;
+ throw std::runtime_error ("audio_osx_source::audio_osx_source");
+ }
+
+// Set up a callback function to retrieve input from the Audio Device
+
+ AURenderCallbackStruct AUCallBack;
+
+ AUCallBack.inputProc = (AURenderCallback)(audio_osx_source::AUInputCallback);
+ AUCallBack.inputProcRefCon = this;
+
+ err = AudioUnitSetProperty (d_InputAU,
+ kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Global,
+ 0,
+ &AUCallBack,
+ sizeof (AURenderCallbackStruct));
+ CheckErrorAndThrow (err, "AudioUnitSetProperty Input Callback",
+ "audio_osx_source::audio_osx_source");
+
+ UInt32 propertySize;
+ AudioStreamBasicDescription asbd_device, asbd_client, asbd_user;
+
+// asbd_device: ASBD of the device that is creating the input data stream
+// asbd_client: ASBD of the client size (output) of the hardware device
+// asbd_user: ASBD of the user's arguments
+
+// Get the Stream Format (device side)
+
+ propertySize = sizeof (asbd_device);
+ err = AudioUnitGetProperty (d_InputAU,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ 1,
+ &asbd_device,
+ &propertySize);
+ CheckErrorAndThrow (err, "AudioUnitGetProperty Device Input Stream Format",
+ "audio_osx_source::audio_osx_source");
+
+#if _OSX_AU_DEBUG_
+ std::cerr << std::endl << "---- Device Stream Format ----" << std::endl;
+ PrintStreamDesc (&asbd_device);
+#endif
+
+// Get the Stream Format (client side)
+ propertySize = sizeof (asbd_client);
+ err = AudioUnitGetProperty (d_InputAU,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ 1,
+ &asbd_client,
+ &propertySize);
+ CheckErrorAndThrow (err, "AudioUnitGetProperty Device Ouput Stream Format",
+ "audio_osx_source::audio_osx_source");
+
+#if _OSX_AU_DEBUG_
+ std::cerr << std::endl << "---- Client Stream Format ----" << std::endl;
+ PrintStreamDesc (&asbd_client);
+#endif
+
+// Set the format of all the AUs to the input/output devices channel count
+
+// get the max number of input (& thus output) channels supported by
+// this device
+ d_n_max_channels = asbd_client.mChannelsPerFrame;
+
+// create the output io signature;
+// no input siganture to set (source is hardware)
+ set_output_signature (gr_make_io_signature (1,
+ d_n_max_channels,
+ sizeof (float)));
+
+// allocate the output circular buffer(s), one per channel
+ d_buffers = (circular_buffer<float>**) new
+ circular_buffer<float>* [d_n_max_channels];
+ UInt32 n_alloc = (UInt32) ceil ((double) d_max_sample_count);
+ for (UInt32 n = 0; n < d_n_max_channels; n++) {
+ d_buffers[n] = new circular_buffer<float> (n_alloc, false, false);
+ }
+
+ d_deviceSampleRate = asbd_device.mSampleRate;
+ d_n_deviceChannels = asbd_device.mChannelsPerFrame;
+
+// create an ASBD for the user's wants
+
+ asbd_user.mSampleRate = d_outputSampleRate;
+ asbd_user.mFormatID = kAudioFormatLinearPCM;
+ asbd_user.mFormatFlags = (kLinearPCMFormatFlagIsFloat |
+ GR_PCM_ENDIANNESS |
+ kLinearPCMFormatFlagIsPacked |
+ kAudioFormatFlagIsNonInterleaved);
+ asbd_user.mBytesPerPacket = 4;
+ asbd_user.mFramesPerPacket = 1;
+ asbd_user.mBytesPerFrame = 4;
+ asbd_user.mChannelsPerFrame = d_n_max_channels;
+ asbd_user.mBitsPerChannel = 32;
+
+ if (d_deviceSampleRate == d_outputSampleRate) {
+// no need to do conversion if asbd_client matches user wants
+ d_passThrough = true;
+ d_leadSizeFrames = d_trailSizeFrames = 0L;
+ } else {
+ d_passThrough = false;
+// Create the audio converter
+
+ err = AudioConverterNew (&asbd_client, &asbd_user, &d_AudioConverter);
+ CheckErrorAndThrow (err, "AudioConverterNew",
+ "audio_osx_source::audio_osx_source");
+
+// Set the audio converter sample rate quality to "max" ...
+// requires more samples, but should sound nicer
+
+ UInt32 ACQuality = kAudioConverterQuality_Max;
+ propertySize = sizeof (ACQuality);
+ err = AudioConverterSetProperty (d_AudioConverter,
+ kAudioConverterSampleRateConverterQuality,
+ propertySize,
+ &ACQuality);
+ CheckErrorAndThrow (err, "AudioConverterSetProperty "
+ "SampleRateConverterQuality",
+ "audio_osx_source::audio_osx_source");
+
+// set the audio converter's prime method to "pre",
+// which uses both leading and trailing frames
+// from the "current input". All of this is handled
+// internally by the AudioConverter; we just supply
+// the frames for conversion.
+
+// UInt32 ACPrimeMethod = kConverterPrimeMethod_None;
+ UInt32 ACPrimeMethod = kConverterPrimeMethod_Pre;
+ propertySize = sizeof (ACPrimeMethod);
+ err = AudioConverterSetProperty (d_AudioConverter,
+ kAudioConverterPrimeMethod,
+ propertySize,
+ &ACPrimeMethod);
+ CheckErrorAndThrow (err, "AudioConverterSetProperty PrimeMethod",
+ "audio_osx_source::audio_osx_source");
+
+// Get the size of the I/O buffer(s) to allow for pre-allocated buffers
+
+// lead frame info (trail frame info is ignored)
+
+ AudioConverterPrimeInfo ACPrimeInfo = {0, 0};
+ propertySize = sizeof (ACPrimeInfo);
+ err = AudioConverterGetProperty (d_AudioConverter,
+ kAudioConverterPrimeInfo,
+ &propertySize,
+ &ACPrimeInfo);
+ CheckErrorAndThrow (err, "AudioConverterGetProperty PrimeInfo",
+ "audio_osx_source::audio_osx_source");
+
+ switch (ACPrimeMethod) {
+ case (kConverterPrimeMethod_None):
+ d_leadSizeFrames =
+ d_trailSizeFrames = 0L;
+ break;
+ case (kConverterPrimeMethod_Normal):
+ d_leadSizeFrames = 0L;
+ d_trailSizeFrames = ACPrimeInfo.trailingFrames;
+ break;
+ default:
+ d_leadSizeFrames = ACPrimeInfo.leadingFrames;
+ d_trailSizeFrames = ACPrimeInfo.trailingFrames;
+ }
+ }
+ d_leadSizeBytes = d_leadSizeFrames * sizeof (Float32);
+ d_trailSizeBytes = d_trailSizeFrames * sizeof (Float32);
+
+ propertySize = sizeof (d_deviceBufferSizeFrames);
+ err = AudioUnitGetProperty (d_InputAU,
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Global,
+ 0,
+ &d_deviceBufferSizeFrames,
+ &propertySize);
+ CheckErrorAndThrow (err, "AudioUnitGetProperty Buffer Frame Size",
+ "audio_osx_source::audio_osx_source");
+
+ d_deviceBufferSizeBytes = d_deviceBufferSizeFrames * sizeof (Float32);
+ d_inputBufferSizeBytes = d_deviceBufferSizeBytes + d_leadSizeBytes;
+ d_inputBufferSizeFrames = d_deviceBufferSizeFrames + d_leadSizeFrames;
+
+// outBufSizeBytes = floor (inBufSizeBytes * rate_out / rate_in)
+// since this is rarely exact, we need another buffer to hold
+// "extra" samples not processed at any given sampling period
+// this buffer must be at least 4 floats in size, but generally
+// follows the rule that
+// extraBufSize = ceil (rate_in / rate_out)*sizeof(float)
+
+ d_extraBufferSizeFrames = ((UInt32) ceil (d_deviceSampleRate
+ / d_outputSampleRate)
+ * sizeof (float));
+ if (d_extraBufferSizeFrames < 4)
+ d_extraBufferSizeFrames = 4;
+ d_extraBufferSizeBytes = d_extraBufferSizeFrames * sizeof (float);
+
+ d_outputBufferSizeFrames = (UInt32) ceil (((Float64) d_inputBufferSizeFrames)
+ * d_outputSampleRate
+ / d_deviceSampleRate);
+ d_outputBufferSizeBytes = d_outputBufferSizeFrames * sizeof (float);
+ d_inputBufferSizeFrames += d_extraBufferSizeFrames;
+
+// pre-alloc all buffers
+
+ AllocAudioBufferList (&d_InputBuffer, d_n_deviceChannels,
+ d_inputBufferSizeBytes);
+ if (d_passThrough == false) {
+ AllocAudioBufferList (&d_OutputBuffer, d_n_max_channels,
+ d_outputBufferSizeBytes);
+ } else {
+ d_OutputBuffer = d_InputBuffer;
+ }
+
+// create the stuff to regulate I/O
+
+ d_cond_data = new gruel::condition_variable ();
+ if (d_cond_data == NULL)
+ CheckErrorAndThrow (errno, "new condition (data)",
+ "audio_osx_source::audio_osx_source");
+
+ d_internal = new gruel::mutex ();
+ if (d_internal == NULL)
+ CheckErrorAndThrow (errno, "new mutex (internal)",
+ "audio_osx_source::audio_osx_source");
+
+// initialize the AU for input
+
+ err = AudioUnitInitialize (d_InputAU);
+ CheckErrorAndThrow (err, "AudioUnitInitialize",
+ "audio_osx_source::audio_osx_source");
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "audio_osx_source Parameters:" << std::endl;
+ std::cerr << " Device Sample Rate is " << d_deviceSampleRate << std::endl;
+ std::cerr << " User Sample Rate is " << d_outputSampleRate << std::endl;
+ std::cerr << " Max Sample Count is " << d_max_sample_count << std::endl;
+ std::cerr << " # Device Channels is " << d_n_deviceChannels << std::endl;
+ std::cerr << " # Max Channels is " << d_n_max_channels << std::endl;
+ std::cerr << " Device Buffer Size is Frames = " << d_deviceBufferSizeFrames << std::endl;
+ std::cerr << " Lead Size is Frames = " << d_leadSizeFrames << std::endl;
+ std::cerr << " Trail Size is Frames = " << d_trailSizeFrames << std::endl;
+ std::cerr << " Input Buffer Size is Frames = " << d_inputBufferSizeFrames << std::endl;
+ std::cerr << " Output Buffer Size is Frames = " << d_outputBufferSizeFrames << std::endl;
+#endif
+}
+
+void
+audio_osx_source::AllocAudioBufferList (AudioBufferList** t_ABL,
+ UInt32 n_channels,
+ UInt32 bufferSizeBytes)
+{
+ FreeAudioBufferList (t_ABL);
+ UInt32 propertySize = (offsetof (AudioBufferList, mBuffers[0]) +
+ (sizeof (AudioBuffer) * n_channels));
+ *t_ABL = (AudioBufferList*) calloc (1, propertySize);
+ (*t_ABL)->mNumberBuffers = n_channels;
+
+ int counter = n_channels;
+
+ while (--counter >= 0) {
+ (*t_ABL)->mBuffers[counter].mNumberChannels = 1;
+ (*t_ABL)->mBuffers[counter].mDataByteSize = bufferSizeBytes;
+ (*t_ABL)->mBuffers[counter].mData = calloc (1, bufferSizeBytes);
+ }
+}
+
+void
+audio_osx_source::FreeAudioBufferList (AudioBufferList** t_ABL)
+{
+// free pre-allocated audio buffer, if it exists
+ if (*t_ABL != NULL) {
+ int counter = (*t_ABL)->mNumberBuffers;
+ while (--counter >= 0)
+ free ((*t_ABL)->mBuffers[counter].mData);
+ free (*t_ABL);
+ (*t_ABL) = 0;
+ }
+}
+
+bool audio_osx_source::IsRunning ()
+{
+ UInt32 AURunning = 0, AUSize = sizeof (UInt32);
+
+ OSStatus err = AudioUnitGetProperty (d_InputAU,
+ kAudioOutputUnitProperty_IsRunning,
+ kAudioUnitScope_Global,
+ 0,
+ &AURunning,
+ &AUSize);
+ CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning",
+ "audio_osx_source::IsRunning");
+
+ return (AURunning);
+}
+
+bool audio_osx_source::start ()
+{
+ if (! IsRunning ()) {
+ OSStatus err = AudioOutputUnitStart (d_InputAU);
+ CheckErrorAndThrow (err, "AudioOutputUnitStart",
+ "audio_osx_source::start");
+ }
+
+ return (true);
+}
+
+bool audio_osx_source::stop ()
+{
+ if (IsRunning ()) {
+ OSStatus err = AudioOutputUnitStop (d_InputAU);
+ CheckErrorAndThrow (err, "AudioOutputUnitStart",
+ "audio_osx_source::stop");
+ for (UInt32 n = 0; n < d_n_user_channels; n++) {
+ d_buffers[n]->abort ();
+ }
+ }
+
+ return (true);
+}
+
+audio_osx_source::~audio_osx_source ()
+{
+ OSStatus err = noErr;
+
+// stop the AudioUnit
+ stop();
+
+#if _OSX_DO_LISTENERS_
+// remove the listeners
+
+ err = AudioUnitRemovePropertyListener
+ (d_InputAU,
+ kAudioUnitProperty_StreamFormat,
+ (AudioUnitPropertyListenerProc) UnitListener);
+ CheckError (err, "~audio_osx_source: AudioUnitRemovePropertyListener");
+
+ err = AudioHardwareRemovePropertyListener
+ (kAudioHardwarePropertyDefaultInputDevice,
+ (AudioHardwarePropertyListenerProc) HardwareListener);
+ CheckError (err, "~audio_osx_source: AudioHardwareRemovePropertyListener");
+#endif
+
+// free pre-allocated audio buffers
+ FreeAudioBufferList (&d_InputBuffer);
+
+ if (d_passThrough == false) {
+ err = AudioConverterDispose (d_AudioConverter);
+ CheckError (err, "~audio_osx_source: AudioConverterDispose");
+ FreeAudioBufferList (&d_OutputBuffer);
+ }
+
+// remove the audio unit
+ err = AudioUnitUninitialize (d_InputAU);
+ CheckError (err, "~audio_osx_source: AudioUnitUninitialize");
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ err = AudioComponentInstanceDispose (d_InputAU);
+ CheckError (err, "~audio_osx_source: AudioComponentInstanceDispose");
+#else
+ err = CloseComponent (d_InputAU);
+ CheckError (err, "~audio_osx_source: CloseComponent");
+#endif
+
+// empty and delete the queues
+ for (UInt32 n = 0; n < d_n_max_channels; n++) {
+ delete d_buffers[n];
+ d_buffers[n] = 0;
+ }
+ delete [] d_buffers;
+ d_buffers = 0;
+
+// close and delete the control stuff
+ delete d_cond_data;
+ d_cond_data = 0;
+ delete d_internal;
+ d_internal = 0;
+}
+
+bool
+audio_osx_source::check_topology (int ninputs, int noutputs)
+{
+// check # inputs to make sure it's valid
+ if (ninputs != 0) {
+ std::cerr << "audio_osx_source::check_topology(): number of input "
+ << "streams provided (" << ninputs
+ << ") should be 0." << std::endl;
+ throw std::runtime_error ("audio_osx_source::check_topology()");
+ }
+
+// check # outputs to make sure it's valid
+ if ((noutputs < 1) | (noutputs > (int) d_n_max_channels)) {
+ std::cerr << "audio_osx_source::check_topology(): number of output "
+ << "streams provided (" << noutputs << ") should be in [1,"
+ << d_n_max_channels << "] for the selected audio device."
+ << std::endl;
+ throw std::runtime_error ("audio_osx_source::check_topology()");
+ }
+
+// save the actual number of output (user) channels
+ d_n_user_channels = noutputs;
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "chk_topo: Actual # user output channels = "
+ << noutputs << std::endl;
+#endif
+
+ return (true);
+}
+
+int
+audio_osx_source::work
+(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ // acquire control to do processing here only
+ gruel::scoped_lock l (*d_internal);
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "work1: SC = " << d_queueSampleCount
+ << ", #OI = " << noutput_items
+ << ", #Chan = " << output_items.size() << std::endl;
+#endif
+
+ // set the actual # of output items to the 'desired' amount then
+ // verify that data is available; if not enough data is available,
+ // either wait until it is (is "do_block" is true), return (0) is no
+ // data is available and "do_block" is false, or process the actual
+ // amount of available data.
+
+ UInt32 actual_noutput_items = noutput_items;
+
+ if (d_queueSampleCount < actual_noutput_items) {
+ if (d_queueSampleCount == 0) {
+ // no data; do_block decides what to do
+ if (d_do_block == true) {
+ while (d_queueSampleCount == 0) {
+ // release control so-as to allow data to be retrieved;
+ // block until there is data to return
+ d_cond_data->wait (l);
+ // the condition's 'notify' was called; acquire control to
+ // keep thread safe
+ }
+ } else {
+ // no data & not blocking; return nothing
+ return (0);
+ }
+ }
+ // use the actual amount of available data
+ actual_noutput_items = d_queueSampleCount;
+ }
+
+ // number of channels
+ int l_counter = (int) output_items.size();
+
+ // copy the items from the circular buffer(s) to 'work's output buffers
+ // verify that the number copied out is as expected.
+
+ while (--l_counter >= 0) {
+ size_t t_n_output_items = actual_noutput_items;
+ d_buffers[l_counter]->dequeue ((float*) output_items[l_counter],
+ &t_n_output_items);
+ if (t_n_output_items != actual_noutput_items) {
+ std::cerr << "audio_osx_source::work(): ERROR: number of "
+ << "available items changing unexpectedly; expecting "
+ << actual_noutput_items << ", got "
+ << t_n_output_items << "." << std::endl;
+ throw std::runtime_error ("audio_osx_source::work()");
+ }
+ }
+
+ // subtract the actual number of items removed from the buffer(s)
+ // from the local accounting of the number of available samples
+
+ d_queueSampleCount -= actual_noutput_items;
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "work2: SC = " << d_queueSampleCount
+ << ", act#OI = " << actual_noutput_items << std::endl
+ << "Returning." << std::endl;
+#endif
+
+ return (actual_noutput_items);
+}
+
+OSStatus
+audio_osx_source::ConverterCallback
+(AudioConverterRef inAudioConverter,
+ UInt32* ioNumberDataPackets,
+ AudioBufferList* ioData,
+ AudioStreamPacketDescription** ioASPD,
+ void* inUserData)
+{
+ // take current device buffers and copy them to the tail of the
+ // input buffers the lead buffer is already there in the first
+ // d_leadSizeFrames slots
+
+ audio_osx_source* This = static_cast<audio_osx_source*>(inUserData);
+ AudioBufferList* l_inputABL = This->d_InputBuffer;
+ UInt32 totalInputBufferSizeBytes = ((*ioNumberDataPackets) * sizeof (float));
+ int counter = This->d_n_deviceChannels;
+ ioData->mNumberBuffers = This->d_n_deviceChannels;
+ This->d_n_ActualInputFrames = (*ioNumberDataPackets);
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cc1: io#DP = " << (*ioNumberDataPackets)
+ << ", TIBSB = " << totalInputBufferSizeBytes
+ << ", #C = " << counter << std::endl;
+#endif
+
+ while (--counter >= 0) {
+ AudioBuffer* l_ioD_AB = &(ioData->mBuffers[counter]);
+ l_ioD_AB->mNumberChannels = 1;
+ l_ioD_AB->mData = (float*)(l_inputABL->mBuffers[counter].mData);
+ l_ioD_AB->mDataByteSize = totalInputBufferSizeBytes;
+ }
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cc2: Returning." << std::endl;
+#endif
+
+ return (noErr);
+}
+
+OSStatus
+audio_osx_source::AUInputCallback (void* inRefCon,
+ AudioUnitRenderActionFlags* ioActionFlags,
+ const AudioTimeStamp* inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList* ioData)
+{
+ OSStatus err = noErr;
+ audio_osx_source* This = static_cast<audio_osx_source*>(inRefCon);
+
+ gruel::scoped_lock l (*This->d_internal);
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cb0: in#F = " << inNumberFrames
+ << ", inBN = " << inBusNumber
+ << ", SC = " << This->d_queueSampleCount << std::endl;
+#endif
+
+// Get the new audio data from the input device
+
+ err = AudioUnitRender (This->d_InputAU,
+ ioActionFlags,
+ inTimeStamp,
+ 1, //inBusNumber,
+ inNumberFrames,
+ This->d_InputBuffer);
+ CheckErrorAndThrow (err, "AudioUnitRender",
+ "audio_osx_source::AUInputCallback");
+
+ UInt32 AvailableInputFrames = inNumberFrames;
+ This->d_n_AvailableInputFrames = inNumberFrames;
+
+// get the number of actual output frames,
+// either via converting the buffer or not
+
+ UInt32 ActualOutputFrames;
+
+ if (This->d_passThrough == true) {
+ ActualOutputFrames = AvailableInputFrames;
+ } else {
+ UInt32 AvailableInputBytes = AvailableInputFrames * sizeof (float);
+ UInt32 AvailableOutputBytes = AvailableInputBytes;
+ UInt32 AvailableOutputFrames = AvailableOutputBytes / sizeof (float);
+ UInt32 propertySize = sizeof (AvailableOutputBytes);
+ err = AudioConverterGetProperty (This->d_AudioConverter,
+ kAudioConverterPropertyCalculateOutputBufferSize,
+ &propertySize,
+ &AvailableOutputBytes);
+ CheckErrorAndThrow (err, "AudioConverterGetProperty CalculateOutputBufferSize", "audio_osx_source::audio_osx_source");
+
+ AvailableOutputFrames = AvailableOutputBytes / sizeof (float);
+
+#if 0
+// when decimating too much, the output sounds warbly due to
+// fluctuating # of output frames
+// This should not be a surprise, but there's probably some
+// clever programming that could lessed the effect ...
+// like finding the "ideal" # of output frames, and keeping
+// that number constant no matter the # of input frames
+ UInt32 l_InputBytes = AvailableOutputBytes;
+ propertySize = sizeof (AvailableOutputBytes);
+ err = AudioConverterGetProperty (This->d_AudioConverter,
+ kAudioConverterPropertyCalculateInputBufferSize,
+ &propertySize,
+ &l_InputBytes);
+ CheckErrorAndThrow (err, "AudioConverterGetProperty CalculateInputBufferSize", "audio_osx_source::audio_osx_source");
+
+ if (l_InputBytes < AvailableInputBytes) {
+// OK to zero pad the input a little
+ AvailableOutputFrames += 1;
+ AvailableOutputBytes = AvailableOutputFrames * sizeof (float);
+ }
+#endif
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cb1: avail: #IF = " << AvailableInputFrames
+ << ", #OF = " << AvailableOutputFrames << std::endl;
+#endif
+ ActualOutputFrames = AvailableOutputFrames;
+
+// convert the data to the correct rate
+// on input, ActualOutputFrames is the number of available output frames
+
+ err = AudioConverterFillComplexBuffer (This->d_AudioConverter,
+ (AudioConverterComplexInputDataProc)(This->ConverterCallback),
+ inRefCon,
+ &ActualOutputFrames,
+ This->d_OutputBuffer,
+ NULL);
+ CheckErrorAndThrow (err, "AudioConverterFillComplexBuffer",
+ "audio_osx_source::AUInputCallback");
+
+// on output, ActualOutputFrames is the actual number of output frames
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cb2: actual: #IF = " << This->d_n_ActualInputFrames
+ << ", #OF = " << AvailableOutputFrames << std::endl;
+ if (This->d_n_ActualInputFrames != AvailableInputFrames)
+ std::cerr << "cb2.1: avail#IF = " << AvailableInputFrames
+ << ", actual#IF = " << This->d_n_ActualInputFrames << std::endl;
+#endif
+ }
+
+// add the output frames to the buffers' queue, checking for overflow
+
+ int l_counter = This->d_n_user_channels;
+ int res = 0;
+
+ while (--l_counter >= 0) {
+ float* inBuffer = (float*) This->d_OutputBuffer->mBuffers[l_counter].mData;
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cb3: enqueuing audio data." << std::endl;
+#endif
+
+ int l_res = This->d_buffers[l_counter]->enqueue (inBuffer, ActualOutputFrames);
+ if (l_res == -1)
+ res = -1;
+ }
+
+ if (res == -1) {
+// data coming in too fast
+// drop oldest buffer
+ fputs ("aO", stderr);
+ fflush (stderr);
+// set the local number of samples available to the max
+ This->d_queueSampleCount = This->d_buffers[0]->buffer_length_items ();
+ } else {
+// keep up the local sample count
+ This->d_queueSampleCount += ActualOutputFrames;
+ }
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cb4: #OI = " << ActualOutputFrames
+ << ", #Cnt = " << This->d_queueSampleCount
+ << ", mSC = " << This->d_max_sample_count << std::endl;
+#endif
+
+// signal that data is available, if appropraite
+ This->d_cond_data->notify_one ();
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "cb5: returning." << std::endl;
+#endif
+
+ return (err);
+}
+
+void
+audio_osx_source::SetDefaultInputDeviceAsCurrent
+()
+{
+// set the default input device
+ AudioDeviceID deviceID;
+ UInt32 dataSize = sizeof (AudioDeviceID);
+ AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
+ &dataSize,
+ &deviceID);
+ OSStatus err = AudioUnitSetProperty (d_InputAU,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ 0,
+ &deviceID,
+ sizeof (AudioDeviceID));
+ CheckErrorAndThrow (err, "AudioUnitSetProperty Current Device",
+ "audio_osx_source::SetDefaultInputDeviceAsCurrent");
+}
+
+#if _OSX_DO_LISTENERS_
+OSStatus
+audio_osx_source::HardwareListener
+(AudioHardwarePropertyID inPropertyID,
+ void *inClientData)
+{
+ OSStatus err = noErr;
+ audio_osx_source* This = static_cast<audio_osx_source*>(inClientData);
+
+ std::cerr << "a_o_s::HardwareListener" << std::endl;
+
+// set the new default hardware input device for use by our AU
+
+ This->SetDefaultInputDeviceAsCurrent ();
+
+// reset the converter to tell it that the stream has changed
+
+ err = AudioConverterReset (This->d_AudioConverter);
+ CheckErrorAndThrow (err, "AudioConverterReset",
+ "audio_osx_source::UnitListener");
+
+ return (err);
+}
+
+OSStatus
+audio_osx_source::UnitListener
+(void *inRefCon,
+ AudioUnit ci,
+ AudioUnitPropertyID inID,
+ AudioUnitScope inScope,
+ AudioUnitElement inElement)
+{
+ OSStatus err = noErr;
+ audio_osx_source* This = static_cast<audio_osx_source*>(inRefCon);
+ AudioStreamBasicDescription asbd;
+
+ std::cerr << "a_o_s::UnitListener" << std::endl;
+
+// get the converter's input ASBD (for printing)
+
+ UInt32 propertySize = sizeof (asbd);
+ err = AudioConverterGetProperty (This->d_AudioConverter,
+ kAudioConverterCurrentInputStreamDescription,
+ &propertySize,
+ &asbd);
+ CheckErrorAndThrow (err, "AudioConverterGetProperty "
+ "CurrentInputStreamDescription",
+ "audio_osx_source::UnitListener");
+
+ std::cerr << "UnitListener: Input Source changed." << std::endl
+ << "Old Source Output Info:" << std::endl;
+ PrintStreamDesc (&asbd);
+
+// get the new input unit's output ASBD
+
+ propertySize = sizeof (asbd);
+ err = AudioUnitGetProperty (This->d_InputAU,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output, 1,
+ &asbd, &propertySize);
+ CheckErrorAndThrow (err, "AudioUnitGetProperty StreamFormat",
+ "audio_osx_source::UnitListener");
+
+ std::cerr << "New Source Output Info:" << std::endl;
+ PrintStreamDesc (&asbd);
+
+// set the converter's input ASBD to this
+
+ err = AudioConverterSetProperty (This->d_AudioConverter,
+ kAudioConverterCurrentInputStreamDescription,
+ propertySize,
+ &asbd);
+ CheckErrorAndThrow (err, "AudioConverterSetProperty "
+ "CurrentInputStreamDescription",
+ "audio_osx_source::UnitListener");
+
+// reset the converter to tell it that the stream has changed
+
+ err = AudioConverterReset (This->d_AudioConverter);
+ CheckErrorAndThrow (err, "AudioConverterReset",
+ "audio_osx_source::UnitListener");
+
+ return (err);
+}
+#endif
diff --git a/gr-audio/lib/osx/audio_osx_source.h b/gr-audio/lib/osx/audio_osx_source.h
new file mode 100644
index 000000000..a373ea94f
--- /dev/null
+++ b/gr-audio/lib/osx/audio_osx_source.h
@@ -0,0 +1,115 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_OSX_SOURCE_H
+#define INCLUDED_AUDIO_OSX_SOURCE_H
+
+#include <gr_audio_source.h>
+#include <string>
+#include <AudioToolbox/AudioToolbox.h>
+#include <AudioUnit/AudioUnit.h>
+#include <circular_buffer.h>
+
+/*!
+ * \brief audio source using OSX
+ *
+ * Input signature is one or two streams of floats.
+ * Samples must be in the range [-1,1].
+ */
+
+class audio_osx_source : public audio_source {
+
+ Float64 d_deviceSampleRate, d_outputSampleRate;
+ int d_channel_config;
+ UInt32 d_inputBufferSizeFrames, d_inputBufferSizeBytes;
+ UInt32 d_outputBufferSizeFrames, d_outputBufferSizeBytes;
+ UInt32 d_deviceBufferSizeFrames, d_deviceBufferSizeBytes;
+ UInt32 d_leadSizeFrames, d_leadSizeBytes;
+ UInt32 d_trailSizeFrames, d_trailSizeBytes;
+ UInt32 d_extraBufferSizeFrames, d_extraBufferSizeBytes;
+ UInt32 d_queueSampleCount, d_max_sample_count;
+ UInt32 d_n_AvailableInputFrames, d_n_ActualInputFrames;
+ UInt32 d_n_user_channels, d_n_max_channels, d_n_deviceChannels;
+ bool d_do_block, d_passThrough, d_waiting_for_data;
+ gruel::mutex* d_internal;
+ gruel::condition_variable* d_cond_data;
+ circular_buffer<float>** d_buffers;
+
+// AudioUnits and Such
+ AudioUnit d_InputAU;
+ AudioBufferList* d_InputBuffer;
+ AudioBufferList* d_OutputBuffer;
+ AudioConverterRef d_AudioConverter;
+
+public:
+ audio_osx_source (int sample_rate = 44100,
+ const std::string device_name = "",
+ bool do_block = true,
+ int channel_config = -1,
+ int max_sample_count = -1);
+
+ ~audio_osx_source ();
+
+ bool start ();
+ bool stop ();
+ bool IsRunning ();
+
+ 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);
+
+private:
+ void SetDefaultInputDeviceAsCurrent ();
+
+ void AllocAudioBufferList (AudioBufferList** t_ABL,
+ UInt32 n_channels,
+ UInt32 inputBufferSizeBytes);
+
+ void FreeAudioBufferList (AudioBufferList** t_ABL);
+
+ static OSStatus ConverterCallback (AudioConverterRef inAudioConverter,
+ UInt32* ioNumberDataPackets,
+ AudioBufferList* ioData,
+ AudioStreamPacketDescription** outASPD,
+ void* inUserData);
+
+ static OSStatus AUInputCallback (void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData);
+#if _OSX_DO_LISTENERS_
+ static OSStatus UnitListener (void *inRefCon,
+ AudioUnit ci,
+ AudioUnitPropertyID inID,
+ AudioUnitScope inScope,
+ AudioUnitElement inElement);
+
+ static OSStatus HardwareListener (AudioHardwarePropertyID inPropertyID,
+ void *inClientData);
+#endif
+};
+
+#endif /* INCLUDED_AUDIO_OSX_SOURCE_H */
diff --git a/gr-audio/lib/osx/circular_buffer.h b/gr-audio/lib/osx/circular_buffer.h
new file mode 100644
index 000000000..48758bf87
--- /dev/null
+++ b/gr-audio/lib/osx/circular_buffer.h
@@ -0,0 +1,315 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006,2009,2010 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CIRCULAR_BUFFER_H_
+#define _CIRCULAR_BUFFER_H_
+
+#include <gruel/thread.h>
+#include <iostream>
+#include <stdexcept>
+
+#ifndef DO_DEBUG
+#define DO_DEBUG 0
+#endif
+
+#if DO_DEBUG
+#define DEBUG(X) do{X} while(0);
+#else
+#define DEBUG(X) do{} while(0);
+#endif
+
+template <class T>
+class circular_buffer
+{
+private:
+// the buffer to use
+ T* d_buffer;
+
+// the following are in Items (type T)
+ size_t d_bufLen_I, d_readNdx_I, d_writeNdx_I;
+ size_t d_n_avail_write_I, d_n_avail_read_I;
+
+// stuff to control access to class internals
+ gruel::mutex* d_internal;
+ gruel::condition_variable* d_readBlock;
+ gruel::condition_variable* d_writeBlock;
+
+// booleans to decide how to control reading, writing, and aborting
+ bool d_doWriteBlock, d_doFullRead, d_doAbort;
+
+ void delete_mutex_cond () {
+ if (d_internal) {
+ delete d_internal;
+ d_internal = NULL;
+ }
+ if (d_readBlock) {
+ delete d_readBlock;
+ d_readBlock = NULL;
+ }
+ if (d_writeBlock) {
+ delete d_writeBlock;
+ d_writeBlock = NULL;
+ }
+ };
+
+public:
+ circular_buffer (size_t bufLen_I,
+ bool doWriteBlock = true, bool doFullRead = false) {
+ if (bufLen_I == 0)
+ throw std::runtime_error ("circular_buffer(): "
+ "Number of items to buffer must be > 0.\n");
+ d_bufLen_I = bufLen_I;
+ d_buffer = (T*) new T[d_bufLen_I];
+ d_doWriteBlock = doWriteBlock;
+ d_doFullRead = doFullRead;
+ d_internal = NULL;
+ d_readBlock = d_writeBlock = NULL;
+ reset ();
+ DEBUG (std::cerr << "c_b(): buf len (items) = " << d_bufLen_
+ << ", doWriteBlock = " << (d_doWriteBlock ? "true" : "false")
+ << ", doFullRead = " << (d_doFullRead ? "true" : "false")
+ << std::endl);
+ };
+
+ ~circular_buffer () {
+ delete_mutex_cond ();
+ delete [] d_buffer;
+ };
+
+ inline size_t n_avail_write_items () {
+ gruel::scoped_lock l (*d_internal);
+ size_t retVal = d_n_avail_write_I;
+ return (retVal);
+ };
+
+ inline size_t n_avail_read_items () {
+ gruel::scoped_lock l (*d_internal);
+ size_t retVal = d_n_avail_read_I;
+ return (retVal);
+ };
+
+ inline size_t buffer_length_items () {return (d_bufLen_I);};
+ inline bool do_write_block () {return (d_doWriteBlock);};
+ inline bool do_full_read () {return (d_doFullRead);};
+
+ void reset () {
+ d_doAbort = false;
+ bzero (d_buffer, d_bufLen_I * sizeof (T));
+ d_readNdx_I = d_writeNdx_I = d_n_avail_read_I = 0;
+ d_n_avail_write_I = d_bufLen_I;
+ delete_mutex_cond ();
+ // create a mutex to handle contention of shared resources;
+ // any routine needed access to shared resources uses lock()
+ // before doing anything, then unlock() when finished.
+ d_internal = new gruel::mutex ();
+ // link the internal mutex to the read and write conditions;
+ // when wait() is called, the internal mutex will automatically
+ // be unlock()'ed. Upon return (from a notify_one() to the condition),
+ // the internal mutex will be lock()'ed.
+ d_readBlock = new gruel::condition_variable ();
+ d_writeBlock = new gruel::condition_variable ();
+ };
+
+/*
+ * enqueue: add the given buffer of item-length to the queue,
+ * first-in-first-out (FIFO).
+ *
+ * inputs:
+ * buf: a pointer to the buffer holding the data
+ *
+ * bufLen_I: the buffer length in items (of the instantiated type)
+ *
+ * returns:
+ * -1: on overflow (write is not blocking, and data is being
+ * written faster than it is being read)
+ * 0: if nothing to do (0 length buffer)
+ * 1: if success
+ * 2: in the process of aborting, do doing nothing
+ *
+ * will throw runtime errors if inputs are improper:
+ * buffer pointer is NULL
+ * buffer length is larger than the instantiated buffer length
+ */
+
+ int enqueue (T* buf, size_t bufLen_I) {
+ DEBUG (std::cerr << "enqueue: buf = " << (void*) buf
+ << ", bufLen = " << bufLen_I
+ << ", #av_wr = " << d_n_avail_write_I
+ << ", #av_rd = " << d_n_avail_read_I << std::endl);
+ if (bufLen_I > d_bufLen_I) {
+ std::cerr << "ERROR: cannot add buffer longer ("
+ << bufLen_I << ") than instantiated length ("
+ << d_bufLen_I << ")." << std::endl;
+ throw std::runtime_error ("circular_buffer::enqueue()");
+ }
+
+ if (bufLen_I == 0)
+ return (0);
+ if (!buf)
+ throw std::runtime_error ("circular_buffer::enqueue(): "
+ "input buffer is NULL.\n");
+ gruel::scoped_lock l (*d_internal);
+ if (d_doAbort) {
+ return (2);
+ }
+ // set the return value to 1: success; change if needed
+ int retval = 1;
+ if (bufLen_I > d_n_avail_write_I) {
+ if (d_doWriteBlock) {
+ while (bufLen_I > d_n_avail_write_I) {
+ DEBUG (std::cerr << "enqueue: #len > #a, waiting." << std::endl);
+ // wait; will automatically unlock() the internal mutex via
+ // the scoped lock
+ d_writeBlock->wait (l);
+ // and auto re-lock() it here.
+ if (d_doAbort) {
+ DEBUG (std::cerr << "enqueue: #len > #a, aborting." << std::endl);
+ return (2);
+ }
+ DEBUG (std::cerr << "enqueue: #len > #a, done waiting." << std::endl);
+ }
+ } else {
+ d_n_avail_read_I = d_bufLen_I - bufLen_I;
+ d_n_avail_write_I = bufLen_I;
+ DEBUG (std::cerr << "circular_buffer::enqueue: overflow" << std::endl);
+ retval = -1;
+ }
+ }
+ size_t n_now_I = d_bufLen_I - d_writeNdx_I, n_start_I = 0;
+ if (n_now_I > bufLen_I)
+ n_now_I = bufLen_I;
+ else if (n_now_I < bufLen_I)
+ n_start_I = bufLen_I - n_now_I;
+ bcopy (buf, &(d_buffer[d_writeNdx_I]), n_now_I * sizeof (T));
+ if (n_start_I) {
+ bcopy (&(buf[n_now_I]), d_buffer, n_start_I * sizeof (T));
+ d_writeNdx_I = n_start_I;
+ } else
+ d_writeNdx_I += n_now_I;
+ d_n_avail_read_I += bufLen_I;
+ d_n_avail_write_I -= bufLen_I;
+ d_readBlock->notify_one ();
+ return (retval);
+ };
+
+/*
+ * dequeue: removes from the queue the number of items requested, or
+ * available, into the given buffer on a FIFO basis.
+ *
+ * inputs:
+ * buf: a pointer to the buffer into which to copy the data
+ *
+ * bufLen_I: pointer to the number of items to remove in items
+ * (of the instantiated type)
+ *
+ * returns:
+ * 0: if nothing to do (0 length buffer)
+ * 1: if success
+ * 2: in the process of aborting, do doing nothing
+ *
+ * will throw runtime errors if inputs are improper:
+ * buffer pointer is NULL
+ * buffer length pointer is NULL
+ * buffer length is larger than the instantiated buffer length
+ */
+
+ int dequeue (T* buf, size_t* bufLen_I) {
+ DEBUG (std::cerr << "dequeue: buf = " << ((void*) buf)
+ << ", *bufLen = " << (*bufLen_I)
+ << ", #av_wr = " << d_n_avail_write_I
+ << ", #av_rd = " << d_n_avail_read_I << std::endl);
+ if (!bufLen_I)
+ throw std::runtime_error ("circular_buffer::dequeue(): "
+ "input bufLen pointer is NULL.\n");
+ if (!buf)
+ throw std::runtime_error ("circular_buffer::dequeue(): "
+ "input buffer pointer is NULL.\n");
+ size_t l_bufLen_I = *bufLen_I;
+ if (l_bufLen_I == 0)
+ return (0);
+ if (l_bufLen_I > d_bufLen_I) {
+ std::cerr << "ERROR: cannot remove buffer longer ("
+ << l_bufLen_I << ") than instantiated length ("
+ << d_bufLen_I << ")." << std::endl;
+ throw std::runtime_error ("circular_buffer::dequeue()");
+ }
+
+ gruel::scoped_lock l (*d_internal);
+ if (d_doAbort) {
+ return (2);
+ }
+ if (d_doFullRead) {
+ while (d_n_avail_read_I < l_bufLen_I) {
+ DEBUG (std::cerr << "dequeue: #a < #len, waiting." << std::endl);
+ // wait; will automatically unlock() the internal mutex via
+ // the scoped lock
+ d_readBlock->wait (l);
+ // and re-lock() it here.
+ if (d_doAbort) {
+ DEBUG (std::cerr << "dequeue: #a < #len, aborting." << std::endl);
+ return (2);
+ }
+ DEBUG (std::cerr << "dequeue: #a < #len, done waiting." << std::endl);
+ }
+ } else {
+ while (d_n_avail_read_I == 0) {
+ DEBUG (std::cerr << "dequeue: #a == 0, waiting." << std::endl);
+ // wait; will automatically unlock() the internal mutex via
+ // the scoped lock
+ d_readBlock->wait (l);
+ // and re-lock() it here.
+ if (d_doAbort) {
+ DEBUG (std::cerr << "dequeue: #a == 0, aborting." << std::endl);
+ return (2);
+ }
+ DEBUG (std::cerr << "dequeue: #a == 0, done waiting." << std::endl);
+ }
+ }
+ if (l_bufLen_I > d_n_avail_read_I)
+ l_bufLen_I = d_n_avail_read_I;
+ size_t n_now_I = d_bufLen_I - d_readNdx_I, n_start_I = 0;
+ if (n_now_I > l_bufLen_I)
+ n_now_I = l_bufLen_I;
+ else if (n_now_I < l_bufLen_I)
+ n_start_I = l_bufLen_I - n_now_I;
+ bcopy (&(d_buffer[d_readNdx_I]), buf, n_now_I * sizeof (T));
+ if (n_start_I) {
+ bcopy (d_buffer, &(buf[n_now_I]), n_start_I * sizeof (T));
+ d_readNdx_I = n_start_I;
+ } else
+ d_readNdx_I += n_now_I;
+ *bufLen_I = l_bufLen_I;
+ d_n_avail_read_I -= l_bufLen_I;
+ d_n_avail_write_I += l_bufLen_I;
+ d_writeBlock->notify_one ();
+ return (1);
+ };
+
+ void abort () {
+ gruel::scoped_lock l (*d_internal);
+ d_doAbort = true;
+ d_writeBlock->notify_one ();
+ d_readBlock->notify_one ();
+ };
+};
+
+#endif /* _CIRCULAR_BUFFER_H_ */
diff --git a/gr-audio/lib/portaudio/audio_portaudio_sink.cc b/gr-audio/lib/portaudio/audio_portaudio_sink.cc
new file mode 100644
index 000000000..7fdb99577
--- /dev/null
+++ b/gr-audio/lib/portaudio/audio_portaudio_sink.cc
@@ -0,0 +1,362 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2011 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 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in he 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_portaudio_sink.h>
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <stdio.h>
+#include <iostream>
+#include <unistd.h>
+#include <stdexcept>
+#include <gri_portaudio.h>
+#include <string.h>
+
+AUDIO_REGISTER_SINK(REG_PRIO_MED, portaudio)(
+ int sampling_rate, const std::string &device_name, bool ok_to_block
+){
+ return audio_sink::sptr(new audio_portaudio_sink(sampling_rate, device_name, ok_to_block));
+}
+
+//#define LOGGING 0 // define to 0 or 1
+
+#define SAMPLE_FORMAT paFloat32
+typedef float sample_t;
+
+// Number of portaudio buffers in the ringbuffer
+static const unsigned int N_BUFFERS = 4;
+
+static std::string
+default_device_name ()
+{
+ return gr_prefs::singleton()->get_string("audio_portaudio", "default_output_device", "");
+}
+
+void
+audio_portaudio_sink::create_ringbuffer(void)
+{
+ int bufsize_samples = d_portaudio_buffer_size_frames * d_output_parameters.channelCount;
+
+ if (d_verbose)
+ fprintf(stderr,"ring buffer size = %d frames\n",
+ N_BUFFERS*bufsize_samples/d_output_parameters.channelCount);
+
+ // FYI, the buffer indicies are in units of samples.
+ d_writer = gr_make_buffer(N_BUFFERS * bufsize_samples, sizeof(sample_t));
+ d_reader = gr_buffer_add_reader(d_writer, 0);
+}
+
+/*
+ * This routine will be called by the PortAudio engine when audio is needed.
+ * It may called at interrupt level on some machines so don't do anything
+ * that could mess up the system like calling malloc() or free().
+ *
+ * Our job is to write framesPerBuffer frames into outputBuffer.
+ */
+int
+portaudio_sink_callback (const void *inputBuffer,
+ void *outputBuffer,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *arg)
+{
+ audio_portaudio_sink *self = (audio_portaudio_sink *)arg;
+ int nreqd_samples =
+ framesPerBuffer * self->d_output_parameters.channelCount;
+
+ int navail_samples = self->d_reader->items_available();
+
+ if (nreqd_samples <= navail_samples) { // We've got enough data...
+ {
+ gruel::scoped_lock guard(self->d_ringbuffer_mutex);
+
+ memcpy(outputBuffer,
+ self->d_reader->read_pointer(),
+ nreqd_samples * sizeof(sample_t));
+ self->d_reader->update_read_pointer(nreqd_samples);
+
+ self->d_ringbuffer_ready = true;
+ }
+
+ // Tell the sink thread there is new room in the ringbuffer.
+ self->d_ringbuffer_cond.notify_one();
+ return paContinue;
+ }
+
+ else { // underrun
+ self->d_nunderuns++;
+ ssize_t r = ::write(2, "aU", 2); // FIXME change to non-blocking call
+ if(r == -1) {
+ perror("audio_portaudio_source::portaudio_source_callback write error to stderr.");
+ }
+
+ // FIXME we should transfer what we've got and pad the rest
+ memset(outputBuffer, 0, nreqd_samples * sizeof(sample_t));
+
+ self->d_ringbuffer_ready = true;
+ self->d_ringbuffer_cond.notify_one(); // Tell the sink to get going!
+
+ return paContinue;
+ }
+}
+
+
+// ----------------------------------------------------------------
+
+audio_portaudio_sink::audio_portaudio_sink(int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block)
+ : gr_sync_block ("audio_portaudio_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_ok_to_block(ok_to_block),
+ d_verbose(gr_prefs::singleton()->get_bool("audio_portaudio", "verbose", false)),
+ d_portaudio_buffer_size_frames(0),
+ d_stream(0),
+ d_ringbuffer_mutex(),
+ d_ringbuffer_cond(),
+ d_ringbuffer_ready(false),
+ d_nunderuns(0)
+{
+ memset(&d_output_parameters, 0, sizeof(d_output_parameters));
+ //if (LOGGING)
+ // d_log = gri_logger::singleton();
+
+ PaError err;
+ int i, numDevices;
+ PaDeviceIndex device = 0;
+ const PaDeviceInfo *deviceInfo = NULL;
+
+ err = Pa_Initialize();
+ if (err != paNoError) {
+ bail ("Initialize failed", err);
+ }
+
+ if (d_verbose)
+ gri_print_devices();
+
+ numDevices = Pa_GetDeviceCount();
+ if (numDevices < 0)
+ bail("Pa Device count failed", 0);
+ if (numDevices == 0)
+ bail("no devices available", 0);
+
+ if (d_device_name.empty())
+ {
+ // FIXME Get smarter about picking something
+ fprintf(stderr,"\nUsing Default Device\n");
+ device = Pa_GetDefaultOutputDevice();
+ deviceInfo = Pa_GetDeviceInfo(device);
+ fprintf(stderr,"%s is the chosen device using %s as the host\n",
+ deviceInfo->name, Pa_GetHostApiInfo(deviceInfo->hostApi)->name);
+ }
+ else
+ {
+ bool found = false;
+ fprintf(stderr,"\nTest Devices\n");
+ for (i=0;i<numDevices;i++) {
+ deviceInfo = Pa_GetDeviceInfo( i );
+ fprintf(stderr,"Testing device name: %s",deviceInfo->name);
+ if (deviceInfo->maxOutputChannels <= 0) {
+ fprintf(stderr,"\n");
+ continue;
+ }
+ if (strstr(deviceInfo->name, d_device_name.c_str())){
+ fprintf(stderr," Chosen!\n");
+ device = i;
+ fprintf(stderr,"%s using %s as the host\n",d_device_name.c_str(),
+ Pa_GetHostApiInfo(deviceInfo->hostApi)->name), fflush(stderr);
+ found = true;
+ deviceInfo = Pa_GetDeviceInfo(device);
+ i = numDevices; // force loop exit
+ }
+ else
+ fprintf(stderr,"\n"),fflush(stderr);
+ }
+
+ if (!found){
+ bail("Failed to find specified device name", 0);
+ exit(1);
+ }
+ }
+
+
+ d_output_parameters.device = device;
+ d_output_parameters.channelCount = deviceInfo->maxOutputChannels;
+ d_output_parameters.sampleFormat = SAMPLE_FORMAT;
+ d_output_parameters.suggestedLatency = deviceInfo->defaultLowOutputLatency;
+ d_output_parameters.hostApiSpecificStreamInfo = NULL;
+
+ // We fill in the real channelCount in check_topology when we know
+ // how many inputs are connected to us.
+
+ // Now that we know the maximum number of channels (allegedly)
+ // supported by the h/w, we can compute a reasonable input
+ // signature. The portaudio specs say that they'll accept any
+ // number of channels from 1 to max.
+ set_input_signature(gr_make_io_signature(1, deviceInfo->maxOutputChannels,
+ sizeof (sample_t)));
+}
+
+
+bool
+audio_portaudio_sink::check_topology (int ninputs, int noutputs)
+{
+ PaError err;
+
+ if (Pa_IsStreamActive(d_stream))
+ {
+ Pa_CloseStream(d_stream);
+ d_stream = 0;
+ d_reader.reset(); // boost::shared_ptr for d_reader = 0
+ d_writer.reset(); // boost::shared_ptr for d_write = 0
+ }
+
+ d_output_parameters.channelCount = ninputs; // # of channels we're really using
+
+#if 1
+ d_portaudio_buffer_size_frames = (int)(0.0213333333 * d_sampling_rate + 0.5); // Force 1024 frame buffers at 48000
+ fprintf(stderr, "Latency = %8.5f, requested sampling_rate = %g\n", // Force latency to 21.3333333.. ms
+ 0.0213333333, (double)d_sampling_rate);
+#endif
+ err = Pa_OpenStream(&d_stream,
+ NULL, // No input
+ &d_output_parameters,
+ d_sampling_rate,
+ d_portaudio_buffer_size_frames,
+ paClipOff,
+ &portaudio_sink_callback,
+ (void*)this);
+
+ if (err != paNoError) {
+ output_error_msg ("OpenStream failed", err);
+ return false;
+ }
+
+#if 0
+ const PaStreamInfo *psi = Pa_GetStreamInfo(d_stream);
+
+ d_portaudio_buffer_size_frames = (int)(d_output_parameters.suggestedLatency * psi->sampleRate);
+ fprintf(stderr, "Latency = %7.4f, psi->sampleRate = %g\n",
+ d_output_parameters.suggestedLatency, psi->sampleRate);
+#endif
+
+ fprintf(stderr, "d_portaudio_buffer_size_frames = %d\n", d_portaudio_buffer_size_frames);
+
+ assert(d_portaudio_buffer_size_frames != 0);
+
+ create_ringbuffer();
+
+ err = Pa_StartStream(d_stream);
+ if (err != paNoError) {
+ output_error_msg ("StartStream failed", err);
+ return false;
+ }
+
+ return true;
+}
+
+audio_portaudio_sink::~audio_portaudio_sink ()
+{
+ Pa_StopStream(d_stream); // wait for output to drain
+ Pa_CloseStream(d_stream);
+ Pa_Terminate();
+}
+
+/*
+ * This version consumes everything sent to it, blocking if required.
+ * I think this will allow us better control of the total buffering/latency
+ * in the audio path.
+ */
+int
+audio_portaudio_sink::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ const float **in = (const float **) &input_items[0];
+ const unsigned nchan = d_output_parameters.channelCount; // # of channels == samples/frame
+
+ int k;
+
+ for (k = 0; k < noutput_items; ){
+ int nframes = d_writer->space_available() / nchan; // How much space in ringbuffer
+ if (nframes == 0){ // no room...
+ if (d_ok_to_block){
+ {
+ gruel::scoped_lock guard(d_ringbuffer_mutex);
+ while (!d_ringbuffer_ready)
+ d_ringbuffer_cond.wait(guard);
+ }
+
+ continue;
+ }
+ else {
+ // There's no room and we're not allowed to block.
+ // (A USRP is most likely controlling the pacing through the pipeline.)
+ // We drop the samples on the ground, and say we processed them all ;)
+ //
+ // FIXME, there's probably room for a bit more finesse here.
+ return noutput_items;
+ }
+ }
+
+ // We can write the smaller of the request and the room we've got
+ {
+ gruel::scoped_lock guard(d_ringbuffer_mutex);
+
+ int nf = std::min(noutput_items - k, nframes);
+ float *p = (float *) d_writer->write_pointer();
+
+ for (int i = 0; i < nf; i++)
+ for (unsigned int c = 0; c < nchan; c++)
+ *p++ = in[c][k + i];
+
+ d_writer->update_write_pointer(nf * nchan);
+ k += nf;
+
+ d_ringbuffer_ready = false;
+ }
+ }
+
+ return k; // tell how many we actually did
+}
+
+void
+audio_portaudio_sink::output_error_msg (const char *msg, int err)
+{
+ fprintf (stderr, "audio_portaudio_sink[%s]: %s: %s\n",
+ d_device_name.c_str (), msg, Pa_GetErrorText(err));
+}
+
+void
+audio_portaudio_sink::bail (const char *msg, int err) throw (std::runtime_error)
+{
+ output_error_msg (msg, err);
+ throw std::runtime_error ("audio_portaudio_sink");
+}
diff --git a/gr-audio/lib/portaudio/audio_portaudio_sink.h b/gr-audio/lib/portaudio/audio_portaudio_sink.h
new file mode 100644
index 000000000..6426a32ac
--- /dev/null
+++ b/gr-audio/lib/portaudio/audio_portaudio_sink.h
@@ -0,0 +1,85 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_AUDIO_PORTAUDIO_SINK_H
+#define INCLUDED_AUDIO_PORTAUDIO_SINK_H
+
+#include <gr_audio_sink.h>
+#include <gr_buffer.h>
+#include <gruel/thread.h>
+#include <string>
+#include <portaudio.h>
+#include <stdexcept>
+//#include <gri_logger.h>
+
+PaStreamCallback portaudio_sink_callback;
+
+
+/*!
+ * \ Audio sink using PORTAUDIO
+ *
+ * Input samples must be in the range [-1,1].
+ */
+class audio_portaudio_sink : public audio_sink {
+
+ friend PaStreamCallback portaudio_sink_callback;
+
+
+ unsigned int d_sampling_rate;
+ std::string d_device_name;
+ bool d_ok_to_block;
+ bool d_verbose;
+
+ unsigned int d_portaudio_buffer_size_frames; // number of frames in a portaudio buffer
+
+ PaStream *d_stream;
+ PaStreamParameters d_output_parameters;
+
+ gr_buffer_sptr d_writer; // buffer used between work and callback
+ gr_buffer_reader_sptr d_reader;
+
+ gruel::mutex d_ringbuffer_mutex;
+ gruel::condition_variable d_ringbuffer_cond;
+ bool d_ringbuffer_ready;
+
+ // random stats
+ int d_nunderuns; // count of underruns
+ //gri_logger_sptr d_log; // handle to non-blocking logging instance
+
+ void output_error_msg (const char *msg, int err);
+ void bail (const char *msg, int err) throw (std::runtime_error);
+ void create_ringbuffer();
+
+
+public:
+ audio_portaudio_sink (int sampling_rate, const std::string device_name,
+ bool ok_to_block);
+
+ ~audio_portaudio_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);
+};
+
+#endif /* INCLUDED_AUDIO_PORTAUDIO_SINK_H */
diff --git a/gr-audio/lib/portaudio/audio_portaudio_source.cc b/gr-audio/lib/portaudio/audio_portaudio_source.cc
new file mode 100644
index 000000000..55828ad21
--- /dev/null
+++ b/gr-audio/lib/portaudio/audio_portaudio_source.cc
@@ -0,0 +1,374 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2011 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 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in he 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_portaudio_source.h>
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <stdio.h>
+#include <iostream>
+#include <unistd.h>
+#include <stdexcept>
+#include <gri_portaudio.h>
+#include <string.h>
+
+AUDIO_REGISTER_SOURCE(REG_PRIO_MED, portaudio)(
+ int sampling_rate, const std::string &device_name, bool ok_to_block
+){
+ return audio_source::sptr(new audio_portaudio_source(sampling_rate, device_name, ok_to_block));
+}
+
+//#define LOGGING 0 // define to 0 or 1
+
+#define SAMPLE_FORMAT paFloat32
+typedef float sample_t;
+
+// Number of portaudio buffers in the ringbuffer
+static const unsigned int N_BUFFERS = 4;
+
+static std::string
+default_device_name ()
+{
+ return gr_prefs::singleton()->get_string("audio_portaudio", "default_input_device", "");
+}
+
+void
+audio_portaudio_source::create_ringbuffer(void)
+{
+ int bufsize_samples = d_portaudio_buffer_size_frames * d_input_parameters.channelCount;
+
+ if (d_verbose)
+ fprintf(stderr, "ring buffer size = %d frames\n",
+ N_BUFFERS*bufsize_samples/d_input_parameters.channelCount);
+
+ // FYI, the buffer indicies are in units of samples.
+ d_writer = gr_make_buffer(N_BUFFERS * bufsize_samples, sizeof(sample_t));
+ d_reader = gr_buffer_add_reader(d_writer, 0);
+}
+
+/*
+ * This routine will be called by the PortAudio engine when audio is needed.
+ * It may called at interrupt level on some machines so don't do anything
+ * that could mess up the system like calling malloc() or free().
+ *
+ * Our job is to copy framesPerBuffer frames from inputBuffer.
+ */
+int
+portaudio_source_callback (const void *inputBuffer,
+ void *outputBuffer,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *arg)
+{
+ audio_portaudio_source *self = (audio_portaudio_source *)arg;
+ int nchan = self->d_input_parameters.channelCount;
+ int nframes_to_copy = framesPerBuffer;
+ int nframes_room = self->d_writer->space_available() / nchan;
+
+ if (nframes_to_copy <= nframes_room){ // We've got room for the data ..
+ //if (LOGGING)
+ // self->d_log->printf("PAsrc cb: f/b = %4ld\n", framesPerBuffer);
+
+ // copy from input buffer to ringbuffer
+ {
+ gruel::scoped_lock(d_ringbuffer_mutex);
+
+ memcpy(self->d_writer->write_pointer(),
+ inputBuffer,
+ nframes_to_copy * nchan * sizeof(sample_t));
+ self->d_writer->update_write_pointer(nframes_to_copy * nchan);
+
+ // Tell the source thread there is new data in the ringbuffer.
+ self->d_ringbuffer_ready = true;
+ }
+
+ self->d_ringbuffer_cond.notify_one();
+ return paContinue;
+ }
+
+ else { // overrun
+ self->d_noverruns++;
+ ssize_t r = ::write(2, "aO", 2); // FIXME change to non-blocking call
+ if(r == -1) {
+ perror("audio_portaudio_source::portaudio_source_callback write error to stderr.");
+ }
+
+ self->d_ringbuffer_ready = false;
+ self->d_ringbuffer_cond.notify_one(); // Tell the sink to get going!
+ return paContinue;
+ }
+}
+
+
+// ----------------------------------------------------------------
+
+audio_portaudio_source::audio_portaudio_source(int sampling_rate,
+ const std::string device_name,
+ bool ok_to_block)
+ : gr_sync_block ("audio_portaudio_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_ok_to_block(ok_to_block),
+ d_verbose(gr_prefs::singleton()->get_bool("audio_portaudio", "verbose", false)),
+ d_portaudio_buffer_size_frames(0),
+ d_stream(0),
+ d_ringbuffer_mutex(),
+ d_ringbuffer_cond(),
+ d_ringbuffer_ready(false),
+ d_noverruns(0)
+{
+ memset(&d_input_parameters, 0, sizeof(d_input_parameters));
+ //if (LOGGING)
+ // d_log = gri_logger::singleton();
+
+ PaError err;
+ int i, numDevices;
+ PaDeviceIndex device = 0;
+ const PaDeviceInfo *deviceInfo = NULL;
+
+
+ err = Pa_Initialize();
+ if (err != paNoError) {
+ bail ("Initialize failed", err);
+ }
+
+ if (d_verbose)
+ gri_print_devices();
+
+ numDevices = Pa_GetDeviceCount();
+ if (numDevices < 0)
+ bail("Pa Device count failed", 0);
+ if (numDevices == 0)
+ bail("no devices available", 0);
+
+ if (d_device_name.empty())
+ {
+ // FIXME Get smarter about picking something
+ device = Pa_GetDefaultInputDevice();
+ deviceInfo = Pa_GetDeviceInfo(device);
+ fprintf(stderr,"%s is the chosen device using %s as the host\n",
+ deviceInfo->name, Pa_GetHostApiInfo(deviceInfo->hostApi)->name);
+ }
+ else
+ {
+ bool found = false;
+
+ for (i=0;i<numDevices;i++) {
+ deviceInfo = Pa_GetDeviceInfo( i );
+ fprintf(stderr,"Testing device name: %s",deviceInfo->name);
+ if (deviceInfo->maxInputChannels <= 0) {
+ fprintf(stderr,"\n");
+ continue;
+ }
+ if (strstr(deviceInfo->name, d_device_name.c_str())){
+ fprintf(stderr," Chosen!\n");
+ device = i;
+ fprintf(stderr,"%s using %s as the host\n",d_device_name.c_str(),
+ Pa_GetHostApiInfo(deviceInfo->hostApi)->name), fflush(stderr);
+ found = true;
+ deviceInfo = Pa_GetDeviceInfo(device);
+ i = numDevices; // force loop exit
+ }
+ else
+ fprintf(stderr,"\n"),fflush(stderr);
+ }
+
+ if (!found){
+ bail("Failed to find specified device name", 0);
+ }
+ }
+
+
+ d_input_parameters.device = device;
+ d_input_parameters.channelCount = deviceInfo->maxInputChannels;
+ d_input_parameters.sampleFormat = SAMPLE_FORMAT;
+ d_input_parameters.suggestedLatency = deviceInfo->defaultLowInputLatency;
+ d_input_parameters.hostApiSpecificStreamInfo = NULL;
+
+ // We fill in the real channelCount in check_topology when we know
+ // how many inputs are connected to us.
+
+ // Now that we know the maximum number of channels (allegedly)
+ // supported by the h/w, we can compute a reasonable output
+ // signature. The portaudio specs say that they'll accept any
+ // number of channels from 1 to max.
+ set_output_signature(gr_make_io_signature(1, deviceInfo->maxInputChannels,
+ sizeof (sample_t)));
+}
+
+
+bool
+audio_portaudio_source::check_topology (int ninputs, int noutputs)
+{
+ PaError err;
+
+ if (Pa_IsStreamActive(d_stream))
+ {
+ Pa_CloseStream(d_stream);
+ d_stream = 0;
+ d_reader.reset(); // boost::shared_ptr for d_reader = 0
+ d_writer.reset(); // boost::shared_ptr for d_write = 0
+ }
+
+ d_input_parameters.channelCount = noutputs; // # of channels we're really using
+
+#if 1
+ d_portaudio_buffer_size_frames = (int)(0.0213333333 * d_sampling_rate + 0.5); // Force 512 frame buffers at 48000
+ fprintf(stderr, "Latency = %8.5f, requested sampling_rate = %g\n", // Force latency to 21.3333333.. ms
+ 0.0213333333, (double)d_sampling_rate);
+#endif
+ err = Pa_OpenStream(&d_stream,
+ &d_input_parameters,
+ NULL, // No output
+ d_sampling_rate,
+ d_portaudio_buffer_size_frames,
+ paClipOff,
+ &portaudio_source_callback,
+ (void*)this);
+
+ if (err != paNoError) {
+ output_error_msg ("OpenStream failed", err);
+ return false;
+ }
+
+#if 0
+ const PaStreamInfo *psi = Pa_GetStreamInfo(d_stream);
+
+ d_portaudio_buffer_size_frames = (int)(d_input_parameters.suggestedLatency * psi->sampleRate);
+ fprintf(stderr, "Latency = %7.4f, psi->sampleRate = %g\n",
+ d_input_parameters.suggestedLatency, psi->sampleRate);
+#endif
+
+ fprintf(stderr, "d_portaudio_buffer_size_frames = %d\n", d_portaudio_buffer_size_frames);
+
+ assert(d_portaudio_buffer_size_frames != 0);
+
+ create_ringbuffer();
+
+ err = Pa_StartStream(d_stream);
+ if (err != paNoError) {
+ output_error_msg ("StartStream failed", err);
+ return false;
+ }
+
+ return true;
+}
+
+audio_portaudio_source::~audio_portaudio_source ()
+{
+ Pa_StopStream(d_stream); // wait for output to drain
+ Pa_CloseStream(d_stream);
+ Pa_Terminate();
+}
+
+int
+audio_portaudio_source::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ float **out = (float **) &output_items[0];
+ const unsigned nchan = d_input_parameters.channelCount; // # of channels == samples/frame
+
+ int k;
+ for (k = 0; k < noutput_items; ){
+
+ int nframes = d_reader->items_available() / nchan; // # of frames in ringbuffer
+ if (nframes == 0){ // no data right now...
+ if (k > 0) // If we've produced anything so far, return that
+ return k;
+
+ if (d_ok_to_block) {
+ gruel:: scoped_lock guard(d_ringbuffer_mutex);
+ while (d_ringbuffer_ready == false)
+ d_ringbuffer_cond.wait(guard); // block here, then try again
+ continue;
+ }
+
+ assert(k == 0);
+
+ // There's no data and we're not allowed to block.
+ // (A USRP is most likely controlling the pacing through the pipeline.)
+ // This is an underun. The scheduler wouldn't have called us if it
+ // had anything better to do. Thus we really need to produce some amount
+ // of "fill".
+ //
+ // There are lots of options for comfort noise, etc.
+ // FIXME We'll fill with zeros for now. Yes, it will "click"...
+
+ // Fill with some frames of zeros
+ {
+ gruel::scoped_lock guard(d_ringbuffer_mutex);
+
+ int nf = std::min(noutput_items - k, (int) d_portaudio_buffer_size_frames);
+ for (int i = 0; i < nf; i++){
+ for (unsigned int c = 0; c < nchan; c++){
+ out[c][k + i] = 0;
+ }
+ }
+ k += nf;
+
+ d_ringbuffer_ready = false;
+ return k;
+ }
+ }
+
+ // We can read the smaller of the request and what's in the buffer.
+ {
+ gruel::scoped_lock guard(d_ringbuffer_mutex);
+
+ int nf = std::min(noutput_items - k, nframes);
+
+ const float *p = (const float *) d_reader->read_pointer();
+ for (int i = 0; i < nf; i++){
+ for (unsigned int c = 0; c < nchan; c++){
+ out[c][k + i] = *p++;
+ }
+ }
+ d_reader->update_read_pointer(nf * nchan);
+ k += nf;
+ d_ringbuffer_ready = false;
+ }
+ }
+
+ return k; // tell how many we actually did
+}
+
+void
+audio_portaudio_source::output_error_msg (const char *msg, int err)
+{
+ fprintf (stderr, "audio_portaudio_source[%s]: %s: %s\n",
+ d_device_name.c_str (), msg, Pa_GetErrorText(err));
+}
+
+void
+audio_portaudio_source::bail (const char *msg, int err) throw (std::runtime_error)
+{
+ output_error_msg (msg, err);
+ throw std::runtime_error ("audio_portaudio_source");
+}
diff --git a/gr-audio/lib/portaudio/audio_portaudio_source.h b/gr-audio/lib/portaudio/audio_portaudio_source.h
new file mode 100644
index 000000000..245b3410b
--- /dev/null
+++ b/gr-audio/lib/portaudio/audio_portaudio_source.h
@@ -0,0 +1,83 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_AUDIO_PORTAUDIO_SOURCE_H
+#define INCLUDED_AUDIO_PORTAUDIO_SOURCE_H
+
+#include <gr_audio_source.h>
+#include <gr_buffer.h>
+#include <gruel/thread.h>
+#include <string>
+#include <portaudio.h>
+#include <stdexcept>
+
+PaStreamCallback portaudio_source_callback;
+
+
+/*!
+ * \ Audio source using PORTAUDIO
+ *
+ * Input samples must be in the range [-1,1].
+ */
+class audio_portaudio_source : public audio_source {
+
+ friend PaStreamCallback portaudio_source_callback;
+
+
+ unsigned int d_sampling_rate;
+ std::string d_device_name;
+ bool d_ok_to_block;
+ bool d_verbose;
+
+ unsigned int d_portaudio_buffer_size_frames; // number of frames in a portaudio buffer
+
+ PaStream *d_stream;
+ PaStreamParameters d_input_parameters;
+
+ gr_buffer_sptr d_writer; // buffer used between work and callback
+ gr_buffer_reader_sptr d_reader;
+
+ gruel::mutex d_ringbuffer_mutex;
+ gruel::condition_variable d_ringbuffer_cond;
+ bool d_ringbuffer_ready;
+
+ // random stats
+ int d_noverruns; // count of overruns
+
+ void output_error_msg (const char *msg, int err);
+ void bail (const char *msg, int err) throw (std::runtime_error);
+ void create_ringbuffer();
+
+
+public:
+ audio_portaudio_source (int sampling_rate, const std::string device_name,
+ bool ok_to_block);
+
+ ~audio_portaudio_source ();
+
+ bool check_topology (int ninputs, int noutputs);
+
+ int work (int ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+#endif /* INCLUDED_AUDIO_PORTAUDIO_SOURCE_H */
diff --git a/gr-audio/lib/portaudio/gr-audio-portaudio.conf b/gr-audio/lib/portaudio/gr-audio-portaudio.conf
new file mode 100644
index 000000000..0dd147443
--- /dev/null
+++ b/gr-audio/lib/portaudio/gr-audio-portaudio.conf
@@ -0,0 +1,10 @@
+# This file contains system wide configuration data for GNU Radio.
+# You may override any setting on a per-user basis by editing
+# ~/.gnuradio/config.conf
+
+[audio_portaudio]
+
+#default_input_device = hw:0,0
+#default_output_device = hw:0,0
+
+verbose = false
diff --git a/gr-audio/lib/portaudio/gri_portaudio.cc b/gr-audio/lib/portaudio/gri_portaudio.cc
new file mode 100644
index 000000000..faa472337
--- /dev/null
+++ b/gr-audio/lib/portaudio/gri_portaudio.cc
@@ -0,0 +1,111 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gri_portaudio.h>
+#include <portaudio.h>
+#include <string.h>
+
+
+PaDeviceIndex
+gri_pa_find_device_by_name(const char *name)
+{
+ int i;
+ int numDevices;
+ const PaDeviceInfo *pdi;
+ int len = strlen( name );
+ PaDeviceIndex result = paNoDevice;
+ numDevices = Pa_GetDeviceCount();
+ for( i=0; i<numDevices; i++ )
+ {
+ pdi = Pa_GetDeviceInfo( i );
+ if( strncmp( name, pdi->name, len ) == 0 )
+ {
+ result = i;
+ break;
+ }
+ }
+ return result;
+}
+
+
+void
+gri_print_devices()
+{
+ int numDevices, defaultDisplayed, myDevice=0;
+ const PaDeviceInfo *deviceInfo;
+
+ numDevices = Pa_GetDeviceCount();
+ if (numDevices < 0)
+ return;
+
+ printf("Number of devices found = %d\n", numDevices);
+
+ for (int i=0; i < numDevices; i++ ) {
+ deviceInfo = Pa_GetDeviceInfo( i );
+ printf( "--------------------------------------- device #%d\n", i );
+ /* Mark global and API specific default devices */
+ defaultDisplayed = 0;
+ if( i == Pa_GetDefaultInputDevice() )
+ {
+ myDevice = i;
+ printf( "[ Default Input" );
+ defaultDisplayed = 1;
+ }
+ else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultInputDevice )
+ {
+ const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
+ printf( "[ Default %s Input", hostInfo->name );
+ defaultDisplayed = 1;
+ }
+
+ if( i == Pa_GetDefaultOutputDevice() )
+ {
+ printf( (defaultDisplayed ? "," : "[") );
+ printf( " Default Output" );
+ defaultDisplayed = 1;
+ }
+ else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice )
+ {
+ const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
+ printf( (defaultDisplayed ? "," : "[") );
+ printf( " Default %s Output", hostInfo->name );
+ defaultDisplayed = 1;
+ }
+ if( defaultDisplayed )
+ printf( " ]\n" );
+
+ /* print device info fields */
+ printf( "Name = %s\n", deviceInfo->name );
+ printf( "Host API = %s\n", Pa_GetHostApiInfo( deviceInfo->hostApi )->name );
+ printf( "Max inputs = %d", deviceInfo->maxInputChannels );
+ printf( ", Max outputs = %d\n", deviceInfo->maxOutputChannels );
+
+ printf( "Default low input latency = %8.3f\n", deviceInfo->defaultLowInputLatency );
+ printf( "Default low output latency = %8.3f\n", deviceInfo->defaultLowOutputLatency );
+ printf( "Default high input latency = %8.3f\n", deviceInfo->defaultHighInputLatency );
+ printf( "Default high output latency = %8.3f\n", deviceInfo->defaultHighOutputLatency );
+ }
+}
diff --git a/gr-audio/lib/portaudio/gri_portaudio.h b/gr-audio/lib/portaudio/gri_portaudio.h
new file mode 100644
index 000000000..36191e25a
--- /dev/null
+++ b/gr-audio/lib/portaudio/gri_portaudio.h
@@ -0,0 +1,32 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_GRI_PORTAUDIO_H
+#define INCLUDED_GRI_PORTAUDIO_H
+
+#include <stdio.h>
+#include <portaudio.h>
+
+PaDeviceIndex gri_pa_find_device_by_name(const char *name);
+void gri_print_devices();
+
+#endif /* INCLUDED_GRI_PORTAUDIO_H */
diff --git a/gr-audio/lib/windows/audio_windows_sink.cc b/gr-audio/lib/windows/audio_windows_sink.cc
new file mode 100644
index 000000000..5284ce173
--- /dev/null
+++ b/gr-audio/lib/windows/audio_windows_sink.cc
@@ -0,0 +1,323 @@
+/* -*- c++ -*- */
+/*
+* Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+* Boston, MA 02110-1301, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_windows_sink.h>
+#include <gr_io_signature.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#include <sstream>
+
+AUDIO_REGISTER_SINK(REG_PRIO_HIGH, windows)(
+ int sampling_rate, const std::string &device_name, bool
+){
+ return audio_sink::sptr(new audio_windows_sink(sampling_rate, device_name));
+}
+
+static const double CHUNK_TIME = 0.1; //0.001; // 100 ms
+
+// FIXME these should query some kind of user preference
+
+static std::string
+default_device_name ()
+{
+ return "WAVE_MAPPER";
+}
+
+audio_windows_sink::audio_windows_sink (int sampling_freq, const std::string device_name)
+ : gr_sync_block ("audio_windows_sink",
+ gr_make_io_signature (1, 2, sizeof (float)),
+ gr_make_io_signature (0, 0, 0)),
+ d_sampling_freq (sampling_freq),
+ d_device_name (device_name.empty ()? default_device_name () : device_name),
+ d_fd (-1), d_buffer (0), d_chunk_size (0)
+{
+ d_wave_write_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+ if (open_waveout_device () < 0)
+ {
+ //fprintf (stderr, "audio_windows_sink:open_waveout_device() failed\n");
+ perror ("audio_windows_sink:open_waveout_device( ) failed\n");
+ throw
+ std::runtime_error ("audio_windows_sink:open_waveout_device() failed");
+ }
+
+ d_chunk_size = (int) (d_sampling_freq * CHUNK_TIME);
+ set_output_multiple (d_chunk_size);
+
+ d_buffer = new short[d_chunk_size * 2];
+
+}
+
+audio_windows_sink::~audio_windows_sink ()
+{
+ /* Free the callback Event */
+ CloseHandle (d_wave_write_event);
+ waveOutClose (d_h_waveout);
+ delete[]d_buffer;
+}
+
+int
+audio_windows_sink::work (int noutput_items,
+ gr_vector_const_void_star & input_items,
+ gr_vector_void_star & output_items)
+{
+ const float *f0, *f1;
+ bool playtestsound = false;
+ if (playtestsound)
+ {
+ // dummy
+
+ f0 = (const float *) input_items[0];
+
+ for (int i = 0; i < noutput_items; i += d_chunk_size)
+ {
+ for (int j = 0; j < d_chunk_size; j++)
+ {
+ d_buffer[2 * j + 0] = (short) (sin (2.0 * 3.1415926535897932384626 * (float) j * 1000.0 / (float) d_sampling_freq) * 8192 + 0); //+32767
+ d_buffer[2 * j + 1] = d_buffer[2 * j + 0];
+ }
+ f0 += d_chunk_size;
+ if (write_waveout
+ ((HPSTR) d_buffer, 2 * d_chunk_size * sizeof (short)) < 0)
+ {
+ fprintf (stderr, "audio_windows_sink: write failed\n");
+ perror ("audio_windows_sink: write failed");
+ }
+ }
+ // break;
+ }
+ else
+ {
+ switch (input_items.size ())
+ {
+
+ case 1: // mono input
+
+ f0 = (const float *) input_items[0];
+
+ for (int i = 0; i < noutput_items; i += d_chunk_size)
+ {
+ for (int j = 0; j < d_chunk_size; j++)
+ {
+ d_buffer[2 * j + 0] = (short) (f0[j] * 32767);
+ d_buffer[2 * j + 1] = (short) (f0[j] * 32767);
+ }
+ f0 += d_chunk_size;
+ if (write_waveout
+ ((HPSTR) d_buffer, 2 * d_chunk_size * sizeof (short)) < 0)
+ {
+ //fprintf (stderr, "audio_windows_sink: write failed\n");
+ perror ("audio_windows_sink: write failed");
+ }
+ }
+ break;
+
+ case 2: // stereo input
+
+ f0 = (const float *) input_items[0];
+ f1 = (const float *) input_items[1];
+
+ for (int i = 0; i < noutput_items; i += d_chunk_size)
+ {
+ for (int j = 0; j < d_chunk_size; j++)
+ {
+ d_buffer[2 * j + 0] = (short) (f0[j] * 32767);
+ d_buffer[2 * j + 1] = (short) (f1[j] * 32767);
+ }
+ f0 += d_chunk_size;
+ f1 += d_chunk_size;
+ if (write_waveout
+ ((HPSTR) d_buffer, 2 * d_chunk_size * sizeof (short)) < 0)
+ {
+ //fprintf (stderr, "audio_windows_sink: write failed\n");
+ perror ("audio_windows_sink: write failed");
+ }
+ }
+ break;
+ }
+ }
+ return noutput_items;
+}
+
+int
+audio_windows_sink::string_to_int (const std::string & s)
+{
+ int i;
+ std::istringstream (s) >> i;
+ return i;
+} //ToInt()
+
+int
+audio_windows_sink::open_waveout_device (void)
+{
+
+ UINT /*UINT_PTR */ u_device_id;
+ /** Identifier of the waveform-audio output device to open. It can be either a device identifier or a handle of an open waveform-audio input device. You can use the following flag instead of a device identifier.
+ *
+ * Value Meaning
+ * WAVE_MAPPER The function selects a waveform-audio output device capable of playing the given format.
+ */
+ if (d_device_name.empty () || default_device_name () == d_device_name)
+ u_device_id = WAVE_MAPPER;
+ else
+ u_device_id = (UINT) string_to_int (d_device_name);
+ // Open a waveform device for output using event callback.
+
+ unsigned long result;
+ //HWAVEOUT outHandle;
+ WAVEFORMATEX wave_format;
+
+ /* Initialize the WAVEFORMATEX for 16-bit, 44KHz, stereo */
+ wave_format.wFormatTag = WAVE_FORMAT_PCM;
+ wave_format.nChannels = 2;
+ wave_format.nSamplesPerSec = d_sampling_freq; //44100;
+ wave_format.wBitsPerSample = 16;
+ wave_format.nBlockAlign =
+ wave_format.nChannels * (wave_format.wBitsPerSample / 8);
+ wave_format.nAvgBytesPerSec =
+ wave_format.nSamplesPerSec * wave_format.nBlockAlign;
+ wave_format.cbSize = 0;
+
+ /* Open the (preferred) Digital Audio Out device. */
+ result = waveOutOpen (&d_h_waveout, WAVE_MAPPER, &wave_format, (DWORD_PTR) d_wave_write_event, 0, CALLBACK_EVENT | WAVE_ALLOWSYNC); //|WAVE_FORMAT_DIRECT | CALLBACK_EVENT| WAVE_ALLOWSYNC
+ if (result)
+ {
+ fprintf (stderr,
+ "audio_windows_sink: Failed to open waveform output device.\n");
+ perror ("audio_windows_sink: Failed to open waveform output device.");
+ //LocalUnlock(hFormat);
+ //LocalFree(hFormat);
+ //mmioClose(hmmio, 0);
+ return -1;
+ }
+
+ //
+ // Do not Swallow the "open" event.
+ //
+ //WaitForSingleObject(d_wave_write_event, INFINITE);
+
+ // Allocate and lock memory for the header.
+
+ d_h_wave_hdr = GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE,
+ (DWORD) sizeof (WAVEHDR));
+ if (d_h_wave_hdr == NULL)
+ {
+ //GlobalUnlock(hData);
+ //GlobalFree(hData);
+ //fprintf (stderr, "audio_windows_sink: Not enough memory for header.\n");
+ perror ("audio_windows_sink: Not enough memory for header.");
+ return -1;
+ }
+
+ d_lp_wave_hdr = (LPWAVEHDR) GlobalLock (d_h_wave_hdr);
+ if (d_lp_wave_hdr == NULL)
+ {
+ //GlobalUnlock(hData);
+ //GlobalFree(hData);
+ //fprintf (stderr, "audio_windows_sink: Failed to lock memory for header.\n");
+ perror ("audio_windows_sink: Failed to lock memory for header.");
+ return -1;
+ }
+ //d_lp_wave_hdr->dwFlags = WHDR_DONE;
+ return 0;
+}
+
+int
+audio_windows_sink::write_waveout (HPSTR lp_data, DWORD dw_data_size)
+{
+ UINT w_result;
+ int teller = 100;
+ // After allocation, set up and prepare header.
+ /*while ((d_lp_wave_hdr->dwFlags & WHDR_DONE)==0 && teller>0)
+ {
+ teller--;
+ Sleep(1);
+ } */
+ // Wait until previous wave write completes (first event is the open event).
+ WaitForSingleObject (d_wave_write_event, 100); //INFINITE
+ d_lp_wave_hdr->lpData = lp_data;
+ d_lp_wave_hdr->dwBufferLength = dw_data_size;
+ d_lp_wave_hdr->dwFlags = 0L;
+ /* Clear the WHDR_DONE bit (which the driver set last time that
+ this WAVEHDR was sent via waveOutWrite and was played). Some
+ drivers need this to be cleared */
+ //d_lp_wave_hdr->dwFlags &= ~WHDR_DONE;
+
+ d_lp_wave_hdr->dwLoops = 0L;
+ w_result =
+ waveOutPrepareHeader (d_h_waveout, d_lp_wave_hdr, sizeof (WAVEHDR));
+ if (w_result != 0)
+ {
+ //GlobalUnlock( hData);
+ //GlobalFree(hData);
+ //fprintf (stderr, "audio_windows_sink: Failed to waveOutPrepareHeader. error %i\n",w_result);
+ perror ("audio_windows_sink: Failed to waveOutPrepareHeader");
+ }
+ // Now the data block can be sent to the output device. The
+ // waveOutWrite function returns immediately and waveform
+ // data is sent to the output device in the background.
+ //while (! readyforplayback) Sleep(1);
+ //readyforplayback=false;
+ //
+ //
+
+ w_result = waveOutWrite (d_h_waveout, d_lp_wave_hdr, sizeof (WAVEHDR));
+ if (w_result != 0)
+ {
+ //GlobalUnlock( hData);
+ //GlobalFree(hData);
+ //fprintf (stderr, "audio_windows_sink: Failed to write block to device.error %i\n",w_result);
+ perror ("audio_windows_sink: Failed to write block to device");
+ switch (w_result)
+ {
+ case MMSYSERR_INVALHANDLE:
+ fprintf (stderr, "Specified device handle is invalid. \n");
+ break;
+ case MMSYSERR_NODRIVER:
+ fprintf (stderr, " No device driver is present. \n");
+ break;
+ case MMSYSERR_NOMEM:
+ fprintf (stderr, " Unable to allocate or lock memory. \n");
+ break;
+ case WAVERR_UNPREPARED:
+ fprintf (stderr,
+ " The data block pointed to by the pwh parameter hasn't been prepared. \n");
+ break;
+ default:
+ fprintf (stderr, "Unknown error %i\n", w_result);
+ }
+ waveOutUnprepareHeader (d_h_waveout, d_lp_wave_hdr, sizeof (WAVEHDR));
+ return -1;
+ }
+ // WaitForSingleObject(d_wave_write_event, INFINITE);
+ return 0;
+}
diff --git a/gr-audio/lib/windows/audio_windows_sink.h b/gr-audio/lib/windows/audio_windows_sink.h
new file mode 100644
index 000000000..6819bd448
--- /dev/null
+++ b/gr-audio/lib/windows/audio_windows_sink.h
@@ -0,0 +1,72 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_WINDOWS_SINK_H
+#define INCLUDED_AUDIO_WINDOWS_SINK_H
+
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX // stops windef.h defining max/min under cygwin
+
+#include <windows.h>
+#include <mmsystem.h>
+
+#include <gr_audio_sink.h>
+#include <string>
+
+/*!
+ * \brief audio sink using winmm mmsystem (win32 only)
+ *
+ * input signature is one or two streams of floats.
+ * Input samples must be in the range [-1,1].
+ */
+
+class audio_windows_sink : public audio_sink
+{
+ int d_sampling_freq;
+ std::string d_device_name;
+ int d_fd;
+ short *d_buffer;
+ int d_chunk_size;
+ HWAVEOUT d_h_waveout;
+ HGLOBAL d_h_wave_hdr;
+ LPWAVEHDR d_lp_wave_hdr;
+ HANDLE d_wave_write_event;
+
+protected:
+ int
+ string_to_int (const std::string & s);
+ int
+ open_waveout_device (void);
+ int
+ write_waveout (HPSTR lp_data, DWORD dw_data_size);
+
+public:
+ audio_windows_sink (int sampling_freq, const std::string device_name = "");
+ ~audio_windows_sink ();
+
+ int
+ work (int noutput_items,
+ gr_vector_const_void_star & input_items,
+ gr_vector_void_star & output_items);
+};
+
+#endif /* INCLUDED_AUDIO_WINDOWS_SINK_H */
diff --git a/gr-audio/lib/windows/audio_windows_source.cc b/gr-audio/lib/windows/audio_windows_source.cc
new file mode 100644
index 000000000..75b0a33bb
--- /dev/null
+++ b/gr-audio/lib/windows/audio_windows_source.cc
@@ -0,0 +1,205 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gr_audio_registry.h"
+#include <audio_windows_source.h>
+#include <gr_io_signature.h>
+//include <sys/soundcard.h>
+//include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <iostream>
+#include <stdexcept>
+
+AUDIO_REGISTER_SOURCE(REG_PRIO_HIGH, windows)(
+ int sampling_rate, const std::string &device_name, bool
+){
+ return audio_source::sptr(new audio_windows_source(sampling_rate, device_name));
+}
+
+static const double CHUNK_TIME = 0.005; // 5 ms
+
+// FIXME these should query some kind of user preference
+
+static std::string
+default_device_name ()
+{
+ return "/dev/dsp";
+}
+
+audio_windows_source::audio_windows_source (int sampling_freq, const std::string device_name)
+ : gr_sync_block ("audio_windows_source",
+ gr_make_io_signature (0, 0, 0),
+ gr_make_io_signature (1, 2, sizeof (float))),
+ d_sampling_freq (sampling_freq),
+ d_device_name (device_name.empty ()? default_device_name () : device_name),
+ d_fd (-1), d_buffer (0), d_chunk_size (0)
+{
+ //FIXME TODO implement me
+#if 0
+ if ((d_fd = open (d_device_name.c_str (), O_RDONLY)) < 0)
+ {
+ fprintf (stderr, "audio_windows_source: ");
+ perror (d_device_name.c_str ());
+ throw
+ std::runtime_error ("audio_windows_source");
+ }
+
+ d_chunk_size = (int) (d_sampling_freq * CHUNK_TIME);
+ set_output_multiple (d_chunk_size);
+
+ d_buffer = new short[d_chunk_size * 2];
+
+ int format = AFMT_S16_NE;
+ int orig_format = format;
+ if (ioctl (d_fd, SNDCTL_DSP_SETFMT, &format) < 0)
+ {
+ std::
+ cerr << "audio_windows_source: " << d_device_name <<
+ " ioctl failed\n";
+ perror (d_device_name.c_str ());
+ throw
+ std::runtime_error ("audio_windows_source");
+ }
+
+ if (format != orig_format)
+ {
+ fprintf (stderr, "audio_windows_source: unable to support format %d\n",
+ orig_format);
+ fprintf (stderr, " card requested %d instead.\n", format);
+ }
+
+ // set to stereo no matter what. Some hardware only does stereo
+ int channels = 2;
+ if (ioctl (d_fd, SNDCTL_DSP_CHANNELS, &channels) < 0 || channels != 2)
+ {
+ perror ("audio_windows_source: could not set STEREO mode");
+ throw
+ std::runtime_error ("audio_windows_source");
+ }
+
+ // set sampling freq
+ int sf = sampling_freq;
+ if (ioctl (d_fd, SNDCTL_DSP_SPEED, &sf) < 0)
+ {
+ std::cerr << "audio_windows_source: "
+ << d_device_name << ": invalid sampling_freq "
+ << sampling_freq << "\n";
+ sampling_freq = 8000;
+ if (ioctl (d_fd, SNDCTL_DSP_SPEED, &sf) < 0)
+ {
+ std::
+ cerr <<
+ "audio_windows_source: failed to set sampling_freq to 8000\n";
+ throw
+ std::runtime_error ("audio_windows_source");
+ }
+ }
+#endif
+}
+
+audio_windows_source::~audio_windows_source ()
+{
+ /*close (d_fd);
+ delete [] d_buffer;
+ */
+}
+
+int
+audio_windows_source::work (int noutput_items,
+ gr_vector_const_void_star & input_items,
+ gr_vector_void_star & output_items)
+{
+ //FIXME TODO implement me
+#if 0
+ float *f0 = (float *) output_items[0];
+ float *f1 = (float *) output_items[1]; // will be invalid if this is mono output
+
+ const int shorts_per_item = 2; // L + R
+ const int bytes_per_item = shorts_per_item * sizeof (short);
+
+ // To minimize latency, never return more than CHUNK_TIME
+ // worth of samples per call to work.
+ // FIXME, we need an API to set this value
+
+ noutput_items = std::min (noutput_items, d_chunk_size);
+
+ int base = 0;
+ int ntogo = noutput_items;
+
+ while (ntogo > 0)
+ {
+ int nbytes = std::min (ntogo, d_chunk_size) * bytes_per_item;
+ int result_nbytes = read (d_fd, d_buffer, nbytes);
+
+ if (result_nbytes < 0)
+ {
+ perror ("audio_windows_source");
+ return -1; // say we're done
+ }
+
+ if ((result_nbytes & (bytes_per_item - 1)) != 0)
+ {
+ fprintf (stderr, "audio_windows_source: internal error.\n");
+ throw std::runtime_error ("internal error");
+ }
+
+ int result_nitems = result_nbytes / bytes_per_item;
+
+ // now unpack samples into output streams
+
+ switch (output_items.size ())
+ {
+ case 1: // mono output
+ for (int i = 0; i < result_nitems; i++)
+ {
+ f0[base + i] = d_buffer[2 * i + 0] * (1.0 / 32767);
+ }
+ break;
+
+ case 2: // stereo output
+ for (int i = 0; i < result_nitems; i++)
+ {
+ f0[base + i] = d_buffer[2 * i + 0] * (1.0 / 32767);
+ f1[base + i] = d_buffer[2 * i + 1] * (1.0 / 32767);
+ }
+ break;
+
+ default:
+ assert (0);
+ }
+
+ ntogo -= result_nitems;
+ base += result_nitems;
+ }
+
+ return noutput_items - ntogo;
+#endif
+ return -1; // EOF
+}
diff --git a/gr-audio/lib/windows/audio_windows_source.h b/gr-audio/lib/windows/audio_windows_source.h
new file mode 100644
index 000000000..36311968d
--- /dev/null
+++ b/gr-audio/lib/windows/audio_windows_source.h
@@ -0,0 +1,56 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004-2011 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 3, 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., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_WINDOWS_SOURCE_H
+#define INCLUDED_AUDIO_WINDOWS_SOURCE_H
+
+#include <gr_audio_source.h>
+#include <string>
+
+/*!
+ * \brief audio source using winmm mmsystem (win32 only)
+ *
+ * Output signature is one or two streams of floats.
+ * Output samples will be in the range [-1,1].
+ */
+
+class audio_windows_source : public audio_source
+{
+
+ int d_sampling_freq;
+ std::string d_device_name;
+ int d_fd;
+ short *d_buffer;
+ int d_chunk_size;
+
+public:
+ audio_windows_source (int sampling_freq, const std::string device_name = "");
+
+ ~audio_windows_source ();
+
+ int
+ work (int noutput_items,
+ gr_vector_const_void_star & input_items,
+ gr_vector_void_star & output_items);
+};
+
+#endif /* INCLUDED_AUDIO_WINDOWS_SOURCE_H */