summaryrefslogtreecommitdiff
path: root/gr-audio-osx
diff options
context:
space:
mode:
authorjcorgan2006-08-03 04:51:51 +0000
committerjcorgan2006-08-03 04:51:51 +0000
commit5d69a524f81f234b3fbc41d49ba18d6f6886baba (patch)
treeb71312bf7f1e8d10fef0f3ac6f28784065e73e72 /gr-audio-osx
downloadgnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.tar.gz
gnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.tar.bz2
gnuradio-5d69a524f81f234b3fbc41d49ba18d6f6886baba.zip
Houston, we have a trunk.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@3122 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'gr-audio-osx')
-rw-r--r--gr-audio-osx/AUTHORS1
-rw-r--r--gr-audio-osx/ChangeLog25
-rw-r--r--gr-audio-osx/Makefile.am25
-rw-r--r--gr-audio-osx/README_OSX61
-rw-r--r--gr-audio-osx/TODO51
-rw-r--r--gr-audio-osx/src/Makefile.am88
-rw-r--r--gr-audio-osx/src/audio_osx.h49
-rw-r--r--gr-audio-osx/src/audio_osx.i95
-rw-r--r--gr-audio-osx/src/audio_osx_sink.cc398
-rw-r--r--gr-audio-osx/src/audio_osx_sink.h96
-rw-r--r--gr-audio-osx/src/audio_osx_source.cc978
-rw-r--r--gr-audio-osx/src/audio_osx_source.h132
-rw-r--r--gr-audio-osx/src/circular_buffer.h326
-rw-r--r--gr-audio-osx/src/mld_threads.h255
-rwxr-xr-xgr-audio-osx/src/qa_osx.py40
-rw-r--r--gr-audio-osx/src/run_tests.in47
-rwxr-xr-xgr-audio-osx/src/test_audio_loop.py65
17 files changed, 2732 insertions, 0 deletions
diff --git a/gr-audio-osx/AUTHORS b/gr-audio-osx/AUTHORS
new file mode 100644
index 000000000..ecc9577a4
--- /dev/null
+++ b/gr-audio-osx/AUTHORS
@@ -0,0 +1 @@
+Michael Dickens <mdickens@nd.edu> NCIP Lab, University of Notre Dame
diff --git a/gr-audio-osx/ChangeLog b/gr-audio-osx/ChangeLog
new file mode 100644
index 000000000..0ae5acf78
--- /dev/null
+++ b/gr-audio-osx/ChangeLog
@@ -0,0 +1,25 @@
+2006-04-22 Michael Dickens <mdickens@nd.edu>
+ NCIP Lab, University of Notre Dame
+
+ Everything is new.
+
+#
+# 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 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
diff --git a/gr-audio-osx/Makefile.am b/gr-audio-osx/Makefile.am
new file mode 100644
index 000000000..077d2aa1f
--- /dev/null
+++ b/gr-audio-osx/Makefile.am
@@ -0,0 +1,25 @@
+#
+# Copyright 2004,2006 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+EXTRA_DIST = README_OSX
+SUBDIRS = src
diff --git a/gr-audio-osx/README_OSX b/gr-audio-osx/README_OSX
new file mode 100644
index 000000000..2a98e96bb
--- /dev/null
+++ b/gr-audio-osx/README_OSX
@@ -0,0 +1,61 @@
+Michael Dickens
+2006-Apr-30
+
+0) This module should compile and install in the same manner as the
+other GNURadio modules (e.g. gnuradio-core), with the possible
+exception that GNU libtool 1.5.20 or newer should be installed and
+used (if not first in the path) via "make LIBTOOL=/..." and so forth.
+Version 1.5.10 has failed making, and while picking version 1.5.20 is
+somewhat arbitrary, the newer version compiles and installs easily
+under OSX.
+
+1) This module should be automatically loaded by the Python command
+"from gr import audio". The audio import script will automatically
+select gr.audio_osx if it is available (though it will try to import
+ALSA first, then OSS, then OSX, and finally WINDOWS audio modules, in
+that order). If that import command doesn't work, try reinstalling
+gnuradio-core from scratch followed by gr-audio-osx.
+
+2) Instantiation arguments for either source or sink are:
+
+* sample_rate : integer : default == 44100
+ OSX converts the integer sample rate to a double internally; it
+ would be nice to have this input as a double natively, but that
+ doesn't work with other audio devices.
+
+* device_name : string : default == "2"
+ For OSX, the device name should be an integer string. This value is
+ the maximum number of channels to allocate (for input or output).
+ In the "source" case (input), the actual number of channels will be
+ whatever is available on that current system input device. In the
+ "sink" case (output), OSX will convert the provided channels into
+ whatever format is required by the current system output device.
+ For example, "3" would try to setup for 3 input or output channels.
+ NOTE that this is a very different use than that for other audio
+ modules (though they can interpret the number of channels from this
+ argument).
+
+* do_block : boolean : default == true
+ If the data transfer buffer between OSX internals and GNURadio gets
+ full, either block (true) or overwrite (false) depending on this
+ variable.
+
+The following are currently non-standard arguments:
+
+* channel_config : integer : default == -1
+ An enum (internally) describing the channel configuration. Not
+ currently used, but rather reserved for future expansion.
+
+* max_sample_count : integer : default == -1
+ The maximum number of samples to buffer between OSX internals and
+ GNURadio. The value -1 is mapped to 1 second's worth of data.
+
+3) When the buffer is full and do_block is false and new data comes
+in, the oldest data will be overwritten. The source will print out
+"iX" each time this happens; the sink will print out "oX".
+
+4) In the "src" directory is a python script "test_audio_loop" which
+connects the default audio input device to the default audio output
+device. This script is very useful in testing that audio is correctly
+installed and both the source and sink are functional. This script is
+not run by "make check".
diff --git a/gr-audio-osx/TODO b/gr-audio-osx/TODO
new file mode 100644
index 000000000..e36ed2b25
--- /dev/null
+++ b/gr-audio-osx/TODO
@@ -0,0 +1,51 @@
+List as of 2006-Apr-22, by Michael Dickens, primary author
+
+* Change buffering to use gr_buffer's and necessary related classes.
+ Currently uses a circular_buffer I wrote myself (in
+ ./src/circular_buffer.h ), which hides the "circular" part from the
+ user but otherwise is very fast as well as thread safe.
+
+* A current limitation of this implementation is that the user cannot
+ dynamically switch audio devices (input or output) during playback
+ and use the new device without stopping the current executing GR and
+ restarting the process. I would like to figure out how to get a
+ CoreAudio "listener" for when the default hardware input / output
+ device changes (e.g. when switched by the user in the "Sound" system
+ preference pane). The code in ./src/audio-osx-source.cc creates
+ listeners for the AudioUnit being used as well as the Hardware
+ device, but these for some reason don't do the trick. It's possible
+ that the Python framework doesn't allow for one to do this.
+
+* In both the source and sink, move the code which defines the "Audio
+ Stream Basic Description" (ASBD) to a routine to do this as needed
+ as start-up as well as in a callback ("listener") if the default
+ device changes.
+
+* Tweak the mutex (d_internal) to only where it is truly needed
+ (around class-specific variables used across all threads) in order
+ to improve performance. Currently the mutex is used at the
+ start and end of any function requiring access to the class variables.
+
+* Change the instantiation arguments ... once those arguments are
+ finalized. Right now gr.audio.source () takes the sample rate (int
+ - should be double), the device name (std::string), a boolean for
+ blocking or not, and 2 completely non-standard ones for channel
+ configuration and the maximum sample count to buffer. These are
+ reasonable for OSX, but might not be for other OSs. Nothing to do
+ right now but wait and discuss.
+
+* Once the previous issue has been resolved, then the current method
+ of determining the number of channels needs to be updated.
+ Currently the "device_name" for OSX should contain a numeric string
+ for the maximum number of channels to use (e.g. "3" means 3
+ channels, generally mapped to 2 channel stereo by OSX). The
+ "device_name" is generally input via "-O" in Python scripts, so "-O
+ 3" would allow for 3 incoming output channels (or fewer). In theory
+ the "channel_config" argument would make more sense for determining
+ channel usage. Example config strings might be "2.1" for stereo w/
+ subwoofer (3 channels), "5.1" or "6.1" for various surround w/
+ subwoofer (6 & 7 channels, respectively). OSX can handle all sorts
+ of channel configurations, but the names for these will be different
+ than those use for OSS or ALMA or Windows ... thus the need for a
+ common naming scheme for all cared-about configurations, possibly
+ with options for other OS-specific options.
diff --git a/gr-audio-osx/src/Makefile.am b/gr-audio-osx/src/Makefile.am
new file mode 100644
index 000000000..54cc32720
--- /dev/null
+++ b/gr-audio-osx/src/Makefile.am
@@ -0,0 +1,88 @@
+#
+# 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 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+# Install this stuff so that it ends up as the gnuradio.audio_osx module
+# This usually ends up at:
+# ${prefix}/lib/python${python_version}/site-packages/gnuradio
+
+ourpythondir = $(grpythondir)
+ourlibdir = $(grpyexecdir)
+
+EXTRA_DIST = run_tests.in
+TESTS = run_tests
+
+LOCAL_IFILES = \
+ audio_osx.i
+
+NON_LOCAL_IFILES = \
+ $(top_srcdir)/gnuradio-core/src/lib/swig/gnuradio.i
+
+ALL_IFILES = \
+ $(LOCAL_IFILES) \
+ $(NON_LOCAL_IFILES)
+
+BUILT_SOURCES = \
+ audio_osx.cc \
+ audio_osx.py
+
+ourpython_PYTHON = \
+ audio_osx.py
+
+INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS)
+
+SWIGCPPPYTHONARGS = -python $(PYTHON_CPPFLAGS) $(STD_DEFINES_AND_INCLUDES)
+
+ourlib_LTLIBRARIES = _audio_osx.la
+
+_audio_osx_la_SOURCES = \
+ audio_osx.cc \
+ audio_osx_sink.cc \
+ audio_osx_source.cc
+
+noinst_HEADERS = \
+ audio_osx.h \
+ circular_buffer.h \
+ mld_threads.h
+
+grinclude_HEADERS = \
+ audio_osx_sink.h \
+ audio_osx_source.h
+
+swiginclude_HEADERS = \
+ $(LOCAL_IFILES)
+
+_audio_osx_la_LIBADD = \
+ $(PYTHON_LDFLAGS) \
+ $(GNURADIO_CORE_LIBS) \
+ -lstdc++
+
+_audio_osx_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version -framework AudioUnit -framework CoreAudio -framework AudioToolbox
+
+audio_osx.cc audio_osx.py: audio_osx.i
+ $(SWIG) $(SWIGCPPPYTHONARGS) -module audio_osx -o audio_osx.cc $<
+
+noinst_PYTHON = qa_osx.py test_audio_loop.py
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) *~ *.pyc run_tests *.loT
+
+CONFIG_CLEAN_FILES = Makefile.in run_tests *.loT
diff --git a/gr-audio-osx/src/audio_osx.h b/gr-audio-osx/src/audio_osx.h
new file mode 100644
index 000000000..f92ca3fcd
--- /dev/null
+++ b/gr-audio-osx/src/audio_osx.h
@@ -0,0 +1,49 @@
+/* -*- 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_OSX_H
+#define INCLUDED_AUDIO_OSX_H
+
+#define CheckErrorAndThrow(err,what,throw_str) \
+if (err) { \
+ OSStatus error = static_cast<OSStatus>(err); \
+ fprintf (stderr, "%s\n Error# %ld ('%4s')\n %s:%d\n", \
+ what, error, (char*)(&err), __FILE__, __LINE__); \
+ fflush (stdout); \
+ throw std::runtime_error (throw_str); \
+}
+
+#define CheckError(err,what) \
+if (err) { \
+ OSStatus error = static_cast<OSStatus>(err); \
+ fprintf (stderr, "%s\n Error# %ld ('%4s')\n %s:%d\n", \
+ what, error, (char*)(&err), __FILE__, __LINE__); \
+ fflush (stdout); \
+}
+
+#ifdef WORDS_BIGENDIAN
+#define GR_PCM_ENDIANNESS kLinearPCMFormatFlagIsBigEndian
+#else
+#define GR_PCM_ENDIANNESS 0
+#endif
+
+#endif /* INCLUDED_AUDIO_OSX_H */
diff --git a/gr-audio-osx/src/audio_osx.i b/gr-audio-osx/src/audio_osx.i
new file mode 100644
index 000000000..9ced419fa
--- /dev/null
+++ b/gr-audio-osx/src/audio_osx.i
@@ -0,0 +1,95 @@
+/* -*- 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+%feature("autodoc","1");
+
+%include "exception.i"
+%import "gnuradio.i" // the common stuff
+
+%{
+#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix
+#include "audio_osx_sink.h"
+#include "audio_osx_source.h"
+#include <stdexcept>
+%}
+
+// ----------------------------------------------------------------
+
+GR_SWIG_BLOCK_MAGIC(audio_osx,sink)
+
+audio_osx_sink_sptr
+audio_osx_make_sink (int sample_rate = 44100,
+ const std::string device_name = "2",
+ bool do_block = TRUE,
+ int channel_config = -1,
+ int max_sample_count = -1
+ ) throw (std::runtime_error);
+
+class audio_osx_sink : public gr_sync_block {
+ protected:
+ 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);
+
+ public:
+ ~audio_osx_sink ();
+
+ bool start ();
+ bool stop ();
+
+ int work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+// ----------------------------------------------------------------
+
+GR_SWIG_BLOCK_MAGIC(audio_osx,source)
+
+audio_osx_source_sptr
+audio_osx_make_source (int sample_rate = 44100,
+ const std::string device_name = "2",
+ bool do_block = TRUE,
+ int channel_config = -1,
+ int max_sample_count = -1
+ ) throw (std::runtime_error);
+
+class audio_osx_source : public gr_sync_block {
+ protected:
+ audio_osx_source (int sample_rate = 44100,
+ const std::string device_name = "2",
+ bool do_block = TRUE,
+ int channel_config = -1,
+ int max_sample_count = -1);
+
+ public:
+ ~audio_osx_source ();
+
+ bool start ();
+ bool stop ();
+
+ int work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
diff --git a/gr-audio-osx/src/audio_osx_sink.cc b/gr-audio-osx/src/audio_osx_sink.cc
new file mode 100644
index 000000000..14b4a5130
--- /dev/null
+++ b/gr-audio-osx/src/audio_osx_sink.cc
@@ -0,0 +1,398 @@
+/* -*- 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define _USE_OMNI_THREADS_
+
+#include <audio_osx_sink.h>
+#include <gr_io_signature.h>
+#include <stdexcept>
+#include <audio_osx.h>
+
+#define _OSX_AU_DEBUG_ 0
+
+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) {
+ fprintf (stderr, "Invalid Sample Rate: %d\n", sample_rate);
+ throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
+ } else
+ d_sample_rate = (Float64) sample_rate;
+
+ if (channel_config <= 0 & channel_config != -1) {
+ fprintf (stderr, "Invalid Channel Config: %d\n", channel_config);
+ 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) {
+ fprintf (stderr, "Error Converting Device Name: %d\n", errno);
+ 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) {
+ fprintf (stderr, "Invalid Max Sample Count: %d\n", max_sample_count);
+ 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
+ ComponentDescription desc;
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ Component comp = FindNextComponent (NULL, &desc);
+ if (comp == NULL) {
+ fprintf (stderr, "FindNextComponent Error\n");
+ throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
+ }
+
+ err = OpenAComponent (comp, &d_OutputAU);
+ CheckErrorAndThrow (err, "OpenAComponent", "audio_osx_sink::audio_osx_sink");
+
+// 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_internal = new mld_mutex ();
+ if (d_internal == NULL)
+ CheckErrorAndThrow (errno, "new mld_mutex (internal)",
+ "audio_osx_source::audio_osx_source");
+
+ d_cond_data = new mld_condition ();
+ if (d_cond_data == NULL)
+ CheckErrorAndThrow (errno, "new mld_condition (data)",
+ "audio_osx_source::audio_osx_source");
+
+// initialize the AU for output
+
+ err = AudioUnitInitialize (d_OutputAU);
+ CheckErrorAndThrow (err, "AudioUnitInitialize",
+ "audio_osx_sink::audio_osx_sink");
+
+#if _OSX_AU_DEBUG_
+ fprintf (stderr, "audio_osx_sink Parameters:\n");
+ fprintf (stderr, " Sample Rate is %g\n", d_sample_rate);
+ fprintf (stderr, " Number of Channels is %ld\n", d_n_channels);
+ fprintf (stderr, " Max # samples to store per channel is %ld",
+ d_max_sample_count);
+#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);
+ CloseComponent (d_OutputAU);
+
+// 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_internal;
+ delete d_cond_data;
+}
+
+audio_osx_sink_sptr
+audio_osx_make_sink (int sampling_freq,
+ const std::string dev,
+ bool do_block,
+ int channel_config,
+ int max_sample_count)
+{
+ return audio_osx_sink_sptr (new audio_osx_sink (sampling_freq,
+ dev,
+ do_block,
+ channel_config,
+ max_sample_count));
+}
+
+int
+audio_osx_sink::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ d_internal->wait ();
+
+ /* 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_
+ fprintf (stderr, "work1: qSC = %ld, lMC = %ld, dmSC = %ld, nOI = %d\n",
+ d_queueSampleCount, l_max_count, d_max_sample_count, noutput_items);
+#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
+ d_internal->post ();
+// block until there is data to return
+ d_cond_data->wait ();
+// the condition's signal() was called; acquire control
+// to keep thread safe
+ d_internal->wait ();
+ }
+ }
+ }
+// 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 ("oX", 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_
+ fprintf (stderr, "work2: #OI = %4d, #Cnt = %4ld, mSC = %ld\n",
+ noutput_items, d_queueSampleCount, d_max_sample_count);
+#endif
+
+// release control to allow for other processing parts to run
+ d_internal->post ();
+
+ 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;
+
+ This->d_internal->wait ();
+
+#if _OSX_AU_DEBUG_
+ fprintf (stderr, "cb_in: SC = %4ld, in#F = %4ld\n",
+ This->d_queueSampleCount, inNumberFrames);
+#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) {
+ UInt32 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_
+ fprintf (stderr, "cb_out: SC = %4ld\n", This->d_queueSampleCount);
+#endif
+
+// signal that data is available
+ This->d_cond_data->signal ();
+
+// release control to allow for other processing parts to run
+ This->d_internal->post ();
+
+ return (err);
+}
diff --git a/gr-audio-osx/src/audio_osx_sink.h b/gr-audio-osx/src/audio_osx_sink.h
new file mode 100644
index 000000000..8cd3be0a1
--- /dev/null
+++ b/gr-audio-osx/src/audio_osx_sink.h
@@ -0,0 +1,96 @@
+/* -*- 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_OSX_SINK_H
+#define INCLUDED_AUDIO_OSX_SINK_H
+
+#include <gr_sync_block.h>
+#include <string>
+#include <list>
+#include <AudioUnit/AudioUnit.h>
+#include <circular_buffer.h>
+
+class audio_osx_sink;
+typedef boost::shared_ptr<audio_osx_sink> audio_osx_sink_sptr;
+
+audio_osx_sink_sptr
+audio_osx_make_sink (int sample_rate = 44100,
+ const std::string device_name = "2",
+ bool do_block = true,
+ int channel_config = -1,
+ int max_sample_count = -1);
+
+/*!
+ * \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 gr_sync_block {
+ friend audio_osx_sink_sptr
+ audio_osx_make_sink (int sample_rate,
+ const std::string device_name,
+ bool do_block,
+ int channel_config,
+ int max_sample_count);
+
+ Float64 d_sample_rate;
+ int d_channel_config;
+ UInt32 d_n_channels;
+ UInt32 d_queueSampleCount, d_max_sample_count;
+ bool d_do_block;
+ mld_mutex_ptr d_internal;
+ mld_condition_ptr d_cond_data;
+ circular_buffer<float>** d_buffers;
+
+// AudioUnits and Such
+ AudioUnit d_OutputAU;
+
+protected:
+ 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);
+
+public:
+ ~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-osx/src/audio_osx_source.cc b/gr-audio-osx/src/audio_osx_source.cc
new file mode 100644
index 000000000..2abf1c2a7
--- /dev/null
+++ b/gr-audio-osx/src/audio_osx_source.cc
@@ -0,0 +1,978 @@
+/* -*- 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define _USE_OMNI_THREADS_
+
+#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
+
+void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
+{
+ if (inDesc == NULL) {
+ fprintf (stderr, "PrintStreamDesc: Can't print a NULL desc!\n");
+ return;
+ }
+
+ fprintf (stderr, " Sample Rate : %g\n", inDesc->mSampleRate);
+ fprintf (stderr, " Format ID : %4s\n", (char*)&inDesc->mFormatID);
+ fprintf (stderr, " Format Flags : %lX\n", inDesc->mFormatFlags);
+ fprintf (stderr, " Bytes per Packet : %ld\n", inDesc->mBytesPerPacket);
+ fprintf (stderr, " Frames per Packet : %ld\n", inDesc->mFramesPerPacket);
+ fprintf (stderr, " Bytes per Frame : %ld\n", inDesc->mBytesPerFrame);
+ fprintf (stderr, " Channels per Frame : %ld\n", inDesc->mChannelsPerFrame);
+ fprintf (stderr, " Bits per Channel : %ld\n", inDesc->mBitsPerChannel);
+}
+
+// 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) {
+ fprintf (stderr, "Invalid Sample Rate: %d\n", sample_rate);
+ throw std::invalid_argument ("audio_osx_source::audio_osx_source");
+ } else
+ d_outputSampleRate = (Float64) sample_rate;
+
+ if (channel_config <= 0 & channel_config != -1) {
+ fprintf (stderr, "Invalid Channel Config: %d\n", channel_config);
+ 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) {
+ fprintf (stderr, "Error Converting Device Name: %d\n", errno);
+ 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) {
+ fprintf (stderr, "Invalid Max Sample Count: %d\n", max_sample_count);
+ throw std::invalid_argument ("audio_osx_source::audio_osx_source");
+ }
+
+ d_max_sample_count = max_sample_count;
+
+#if _OSX_AU_DEBUG_
+ fprintf (stderr, "source(): max # samples = %ld", d_max_sample_count);
+#endif
+
+ OSStatus err = noErr;
+
+// create the default AudioUnit for input
+
+// Open the default input unit
+ ComponentDescription InputDesc;
+
+ InputDesc.componentType = kAudioUnitType_Output;
+ InputDesc.componentSubType = kAudioUnitSubType_HALOutput;
+ InputDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ InputDesc.componentFlags = 0;
+ InputDesc.componentFlagsMask = 0;
+
+ Component comp = FindNextComponent (NULL, &InputDesc);
+ if (comp == NULL) {
+ fprintf (stderr, "FindNextComponent Error\n");
+ throw std::runtime_error ("audio_osx_source::audio_osx_source");
+ }
+
+ err = OpenAComponent (comp, &d_InputAU);
+ CheckErrorAndThrow (err, "OpenAComponent",
+ "audio_osx_source::audio_osx_source");
+
+ 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) {
+ fprintf (stderr, "Selected Audio Device does not support Input.\n");
+ 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_
+ fprintf (stderr, "---- Device Stream Format ----\n" );
+ 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_
+ fprintf (stderr, "---- Client Stream Format ----\n");
+ 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_internal = new mld_mutex ();
+ if (d_internal == NULL)
+ CheckErrorAndThrow (errno, "new mld_mutex (internal)",
+ "audio_osx_source::audio_osx_source");
+
+ d_cond_data = new mld_condition ();
+ if (d_cond_data == NULL)
+ CheckErrorAndThrow (errno, "new mld_condition (data)",
+ "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_
+ fprintf (stderr, "audio_osx_source Parameters:\n");
+ fprintf (stderr, " Device Sample Rate is %g\n", d_deviceSampleRate);
+ fprintf (stderr, " User Sample Rate is %g\n", d_outputSampleRate);
+ fprintf (stderr, " Max Sample Count is %ld\n", d_max_sample_count);
+ fprintf (stderr, " # Device Channels is %ld\n", d_n_deviceChannels);
+ fprintf (stderr, " # Max Channels is %ld\n", d_n_max_channels);
+ fprintf (stderr, " Device Buffer Size is Frames = %ld\n",
+ d_deviceBufferSizeFrames);
+ fprintf (stderr, " Lead Size is Frames = %ld\n",
+ d_leadSizeFrames);
+ fprintf (stderr, " Trail Size is Frames = %ld\n",
+ d_trailSizeFrames);
+ fprintf (stderr, " Input Buffer Size is Frames = %ld\n",
+ d_inputBufferSizeFrames);
+ fprintf (stderr, " Output Buffer Size is Frames = %ld\n",
+ d_outputBufferSizeFrames);
+#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");
+
+ err = CloseComponent (d_InputAU);
+ CheckError (err, "~audio_osx_source: CloseComponent");
+
+// 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_internal;
+ delete d_cond_data;
+}
+
+audio_osx_source_sptr
+audio_osx_make_source (int sampling_freq,
+ const std::string device_name,
+ bool do_block,
+ int channel_config,
+ int max_sample_count)
+{
+ return audio_osx_source_sptr (new audio_osx_source (sampling_freq,
+ device_name,
+ do_block,
+ channel_config,
+ max_sample_count));
+}
+
+bool
+audio_osx_source::check_topology (int ninputs, int noutputs)
+{
+// check # inputs to make sure it's valid
+ if (ninputs != 0) {
+ fprintf (stderr, "audio_osx_source::check_topology(): "
+ "number of input streams provided (%d) should be 0.\n",
+ ninputs);
+ 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)) {
+ fprintf (stderr, "audio_osx_source::check_topology(): "
+ "number of output streams provided (%d) should be in "
+ "[1,%ld] for the selected audio device.\n",
+ noutputs, d_n_max_channels);
+ 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_
+ fprintf (stderr, "chk_topo: Actual # user output channels = %d\n",
+ noutputs);
+#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
+ d_internal->wait ();
+
+#if _OSX_AU_DEBUG_
+ fprintf (stderr, "work1: SC = %4ld, #OI = %4d, #Chan = %ld\n",
+ d_queueSampleCount, noutput_items, output_items.size());
+#endif
+
+// ?: always block until there is something to output from the source
+// or return anything that is available, even if it's less than desired?
+
+ 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
+ d_internal->post ();
+// block until there is data to return
+ d_cond_data->wait ();
+// the condition's signal() was called; acquire control
+// to keep thread safe
+ d_internal->wait ();
+ }
+ } else {
+// not enough data & not blocking; return nothing
+ return (0);
+ }
+ }
+ actual_noutput_items = d_queueSampleCount;
+ }
+
+ int l_counter = (int) output_items.size();
+
+// get the items from the circular buffers
+ while (--l_counter >= 0) {
+ UInt32 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) {
+ fprintf (stderr, "audio_osx_source::work(): "
+ "number of available items changing "
+ "unexpectedly; expecting %ld, got %ld.\n",
+ actual_noutput_items, t_n_output_items);
+ throw std::runtime_error ("audio_osx_source::work()");
+ }
+ }
+
+ d_queueSampleCount -= actual_noutput_items;
+
+#if _OSX_AU_DEBUG_
+ fprintf (stderr, "work2: SC = %4ld, act#OI = %4ld\n",
+ d_queueSampleCount, actual_noutput_items);
+#endif
+
+// release control to allow for other processing parts to run
+ d_internal->post ();
+
+ 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_
+ fprintf (stderr, "cc1: io#DP = %ld, TIBSB = %ld, #C = %d\n",
+ *ioNumberDataPackets, totalInputBufferSizeBytes, counter);
+#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;
+ }
+
+ 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);
+
+ This->d_internal->wait ();
+
+#if _OSX_AU_DEBUG_
+ fprintf (stderr, "cb0: in#F = %4ld, inBN = %ld, SC = %4ld\n",
+ inNumberFrames, inBusNumber, This->d_queueSampleCount);
+#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_
+ fprintf (stderr, "cb1: avail: #IF = %ld, #OF = %ld\n",
+ AvailableInputFrames, AvailableOutputFrames);
+#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_
+ fprintf (stderr, "cb2: actual: #IF = %ld, #OF = %ld\n",
+ This->d_n_ActualInputFrames, AvailableOutputFrames);
+ if (This->d_n_ActualInputFrames != AvailableInputFrames)
+ fprintf (stderr, "cb2.1: avail#IF = %ld, actual#IF = %ld\n",
+ AvailableInputFrames, This->d_n_ActualInputFrames);
+#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;
+ 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_
+ fprintf (stderr, "cb5: #OI = %4ld, #Cnt = %4ld, mSC = %ld, \n",
+ ActualOutputFrames, This->d_queueSampleCount,
+ This->d_max_sample_count);
+#endif
+
+// signal that data is available, if appropraite
+ This->d_cond_data->signal ();
+
+// release control to allow for other processing parts to run
+ This->d_internal->post ();
+
+ 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);
+
+ fprintf (stderr, "a_o_s::HardwareListener\n");
+
+// 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;
+
+ fprintf (stderr, "a_o_s::UnitListener\n");
+
+// 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");
+
+ fprintf (stderr, "UnitListener: Input Source changed.\n"
+ "Old Source Output Info:\n");
+ 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");
+
+ fprintf (stderr, "New Source Output Info:\n");
+ 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-osx/src/audio_osx_source.h b/gr-audio-osx/src/audio_osx_source.h
new file mode 100644
index 000000000..1799588ef
--- /dev/null
+++ b/gr-audio-osx/src/audio_osx_source.h
@@ -0,0 +1,132 @@
+/* -*- 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef INCLUDED_AUDIO_OSX_SOURCE_H
+#define INCLUDED_AUDIO_OSX_SOURCE_H
+
+#include <gr_sync_block.h>
+#include <string>
+#include <AudioToolbox/AudioToolbox.h>
+#include <AudioUnit/AudioUnit.h>
+#include <circular_buffer.h>
+
+class audio_osx_source;
+typedef boost::shared_ptr<audio_osx_source> audio_osx_source_sptr;
+
+audio_osx_source_sptr
+audio_osx_make_source (int sample_rate = 44100,
+ const std::string device_name = "",
+ bool do_block = true,
+ int channel_config = -1,
+ int max_sample_count = -1);
+
+/*!
+ * \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 gr_sync_block {
+ friend audio_osx_source_sptr
+ audio_osx_make_source (int sample_rate,
+ const std::string device_name,
+ bool do_block,
+ int channel_config,
+ int max_sample_count);
+
+ 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;
+ mld_mutex_ptr d_internal;
+ mld_condition_ptr d_cond_data;
+ circular_buffer<float>** d_buffers;
+
+// AudioUnits and Such
+ AudioUnit d_InputAU;
+ AudioBufferList* d_InputBuffer;
+ AudioBufferList* d_OutputBuffer;
+ AudioConverterRef d_AudioConverter;
+
+protected:
+ 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);
+
+public:
+ ~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-osx/src/circular_buffer.h b/gr-audio-osx/src/circular_buffer.h
new file mode 100644
index 000000000..f644e7d02
--- /dev/null
+++ b/gr-audio-osx/src/circular_buffer.h
@@ -0,0 +1,326 @@
+/* -*- 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CIRCULAR_BUFFER_H_
+#define _CIRCULAR_BUFFER_H_
+
+#include "mld_threads.h"
+#include <stdexcept>
+
+#define DO_DEBUG 0
+
+template <class T> class circular_buffer
+{
+private:
+// the buffer to use
+ T* d_buffer;
+
+// the following are in Items (type T)
+ UInt32 d_bufLen_I, d_readNdx_I, d_writeNdx_I;
+ UInt32 d_n_avail_write_I, d_n_avail_read_I;
+
+// stuff to control access to class internals
+ mld_mutex_ptr d_internal;
+ mld_condition_ptr d_readBlock, 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 (UInt32 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 ();
+#if DO_DEBUG
+ fprintf (stderr, "c_b(): buf len (items) = %ld, "
+ "doWriteBlock = %s, doFullRead = %s\n", d_bufLen_I,
+ (d_doWriteBlock ? "true" : "false"),
+ (d_doFullRead ? "true" : "false"));
+#endif
+ };
+
+ ~circular_buffer () {
+ delete_mutex_cond ();
+ delete [] d_buffer;
+ };
+
+ inline UInt32 n_avail_write_items () {
+ d_internal->lock ();
+ UInt32 retVal = d_n_avail_write_I;
+ d_internal->unlock ();
+ return (retVal);
+ };
+
+ inline UInt32 n_avail_read_items () {
+ d_internal->lock ();
+ UInt32 retVal = d_n_avail_read_I;
+ d_internal->unlock ();
+ return (retVal);
+ };
+
+ inline UInt32 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 ();
+ d_internal = new mld_mutex ();
+ d_readBlock = new mld_condition ();
+ d_writeBlock = new mld_condition ();
+ };
+
+/*
+ * 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, UInt32 bufLen_I) {
+#if DO_DEBUG
+ fprintf (stderr, "enqueue: buf = %X, bufLen = %ld, #av_wr = %ld, "
+ "#av_rd = %ld.\n", (unsigned int)buf, bufLen_I,
+ d_n_avail_write_I, d_n_avail_read_I);
+#endif
+ if (bufLen_I > d_bufLen_I) {
+ fprintf (stderr, "cannot add buffer longer (%ld"
+ ") than instantiated length (%ld"
+ ").\n", bufLen_I, d_bufLen_I);
+ 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");
+ d_internal->lock ();
+ if (d_doAbort) {
+ d_internal->unlock ();
+ return (2);
+ }
+ if (bufLen_I > d_n_avail_write_I) {
+ if (d_doWriteBlock) {
+ while (bufLen_I > d_n_avail_write_I) {
+#if DO_DEBUG
+ fprintf (stderr, "enqueue: #len > #a, waiting.\n");
+#endif
+ d_internal->unlock ();
+ d_writeBlock->wait ();
+ d_internal->lock ();
+ if (d_doAbort) {
+ d_internal->unlock ();
+#if DO_DEBUG
+ fprintf (stderr, "enqueue: #len > #a, aborting.\n");
+#endif
+ return (2);
+ }
+#if DO_DEBUG
+ fprintf (stderr, "enqueue: #len > #a, done waiting.\n");
+#endif
+ }
+ } else {
+ d_n_avail_read_I = d_bufLen_I - bufLen_I;
+ d_n_avail_write_I = bufLen_I;
+#if DO_DEBUG
+ fprintf (stderr, "circular_buffer::enqueue: overflow\n");
+#endif
+ return (-1);
+ }
+ }
+ UInt32 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->signal ();
+ d_internal->unlock ();
+ return (1);
+ };
+
+/*
+ * 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, UInt32* bufLen_I) {
+#if DO_DEBUG
+ fprintf (stderr, "dequeue: buf = %X, *bufLen = %ld, #av_wr = %ld, "
+ "#av_rd = %ld.\n", (unsigned int)buf, *bufLen_I,
+ d_n_avail_write_I, d_n_avail_read_I);
+#endif
+ 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");
+ UInt32 l_bufLen_I = *bufLen_I;
+ if (l_bufLen_I == 0)
+ return (0);
+ if (l_bufLen_I > d_bufLen_I) {
+ fprintf (stderr, "cannot remove buffer longer (%ld"
+ ") than instantiated length (%ld"
+ ").\n", l_bufLen_I, d_bufLen_I);
+ throw std::runtime_error ("circular_buffer::dequeue()");
+ }
+
+ d_internal->lock ();
+ if (d_doAbort) {
+ d_internal->unlock ();
+ return (2);
+ }
+ if (d_doFullRead) {
+ while (d_n_avail_read_I < l_bufLen_I) {
+#if DO_DEBUG
+ fprintf (stderr, "dequeue: #a < #len, waiting.\n");
+#endif
+ d_internal->unlock ();
+ d_readBlock->wait ();
+ d_internal->lock ();
+ if (d_doAbort) {
+ d_internal->unlock ();
+#if DO_DEBUG
+ fprintf (stderr, "dequeue: #a < #len, aborting.\n");
+#endif
+ return (2);
+ }
+#if DO_DEBUG
+ fprintf (stderr, "dequeue: #a < #len, done waiting.\n");
+#endif
+ }
+ } else {
+ while (d_n_avail_read_I == 0) {
+#if DO_DEBUG
+ fprintf (stderr, "dequeue: #a == 0, waiting.\n");
+#endif
+ d_internal->unlock ();
+ d_readBlock->wait ();
+ d_internal->lock ();
+ if (d_doAbort) {
+ d_internal->unlock ();
+#if DO_DEBUG
+ fprintf (stderr, "dequeue: #a == 0, aborting.\n");
+#endif
+ return (2);
+ }
+#if DO_DEBUG
+ fprintf (stderr, "dequeue: #a == 0, done waiting.\n");
+#endif
+ }
+ }
+ if (l_bufLen_I > d_n_avail_read_I)
+ l_bufLen_I = d_n_avail_read_I;
+ UInt32 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->signal ();
+ d_internal->unlock ();
+ return (1);
+ };
+
+ void abort () {
+ d_internal->lock ();
+ d_doAbort = true;
+ d_writeBlock->signal ();
+ d_readBlock->signal ();
+ d_internal->unlock ();
+ };
+};
+
+#endif /* _CIRCULAR_BUFFER_H_ */
diff --git a/gr-audio-osx/src/mld_threads.h b/gr-audio-osx/src/mld_threads.h
new file mode 100644
index 000000000..12adc23ee
--- /dev/null
+++ b/gr-audio-osx/src/mld_threads.h
@@ -0,0 +1,255 @@
+/* -*- 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _INCLUDED_MLD_THREADS_H_
+#define _INCLUDED_MLD_THREADS_H_
+
+/* classes which allow for either pthreads or omni_threads */
+
+#ifdef _USE_OMNI_THREADS_
+#include <gnuradio/omnithread.h>
+#else
+#include <pthread.h>
+#endif
+
+#include <stdexcept>
+
+#define __INLINE__ inline
+
+class mld_condition_t;
+
+class mld_mutex_t {
+#ifdef _USE_OMNI_THREADS_
+ typedef omni_mutex l_mutex, *l_mutex_ptr;
+#else
+ typedef pthread_mutex_t l_mutex, *l_mutex_ptr;
+#endif
+
+ friend class mld_condition_t;
+
+private:
+ l_mutex_ptr d_mutex;
+
+protected:
+ inline l_mutex_ptr mutex () { return (d_mutex); };
+
+public:
+ __INLINE__ mld_mutex_t () {
+#ifdef _USE_OMNI_THREADS_
+ d_mutex = new omni_mutex ();
+#else
+ d_mutex = (l_mutex_ptr) new l_mutex;
+ int l_ret = pthread_mutex_init (d_mutex, NULL);
+ if (l_ret != 0) {
+ fprintf (stderr, "Error %d creating mutex.\n", l_ret);
+ throw std::runtime_error ("mld_mutex_t::mld_mutex_t()\n");
+ }
+#endif
+ };
+
+ __INLINE__ ~mld_mutex_t () {
+ unlock ();
+#ifndef _USE_OMNI_THREADS_
+ int l_ret = pthread_mutex_destroy (d_mutex);
+ if (l_ret != 0) {
+ fprintf (stderr, "mld_mutex_t::~mld_mutex_t(): "
+ "Error %d destroying mutex.\n", l_ret);
+ }
+#endif
+ delete d_mutex;
+ d_mutex = NULL;
+ };
+
+ __INLINE__ void lock () {
+#ifdef _USE_OMNI_THREADS_
+ d_mutex->lock ();
+#else
+ int l_ret = pthread_mutex_lock (d_mutex);
+ if (l_ret != 0) {
+ fprintf (stderr, "mld_mutex_t::lock(): "
+ "Error %d locking mutex.\n", l_ret);
+ }
+#endif
+ };
+
+ __INLINE__ void unlock () {
+#ifdef _USE_OMNI_THREADS_
+ d_mutex->unlock ();
+#else
+ int l_ret = pthread_mutex_unlock (d_mutex);
+ if (l_ret != 0) {
+ fprintf (stderr, "mld_mutex_t::unlock(): "
+ "Error %d locking mutex.\n", l_ret);
+ }
+#endif
+ };
+
+ __INLINE__ bool trylock () {
+#ifdef _USE_OMNI_THREADS_
+ int l_ret = d_mutex->trylock ();
+#else
+ int l_ret = pthread_mutex_unlock (d_mutex);
+#endif
+ return (l_ret == 0 ? true : false);
+ };
+
+ inline void acquire () { lock(); };
+ inline void release () { unlock(); };
+ inline void wait () { lock(); };
+ inline void post () { unlock(); };
+};
+
+typedef mld_mutex_t mld_mutex, *mld_mutex_ptr;
+
+class mld_condition_t {
+#ifdef _USE_OMNI_THREADS_
+ typedef omni_condition l_condition, *l_condition_ptr;
+#else
+ typedef pthread_cond_t l_condition, *l_condition_ptr;
+#endif
+
+private:
+ l_condition_ptr d_condition;
+ mld_mutex_ptr d_mutex;
+ bool d_waiting;
+
+public:
+ __INLINE__ mld_condition_t () {
+ d_waiting = false;
+ d_mutex = new mld_mutex ();
+#ifdef _USE_OMNI_THREADS_
+ d_condition = new omni_condition (d_mutex->mutex ());
+#else
+ d_condition = (l_condition_ptr) new l_condition;
+ int l_ret = pthread_cond_init (d_condition, NULL);
+ if (l_ret != 0) {
+ fprintf (stderr, "Error %d creating condition.\n", l_ret);
+ throw std::runtime_error ("mld_condition_t::mld_condition_t()\n");
+ }
+#endif
+ };
+
+ __INLINE__ ~mld_condition_t () {
+ signal ();
+#ifndef _USE_OMNI_THREADS_
+ int l_ret = pthread_cond_destroy (d_condition);
+ if (l_ret != 0) {
+ fprintf (stderr, "mld_condition_t::mld_condition_t(): "
+ "Error %d destroying condition.\n", l_ret);
+ }
+#endif
+ delete d_condition;
+ d_condition = NULL;
+ delete d_mutex;
+ d_mutex = NULL;
+ };
+
+ __INLINE__ void signal () {
+ if (d_waiting == true) {
+#ifdef _USE_OMNI_THREADS_
+ d_condition->signal ();
+#else
+ int l_ret = pthread_cond_signal (d_condition);
+ if (l_ret != 0) {
+ fprintf (stderr, "mld_condition_t::signal(): "
+ "Error %d.\n", l_ret);
+ }
+#endif
+ d_waiting = false;
+ }
+ };
+
+ __INLINE__ void wait () {
+ if (d_waiting == false) {
+ d_waiting = true;
+#ifdef _USE_OMNI_THREADS_
+ d_condition->wait ();
+#else
+ int l_ret = pthread_cond_wait (d_condition, d_mutex->mutex ());
+ if (l_ret != 0) {
+ fprintf (stderr, "mld_condition_t::wait(): "
+ "Error %d.\n", l_ret);
+ }
+#endif
+ }
+ };
+};
+
+typedef mld_condition_t mld_condition, *mld_condition_ptr;
+
+class mld_thread_t {
+#ifdef _USE_OMNI_THREADS_
+ typedef omni_thread l_thread, *l_thread_ptr;
+#else
+ typedef pthread_t l_thread, *l_thread_ptr;
+#endif
+
+private:
+#ifndef _USE_OMNI_THREADS_
+ l_thread d_thread;
+ void (*d_start_routine)(void*);
+ void *d_arg;
+#else
+ l_thread_ptr d_thread;
+#endif
+
+#ifndef _USE_OMNI_THREADS_
+ static void* local_start_routine (void *arg) {
+ mld_thread_t* This = (mld_thread_t*) arg;
+ (*(This->d_start_routine))(This->d_arg);
+ return (NULL);
+ };
+#endif
+
+public:
+ __INLINE__ mld_thread_t (void (*start_routine)(void *), void *arg) {
+#ifdef _USE_OMNI_THREADS_
+ d_thread = new omni_thread (start_routine, arg);
+ d_thread->start ();
+#else
+ d_start_routine = start_routine;
+ d_arg = arg;
+ int l_ret = pthread_create (&d_thread, NULL, local_start_routine, this);
+ if (l_ret != 0) {
+ fprintf (stderr, "Error %d creating thread.\n", l_ret);
+ throw std::runtime_error ("mld_thread_t::mld_thread_t()\n");
+ }
+#endif
+ };
+
+ __INLINE__ ~mld_thread_t () {
+#ifdef _USE_OMNI_THREADS_
+// delete d_thread;
+ d_thread = NULL;
+#else
+ int l_ret = pthread_detach (d_thread);
+ if (l_ret != 0) {
+ fprintf (stderr, "Error %d detaching thread.\n", l_ret);
+ throw std::runtime_error ("mld_thread_t::~mld_thread_t()\n");
+ }
+#endif
+ };
+};
+
+typedef mld_thread_t mld_thread, *mld_thread_ptr;
+
+#endif /* _INCLUDED_MLD_THREADS_H_ */
diff --git a/gr-audio-osx/src/qa_osx.py b/gr-audio-osx/src/qa_osx.py
new file mode 100755
index 000000000..1e85c1581
--- /dev/null
+++ b/gr-audio-osx/src/qa_osx.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#
+# 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 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import audio_osx
+
+class qa_osx (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = None
+
+ def test_000_nop (self):
+ """Just see if we can import the module...
+ They may not have OSX drivers, etc. Don't try to run anything"""
+ pass
+
+if __name__ == '__main__':
+ gr_unittest.main ()
diff --git a/gr-audio-osx/src/run_tests.in b/gr-audio-osx/src/run_tests.in
new file mode 100644
index 000000000..52de4dd13
--- /dev/null
+++ b/gr-audio-osx/src/run_tests.in
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# All this strange PYTHONPATH manipulation is required to run our
+# tests using our just built shared library and swig-generated python
+# code prior to installation.
+
+# build tree == src tree unless you're doing a VPATH build.
+# If you don't know what a VPATH build is, you're not doing one. Relax...
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+# Where to look in the build tree for our shared library
+libbld=@abs_top_builddir@/gr-audio-osx/src
+# Where to look in the src tree for swig generated python code
+libsrc=@abs_top_srcdir@/gr-audio-osx/src
+# Where to look in the src tree for hand written python code
+py=@abs_top_srcdir@/gr-audio-osx/src
+
+# Where to look for GNU Radio python modules in current build tree
+# FIXME this is wrong on a distcheck. We really need to ask gnuradio-core
+# where it put its python files.
+grpythonbld=@abs_top_builddir@/gnuradio-core/src/python/:@abs_top_builddir@/gnuradio-core/src/lib/swig/:@abs_top_builddir@/gnuradio-core/src/lib/swig/.libs
+
+PYTHONPATH="$grpythonbld:$libbld:$libbld/.libs:$libsrc:$py:$PYTHONPATH"
+export PYTHONPATH
+
+#
+# This is the simple part...
+# Run everything that matches qa_*.py and return the final result.
+#
+
+ok=yes
+for file in @srcdir@/qa_*.py
+do
+ if ! $file
+ then
+ ok=no
+ fi
+done
+
+if [ $ok = yes ]
+then
+ exit 0
+else
+ exit 1
+fi
diff --git a/gr-audio-osx/src/test_audio_loop.py b/gr-audio-osx/src/test_audio_loop.py
new file mode 100755
index 000000000..825908f57
--- /dev/null
+++ b/gr-audio-osx/src/test_audio_loop.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#
+# 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 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr
+from gnuradio import audio
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+
+class my_graph(gr.flow_graph):
+
+ def __init__(self):
+ gr.flow_graph.__init__(self)
+
+ parser = OptionParser(option_class=eng_option)
+ parser.add_option("-O", "--audio-output",
+ type="string",
+ default="",
+ help="audio output device name. E.g., hw:0,0 or /dev/dsp")
+ parser.add_option("-I", "--audio-input",
+ type="string",
+ default="",
+ help="audio input device name. E.g., hw:0,0 or /dev/dsp")
+ parser.add_option("-r", "--sample-rate",
+ type="eng_float",
+ default=48000,
+ help="set sample rate to RATE (48000)")
+ (options, args) = parser.parse_args ()
+ if len(args) != 0:
+ parser.print_help()
+ raise SystemExit, 1
+
+ sample_rate = int(options.sample_rate)
+ src = audio.source (sample_rate, options.audio_output)
+ dst = audio.sink (sample_rate, options.audio_output)
+
+ max_chan = max (src.output_signature().max_streams(),
+ dst.output_signature().max_streams())
+
+ for i in range (max_chan):
+ self.connect ((src, i), (dst, i))
+
+if __name__ == '__main__':
+ try:
+ my_graph().run()
+ except KeyboardInterrupt:
+ pass