[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Commit-gnuradio] [gnuradio] 01/06: fix gr-audio osx: + use GNU Radio pr
From: |
git |
Subject: |
[Commit-gnuradio] [gnuradio] 01/06: fix gr-audio osx: + use GNU Radio preferences file to set default input and output audio device, if provided; + use gr::logger for all non-debug messages; + case-insensitive string find with desired audio device name; + fixes buffer allocation bug with low sample rates; + allows using a specific (named) audio device, or the default; + handles the case when the selected audio device becomes unavailable (e.g., a USB stick is removed while in use); + if no audio device name is provided, uses the default audio device as found in System Preferences::Sound; + handles the case when the default audio device is in use, and the user changes that audio device in System Preferences::Sound, by internally resetting to use the newly selected audio device; + all non-Apple names are now lower_case, not CamelCase; + move osx_impl functions to gr::audio::osx, and use them correctly; + install osx_impl.h to expose gr::audio::osx functions, but iff OSX audio is enabled. |
Date: |
Sat, 8 Mar 2014 16:32:44 +0000 (UTC) |
This is an automated email from the git hooks/post-receive script.
trondeau pushed a commit to branch master
in repository gnuradio.
commit 6e0895cb3cbc355060eab037ef74b8e237dcf133
Author: Michael Dickens <address@hidden>
Date: Fri Mar 7 11:10:07 2014 -0500
fix gr-audio osx:
+ use GNU Radio preferences file to set default input and output audio
device, if provided;
+ use gr::logger for all non-debug messages;
+ case-insensitive string find with desired audio device name;
+ fixes buffer allocation bug with low sample rates;
+ allows using a specific (named) audio device, or the default;
+ handles the case when the selected audio device becomes unavailable
(e.g., a USB stick is removed while in use);
+ if no audio device name is provided, uses the default audio device as
found in System Preferences::Sound;
+ handles the case when the default audio device is in use, and the user
changes that audio device in System Preferences::Sound, by internally resetting
to use the newly selected audio device;
+ all non-Apple names are now lower_case, not CamelCase;
+ move osx_impl functions to gr::audio::osx, and use them correctly;
+ install osx_impl.h to expose gr::audio::osx functions, but iff OSX audio
is enabled.
---
gr-audio/CMakeLists.txt | 2 +-
gr-audio/include/gnuradio/audio/CMakeLists.txt | 14 +-
gr-audio/include/gnuradio/audio/osx_impl.h | 84 ++
gr-audio/lib/CMakeLists.txt | 9 +
gr-audio/lib/osx/gr-audio-osx.conf | 11 +
gr-audio/lib/osx/osx_common.h | 73 +
gr-audio/lib/osx/osx_impl.cc | 313 ++++
gr-audio/lib/osx/osx_impl.h | 78 -
gr-audio/lib/osx/osx_sink.cc | 1015 ++++++++++---
gr-audio/lib/osx/osx_sink.h | 117 +-
gr-audio/lib/osx/osx_source.cc | 1881 +++++++++++++++---------
gr-audio/lib/osx/osx_source.h | 192 ++-
12 files changed, 2648 insertions(+), 1141 deletions(-)
diff --git a/gr-audio/CMakeLists.txt b/gr-audio/CMakeLists.txt
index ad39105..99e0bc1 100644
--- a/gr-audio/CMakeLists.txt
+++ b/gr-audio/CMakeLists.txt
@@ -86,8 +86,8 @@ CPACK_COMPONENT("audio_swig"
########################################################################
# Add subdirectories
########################################################################
-add_subdirectory(include/gnuradio/audio)
add_subdirectory(lib)
+add_subdirectory(include/gnuradio/audio)
add_subdirectory(doc)
if(ENABLE_PYTHON)
add_subdirectory(swig)
diff --git a/gr-audio/include/gnuradio/audio/CMakeLists.txt
b/gr-audio/include/gnuradio/audio/CMakeLists.txt
index 1cfbb9b..7e068e3 100644
--- a/gr-audio/include/gnuradio/audio/CMakeLists.txt
+++ b/gr-audio/include/gnuradio/audio/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2011,2013 Free Software Foundation, Inc.
+# Copyright 2011,2013-2014 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -20,10 +20,14 @@
########################################################################
# Install header files
########################################################################
-install(FILES
- api.h
- source.h
- sink.h
+
+SET(gr_audio_install_files api.h source.h sink.h)
+
+if(OSX_AUDIO_VALID)
+ list(APPEND gr_audio_install_files osx_impl.h)
+endif(OSX_AUDIO_VALID)
+
+install(FILES ${gr_audio_install_files}
DESTINATION ${GR_INCLUDE_DIR}/gnuradio/audio
COMPONENT "audio_devel"
)
diff --git a/gr-audio/include/gnuradio/audio/osx_impl.h
b/gr-audio/include/gnuradio/audio/osx_impl.h
new file mode 100644
index 0000000..891685a
--- /dev/null
+++ b/gr-audio/include/gnuradio/audio/osx_impl.h
@@ -0,0 +1,84 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006, 2013-2014 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_IMPL_H
+#define INCLUDED_AUDIO_OSX_IMPL_H
+
+#include <gnuradio/audio/api.h>
+
+#include <iostream>
+#include <vector>
+
+#include <string.h>
+
+#include <AudioToolbox/AudioToolbox.h>
+#include <AudioUnit/AudioUnit.h>
+
+namespace gr {
+namespace audio {
+namespace osx {
+
+// 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
+
+// helper function to print an ASBD
+
+extern std::ostream& GR_AUDIO_API
+operator<<
+(std::ostream& s,
+ const AudioStreamBasicDescription& asbd);
+
+// returns the number of channels for the provided AudioDeviceID,
+// input and/or output depending on if the pointer is valid.
+
+extern void GR_AUDIO_API
+get_num_channels_for_audio_device_id
+(AudioDeviceID ad_id,
+ UInt32* n_input,
+ UInt32* n_output);
+
+// search all known audio devices, input or output, for all that
+// match the provided device_name string (in part or in whole).
+// Returns a vector of all matching IDs, and another of all
+// matching names. If the device name is empty, then match all
+// input or output devices.
+
+extern void GR_AUDIO_API
+find_audio_devices
+(const std::string& device_name,
+ bool is_input,
+ std::vector < AudioDeviceID >* all_ad_ids,
+ std::vector < std::string >* all_names);
+
+} /* namespace osx */
+} /* namespace audio */
+} /* namespace gr */
+
+#endif /* INCLUDED_AUDIO_OSX_IMPL_H */
diff --git a/gr-audio/lib/CMakeLists.txt b/gr-audio/lib/CMakeLists.txt
index 4eb6f8e..f07373d 100644
--- a/gr-audio/lib/CMakeLists.txt
+++ b/gr-audio/lib/CMakeLists.txt
@@ -106,6 +106,8 @@ CHECK_INCLUDE_FILE_CXX(AudioToolbox/AudioToolbox.h
AUDIO_TOOLBOX_H)
if(AUDIO_UNIT_H AND AUDIO_TOOLBOX_H)
+ set(OSX_AUDIO_VALID 1 CACHE INTERNAL "OSX Audio is valid")
+
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/osx)
list(APPEND gr_audio_libs
"-framework AudioUnit"
@@ -114,10 +116,17 @@ if(AUDIO_UNIT_H AND AUDIO_TOOLBOX_H)
"-framework Carbon"
)
list(APPEND gr_audio_sources
+ ${CMAKE_CURRENT_SOURCE_DIR}/osx/osx_impl.cc
${CMAKE_CURRENT_SOURCE_DIR}/osx/osx_source.cc
${CMAKE_CURRENT_SOURCE_DIR}/osx/osx_sink.cc
)
+ list(APPEND gr_audio_confs
${CMAKE_CURRENT_SOURCE_DIR}/osx/gr-audio-osx.conf)
+
+else(AUDIO_UNIT_H AND AUDIO_TOOLBOX_H)
+
+ set(OSX_AUDIO_VALID 0 CACHE INTERNAL "OSX Audio is not valid")
+
endif(AUDIO_UNIT_H AND AUDIO_TOOLBOX_H)
########################################################################
diff --git a/gr-audio/lib/osx/gr-audio-osx.conf
b/gr-audio/lib/osx/gr-audio-osx.conf
new file mode 100644
index 0000000..a4fed33
--- /dev/null
+++ b/gr-audio/lib/osx/gr-audio-osx.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. For OSX audio only, you can use a unique
+# subset of the actual device name to set these variables. By this
+# default, the OSX audio will use whatever devices are selected in
+# System Preferences::Sound for input and output.
+
+[audio_osx]
+
+default_input_device =
+default_output_device =
diff --git a/gr-audio/lib/osx/osx_common.h b/gr-audio/lib/osx/osx_common.h
new file mode 100644
index 0000000..34266f2
--- /dev/null
+++ b/gr-audio/lib/osx/osx_common.h
@@ -0,0 +1,73 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 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_COMMON_H
+#define INCLUDED_AUDIO_OSX_COMMON_H
+
+#include <gnuradio/audio/osx_impl.h>
+
+namespace gr {
+namespace audio {
+namespace osx {
+
+#define _OSX_AU_DEBUG_ 0
+#define _OSX_AU_DEBUG_RENDER_ 0
+
+#define check_error_and_throw(err,what,throw_str) \
+ if(err) { \
+ OSStatus error = static_cast<OSStatus>(err); \
+ char err_str[5]; \
+ *((UInt32*)err_str) = error; \
+ err_str[4] = 0; \
+ GR_LOG_FATAL(d_logger, boost::format(what)); \
+ GR_LOG_FATAL(d_logger, boost::format(" Error# %u ('%s')") \
+ % error % err_str); \
+ GR_LOG_FATAL(d_logger, boost::format(" %s:%d") \
+ % __FILE__ %__LINE__); \
+ throw std::runtime_error(throw_str); \
+ }
+
+#define check_error(err,what) \
+ if(err) { \
+ OSStatus error = static_cast<OSStatus>(err); \
+ char err_str[5]; \
+ *((UInt32*)err_str) = error; \
+ err_str[4] = 0; \
+ GR_LOG_WARN(d_logger, boost::format(what));
\
+ GR_LOG_WARN(d_logger, boost::format(" Error# %u ('%s')") \
+ % error % err_str); \
+ GR_LOG_WARN(d_logger, boost::format(" %s:%d") \
+ % __FILE__ %__LINE__); \
+ }
+
+#include <boost/detail/endian.hpp> //BOOST_BIG_ENDIAN
+#ifdef BOOST_BIG_ENDIAN
+#define GR_PCM_ENDIANNESS kLinearPCMFormatFlagIsBigEndian
+#else
+#define GR_PCM_ENDIANNESS 0
+#endif
+
+} /* namespace osx */
+} /* namespace audio */
+} /* namespace gr */
+
+#endif /* INCLUDED_AUDIO_OSX_COMMON_H */
diff --git a/gr-audio/lib/osx/osx_impl.cc b/gr-audio/lib/osx/osx_impl.cc
new file mode 100644
index 0000000..c4e580b
--- /dev/null
+++ b/gr-audio/lib/osx/osx_impl.cc
@@ -0,0 +1,313 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2014 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 "audio_registry.h"
+
+#include <gnuradio/audio/osx_impl.h>
+
+#include <algorithm>
+#include <iostream>
+#include <locale>
+#include <stdexcept>
+
+namespace gr {
+namespace audio {
+namespace osx {
+
+std::ostream&
+operator<<
+(std::ostream& s,
+ const AudioStreamBasicDescription& asbd)
+{
+ char format_id[5];
+ *((UInt32*)format_id) = asbd.mFormatID;
+ format_id[4] = 0;
+ s << " Sample Rate : " << asbd.mSampleRate << std::endl;
+ s << " Format ID : " << format_id << std::endl;
+ s << " Format Flags : " << asbd.mFormatFlags << std::endl;
+ s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsFloat) != 0)
+ << " : Is Float" << std::endl;
+ s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0)
+ << " : Is Big Endian" << std::endl;
+ s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
+ << " : Is Signed Integer" << std::endl;
+ s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsPacked) != 0)
+ << " : Is Packed" << std::endl;
+ s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsAlignedHigh) != 0)
+ << " : Is Aligned High" << std::endl;
+ s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0)
+ << " : Is Non-Interleaved" << std::endl;
+ s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsNonMixable) != 0)
+ << " : Is Non-Mixable" << std::endl;
+ s << " Bytes / Packet : " << asbd.mBytesPerPacket << std::endl;
+ s << " Frames / Packet : " << asbd.mFramesPerPacket << std::endl;
+ s << " Bytes / Frame : " << asbd.mBytesPerFrame << std::endl;
+ s << " Channels / Frame : " << asbd.mChannelsPerFrame << std::endl;
+ s << " Bits / Channel : " << asbd.mBitsPerChannel;
+ return(s);
+};
+
+static UInt32
+_get_num_channels
+(AudioDeviceID ad_id,
+ AudioObjectPropertyScope scope)
+{
+ // retrieve the AudioBufferList associated with this ID using
+ // the provided scope
+
+ UInt32 num_channels = 0;
+ UInt32 prop_size = 0;
+ AudioObjectPropertyAddress ao_address = {
+ kAudioDevicePropertyStreamConfiguration, scope, 0
+ };
+ OSStatus err = noErr;
+ if ((err = AudioObjectGetPropertyDataSize
+ (ad_id, &ao_address, 0, NULL,
+ &prop_size)) == noErr) {
+ boost::scoped_array<AudioBufferList> buf_list
+ (reinterpret_cast<AudioBufferList*>
+ (new char[prop_size]));
+ if ((err = AudioObjectGetPropertyData
+ (ad_id, &ao_address, 0, NULL,
+ &prop_size, buf_list.get())) == noErr) {
+ for (UInt32 mm = 0; mm < buf_list.get()->mNumberBuffers; ++mm) {
+ num_channels += buf_list.get()->mBuffers[mm].mNumberChannels;
+ }
+ }
+ else {
+ // assume 2 channels
+ num_channels = 2;
+ }
+ }
+ else {
+ // assume 2 channels
+ num_channels = 2;
+ }
+ return(num_channels);
+}
+
+// works with both char and wchar_t
+template<typename charT>
+struct ci_equal {
+ ci_equal( const std::locale& loc ) : loc_(loc) {}
+ bool operator()(charT ch1, charT ch2) {
+ return std::tolower(ch1, loc_) == std::tolower(ch2, loc_);
+ }
+private:
+ const std::locale& loc_;
+};
+
+// find substring (case insensitive)
+static std::string::size_type ci_find_substr
+(const std::string& str1, const std::string& str2,
+ const std::locale& loc = std::locale())
+{
+ std::string::const_iterator it = std::search
+ (str1.begin(), str1.end(),
+ str2.begin(), str2.end(),
+ ci_equal<std::string::value_type>(loc));
+ if (it != str1.end()) {
+ return(it - str1.begin());
+ }
+ // not found
+ return(std::string::npos);
+}
+
+void
+get_num_channels_for_audio_device_id
+(AudioDeviceID ad_id,
+ UInt32* n_input,
+ UInt32* n_output)
+{
+ if (n_input) {
+ *n_input = _get_num_channels
+ (ad_id, kAudioDevicePropertyScopeInput);
+ }
+ if (n_output) {
+ *n_output = _get_num_channels
+ (ad_id, kAudioDevicePropertyScopeOutput);
+ }
+}
+
+void
+find_audio_devices
+(const std::string& device_name,
+ bool is_input,
+ std::vector < AudioDeviceID >* all_ad_ids,
+ std::vector < std::string >* all_names)
+{
+ if ((!all_ad_ids) && (!all_names)) {
+ // if nothing is requested, no point in doing anything!
+ return;
+ }
+
+ OSStatus err = noErr;
+
+ // set the default audio device id to "unknown"
+
+ AudioDeviceID d_ad_id = kAudioDeviceUnknown;
+
+ // retrieve the size of the array of known audio device IDs
+
+ UInt32 prop_size = 0;
+
+ AudioObjectPropertyAddress ao_address = {
+ kAudioHardwarePropertyDevices,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ if ((err = AudioObjectGetPropertyDataSize
+ (kAudioObjectSystemObject, &ao_address,
+ 0, NULL, &prop_size)) != noErr) {
+#if _OSX_AU_DEBUG_
+ std::cerr << "audio_osx::find_audio_devices: "
+ << "Unable to retrieve number of audio objects: "
+ << err << std::endl;
+#endif
+ return;
+ }
+
+ // get the total number of audio devices (input and output)
+
+ UInt32 num_devices = prop_size / sizeof(AudioDeviceID);
+
+ // retrieve all audio device ids
+
+ boost::scoped_array < AudioDeviceID > all_dev_ids
+ (new AudioDeviceID[num_devices]);
+
+ if ((err = AudioObjectGetPropertyData
+ (kAudioObjectSystemObject, &ao_address,
+ 0, NULL, &prop_size, all_dev_ids.get())) != noErr) {
+#if _OSX_AU_DEBUG_
+ std::cerr << "audio_osx::find_audio_devices: "
+ << "Unable to retrieve audio object ids: "
+ << err << std::endl;
+#endif
+ return;
+ }
+
+ // success; loop over all retrieved output device ids, retrieving
+ // the name for each and comparing with the desired name.
+
+ std::vector< std::string > valid_names(num_devices);
+ std::vector< UInt32 > valid_indices(num_devices);
+ UInt32 num_found_devices = 0;
+ AudioObjectPropertyScope scope = is_input ?
+ kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+
+ for (UInt32 nn = 0; nn < num_devices; ++nn) {
+
+ // make sure this device has input / output channels (it might
+ // also have output / input channels, too, but we do not care
+ // about that here)
+
+ AudioDeviceID t_id = all_dev_ids[nn];
+
+ if (is_input) {
+ UInt32 n_input_channels = 0;
+ get_num_channels_for_audio_device_id
+ (t_id, &n_input_channels, NULL);
+ if (n_input_channels == 0) {
+ // no input channels; must be output device; just continue
+ // to the next audio device.
+ continue;
+ }
+ } else {
+ UInt32 n_output_channels = 0;
+ get_num_channels_for_audio_device_id
+ (t_id, NULL, &n_output_channels);
+ if (n_output_channels == 0) {
+ // no output channels; must be input device; just continue
+ // to the next audio device.
+ continue;
+ }
+ }
+
+ // retrieve the device name; max name length is 64 characters.
+
+ prop_size = 65;
+ char c_name_buf[prop_size];
+ bzero((void*)c_name_buf, prop_size);
+ --prop_size;
+
+ AudioObjectPropertyAddress ao_address = {
+ kAudioDevicePropertyDeviceName, scope, 0
+ };
+
+ if ((err = AudioObjectGetPropertyData
+ (t_id, &ao_address, 0, NULL,
+ &prop_size, (void*)c_name_buf)) != noErr) {
+#if _OSX_AU_DEBUG_
+ std::cerr << "audio_osx::find_audio_devices: "
+ << "Unable to retrieve audio device name #"
+ << (nn+1) << ": " << err << std::endl;
+#endif
+ continue;
+ }
+ std::string name_buf(c_name_buf);
+
+ // compare the retreived name with the desired one, if
+ // provided; case insensitive.
+
+ if (device_name.length() > 0) {
+
+ std::string::size_type found =
+ ci_find_substr(name_buf, device_name);
+ if (found == std::string::npos) {
+ // not found; continue to the next ID
+ continue;
+ }
+ }
+
+ // store this info
+
+ valid_names[nn] = name_buf;
+ valid_indices[num_found_devices++] = nn;
+
+ }
+
+ // resize valid function arguments, then copy found values
+
+ if (all_ad_ids) {
+ all_ad_ids->resize(num_found_devices);
+ for (UInt32 nn = 0; nn < num_found_devices; ++nn) {
+ (*all_ad_ids)[nn] = all_dev_ids[valid_indices[nn]];
+ }
+ }
+
+ if (all_names) {
+ all_names->resize(num_found_devices);
+ for (UInt32 nn = 0; nn < num_found_devices; ++nn) {
+ (*all_names)[nn] = valid_names[valid_indices[nn]];
+ }
+ }
+}
+
+} /* namespace osx */
+} /* namespace audio */
+} /* namespace gr */
diff --git a/gr-audio/lib/osx/osx_impl.h b/gr-audio/lib/osx/osx_impl.h
deleted file mode 100644
index 5a12bac..0000000
--- a/gr-audio/lib/osx/osx_impl.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* -*- c++ -*- */
-/*
- * Copyright 2006, 2013 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_IMPL_H
-#define INCLUDED_AUDIO_OSX_IMPL_H
-
-#include <iostream>
-#include <string.h>
-
-namespace gr {
- namespace audio {
-
-#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); \
- }
-
-#include <boost/detail/endian.hpp> //BOOST_BIG_ENDIAN
-#ifdef BOOST_BIG_ENDIAN
-#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
-
- } /* namespace audio */
-} /* namespace gr */
-
-#endif /* INCLUDED_AUDIO_OSX_IMPL_H */
diff --git a/gr-audio/lib/osx/osx_sink.cc b/gr-audio/lib/osx/osx_sink.cc
index 7c5be43..90c3745 100644
--- a/gr-audio/lib/osx/osx_sink.cc
+++ b/gr-audio/lib/osx/osx_sink.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2006-2011,2013 Free Software Foundation, Inc.
+ * Copyright 2006-2011,2013-2014 Free Software Foundation, Inc.
*
* This file is part of GNU Radio.
*
@@ -25,91 +25,212 @@
#endif
#include "audio_registry.h"
-#include <osx_sink.h>
-#include <osx_impl.h>
+#include "osx_sink.h"
+
#include <gnuradio/io_signature.h>
+#include <gnuradio/prefs.h>
#include <stdexcept>
namespace gr {
namespace audio {
-#define _OSX_AU_DEBUG_ 0
-
- AUDIO_REGISTER_SINK(REG_PRIO_HIGH, osx)(int sampling_rate,
- const std::string &device_name,
- bool ok_to_block)
+ AUDIO_REGISTER_SINK(REG_PRIO_HIGH, osx)
+ (int sampling_rate,
+ const std::string& device_name,
+ bool ok_to_block)
{
return sink::sptr
(new osx_sink(sampling_rate, device_name, ok_to_block));
}
+ static std::string
+ default_device_name()
+ {
+ return prefs::singleton()->get_string
+ ("audio_osx", "default_input_device", "built-in");
+ }
+
osx_sink::osx_sink(int sample_rate,
- const std::string device_name,
- bool do_block,
- int channel_config,
- int max_sample_count)
+ const std::string& device_name,
+ bool ok_to_block)
: sync_block("audio_osx_sink",
io_signature::make(0, 0, 0),
io_signature::make(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)
+ d_input_sample_rate(0.0), d_n_user_channels(0),
+ d_n_dev_channels(0), d_queue_sample_count(0),
+ d_buffer_sample_count(0), d_ok_to_block(ok_to_block),
+ d_do_reset(false), d_hardware_changed(false),
+ d_using_default_device(false), d_waiting_for_data(false),
+ d_desired_name(device_name.empty() ? default_device_name()
+ : device_name),
+ d_output_au(0), d_output_ad_id(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;
+ GR_LOG_ERROR(d_logger, boost::format
+ ("Invalid Sample Rate: %d")
+ % sample_rate);
+ throw std::invalid_argument("audio_osx_sink");
+ }
+ else {
+ d_input_sample_rate = (Float64)sample_rate;
}
- d_n_channels = d_channel_config = channel_config;
+ // set up for audio output using the stored desired parameters
+
+ setup();
+ }
+
+ void osx_sink::setup()
+ {
+ OSStatus err = noErr;
+
+ // set the default output audio device id to "unknown"
+
+ d_output_ad_id = kAudioDeviceUnknown;
- // set the input signature
+ // try to find the output audio device, if specified
- set_input_signature(io_signature::make(1, d_n_channels, sizeof(float)));
+ std::vector < AudioDeviceID > all_ad_ids;
+ std::vector < std::string > all_names;
- // check that the max # of samples to store is valid
+ osx::find_audio_devices
+ (d_desired_name, false,
+ &all_ad_ids, &all_names);
- 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");
+ // check number of device(s) returned
+
+ if (d_desired_name.length() != 0) {
+ if (all_ad_ids.size() == 1) {
+
+ // exactly 1 match was found; see if it was partial
+
+ if (all_names[0].compare(d_desired_name) != 0) {
+
+ // yes: log the full device name
+ GR_LOG_INFO(d_logger, boost::format
+ ("Using output audio device '%s'.")
+ % all_names[0]);
+
+ }
+
+ // store info on this device
+
+ d_output_ad_id = all_ad_ids[0];
+ d_selected_name = all_names[0];
+
+ } else {
+
+ // either 0 or more than 1 device was found; get all output
+ // device names, print those, and error out.
+
+ osx::find_audio_devices("", false, NULL, &all_names);
+
+ std::string err_str("\n\nA unique output audio device name "
+ "matching the string '");
+ err_str += d_desired_name;
+ err_str += "' was not found.\n\n";
+ err_str += "The current known output audio device name";
+ err_str += ((all_names.size() > 1) ? "s are" : " is");
+ err_str += ":\n";
+ for (UInt32 nn = 0; nn < all_names.size(); ++nn) {
+ err_str += " " + all_names[nn] + "\n";
+ }
+ GR_LOG_ERROR(d_logger, boost::format(err_str));
+ throw std::runtime_error("audio_osx_sink::setup");
+
+ }
}
- d_max_sample_count = max_sample_count;
+ // if no output audio device id was found, use the default
+ // output audio device as set in System Preferences.
+
+ if (d_output_ad_id == kAudioDeviceUnknown) {
+
+ UInt32 size = sizeof(AudioDeviceID);
+ AudioObjectPropertyAddress ao_address = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
- // allocate the output circular buffer(s), one per channel
+ err = AudioObjectGetPropertyData
+ (kAudioObjectSystemObject, &ao_address,
+ 0, NULL, &size, &d_output_ad_id);
+ check_error_and_throw
+ (err, "Getting the default output audio device ID failed",
+ "audio_osx_sink::setup");
+
+ {
+ // retrieve the device name; max name length is 64 characters.
+
+ UInt32 prop_size = 65;
+ char c_name_buf[prop_size];
+ bzero((void*)c_name_buf, prop_size);
+ --prop_size;
+
+ AudioObjectPropertyAddress ao_address = {
+ kAudioDevicePropertyDeviceName,
+ kAudioDevicePropertyScopeOutput, 0
+ };
+
+ if ((err = AudioObjectGetPropertyData
+ (d_output_ad_id, &ao_address, 0, NULL,
+ &prop_size, (void*)c_name_buf)) != noErr) {
+
+ check_error(err, "Unable to retrieve output audio device name");
+
+ } else {
+
+ GR_LOG_INFO(d_logger, boost::format
+ ("\n\nUsing output audio device '%s'.\n ... "
+ "which is the current default output audio"
+ " device.\n Changing the default output"
+ " audio device in the System Preferences"
+ " will \n result in changing it here, too "
+ "(with an internal reconfiguration).\n") %
+ std::string(c_name_buf));
+
+ }
+
+ d_selected_name = c_name_buf;
+
+ }
+
+ d_using_default_device = true;
- 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;
+ // retrieve the total number of channels for the selected audio
+ // output device
+
+ osx::get_num_channels_for_audio_device_id
+ (d_output_ad_id, NULL, &d_n_dev_channels);
+
+ // set the block input signature, if not already set
+ // (d_n_user_channels is set in check_topology, which is called
+ // before the flow-graph is running)
+
+ if (d_n_user_channels == 0) {
+ set_input_signature(io_signature::make
+ (1, d_n_dev_channels, sizeof(float)));
+ }
+
+ // set the interim buffer size; to work with the GR scheduler,
+ // must be at least 16kB. Pick 50 kB since that's plenty yet
+ // not very much.
+
+ d_buffer_sample_count = (d_input_sample_rate < 50000.0 ?
+ 50000 : (UInt32)d_input_sample_rate);
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "sink(): max # samples = "
+ << d_buffer_sample_count << std::endl;
+#endif
+
+ // create the default AudioUnit for output:
// Open the default output unit
+
#ifndef GR_USE_OLD_AUDIO_UNIT
AudioComponentDescription desc;
#else
@@ -123,164 +244,440 @@ namespace gr {
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");
+ if(!comp) {
+ GR_LOG_FATAL(d_logger, boost::format
+ ("AudioComponentFindNext Failed"));
+ throw std::runtime_error("audio_osx_sink::setup");
}
+ err = AudioComponentInstanceNew(comp, &d_output_au);
+ check_error_and_throw(err, "AudioComponentInstanceNew Failed",
+ "audio_osx_sink::setup");
+
#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");
+ GR_LOG_FATAL(d_logger, boost::format
+ ("FindNextComponent Failed"));
+ throw std::runtime_error("audio_osx_sink::setup");
}
-#endif
+ err = OpenAComponent(comp, &d_output_au);
+ check_error_and_throw(err, "OpenAComponent Failed",
+ "audio_osx_sink::setup");
-#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 the selected device ID as the current output device
+
+ err = AudioUnitSetProperty
+ (d_output_au, kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global, 0,
+ &d_output_ad_id, sizeof(d_output_ad_id));
+ check_error_and_throw
+ (err, "Setting selected output device as current failed",
+ "audio_osx_sink::setup");
+
// Set up a callback function to generate output to the output unit
- AURenderCallbackStruct input;
- input.inputProc = (AURenderCallback)(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 |
+ AURenderCallbackStruct au_callback = {
+ reinterpret_cast<AURenderCallback>
+ (&osx_sink::au_output_callback),
+ reinterpret_cast<void*>(this)
+ };
+ UInt32 prop_size = (UInt32)sizeof(au_callback);
+
+ err = AudioUnitSetProperty
+ (d_output_au,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input, 0,
+ &au_callback, prop_size);
+ check_error_and_throw
+ (err, "Set Render Callback",
+ "audio_osx_sink::setup");
+
+ // create the stream format for the output unit, so that it
+ // handles any format conversions. Set number of channels in
+ // ::start, once the actual number of channels is known.
+
+ memset((void*)(&d_stream_format), 0, sizeof(d_stream_format));
+ d_stream_format.mSampleRate = d_input_sample_rate;
+ d_stream_format.mFormatID = kAudioFormatLinearPCM;
+ d_stream_format.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 gr::thread::condition_variable();
- if(d_cond_data == NULL)
- CheckErrorAndThrow(errno, "new condition (data)",
- "audio_osx_sink::audio_osx_sink");
-
- d_internal = new gr::thread::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");
+ d_stream_format.mBytesPerPacket = sizeof(float);
+ d_stream_format.mFramesPerPacket = 1;
+ d_stream_format.mBytesPerFrame = sizeof(float);
+ d_stream_format.mBitsPerChannel = 8*sizeof(float);
+
+ // set the render quality to maximum
+
+ UInt32 render_quality = kRenderQuality_Max;
+ prop_size = (UInt32)sizeof(render_quality);
+ err = AudioUnitSetProperty
+ (d_output_au,
+ kAudioUnitProperty_RenderQuality,
+ kAudioUnitScope_Global, 0,
+ &render_quality, prop_size);
+ check_error(err, "Setting render quality failed");
+
+ // clear the RunLoop (whatever that is); needed, for some
+ // reason, before a listener will work.
+
+ {
+ CFRunLoopRef the_run_loop = NULL;
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyRunLoop,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ prop_size = (UInt32)sizeof(the_run_loop);
+ err = AudioObjectSetPropertyData
+ (kAudioObjectSystemObject, &property, 0, NULL,
+ prop_size, &the_run_loop);
+ check_error(err, "Clearing RunLoop failed; "
+ "Audio Output Device Listener might not work.");
+ }
+
+ // set up listeners
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+
+ // 10.4 and newer
+
+ {
+
+ // set up a listener if hardware changes (at all)
+
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyDevices,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ err = AudioObjectAddPropertyListener
+ (kAudioObjectSystemObject, &property,
+ reinterpret_cast<AudioObjectPropertyListenerProc>
+ (&osx_sink::hardware_listener),
+ reinterpret_cast<void*>(this));
+ check_error(err, "Adding Audio Hardware Listener failed");
+ }
+
+ if (d_using_default_device) {
+
+ // set up a listener for the default output device so that if
+ // the device changes, this routine will be called and we can
+ // internally handle this change (if/as necesary)
+
+ {
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ err = AudioObjectAddPropertyListener
+ (kAudioObjectSystemObject, &property,
+ reinterpret_cast<AudioObjectPropertyListenerProc>
+ (&osx_sink::hardware_listener),
+ reinterpret_cast<void*>(this));
+ check_error(err, "Adding Default Output Audio Listener failed");
+ }
+ }
+
+#else
+
+ // 10.5 and older
+
+ err = AudioHardwareAddPropertyListener
+ (kAudioHardwarePropertyDevices,
+ reinterpret_cast<AudioHardwarePropertyListenerProc>
+ (&osx_sink::hardware_listener),
+ reinterpret_cast<void*>(this));
+ check_error(err, "Adding Audio Hardware Listener failed");
+
+ if (d_using_default_device) {
+
+ err = AudioHardwareAddPropertyListener
+ (kAudioHardwarePropertyDefaultOutputDevice,
+ reinterpret_cast<AudioHardwarePropertyListenerProc>
+ (&osx_sink::default_listener),
+ reinterpret_cast<void*>(this));
+ check_error(err, "Adding Default Output Audio Listener failed");
+
+ }
+
+#endif
+
+ // initialize the AU for output, so that it is ready to be used
+
+ err = AudioUnitInitialize(d_output_au);
+ check_error_and_throw
+ (err, "AudioUnit Initialize Failed",
+ "audio_osx_sink::setup");
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "audio_osx_sink Parameters:" << std::endl
+ << " Sample Rate is " << d_input_sample_rate << std::endl
+ << " Max # samples to store per channel is "
+ << d_buffer_sample_count << std::endl;
+#endif
+ }
+
+ void osx_sink::teardown()
+ {
+ OSStatus err = noErr;
+
+ // stop the AudioUnit
+
+ stop();
+
+ if (d_using_default_device) {
+ // remove the listener
+
+ OSStatus err = noErr;
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ err = AudioObjectRemovePropertyListener
+ (kAudioObjectSystemObject, &property,
+ reinterpret_cast<AudioObjectPropertyListenerProc>
+ (&osx_sink::hardware_listener),
+ reinterpret_cast<void*>(this));
+#if _OSX_AU_DEBUG_
+ check_error(err, "teardown: AudioObjectRemovePropertyListener "
+ "hardware failed");
+#endif
+ }
+
+ // uninitialize the AudioUnit
+
+ err = AudioUnitUninitialize(d_output_au);
+#if _OSX_AU_DEBUG_
+ check_error(err, "teardown: AudioUnitUninitialize failed");
+#endif
+
+ // dispose / close the AudioUnit
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+ err = AudioComponentInstanceDispose(d_output_au);
#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;
+ check_error(err, "teardown: AudioComponentInstanceDispose failed");
#endif
-}
+#else
+ CloseComponent(d_output_au);
+#if _OSX_AU_DEBUG_
+ check_error(err, "teardown: CloseComponent failed");
+#endif
+#endif
+
+ // delete buffers
+
+ for(UInt32 nn = 0; nn < d_buffers.size(); ++nn) {
+ delete d_buffers[nn];
+ d_buffers[nn] = 0;
+ }
+ d_buffers.resize(0);
+
+ // clear important variables; not # user channels
+
+ d_n_dev_channels = d_n_buffer_channels =
+ d_queue_sample_count = d_buffer_sample_count = 0;
+ d_using_default_device = false;
+ d_output_au = 0;
+ d_output_ad_id = 0;
+ }
bool
- osx_sink::IsRunning()
+ osx_sink::is_running()
{
- 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);
+ UInt32 au_running = 0;
+
+ if (d_output_au) {
+
+ UInt32 prop_size = (UInt32)sizeof(UInt32);
+ OSStatus err = AudioUnitGetProperty
+ (d_output_au,
+ kAudioOutputUnitProperty_IsRunning,
+ kAudioUnitScope_Global, 0,
+ &au_running, &prop_size);
+ check_error_and_throw
+ (err, "AudioUnitGetProperty IsRunning",
+ "audio_osx_sink::is_running");
+
+ }
+
+ return(au_running != 0);
}
bool
- osx_sink::start()
+ osx_sink::check_topology(int ninputs, int noutputs)
{
- if(!IsRunning()) {
- OSStatus err = AudioOutputUnitStart(d_OutputAU);
- CheckErrorAndThrow(err, "AudioOutputUnitStart",
- "audio_osx_sink::start");
+ // check # output to make sure it's valid
+ if(noutputs != 0) {
+
+ GR_LOG_FATAL(d_logger, boost::format
+ ("check_topology(): number of output "
+ "streams provided (%d) should be 0.")
+ % noutputs);
+ throw std::runtime_error
+ ("audio_osx_sink::check_topology");
+
+ }
+
+ // check # outputs to make sure it's valid
+ if((ninputs < 1) | (ninputs > (int) d_n_dev_channels)) {
+
+ GR_LOG_FATAL(d_logger, boost::format
+ ("check_topology(): number of input "
+ "streams provided (%d) should be in [1,%d] "
+ "for the selected output audio device.")
+ % ninputs % d_n_dev_channels);
+ throw std::runtime_error("audio_osx_sink::check_topology");
+
}
+ // save the actual number of input (user) channels
+
+ d_n_user_channels = ninputs;
+
+#if _OSX_AU_DEBUG_
+ std::cerr << "chk_topo: Actual # user input channels = "
+ << d_n_user_channels << std::endl;
+#endif
+
return (true);
}
+ void
+ osx_sink::check_channels(bool force_reset)
+ {
+ if (d_buffers.size() == 0) {
+
+ // allocate the output circular buffer(s), one per user channel
+
+ d_buffers.resize(d_n_user_channels);
+ for(UInt32 nn = 0; nn < d_n_user_channels; ++nn) {
+ d_buffers[nn] = new circular_buffer<float>
+ (d_buffer_sample_count, false, false);
+ }
+ }
+ else {
+ if(d_buffers.size() == d_n_user_channels) {
+ if (force_reset) {
+ for(UInt32 nn = 0; nn < d_buffers.size(); ++nn) {
+ d_buffers[nn]->reset();
+ }
+ }
+ return;
+ }
+
+ // reallocate the output circular buffer(s)
+
+ if (d_n_user_channels < d_buffers.size()) {
+
+ // too many buffers; delete some
+
+ for (UInt32 nn = d_n_user_channels; nn < d_buffers.size(); ++nn) {
+ delete d_buffers[nn];
+ d_buffers[nn] = 0;
+ }
+ d_buffers.resize(d_n_user_channels);
+
+ // reset remaining buffers
+
+ for(UInt32 nn = 0; nn < d_buffers.size(); ++nn) {
+ d_buffers[nn]->reset();
+ }
+ }
+ else {
+
+ // too few buffers; create some more
+
+ // reset old buffers first
+
+ for(UInt32 nn = 0; nn < d_buffers.size(); ++nn) {
+ d_buffers[nn]->reset();
+ }
+
+ d_buffers.resize(d_n_user_channels);
+ for (UInt32 nn = d_buffers.size(); nn < d_n_user_channels; ++nn) {
+ d_buffers[nn] = new circular_buffer<float>
+ (d_buffer_sample_count, false, false);
+ }
+ }
+ }
+
+ // reset the output audio unit for the correct number of channels
+ // have to uninitialize, set, initialize.
+
+ OSStatus err = AudioUnitUninitialize(d_output_au);
+ check_error(err, "AudioUnitUninitialize");
+
+ d_stream_format.mChannelsPerFrame = d_n_user_channels;
+
+ err = AudioUnitSetProperty
+ (d_output_au,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 0,
+ &d_stream_format, sizeof(d_stream_format));
+ check_error_and_throw
+ (err, "AudioUnitSetProperty StreamFormat",
+ "audio_osx_sink::check_channels");
+
+ // initialize the AU for output, so that it is ready to be used
+
+ err = AudioUnitInitialize(d_output_au);
+ check_error_and_throw
+ (err, "AudioUnitInitialize",
+ "audio_osx_sink::check_channels");
+ }
+
bool
- osx_sink::stop()
+ osx_sink::start()
{
- if(IsRunning ()) {
- OSStatus err = AudioOutputUnitStop(d_OutputAU);
- CheckErrorAndThrow(err, "AudioOutputUnitStop",
- "audio_osx_sink::stop");
+ if(!is_running() && d_output_au) {
- for(UInt32 n = 0; n < d_n_channels; n++) {
- d_buffers[n]->abort();
- }
+ // check channels, (re)allocate and reset buffers if/as necessary
+
+ check_channels(true);
+
+ // start the audio unit (should never fail)
+
+ OSStatus err = AudioOutputUnitStart(d_output_au);
+ check_error_and_throw
+ (err, "AudioOutputUnitStart",
+ "audio_osx_sink::start");
}
return (true);
}
- osx_sink::~osx_sink()
+ bool
+ osx_sink::stop()
{
- // stop and close the AudioUnit
- stop();
- AudioUnitUninitialize(d_OutputAU);
-#ifndef GR_USE_OLD_AUDIO_UNIT
- AudioComponentInstanceDispose(d_OutputAU);
-#else
- CloseComponent(d_OutputAU);
-#endif
+ if(is_running()) {
- // empty and delete the queues
- for(UInt32 n = 0; n < d_n_channels; n++) {
- delete d_buffers[n];
- d_buffers[n] = 0;
+ // stop the audio unit (should never fail)
+
+ OSStatus err = AudioOutputUnitStop(d_output_au);
+ check_error_and_throw
+ (err, "AudioOutputUnitStop",
+ "audio_osx_sink::stop");
+
+ // abort all buffers
+
+ for(UInt32 nn = 0; nn < d_n_user_channels; ++nn) {
+ d_buffers[nn]->abort();
+ }
}
- 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;
+ return(true);
}
int
@@ -288,19 +685,90 @@ namespace gr {
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
- gr::thread::scoped_lock l(*d_internal);
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_sink::work: Starting." << std::endl;
+#endif
+ if (d_do_reset) {
+ if (d_hardware_changed) {
+
+ // see if the current AudioDeviceID is still available
+
+ std::vector < AudioDeviceID > all_ad_ids;
+ osx::find_audio_devices
+ (d_desired_name, false,
+ &all_ad_ids, NULL);
+ bool found = false;
+ for (UInt32 nn = 0; (nn < all_ad_ids.size()) && (!found);
+ ++nn) {
+ found = (all_ad_ids[nn] == d_output_ad_id);
+ }
+ if (!found) {
+
+ GR_LOG_FATAL(d_logger, boost::format
+ ("The selected output audio device ('%s') "
+ "is no longer available.\n")
+ % d_selected_name);
+ return(gr::block::WORK_DONE);
+
+ }
+
+ d_do_reset = d_hardware_changed = false;
+
+ }
+ else {
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_sink::work: doing reset."
+ << std::endl;
+#endif
+
+ GR_LOG_WARN(d_logger, boost::format
+ ("\n\nThe default output audio device has "
+ "changed; resetting audio.\nThere may "
+ "be a sound glitch while resetting.\n"));
+
+ // for any changes, just tear down the current
+ // configuration, then set it up again using the user's
+ // parameters to try to make selections.
+
+ teardown();
+
+ gr::thread::scoped_lock l(d_internal);
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_sink::work: mutex locked."
+ << std::endl;
+#endif
+
+ setup();
+ start();
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_sink: returning after reset."
+ << std::endl;
+#endif
+ return(0);
+ }
+ }
- /* 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. */
+ gr::thread::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]. If the number of user/graph
+ // channels is less than the number of device channels, copy the
+ // data from the last / highest number channel to remaining
+ // device channels.
UInt32 l_max_count;
- int diff_count = d_max_sample_count - noutput_items;
- if(diff_count < 0)
+ int diff_count = d_buffer_sample_count - noutput_items;
+ if(diff_count < 0) {
l_max_count = 0;
- else
+ }
+ else {
l_max_count = (UInt32)diff_count;
+ }
#if 0
if(l_max_count < d_queueItemLength->back()) {
@@ -309,47 +777,66 @@ namespace gr {
}
#endif
-#if _OSX_AU_DEBUG_
- std::cerr << "work1: qSC = " << d_queueSampleCount
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "work1: qSC = " << d_queue_sample_count
<< ", lMC = "<< l_max_count
- << ", dmSC = " << d_max_sample_count
+ << ", dmSC = " << d_buffer_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
- }
- }
+ if(d_queue_sample_count > l_max_count) {
+ // data coming in too fast; ok_to_block decides what to do
+ if(d_ok_to_block == true) {
+ // block until there is data to return, or on reset
+ while(d_queue_sample_count > l_max_count) {
+ // release control so-as to allow data to be retrieved;
+ // block until there is data to return
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_sink::work: waiting." << std::endl;
+#endif
+ d_waiting_for_data = true;
+ d_cond_data.wait(l);
+ d_waiting_for_data = false;
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_sink::work: done waiting." << std::endl;
+#endif
+ // the condition's 'notify' was called; acquire control to
+ // keep thread safe
+
+ // if doing a reset, just return here; reset will pick
+ // up the next time this method is called.
+ if (d_do_reset) {
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_sink::work: "
+ "returning for reset." << std::endl;
+#endif
+ return(0);
+ }
+ }
+ }
}
+
// 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;
+ UInt32 nn;
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)
+ for(nn = 0; nn < l_size; ++nn) {
+ inBuffer = (float*)input_items[nn];
+ int l_res = d_buffers[nn]->enqueue(inBuffer, noutput_items);
+ if(l_res == -1) {
res = -1;
+ }
}
- while(l_counter < d_n_channels) {
+ while(nn < d_n_user_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)
+ int l_res = d_buffers[nn++]->enqueue(inBuffer, noutput_items);
+ if(l_res == -1) {
res = -1;
+ }
}
if(res == -1) {
@@ -358,72 +845,126 @@ namespace gr {
fputs("aO", stderr);
fflush(stderr);
// set the local number of samples available to the max
- d_queueSampleCount = d_buffers[0]->buffer_length_items();
+ d_queue_sample_count = d_buffers[0]->buffer_length_items();
}
else {
// keep up the local sample count
- d_queueSampleCount += noutput_items;
+ d_queue_sample_count += noutput_items;
}
-#if _OSX_AU_DEBUG_
+#if _OSX_AU_DEBUG_RENDER_
std::cerr << "work2: #OI = "
<< noutput_items << ", #Cnt = "
- << d_queueSampleCount << ", mSC = "
- << d_max_sample_count << std::endl;
+ << d_queue_sample_count << ", mSC = "
+ << d_buffer_sample_count << std::endl;
#endif
return (noutput_items);
}
OSStatus
- osx_sink::AUOutputCallback(void *inRefCon,
- AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList *ioData)
+ osx_sink::au_output_callback
+ (void* in_ref_con,
+ AudioUnitRenderActionFlags* io_action_flags,
+ const AudioTimeStamp* in_time_stamp,
+ UInt32 in_bus_number,
+ UInt32 in_number_frames,
+ AudioBufferList* io_data)
{
- osx_sink* This = (osx_sink*)inRefCon;
+ osx_sink* This = reinterpret_cast<osx_sink*>(in_ref_con);
OSStatus err = noErr;
- gr::thread::scoped_lock l(*This->d_internal);
+ gr::thread::scoped_lock l(This->d_internal);
-#if _OSX_AU_DEBUG_
- std::cerr << "cb_in: SC = " << This->d_queueSampleCount
- << ", in#F = " << inNumberFrames << std::endl;
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "cb_in: SC = " << This->d_queue_sample_count
+ << ", in#F = " << in_number_frames << std::endl;
#endif
- if(This->d_queueSampleCount < inNumberFrames) {
+ if(This->d_queue_sample_count < in_number_frames) {
// 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");
+ int nn = This->d_n_user_channels;
+
+ while(--nn >= 0) {
+ size_t t_n_output_items = in_number_frames;
+ float* out_buffer = (float*)(io_data->mBuffers[nn].mData);
+ This->d_buffers[nn]->dequeue(out_buffer, &t_n_output_items);
+ if(t_n_output_items != in_number_frames) {
+ throw std::runtime_error
+ ("audio_osx_sink::au_output_callback: "
+ "number of available items changing "
+ "unexpectedly (should never happen).");
}
}
- This->d_queueSampleCount -= inNumberFrames;
+ This->d_queue_sample_count -= in_number_frames;
}
-#if _OSX_AU_DEBUG_
- std::cerr << "cb_out: SC = " << This->d_queueSampleCount << std::endl;
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "cb_out: SC = "
+ << This->d_queue_sample_count << std::endl;
#endif
- // signal that data is available
- This->d_cond_data->notify_one();
+ // signal that data is available, if appropraite
+
+ if (This->d_waiting_for_data) {
+ This->d_cond_data.notify_one();
+ }
return (err);
}
+#ifndef GR_USE_OLD_AUDIO_UNIT
+
+ OSStatus
+ osx_sink::hardware_listener
+ (AudioObjectID in_object_id,
+ UInt32 in_num_addresses,
+ const AudioObjectPropertyAddress in_addresses[],
+ void* in_client_data)
+
+#else
+
+ OSStatus
+ osx_sink::hardware_listener
+ (AudioHardwarePropertyID in_property_id,
+ void* in_client_data)
+
+#endif
+ {
+ osx_sink* This = static_cast
+ <osx_sink*>(in_client_data);
+ This->reset(true);
+ return(noErr);
+ }
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+
+ OSStatus
+ osx_sink::default_listener
+ (AudioObjectID in_object_id,
+ UInt32 in_num_addresses,
+ const AudioObjectPropertyAddress in_addresses[],
+ void* in_client_data)
+
+#else
+
+ OSStatus
+ osx_sink::default_listener
+ (AudioHardwarePropertyID in_property_id,
+ void* in_client_data)
+
+#endif
+ {
+ osx_sink* This = reinterpret_cast
+ <osx_sink*>(in_client_data);
+ This->reset(false);
+ return(noErr);
+ }
+
} /* namespace audio */
} /* namespace gr */
diff --git a/gr-audio/lib/osx/osx_sink.h b/gr-audio/lib/osx/osx_sink.h
index 241e10a..ec38b10 100644
--- a/gr-audio/lib/osx/osx_sink.h
+++ b/gr-audio/lib/osx/osx_sink.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2006-2011,2013 Free Software Foundation, Inc.
+ * Copyright 2006-2011,2013-2014 Free Software Foundation, Inc.
*
* This file is part of GNU Radio.
*
@@ -24,10 +24,9 @@
#define INCLUDED_AUDIO_OSX_SINK_H
#include <gnuradio/audio/sink.h>
-#include <string>
-#include <list>
-#include <AudioUnit/AudioUnit.h>
-#include <circular_buffer.h>
+
+#include "osx_common.h"
+#include "circular_buffer.h"
namespace gr {
namespace audio {
@@ -42,44 +41,96 @@ namespace gr {
class osx_sink : public sink
{
- Float64 d_sample_rate;
- int d_channel_config;
- UInt32 d_n_channels;
- UInt32 d_queueSampleCount, d_max_sample_count;
- bool d_do_block;
- gr::thread::mutex* d_internal;
- gr::thread::condition_variable* d_cond_data;
- circular_buffer<float>** d_buffers;
+ protected:
+
+ Float64 d_input_sample_rate;
+ UInt32 d_n_user_channels, d_n_dev_channels, d_n_buffer_channels;
+ UInt32 d_queue_sample_count, d_buffer_sample_count;
+ bool d_ok_to_block, d_do_reset, d_hardware_changed;
+ bool d_using_default_device, d_waiting_for_data;
+ gr::thread::mutex d_internal;
+ gr::thread::condition_variable d_cond_data;
+ std::vector < circular_buffer < float > *> d_buffers;
+ std::string d_desired_name, d_selected_name;
// AudioUnits and Such
- AudioUnit d_OutputAU;
+
+ AudioUnit d_output_au;
+ AudioDeviceID d_output_ad_id;
+ AudioStreamBasicDescription d_stream_format;
public:
- 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);
- ~osx_sink();
+ osx_sink(int sample_rate,
+ const std::string& device_name,
+ bool ok_to_block);
- bool IsRunning();
- bool start();
- bool stop();
+ inline virtual ~osx_sink() {
+ teardown();
+ }
- int work(int noutput_items,
- gr_vector_const_void_star &input_items,
- gr_vector_void_star &output_items);
+ virtual bool check_topology(int ninputs, int noutputs);
+ virtual bool start();
+ virtual bool stop();
+
+ virtual int work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+
+ inline void reset(bool hardware_changed) {
+ d_hardware_changed = hardware_changed;
+ d_do_reset = true;
+ }
private:
- static OSStatus AUOutputCallback(void *inRefCon,
- AudioUnitRenderActionFlags
*ioActionFlags,
- const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList *ioData);
- };
+ bool is_running();
+
+ void setup();
+
+ void teardown();
+
+ void check_channels(bool force_reset);
+
+ static OSStatus au_output_callback
+ (void* in_ref_con,
+ AudioUnitRenderActionFlags* io_action_flags,
+ const AudioTimeStamp* in_time_stamp,
+ UInt32 in_bus_number,
+ UInt32 in_number_frames,
+ AudioBufferList* io_data);
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+
+ // OSX 10.4 and newer
+
+ static OSStatus hardware_listener
+ (AudioObjectID in_object_id,
+ UInt32 in_num_addresses,
+ const AudioObjectPropertyAddress in_addresses[],
+ void* in_client_data);
+
+ static OSStatus default_listener
+ (AudioObjectID in_object_id,
+ UInt32 in_num_addresses,
+ const AudioObjectPropertyAddress in_addresses[],
+ void* in_client_data);
+
+#else
+
+ // OSX 10.6 and older; removed as of 10.7
+
+ static OSStatus hardware_listener
+ (AudioHardwarePropertyID in_property_id,
+ void* in_client_data);
+
+ static OSStatus default_listener
+ (AudioHardwarePropertyID in_property_id,
+ void* in_client_data);
+
+#endif
+
+ };
} /* namespace audio */
} /* namespace gr */
diff --git a/gr-audio/lib/osx/osx_source.cc b/gr-audio/lib/osx/osx_source.cc
index 3e2dcef..93d857e 100644
--- a/gr-audio/lib/osx/osx_source.cc
+++ b/gr-audio/lib/osx/osx_source.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2006-2011,2013 Free Software Foundation, Inc.
+ * Copyright 2006-2011,2013-2014 Free Software Foundation, Inc.
*
* This file is part of GNU Radio.
*
@@ -25,370 +25,484 @@
#endif
#include "audio_registry.h"
-#include <osx_source.h>
-#include <osx_impl.h>
+#include "osx_source.h"
+
#include <gnuradio/io_signature.h>
+#include <gnuradio/prefs.h>
#include <stdexcept>
namespace gr {
namespace audio {
-#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)
+ AUDIO_REGISTER_SOURCE(REG_PRIO_HIGH, osx)
+ (int sampling_rate,
+ const std::string& device_name,
+ bool ok_to_block)
{
return source::sptr
(new osx_source(sampling_rate, device_name, ok_to_block));
}
- void
- PrintStreamDesc(AudioStreamBasicDescription *inDesc)
+ static std::string
+ default_device_name()
{
- 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;
+ return prefs::singleton()->get_string
+ ("audio_osx", "default_input_device", "built-in");
}
- // FIXME these should query some kind of user preference
-
- osx_source::osx_source(int sample_rate,
- const std::string device_name,
- bool do_block,
- int channel_config,
- int max_sample_count)
+ osx_source::osx_source
+ (int sample_rate,
+ const std::string& device_name,
+ bool ok_to_block)
: sync_block("audio_osx_source",
- io_signature::make(0, 0, 0),
- io_signature::make(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)
+ io_signature::make(0, 0, 0),
+ io_signature::make(0, 0, 0)),
+ d_device_sample_rate(0.0), d_output_sample_rate(0.0),
+ d_input_buffer_size_frames(0), d_input_buffer_size_bytes(0),
+ d_output_buffer_size_frames(0), d_output_buffer_size_bytes(0),
+ d_device_buffer_size_frames(0), d_device_buffer_size_bytes(0),
+ d_lead_size_frames(0), d_lead_size_bytes(0),
+ d_trail_size_frames(0), d_trail_size_bytes(0),
+ d_extra_buffer_size_frames(0), d_extra_buffer_size_bytes(0),
+ d_queue_sample_count(0), d_buffer_sample_count(0),
+ d_n_available_input_frames(0), d_n_actual_input_frames(0),
+ d_n_user_channels(0), d_n_dev_channels(0),
+ d_ok_to_block(ok_to_block), d_pass_through(false),
+ d_waiting_for_data(false), d_do_reset(false),
+ d_hardware_changed(false), d_using_default_device(false),
+ d_desired_name(device_name.empty() ? default_device_name()
+ : device_name),
+ d_input_ad_id(0), d_input_au(0), d_input_buffer(0),
+ d_output_buffer(0), d_audio_converter(0)
{
+ // set the desired output sample rate
+
if(sample_rate <= 0) {
- std::cerr << "Invalid Sample Rate: " << sample_rate << std::endl;
- throw std::invalid_argument("audio_osx_source::audio_osx_source");
+ GR_LOG_ERROR(d_logger, boost::format
+ ("Invalid Sample Rate: %d")
+ % sample_rate);
+ throw std::invalid_argument("audio_osx_source");
}
- else
- d_outputSampleRate = (Float64)sample_rate;
+ else {
+ d_output_sample_rate = (Float64)sample_rate;
+ }
+
+ // set up for audio input using the stored desired parameters
+
+ setup();
+ }
+
+ void osx_source::setup()
+ {
+ OSStatus err = noErr;
+
+ // set the default input audio device id to "unknown"
+
+ d_input_ad_id = kAudioDeviceUnknown;
+
+ // try to find the input audio device, if specified
+
+ std::vector < AudioDeviceID > all_ad_ids;
+ std::vector < std::string > all_names;
- 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");
+ osx::find_audio_devices
+ (d_desired_name, true,
+ &all_ad_ids, &all_names);
+
+ // check number of device(s) returned
+
+ if (d_desired_name.length() != 0) {
+ if (all_ad_ids.size() == 1) {
+
+ // exactly 1 match was found; see if it was partial
+
+ if (all_names[0].compare(d_desired_name) != 0) {
+
+ // yes: log the full device name
+ GR_LOG_INFO(d_logger, boost::format
+ ("Using input audio device '%s'.")
+ % all_names[0]);
+
+ }
+
+ // store info on this device
+
+ d_input_ad_id = all_ad_ids[0];
+ d_selected_name = all_names[0];
+
+ } else {
+
+ // either 0 or more than 1 device was found; get all input
+ // device names, print those, and error out.
+
+ osx::find_audio_devices("", true, NULL, &all_names);
+
+ std::string err_str("\n\nA unique input audio device name "
+ "matching the string '");
+ err_str += d_desired_name;
+ err_str += "' was not found.\n\n";
+ err_str += "The current known input audio device name";
+ err_str += ((all_names.size() > 1) ? "s are" : " is");
+ err_str += ":\n";
+ for (UInt32 nn = 0; nn < all_names.size(); ++nn) {
+ err_str += " " + all_names[nn] + "\n";
+ }
+ GR_LOG_ERROR(d_logger, boost::format(err_str));
+ throw std::runtime_error("audio_osx_source::setup");
+
+ }
}
- 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;
+
+ // if no input audio device id was found or no specific device
+ // name was provided, use the default input audio device id as
+ // set in System Preferences.
+
+ if (d_input_ad_id == kAudioDeviceUnknown) {
+
+ UInt32 prop_size = (UInt32)sizeof(AudioDeviceID);
+ AudioObjectPropertyAddress ao_address = {
+ kAudioHardwarePropertyDefaultInputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ err = AudioObjectGetPropertyData
+ (kAudioObjectSystemObject, &ao_address,
+ 0, NULL, &prop_size, &d_input_ad_id);
+ check_error_and_throw
+ (err, "Getting the default input audio device ID failed",
+ "audio_osx_source::setup");
+
+ {
+ // retrieve the device name; max name length is 64 characters.
+
+ UInt32 prop_size = 65;
+ char c_name_buf[prop_size];
+ memset((void*)c_name_buf, 0, (size_t)prop_size);
+ --prop_size;
+
+ AudioObjectPropertyAddress ao_address = {
+ kAudioDevicePropertyDeviceName,
+ kAudioDevicePropertyScopeInput, 0
+ };
+
+ if ((err = AudioObjectGetPropertyData
+ (d_input_ad_id, &ao_address, 0, NULL,
+ &prop_size, (void*)c_name_buf)) != noErr) {
+
+ check_error(err, "Unable to retrieve input audio device name");
+
+ } else {
+
+ GR_LOG_INFO(d_logger, boost::format
+ ("\n\nUsing input audio device '%s'.\n ... "
+ "which is the current default input audio"
+ " device.\n Changing the default input"
+ " audio device in the System Preferences"
+ " will \n result in changing it here, too "
+ "(with an internal reconfiguration).\n") %
+ std::string(c_name_buf));
+
+ }
+
+ d_selected_name = c_name_buf;
+
+ }
+
+ d_using_default_device = true;
+
}
- d_channel_config = channel_config;
+ // retrieve the total number of channels for the selected input
+ // audio device
+
+ osx::get_num_channels_for_audio_device_id
+ (d_input_ad_id, &d_n_dev_channels, NULL);
- // check that the max # of samples to store is valid
+ // set the block output signature, if not already set
+ // (d_n_user_channels is set in check_topology, which is called
+ // before the flow-graph is running)
- 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");
+ if (d_n_user_channels == 0) {
+ set_output_signature(io_signature::make
+ (1, d_n_dev_channels, sizeof(float)));
}
- d_max_sample_count = max_sample_count;
+ // set the interim buffer size; to work with the GR scheduler,
+ // must be at least 16kB. Pick 50 kB since that's plenty yet
+ // not very much.
+
+ d_buffer_sample_count = (d_output_sample_rate < 50000.0 ?
+ 50000 : (UInt32)d_output_sample_rate);
#if _OSX_AU_DEBUG_
- std::cerr << "source(): max # samples = " << d_max_sample_count <<
std::endl;
+ std::cerr << "source(): max # samples = "
+ << d_buffer_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;
+ AudioComponentDescription desc;
#else
- ComponentDescription InputDesc;
+ ComponentDescription desc;
#endif
- InputDesc.componentType = kAudioUnitType_Output;
- InputDesc.componentSubType = kAudioUnitSubType_HALOutput;
- InputDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
- InputDesc.componentFlags = 0;
- InputDesc.componentFlagsMask = 0;
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_HALOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.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");
- }
+ AudioComponent comp = AudioComponentFindNext(NULL, &desc);
+ if(!comp) {
+ GR_LOG_FATAL(d_logger, boost::format
+ ("AudioComponentFindNext Failed"));
+ throw std::runtime_error("audio_osx_source::setup");
+ }
+ err = AudioComponentInstanceNew(comp, &d_input_au);
+ check_error_and_throw(err, "AudioComponentInstanceNew Failed",
+ "audio_osx_source::setup");
-#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;
+ Component comp = FindNextComponent(NULL, &desc);
+ if(!comp) {
+ GR_LOG_FATAL(d_logger, boost::format
+ ("FindNextComponent Failed"));
+ throw std::runtime_error("audio_osx_source::setup");
+ }
+ err = OpenAComponent(comp, &d_input_au);
+ check_error_and_throw(err, "OpenAComponent Failed",
+ "audio_osx_source::setup");
+
+#endif
// 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
+ UInt32 enable_io = 1;
+ err = AudioUnitSetProperty
+ (d_input_au,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Input, 1, // input element
+ &enable_io, sizeof(enable_io));
+ check_error_and_throw
+ (err, "AudioUnitSetProperty Input Enable",
+ "audio_osx_source::setup");
- 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
+ // Disable output on the AUHAL
- // 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");
- }
+ enable_io = 0;
+ err = AudioUnitSetProperty
+ (d_input_au,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Output, 0, // output element
+ &enable_io, sizeof(enable_io));
+ check_error_and_throw
+ (err, "AudioUnitSetProperty Output Disable",
+ "audio_osx_source::setup");
+
+ // set the selected device ID as the current input device
+
+ err = AudioUnitSetProperty
+ (d_input_au, kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global, 0,
+ &d_input_ad_id, sizeof(d_input_ad_id));
+ check_error_and_throw
+ (err, "Setting selected input device as current failed",
+ "audio_osx_source::setup");
// Set up a callback function to retrieve input from the Audio Device
- AURenderCallbackStruct AUCallBack;
+ AURenderCallbackStruct au_callback = {
+ reinterpret_cast<AURenderCallback>
+ (&osx_source::au_input_callback),
+ reinterpret_cast<void*>(this)
+ };
+ UInt32 prop_size = (UInt32)sizeof(au_callback);
+
+ err = AudioUnitSetProperty
+ (d_input_au,
+ kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Global, 0,
+ &au_callback, prop_size);
+ check_error_and_throw
+ (err, "Set Input Callback",
+ "audio_osx_source::setup");
+
+ // Get the Stream Format (device side; cannot generally be changed)
+
+ prop_size = (UInt32)sizeof(d_asbd_device);
+ memset((void*)(&d_asbd_device), 0, (size_t)prop_size);
+ err = AudioUnitGetProperty
+ (d_input_au,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 1,
+ &d_asbd_device, &prop_size);
+ check_error_and_throw
+ (err, "Get Device Input Stream Format (before) failed",
+ "audio_osx_source::setup");
- AUCallBack.inputProc = (AURenderCallback)(osx_source::AUInputCallback);
- AUCallBack.inputProcRefCon = this;
+#if _OSX_AU_DEBUG_
+ std::cerr << std::endl << "---- Device Stream Format (before) ----"
+ << std::endl << d_asbd_device << std::endl << std::endl;
+#endif
- err = AudioUnitSetProperty(d_InputAU,
- kAudioOutputUnitProperty_SetInputCallback,
- kAudioUnitScope_Global,
- 0,
- &AUCallBack,
- sizeof (AURenderCallbackStruct));
- CheckErrorAndThrow(err, "AudioUnitSetProperty Input Callback",
- "audio_osx_source::audio_osx_source");
+ // try to set the device (input) side of the audio device to the
+ // sample rate of this source. This will likely fail, and
+ // that's OK; just ignore the error since we can accomplish
+ // audio input in other ways.
+
+ prop_size = (UInt32)sizeof(d_asbd_device);
+ d_asbd_device.mSampleRate = d_output_sample_rate;
+ err = AudioUnitSetProperty
+ (d_input_au,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 1,
+ &d_asbd_device, prop_size);
+#if _OSX_AU_DEBUG_
+ check_error
+ (err, "Set Device Input Stream Format failed (expected)");
+#endif
- UInt32 propertySize;
- AudioStreamBasicDescription asbd_device, asbd_client, asbd_user;
+ memset((void*)(&d_asbd_device), 0, (size_t)prop_size);
+ err = AudioUnitGetProperty
+ (d_input_au,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 1,
+ &d_asbd_device, &prop_size);
+ check_error_and_throw
+ (err, "Get Device Input Stream Format (after) failed",
+ "audio_osx_source::setup");
- // 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
+#if _OSX_AU_DEBUG_
+ std::cerr << std::endl << "---- Device Stream Format (after) ----"
+ << std::endl << d_asbd_device << std::endl << std::endl;
+#endif
- // Get the Stream Format (device side)
+ d_device_sample_rate = d_asbd_device.mSampleRate;
- 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");
+ // Get the Stream Format (client side; might be changeable)
+
+ prop_size = (UInt32)sizeof(d_asbd_client);
+ memset((void*)(&d_asbd_client), 0, (size_t)prop_size);
+ err = AudioUnitGetProperty
+ (d_input_au,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output, 1,
+ &d_asbd_client, &prop_size);
+ check_error_and_throw
+ (err, "Get Device Output Stream Format (before) failed",
+ "audio_osx_source::setup");
#if _OSX_AU_DEBUG_
- std::cerr << std::endl << "---- Device Stream Format ----" << std::endl;
- PrintStreamDesc(&asbd_device);
+ std::cerr << "---- Client Stream Format (Before) ----"
+ << std::endl << d_asbd_client << std::endl << std::endl;
#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");
+ // Set the format of all the AUs to the
+ // input/output devices channel count
+
+ d_asbd_client.mFormatID = kAudioFormatLinearPCM;
+ d_asbd_client.mFormatFlags = (kAudioFormatFlagIsFloat |
+ kAudioFormatFlagIsPacked |
+ kAudioFormatFlagIsNonInterleaved);
+ if((d_asbd_client.mFormatID == kAudioFormatLinearPCM) &&
+ (d_n_dev_channels == 1)) {
+ d_asbd_client.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
+ }
+ d_asbd_client.mBytesPerFrame = (UInt32)sizeof(float);
+ d_asbd_client.mFramesPerPacket = 1;
+ d_asbd_client.mBitsPerChannel = d_asbd_client.mBytesPerFrame * 8;
+ d_asbd_client.mChannelsPerFrame = d_n_dev_channels;
+ d_asbd_client.mBytesPerPacket = d_asbd_client.mBytesPerFrame;
+
+ // according to Apple docs [see, e.g., Apple Technical Note
+ // TN2091 "Device input using the HAL Output Audio Unit"], the
+ // device input and output sample rate must be the same; do
+ // sample rate conversion elsewhere.
+
+ d_asbd_client.mSampleRate = d_asbd_device.mSampleRate;
+ err = AudioUnitSetProperty
+ (d_input_au,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output, 1,
+ &d_asbd_client, prop_size);
+ check_error_and_throw
+ (err, "Set Device Ouput Stream Format failed",
+ "audio_osx_source::setup");
+
+ // Get the Stream Format (client side), again
+
+ prop_size = (UInt32)sizeof(d_asbd_client);
+ memset((void*)(&d_asbd_client), 0, (size_t)prop_size);
+ err = AudioUnitGetProperty
+ (d_input_au,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output, 1,
+ &d_asbd_client, &prop_size);
+ check_error_and_throw
+ (err, "Get Device Output Stream Format (after) failed",
+ "audio_osx_source::setup");
#if _OSX_AU_DEBUG_
- std::cerr << std::endl << "---- Client Stream Format ----" << std::endl;
- PrintStreamDesc(&asbd_client);
+ std::cerr << "---- Client Stream Format (After) ----"
+ << std::endl << d_asbd_client << std::endl << std::endl;
#endif
- // Set the format of all the AUs to the input/output devices channel
count
+ d_pass_through = (d_asbd_client.mSampleRate == d_output_sample_rate);
- // get the max number of input (& thus output) channels supported by
- // this device
- d_n_max_channels = asbd_device.mChannelsPerFrame;
+ if (d_pass_through) {
- // create the output io signature;
- // no input siganture to set (source is hardware)
- set_output_signature(io_signature::make(1,
- d_n_max_channels,
- sizeof(float)));
+ // no need to do conversion if d_asbd_client matches user wants
+ d_lead_size_frames = d_trail_size_frames = 0L;
- // 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);
}
+ else {
- d_deviceSampleRate = asbd_device.mSampleRate;
- d_n_deviceChannels = asbd_device.mChannelsPerFrame;
+ // create an ASBD for the user's wants
+
+ memset((void*)(&d_asbd_user), 0, sizeof(d_asbd_user));
+ d_asbd_user.mSampleRate = d_output_sample_rate;
+ d_asbd_user.mFormatID = kAudioFormatLinearPCM;
+ d_asbd_user.mFormatFlags = (kLinearPCMFormatFlagIsFloat |
+ GR_PCM_ENDIANNESS |
+ kLinearPCMFormatFlagIsPacked |
+ kAudioFormatFlagIsNonInterleaved);
+ d_asbd_user.mBytesPerPacket = (UInt32)sizeof(float);
+ d_asbd_user.mFramesPerPacket = 1;
+ d_asbd_user.mBytesPerFrame = d_asbd_user.mBytesPerPacket;
+ d_asbd_user.mChannelsPerFrame = d_n_dev_channels;
+ d_asbd_user.mBitsPerChannel = d_asbd_user.mBytesPerPacket * 8;
- asbd_client.mSampleRate = asbd_device.mSampleRate;
- asbd_client.mFormatID = kAudioFormatLinearPCM;
- asbd_client.mFormatFlags = (kAudioFormatFlagIsFloat |
- kAudioFormatFlagIsPacked |
- kAudioFormatFlagIsNonInterleaved);
- if((asbd_client.mFormatID == kAudioFormatLinearPCM) &&
- (d_n_deviceChannels == 1)) {
- asbd_client.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
- }
- asbd_client.mBytesPerFrame = sizeof(float);
- asbd_client.mFramesPerPacket = 1;
- asbd_client.mBitsPerChannel = asbd_client.mBytesPerFrame * 8;
- asbd_client.mChannelsPerFrame = d_n_deviceChannels;
- asbd_client.mBytesPerPacket = asbd_client.mBytesPerFrame;
-
- propertySize = sizeof(AudioStreamBasicDescription);
- err = AudioUnitSetProperty(d_InputAU,
- kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Output,
- 1,
- &asbd_client,
- propertySize);
- CheckErrorAndThrow(err, "AudioUnitSetProperty Device Ouput Stream
Format",
- "audio_osx_source::audio_osx_source");
-
- // 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 = sizeof(float);
- asbd_user.mFramesPerPacket = 1;
- asbd_user.mBytesPerFrame = asbd_user.mBytesPerPacket;
- asbd_user.mChannelsPerFrame = d_n_deviceChannels;
- asbd_user.mBitsPerChannel = asbd_user.mBytesPerPacket * 8;
-
- 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");
+ err = AudioConverterNew(&d_asbd_client,
+ &d_asbd_user,
+ &d_audio_converter);
+ check_error_and_throw
+ (err, "AudioConverterNew failed",
+ "audio_osx_source::setup");
// 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");
+ UInt32 ac_quality = kAudioConverterQuality_Max;
+ prop_size = (UInt32)sizeof(ac_quality);
+ err = AudioConverterSetProperty
+ (d_audio_converter,
+ kAudioConverterSampleRateConverterQuality,
+ prop_size, &ac_quality);
+ check_error_and_throw
+ (err, "Set Sample Rate Converter Quality failed",
+ "audio_osx_source::setup");
// set the audio converter's prime method to "pre",
// which uses both leading and trailing frames
@@ -396,59 +510,52 @@ namespace gr {
// 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;
- }
+ UInt32 ac_prime_method = kConverterPrimeMethod_Pre;
+ prop_size = (UInt32)sizeof(ac_prime_method);
+ err = AudioConverterSetProperty
+ (d_audio_converter,
+ kAudioConverterPrimeMethod,
+ prop_size, &ac_prime_method);
+ check_error_and_throw
+ (err, "Set Prime Method failed",
+ "audio_osx_source::setup");
+
+ // Get the size of the priming I/O buffer space to allow for
+ // pre-allocated buffers
+
+ AudioConverterPrimeInfo ac_prime_info = {0, 0};
+ prop_size = (UInt32)sizeof(ac_prime_info);
+ err = AudioConverterGetProperty
+ (d_audio_converter,
+ kAudioConverterPrimeInfo,
+ &prop_size, &ac_prime_info);
+ check_error_and_throw
+ (err, "Get Prime Info failed",
+ "audio_osx_source::setup");
+
+ d_lead_size_frames = ac_prime_info.leadingFrames;
+ d_trail_size_frames = ac_prime_info.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;
+
+ d_lead_size_bytes = d_lead_size_frames * sizeof(float);
+ d_trail_size_bytes = d_trail_size_frames * sizeof(float);
+
+ prop_size = (UInt32)sizeof(d_device_buffer_size_frames);
+ err = AudioUnitGetProperty
+ (d_input_au,
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Global, 0,
+ &d_device_buffer_size_frames, &prop_size);
+ check_error_and_throw
+ (err, "Get Buffer Frame Size failed",
+ "audio_osx_source::setup");
+
+ d_device_buffer_size_bytes = (d_device_buffer_size_frames *
+ sizeof(float));
+ d_input_buffer_size_bytes = (d_device_buffer_size_bytes +
+ d_lead_size_bytes);
+ d_input_buffer_size_frames = (d_device_buffer_size_frames +
+ d_lead_size_frames);
// outBufSizeBytes = floor (inBufSizeBytes * rate_out / rate_in)
// since this is rarely exact, we need another buffer to hold
@@ -457,197 +564,448 @@ namespace gr {
// 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);
+ d_extra_buffer_size_frames =
+ ((UInt32)ceil(d_device_sample_rate /
+ d_output_sample_rate) *
+ sizeof(float));
+ if(d_extra_buffer_size_frames < 4)
+ d_extra_buffer_size_frames = 4;
+ d_extra_buffer_size_bytes =
+ d_extra_buffer_size_frames * sizeof(float);
+
+ d_output_buffer_size_frames =
+ (UInt32)ceil(((Float64)d_input_buffer_size_frames) *
+ d_output_sample_rate / d_device_sample_rate);
+ d_output_buffer_size_bytes =
+ d_output_buffer_size_frames * sizeof(float);
+ d_input_buffer_size_frames += d_extra_buffer_size_frames;
+
+ // pre-alloc all CoreAudio buffers
+
+ alloc_audio_buffer_list
+ (&d_input_buffer, d_n_dev_channels,
+ d_input_buffer_size_bytes);
+ if(!d_pass_through) {
+ alloc_audio_buffer_list
+ (&d_output_buffer, d_n_dev_channels,
+ d_output_buffer_size_bytes);
}
else {
- d_OutputBuffer = d_InputBuffer;
+ d_output_buffer = d_input_buffer;
+ }
+
+ // allocate the output circular buffer(s), one per device
+ // channel (the user may select fewer channels; those buffers
+ // just won't get used).
+
+ d_buffers.resize(d_n_dev_channels);
+ for(UInt32 nn = 0; nn < d_n_dev_channels; ++nn) {
+ d_buffers[nn] = new circular_buffer<float>
+ (d_buffer_sample_count, false, false);
+ }
+
+ // clear the RunLoop (whatever that is); needed, for some
+ // reason, before a listener will work.
+
+ {
+ CFRunLoopRef the_run_loop = NULL;
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyRunLoop,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ prop_size = (UInt32)sizeof(the_run_loop);
+ err = AudioObjectSetPropertyData
+ (kAudioObjectSystemObject, &property, 0, NULL,
+ prop_size, &the_run_loop);
+ check_error(err, "Clearing RunLoop failed; "
+ "Audio Input Device Listener might not work.");
+ }
+
+ // set up listeners
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+
+ // 10.4 and newer
+
+ {
+
+ // set up a listener if hardware changes (at all)
+
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyDevices,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ err = AudioObjectAddPropertyListener
+ (kAudioObjectSystemObject, &property,
+ reinterpret_cast<AudioObjectPropertyListenerProc>
+ (&osx_source::hardware_listener),
+ reinterpret_cast<void*>(this));
+ check_error(err, "Adding Audio Hardware Listener failed");
}
- // create the stuff to regulate I/O
+ if (d_using_default_device) {
- d_cond_data = new gr::thread::condition_variable();
- if(d_cond_data == NULL)
- CheckErrorAndThrow(errno, "new condition (data)",
- "audio_osx_source::audio_osx_source");
+ // set up a listener if default hardware input device changes
- d_internal = new gr::thread::mutex();
- if(d_internal == NULL)
- CheckErrorAndThrow(errno, "new mutex (internal)",
- "audio_osx_source::audio_osx_source");
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyDefaultInputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
- // initialize the AU for input
+ err = AudioObjectAddPropertyListener
+ (kAudioObjectSystemObject, &property,
+ reinterpret_cast<AudioObjectPropertyListenerProc>
+ (&osx_source::default_listener),
+ reinterpret_cast<void*>(this));
+ check_error(err, "Adding Default Input Audio Listener failed");
- err = AudioUnitInitialize(d_InputAU);
- CheckErrorAndThrow(err, "AudioUnitInitialize",
- "audio_osx_source::audio_osx_source");
+ }
+
+#else
+
+ // 10.5 and older
+
+ err = AudioHardwareAddPropertyListener
+ (kAudioHardwarePropertyDevices,
+ reinterpret_cast<AudioHardwarePropertyListenerProc>
+ (&osx_source::hardware_listener),
+ reinterpret_cast<void*>(this));
+ check_error(err, "Adding Audio Hardware Listener failed");
+
+ if (d_using_default_device) {
+
+ err = AudioHardwareAddPropertyListener
+ (kAudioHardwarePropertyDefaultInputDevice,
+ reinterpret_cast<AudioHardwarePropertyListenerProc>
+ (&osx_source::default_listener),
+ reinterpret_cast<void*>(this));
+ check_error(err, "Adding Default Input Audio Listener failed");
+
+ }
+
+#endif
+
+ // initialize the AU for input, so that it is ready to be used
+
+ err = AudioUnitInitialize(d_input_au);
+ check_error_and_throw
+ (err, "AudioUnitInitialize",
+ "audio_osx_source::check_channels");
#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;
+ std::cerr << std::endl << "audio_osx_source Parameters:"
+ << std::endl << " Device Sample Rate is "
+ << d_device_sample_rate << std::endl
+ << " Client Sample Rate is "
+ << (d_pass_through ? d_output_sample_rate :
+ d_device_sample_rate) << std::endl
+ << " User Sample Rate is "
+ << d_output_sample_rate << std::endl
+ << " Do Passthrough is "
+ << (d_pass_through ? "true" : "false") << std::endl
+ << " Max Sample Count is "
+ << d_buffer_sample_count << std::endl
+ << " # Device Channels is "
+ << d_n_dev_channels << std::endl
+ << " Device Buffer Size in Frames = "
+ << d_device_buffer_size_frames << std::endl
+ << " Lead Size in Frames = "
+ << d_lead_size_frames << std::endl
+ << " Trail Size in Frames = "
+ << d_trail_size_frames << std::endl
+ << " Input Buffer Size in Frames = "
+ << d_input_buffer_size_frames << std::endl
+ << " Output Buffer Size in Frames = "
+ << d_output_buffer_size_frames << std::endl
+ << std::endl;
#endif
}
void
- osx_source::AllocAudioBufferList(AudioBufferList** t_ABL,
- UInt32 n_channels,
- UInt32 bufferSizeBytes)
+ osx_source::alloc_audio_buffer_list
+ (AudioBufferList** t_abl,
+ UInt32 n_channels,
+ UInt32 input_buffer_size_bytes)
{
- FreeAudioBufferList(t_ABL);
- UInt32 propertySize = (offsetof(AudioBufferList, mBuffers[0]) +
- (sizeof(AudioBuffer) * n_channels));
- *t_ABL = (AudioBufferList*)calloc(1, propertySize);
- (*t_ABL)->mNumberBuffers = n_channels;
+ free_audio_buffer_list(t_abl);
+ UInt32 prop_size = (offsetof(AudioBufferList, mBuffers[0]) +
+ (sizeof(AudioBuffer) * n_channels));
+ *t_abl = (AudioBufferList*)calloc(1, prop_size);
+ (*t_abl)->mNumberBuffers = n_channels;
int counter = n_channels;
+#if _OSX_AU_DEBUG_
+ std::cerr << "alloc_audio_buffer_list: (#chan, #bytes) == ("
+ << n_channels << ", " << input_buffer_size_bytes
+ << ")" << std::endl;
+#endif
+
while(--counter >= 0) {
- (*t_ABL)->mBuffers[counter].mNumberChannels = 1;
- (*t_ABL)->mBuffers[counter].mDataByteSize = bufferSizeBytes;
- (*t_ABL)->mBuffers[counter].mData = calloc (1, bufferSizeBytes);
+ AudioBuffer* t_ab = &((*t_abl)->mBuffers[counter]);
+ t_ab->mNumberChannels = 1;
+ t_ab->mDataByteSize = input_buffer_size_bytes;
+ t_ab->mData = calloc(1, input_buffer_size_bytes);
}
}
void
- osx_source::FreeAudioBufferList(AudioBufferList** t_ABL)
+ osx_source::free_audio_buffer_list(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;
+ if(*t_abl) {
+ int counter = (*t_abl)->mNumberBuffers;
+ while(--counter >= 0) {
+ AudioBuffer* t_ab = &((*t_abl)->mBuffers[counter]);
+ free(t_ab->mData);
+ t_ab->mData = 0;
+ }
+ free(*t_abl);
+ (*t_abl) = 0;
}
}
bool
- osx_source::IsRunning()
+ osx_source::is_running()
{
- 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);
+ UInt32 au_running = 0;
+ if (d_input_au) {
+
+ UInt32 prop_size = (UInt32)sizeof(au_running);
+ OSStatus err = AudioUnitGetProperty
+ (d_input_au,
+ kAudioOutputUnitProperty_IsRunning,
+ kAudioUnitScope_Global, 0,
+ &au_running, &prop_size);
+ check_error_and_throw
+ (err, "AudioUnitGetProperty IsRunning",
+ "audio_osx_source::is_running");
+ }
+
+ return(au_running != 0);
}
bool
osx_source::start()
{
- if(! IsRunning ()) {
- OSStatus err = AudioOutputUnitStart(d_InputAU);
- CheckErrorAndThrow(err, "AudioOutputUnitStart",
+#if _OSX_AU_DEBUG_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::start: Starting." << std::endl;
+#endif
+
+ if((!is_running ()) && d_input_au) {
+
+#if _OSX_AU_DEBUG_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::start: Starting Audio Unit."
+ << std::endl;
+#endif
+
+ // reset buffers before starting
+
+ for(UInt32 nn = 0; nn < d_buffers.size(); ++nn) {
+ d_buffers[nn]->reset();
+ }
+
+ // start the audio unit
+
+ OSStatus err = AudioOutputUnitStart(d_input_au);
+ check_error_and_throw(err, "AudioOutputUnitStart",
"audio_osx_source::start");
+
+ // clear reset (will sometimes be necessary, and it has to
+ // happen after AudioOutputUnitStart)
+
+ d_do_reset = false;
+
}
+#if _OSX_AU_DEBUG_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::start: Returning." << std::endl;
+#endif
+
return (true);
}
bool
osx_source::stop()
{
- if(IsRunning ()) {
- OSStatus err = AudioOutputUnitStop(d_InputAU);
- CheckErrorAndThrow(err, "AudioOutputUnitStart",
+#if _OSX_AU_DEBUG_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::stop: Starting." << std::endl;
+#endif
+ if(is_running ()) {
+
+#if _OSX_AU_DEBUG_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::stop: stopping audio unit."
+ << std::endl;
+#endif
+
+ // stop the audio unit
+
+ OSStatus err = AudioOutputUnitStop(d_input_au);
+ check_error_and_throw(err, "AudioOutputUnitStart",
"audio_osx_source::stop");
- for(UInt32 n = 0; n < d_n_user_channels; n++) {
- d_buffers[n]->abort ();
+
+ // abort all buffers
+
+ for(UInt32 nn = 0; nn < d_n_user_channels; ++nn) {
+ d_buffers[nn]->abort ();
}
}
+#if _OSX_AU_DEBUG_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::stop: Returning." << std::endl;
+#endif
+
return (true);
}
- osx_source::~osx_source()
+ void
+ osx_source::teardown()
{
+#if _OSX_AU_DEBUG_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::teardown: Starting." << std::endl;
+#endif
+
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");
+#ifndef GR_USE_OLD_AUDIO_UNIT
+
+ // 10.4 and newer
+ {
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyDevices,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ err = AudioObjectRemovePropertyListener
+ (kAudioObjectSystemObject, &property,
+ reinterpret_cast<AudioObjectPropertyListenerProc>
+ (&osx_source::hardware_listener),
+ reinterpret_cast<void*>(this));
+#if _OSX_AU_DEBUG_
+ check_error(err, "teardown: AudioObjectRemovePropertyListener "
+ "hardware failed");
+#endif
+
+ }
+
+ if (d_using_default_device) {
+
+ AudioObjectPropertyAddress property = {
+ kAudioHardwarePropertyDefaultInputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ err = AudioObjectRemovePropertyListener
+ (kAudioObjectSystemObject, &property,
+ reinterpret_cast<AudioObjectPropertyListenerProc>
+ (&osx_source::default_listener),
+ reinterpret_cast<void*>(this));
+#if _OSX_AU_DEBUG_
+ check_error(err, "AudioObjectRemovePropertyListener default");
+#endif
+
+ d_using_default_device = false;
+
+ }
+#else
+
+ // 10.5 and older
err = AudioHardwareRemovePropertyListener
- (kAudioHardwarePropertyDefaultInputDevice,
- (AudioHardwarePropertyListenerProc)HardwareListener);
- CheckError(err, "~audio_osx_source:
AudioHardwareRemovePropertyListener");
+ (kAudioHardwarePropertyDevices,
+ reinterpret_cast<AudioHardwarePropertyListenerProc>
+ (&osx_source::hardware_listener)
+ reinterpret_cast<void*>(this));
+#if _OSX_AU_DEBUG_
+ check_error(err, "AudioObjectRemovePropertyListener hardware");
+#endif
+
+ if (d_using_default_device) {
+ err = AudioHardwareRemovePropertyListener
+ (kAudioHardwarePropertyDefaultInputDevice,
+ reinterpret_cast<AudioHardwarePropertyListenerProc>
+ (&osx_source::default_listener),
+ reinterpret_cast<void*>(this));
+#if _OSX_AU_DEBUG_
+ check_error(err, "AudioObjectRemovePropertyListener default");
#endif
+ d_using_default_device = false;
+
+ }
+
+#endif // GR_USE_OLD_AUDIO_UNIT
// free pre-allocated audio buffers
- FreeAudioBufferList(&d_InputBuffer);
+ free_audio_buffer_list(&d_input_buffer);
- if(d_passThrough == false) {
- err = AudioConverterDispose(d_AudioConverter);
- CheckError(err, "~audio_osx_source: AudioConverterDispose");
- FreeAudioBufferList(&d_OutputBuffer);
+ if(!d_pass_through) {
+ err = AudioConverterDispose(d_audio_converter);
+#if _OSX_AU_DEBUG_
+ check_error(err, "~audio_osx_source: AudioConverterDispose");
+#endif
+ free_audio_buffer_list(&d_output_buffer);
}
// remove the audio unit
- err = AudioUnitUninitialize(d_InputAU);
- CheckError(err, "~audio_osx_source: AudioUnitUninitialize");
+ err = AudioUnitUninitialize(d_input_au);
+#if _OSX_AU_DEBUG_
+ check_error(err, "~audio_osx_source: AudioUnitUninitialize");
+#endif
#ifndef GR_USE_OLD_AUDIO_UNIT
- err = AudioComponentInstanceDispose(d_InputAU);
- CheckError(err, "~audio_osx_source: AudioComponentInstanceDispose");
+ err = AudioComponentInstanceDispose(d_input_au);
+#if _OSX_AU_DEBUG_
+ check_error(err, "~audio_osx_source: AudioComponentInstanceDispose");
+#endif
#else
- err = CloseComponent(d_InputAU);
- CheckError(err, "~audio_osx_source: CloseComponent");
+ err = CloseComponent(d_input_au);
+#if _OSX_AU_DEBUG_
+ check_error(err, "~audio_osx_source: CloseComponent");
+#endif
#endif
// empty and delete the queues
- for(UInt32 n = 0; n < d_n_max_channels; n++) {
- delete d_buffers[n];
- d_buffers[n] = 0;
+
+ for(UInt32 nn = 0; nn < d_buffers.size(); ++nn) {
+ delete d_buffers[nn];
+ d_buffers[nn] = 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;
+ d_buffers.resize(0);
+
+ // clear important variables; not # user channels
+
+ d_queue_sample_count = 0;
+ d_device_sample_rate = 0;
+ d_n_dev_channels = 0;
+ d_input_ad_id = 0;
+ d_input_au = 0;
+ d_input_buffer = d_output_buffer = 0;
+ d_audio_converter = 0;
+ d_using_default_device = false;
+
+#if _OSX_AU_DEBUG_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::teardown: Returning." << std::endl;
+#endif
}
bool
@@ -655,22 +1013,30 @@ namespace gr {
{
// 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()");
+
+ GR_LOG_FATAL(d_logger, boost::format
+ ("check_topology(): number of input "
+ "streams provided (%d) should be 0.")
+ % 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)) {
- 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()");
+ if((noutputs < 1) | (noutputs > (int) d_n_dev_channels)) {
+
+ GR_LOG_FATAL(d_logger, boost::format
+ ("check_topology(): number of output "
+ "streams provided (%d) should be in [1,%d] "
+ "for the selected input audio device.")
+ % noutputs % d_n_dev_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_
@@ -678,7 +1044,7 @@ namespace gr {
<< noutputs << std::endl;
#endif
- return (true);
+ return(true);
}
int
@@ -686,109 +1052,222 @@ namespace gr {
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
- // acquire control to do processing here only
- gr::thread::scoped_lock l(*d_internal);
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::work: Starting." << std::endl;
+#endif
+ if (d_do_reset) {
+ if (d_hardware_changed) {
-#if _OSX_AU_DEBUG_
- std::cerr << "work1: SC = " << d_queueSampleCount
+ // see if the current AudioDeviceID is still available
+
+ std::vector < AudioDeviceID > all_ad_ids;
+ osx::find_audio_devices
+ (d_desired_name, true,
+ &all_ad_ids, NULL);
+ bool found = false;
+ for (UInt32 nn = 0; (nn < all_ad_ids.size()) && (!found);
+ ++nn) {
+ found = (all_ad_ids[nn] == d_input_ad_id);
+ }
+ if (!found) {
+
+ GR_LOG_FATAL(d_logger, boost::format
+ ("The selected input audio device ('%s') "
+ "is no longer available.\n")
+ % d_selected_name);
+ return(gr::block::WORK_DONE);
+
+ }
+
+ d_do_reset = d_hardware_changed = false;
+
+ } else {
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: doing reset."
+ << std::endl;
+#endif
+
+ GR_LOG_WARN(d_logger, boost::format
+ ("\n\nThe default input audio device has "
+ "changed; resetting audio.\nThere may "
+ "be a sound glitch while resetting.\n"));
+
+ // for any changes, just tear down the current
+ // configuration, then set it up again using the user's
+ // parameters to try to make selections.
+
+ teardown();
+
+ gr::thread::scoped_lock l(d_internal);
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: mutex locked."
+ << std::endl;
+#endif
+
+ setup();
+ start();
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: returning after reset."
+ << std::endl;
+#endif
+ return(0);
+ }
+ }
+
+ gr::thread::scoped_lock l(d_internal);
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: mutex locked." << std::endl;
+#endif
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "work1: SC = " << d_queue_sample_count
<< ", #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.
+ // verify that data is available; if not enough data is
+ // available, either wait until it is (is "ok_to_block" is
+ // true), return (0) is no data is available and "ok_to_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) {
+ if(d_queue_sample_count < actual_noutput_items) {
+ if(d_queue_sample_count == 0) {
+ // no data; ok_to_block decides what to do
+ if(d_ok_to_block == true) {
+ // block until there is data to return, or on reset
+ while(d_queue_sample_count == 0) {
// release control so-as to allow data to be retrieved;
// block until there is data to return
- d_cond_data->wait(l);
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: waiting."
+ << std::endl;
+#endif
+ d_waiting_for_data = true;
+ d_cond_data.wait(l);
+ d_waiting_for_data = false;
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: done waiting."
+ << std::endl;
+#endif
// the condition's 'notify' was called; acquire control to
// keep thread safe
+
+ // if doing a reset, just return here; reset will pick
+ // up the next time this method is called.
+ if (d_do_reset) {
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: "
+ "returning for reset." << std::endl;
+#endif
+ return(0);
+ }
}
}
else {
// no data & not blocking; return nothing
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: no data "
+ "& not blocking; returning 0." << std::endl;
+#endif
return (0);
}
}
+
// use the actual amount of available data
- actual_noutput_items = d_queueSampleCount;
+ actual_noutput_items = d_queue_sample_count;
}
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: copying "
+ << actual_noutput_items << " items per channel"
+ << std::endl;
+#endif
+
// 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.
+ // 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);
+ 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;
+
+ GR_LOG_FATAL(d_logger, boost::format
+ ("work(): ERROR: number of available "
+ "items changing unexpectedly; expecting %d"
+ ", got %d.")
+ % actual_noutput_items % t_n_output_items);
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;
+ d_queue_sample_count -= actual_noutput_items;
-#if _OSX_AU_DEBUG_
- std::cerr << "work2: SC = " << d_queueSampleCount
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "work2: SC = " << d_queue_sample_count
<< ", act#OI = " << actual_noutput_items << std::endl
<< "Returning." << std::endl;
#endif
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::work: returning." << std::endl;
+#endif
return (actual_noutput_items);
}
OSStatus
- osx_source::ConverterCallback(AudioConverterRef inAudioConverter,
- UInt32* ioNumberDataPackets,
- AudioBufferList* ioData,
- AudioStreamPacketDescription** ioASPD,
- void* inUserData)
+ osx_source::converter_callback
+ (AudioConverterRef in_audio_converter,
+ UInt32* io_number_data_packets,
+ AudioBufferList* io_data,
+ AudioStreamPacketDescription** out_aspd,
+ void* in_user_data)
{
- // 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
-
- osx_source* This = static_cast<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
+ // This callback is for us to provide the buffers to CoreAudio
+ // for conversion. We need to set the buffers in the provided
+ // buffer list (io_data) to the buffers we know about and use to
+ // do data input (d_input_buffers).
+
+ osx_source* This = static_cast<osx_source*>(in_user_data);
+ AudioBufferList* l_input_abl = This->d_input_buffer;
+ UInt32 total_input_buffer_size_bytes =
+ ((*io_number_data_packets) * sizeof(float));
+ int counter = This->d_n_dev_channels;
+ io_data->mNumberBuffers = This->d_n_dev_channels;
+ This->d_n_actual_input_frames = (*io_number_data_packets);
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "cc1: io#DP = " << (*io_number_data_packets)
+ << ", TIBSB = " << total_input_buffer_size_bytes
<< ", #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;
+ AudioBuffer* t_ab = &(io_data->mBuffers[counter]);
+ t_ab->mNumberChannels = 1;
+ t_ab->mData = l_input_abl->mBuffers[counter].mData;
+ t_ab->mDataByteSize = total_input_buffer_size_bytes;
}
-#if _OSX_AU_DEBUG_
+#if _OSX_AU_DEBUG_RENDER_
std::cerr << "cc2: Returning." << std::endl;
#endif
@@ -796,59 +1275,89 @@ namespace gr {
}
OSStatus
- osx_source::AUInputCallback(void* inRefCon,
- AudioUnitRenderActionFlags* ioActionFlags,
- const AudioTimeStamp* inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList* ioData)
+ osx_source::au_input_callback
+ (void *in_ref_con,
+ AudioUnitRenderActionFlags *io_action_flags,
+ const AudioTimeStamp *in_time_stamp,
+ UInt32 in_bus_number,
+ UInt32 in_number_frames,
+ AudioBufferList *io_data)
{
- OSStatus err = noErr;
- osx_source* This = static_cast<osx_source*>(inRefCon);
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << ((void*)(pthread_self()))
+ << " : audio_osx_source::au_input_callback: Starting."
+ << std::endl;
+#endif
- gr::thread::scoped_lock l(*This->d_internal);
+ osx_source* This = reinterpret_cast
+ <osx_source*>(in_ref_con);
+ gr::thread::scoped_lock l(This->d_internal);
+ gr::logger_ptr d_logger = This->d_logger;
-#if _OSX_AU_DEBUG_
- std::cerr << "cb0: in#F = " << inNumberFrames
- << ", inBN = " << inBusNumber
- << ", SC = " << This->d_queueSampleCount << std::endl;
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "audio_osx_source::au_input_callback: mutex locked."
+ << std::endl;
#endif
- // Get the new audio data from the input device
+ OSStatus err = noErr;
- err = AudioUnitRender(This->d_InputAU,
- ioActionFlags,
- inTimeStamp,
- 1, //inBusNumber,
- inNumberFrames,
- This->d_InputBuffer);
- CheckErrorAndThrow(err, "AudioUnitRender",
- "audio_osx_source::AUInputCallback");
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "cb0: in#Fr = " << in_number_frames
+ << ", inBus# = " << in_bus_number
+ << ", idD = " << ((void*)io_data)
+ << ", d_ib = " << ((void*)(This->d_input_buffer))
+ << ", d_ib#c = " << This->d_input_buffer->mNumberBuffers
+ << ", SC = " << This->d_queue_sample_count << std::endl;
+#endif
- UInt32 AvailableInputFrames = inNumberFrames;
- This->d_n_AvailableInputFrames = inNumberFrames;
+ if (This->d_do_reset) {
- // get the number of actual output frames,
- // either via converting the buffer or not
+ // clear audio data; do not render since it will generate an error
+
+ AudioBufferList* t_abl = This->d_input_buffer;
+ for (UInt32 nn = 0; nn < t_abl->mNumberBuffers; ++nn) {
+ AudioBuffer* t_ab = &(t_abl->mBuffers[nn]);
+ memset(t_ab->mData, 0, (size_t)((t_ab->mDataByteSize) *
+ (t_ab->mNumberChannels)));
+ }
+ } else {
+
+ // Get the new audio data from the input device
- UInt32 ActualOutputFrames;
+ err = AudioUnitRender
+ (This->d_input_au, io_action_flags,
+ in_time_stamp, 1, //inBusNumber,
+ in_number_frames, This->d_input_buffer);
+ check_error_and_throw
+ (err, "AudioUnitRender",
+ "audio_osx_source::au_input_callback");
- 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);
+
+ UInt32 available_input_frames =
+ This->d_n_available_input_frames = in_number_frames;
+
+ // get the number of actual output frames,
+ // either via converting the buffer or not
+
+ UInt32 actual_output_frames = available_input_frames;
+
+ if(!This->d_pass_through) {
+ UInt32 available_input_bytes =
+ available_input_frames * sizeof(float);
+ UInt32 available_output_bytes = available_input_bytes;
+ UInt32 prop_size = sizeof(available_output_bytes);
+ err = AudioConverterGetProperty
+ (This->d_audio_converter,
+ kAudioConverterPropertyCalculateOutputBufferSize,
+ &prop_size,
+ &available_output_bytes);
+ check_error_and_throw
+ (err, "Get Output Buffer Size failed",
+ "audio_osx_source::au_input_callback");
+
+ UInt32 available_output_frames =
+ available_output_bytes / sizeof(float);
#if 0
// when decimating too much, the output sounds warbly due to
@@ -857,65 +1366,73 @@ namespace gr {
// 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) {
+
+ UInt32 l_input_bytes = available_output_bytes;
+ prop_size = sizeof(available_output_bytes);
+ err = AudioConverterGetProperty
+ (This->d_audio_converter,
+ kAudioConverterPropertyCalculateInputBufferSize,
+ &prop_size, &l_input_bytes);
+ check_error_and_throw
+ (err, "Get Input Buffer Size failed",
+ "audio_osx_source::au_input_callback");
+
+ if(l_input_bytes < available_input_bytes) {
// OK to zero pad the input a little
- AvailableOutputFrames += 1;
- AvailableOutputBytes = AvailableOutputFrames * sizeof(float);
+ ++available_output_frames;
+ available_output_bytes = available_output_frames * 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;
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "cb1: avail: #IF = " << available_input_frames
+ << ", #OF = " << available_output_frames << std::endl;
+#endif
+ actual_output_frames = available_output_frames;
+
+ // convert the data to the correct rate; on input,
+ // actual_output_frames is the number of available output frames
+
+ err = AudioConverterFillComplexBuffer
+ (This->d_audio_converter,
+ reinterpret_cast<AudioConverterComplexInputDataProc>
+ (&(This->converter_callback)),
+ in_ref_con,
+ &actual_output_frames,
+ This->d_output_buffer,
+ NULL);
+ check_error_and_throw
+ (err, "AudioConverterFillComplexBuffer failed",
+ "audio_osx_source::au_input_callback");
+
+ // on output, actual_output_frames is the actual number of
+ // output frames
+
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "cb2: actual: #IF = " << This->d_n_actual_input_frames
+ << ", #OF = " << actual_output_frames << std::endl;
+ if(This->d_n_actual_input_frames != available_input_frames)
+ std::cerr << "cb2.1: avail#IF = " << available_input_frames
+ << ", actual#IF = " << This->d_n_actual_input_frames <<
std::endl;
#endif
}
// add the output frames to the buffers' queue, checking for overflow
- int l_counter = This->d_n_user_channels;
+ int counter = This->d_n_user_channels;
int res = 0;
- while(--l_counter >= 0) {
- float* inBuffer = (float*)
This->d_OutputBuffer->mBuffers[l_counter].mData;
+ while(--counter >= 0) {
+ float* in_buffer = (float*)
+ This->d_output_buffer->mBuffers[counter].mData;
-#if _OSX_AU_DEBUG_
+#if _OSX_AU_DEBUG_RENDER_
std::cerr << "cb3: enqueuing audio data." << std::endl;
#endif
- int l_res = This->d_buffers[l_counter]->enqueue(inBuffer,
ActualOutputFrames);
+ int l_res = This->d_buffers[counter]->enqueue
+ (in_buffer, actual_output_frames);
if(l_res == -1)
res = -1;
}
@@ -926,152 +1443,80 @@ namespace gr {
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();
+ This->d_queue_sample_count =
+ This->d_buffers[0]->buffer_length_items();
}
else {
// keep up the local sample count
- This->d_queueSampleCount += ActualOutputFrames;
+ This->d_queue_sample_count += actual_output_frames;
}
-#if _OSX_AU_DEBUG_
- std::cerr << "cb4: #OI = " << ActualOutputFrames
- << ", #Cnt = " << This->d_queueSampleCount
- << ", mSC = " << This->d_max_sample_count << std::endl;
+#if _OSX_AU_DEBUG_RENDER_
+ std::cerr << "cb4: #OI = " << actual_output_frames
+ << ", #Cnt = " << This->d_queue_sample_count
+ << ", mSC = " << This->d_buffer_sample_count << std::endl;
#endif
// signal that data is available, if appropraite
- This->d_cond_data->notify_one();
-#if _OSX_AU_DEBUG_
+ if (This->d_waiting_for_data) {
+ This->d_cond_data.notify_one();
+ }
+
+#if _OSX_AU_DEBUG_RENDER_
std::cerr << "cb5: returning." << std::endl;
#endif
- return (err);
+ return(err);
}
- void
- osx_source::SetDefaultInputDeviceAsCurrent()
- {
- // set the default input device
- AudioDeviceID deviceID = 0;
- UInt32 dataSize = sizeof (AudioDeviceID);
- OSStatus err = noErr;
-
#ifndef GR_USE_OLD_AUDIO_UNIT
- AudioObjectPropertyAddress theAddress =
- { kAudioHardwarePropertyDefaultInputDevice,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
- &theAddress,
- 0,
- NULL,
- &dataSize,
- &deviceID);
-#else
- err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
- &dataSize,
- &deviceID);
-#endif
-
- CheckErrorAndThrow(err, "Get Audio Unit Property for Current Device",
- "audio_osx_source::SetDefaultInputDeviceAsCurrent");
-
- 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
- osx_source::HardwareListener(AudioHardwarePropertyID inPropertyID,
- void *inClientData)
- {
- OSStatus err = noErr;
- osx_source* This = static_cast<osx_source*>(inClientData);
-
- std::cerr << "a_o_s::HardwareListener" << std::endl;
-
- // set the new default hardware input device for use by our AU
+ osx_source::hardware_listener
+ (AudioObjectID in_object_id,
+ UInt32 in_num_addresses,
+ const AudioObjectPropertyAddress in_addresses[],
+ void* in_client_data)
- 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);
- }
+#else
OSStatus
- osx_source::UnitListener(void *inRefCon,
- AudioUnit ci,
- AudioUnitPropertyID inID,
- AudioUnitScope inScope,
- AudioUnitElement inElement)
- {
- OSStatus err = noErr;
- osx_source* This = static_cast<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");
+ osx_source::hardware_listener
+ (AudioHardwarePropertyID in_property_id,
+ void* in_client_data)
- std::cerr << "New Source Output Info:" << std::endl;
- PrintStreamDesc(&asbd);
+#endif
+ {
+ osx_source* This = reinterpret_cast
+ <osx_source*>(in_client_data);
+ This->reset(true);
+ return(noErr);
+ }
- // set the converter's input ASBD to this
+#ifndef GR_USE_OLD_AUDIO_UNIT
- err = AudioConverterSetProperty(This->d_AudioConverter,
-
kAudioConverterCurrentInputStreamDescription,
- propertySize,
- &asbd);
- CheckErrorAndThrow(err, "AudioConverterSetProperty "
- "CurrentInputStreamDescription",
- "audio_osx_source::UnitListener");
+ OSStatus
+ osx_source::default_listener
+ (AudioObjectID in_object_id,
+ UInt32 in_num_addresses,
+ const AudioObjectPropertyAddress in_addresses[],
+ void* in_client_data)
- // reset the converter to tell it that the stream has changed
+#else
- err = AudioConverterReset(This->d_AudioConverter);
- CheckErrorAndThrow(err, "AudioConverterReset",
- "audio_osx_source::UnitListener");
+ OSStatus
+ osx_source::default_listener
+ (AudioHardwarePropertyID in_property_id,
+ void* in_client_data)
- return (err);
+#endif
+ {
+ osx_source* This = reinterpret_cast
+ <osx_source*>(in_client_data);
+ This->reset(false);
+ return(noErr);
}
-#endif /* _OSX_DO_LISTENERS_ */
} /* namespace audio */
} /* namespace gr */
diff --git a/gr-audio/lib/osx/osx_source.h b/gr-audio/lib/osx/osx_source.h
index 0b675f8..3c9147d 100644
--- a/gr-audio/lib/osx/osx_source.h
+++ b/gr-audio/lib/osx/osx_source.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2006-2011,2013 Free Software Foundation, Inc.
+ * Copyright 2006-2011,2013-2014 Free Software Foundation, Inc.
*
* This file is part of GNU Radio.
*
@@ -24,10 +24,9 @@
#define INCLUDED_AUDIO_OSX_SOURCE_H
#include <gnuradio/audio/source.h>
-#include <string>
-#include <AudioToolbox/AudioToolbox.h>
-#include <AudioUnit/AudioUnit.h>
-#include <circular_buffer.h>
+
+#include "osx_common.h"
+#include "circular_buffer.h"
namespace gr {
namespace audio {
@@ -41,80 +40,135 @@ namespace gr {
*/
class osx_source : public 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;
- gr::thread::mutex* d_internal;
- gr::thread::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;
+ private:
+
+ Float64 d_device_sample_rate, d_output_sample_rate;
+ UInt32 d_input_buffer_size_frames, d_input_buffer_size_bytes;
+ UInt32 d_output_buffer_size_frames, d_output_buffer_size_bytes;
+ UInt32 d_device_buffer_size_frames, d_device_buffer_size_bytes;
+ UInt32 d_lead_size_frames, d_lead_size_bytes;
+ UInt32 d_trail_size_frames, d_trail_size_bytes;
+ UInt32 d_extra_buffer_size_frames, d_extra_buffer_size_bytes;
+ UInt32 d_queue_sample_count, d_buffer_sample_count;
+ UInt32 d_n_available_input_frames, d_n_actual_input_frames;
+ UInt32 d_n_user_channels, d_n_dev_channels;
+ bool d_ok_to_block, d_pass_through;
+ bool d_waiting_for_data, d_do_reset, d_hardware_changed;
+ bool d_using_default_device;
+ gr::thread::mutex d_internal;
+ gr::thread::condition_variable d_cond_data;
+ std::vector < circular_buffer<float>* > d_buffers;
+ std::string d_desired_name, d_selected_name;
+
+ // CoreAudio variables
+
+ AudioDeviceID d_input_ad_id;
+ AudioUnit d_input_au;
+ AudioBufferList* d_input_buffer;
+ AudioBufferList* d_output_buffer;
+ AudioConverterRef d_audio_converter;
+
+ // d_asbd_device: ASBD of the device that is creating the input
+ // data stream
+
+ AudioStreamBasicDescription d_asbd_device;
+
+ // d_asbd_client: ASBD of the client side (output) of the
+ // hardware device
+
+ AudioStreamBasicDescription d_asbd_client;
+
+ // d_asbd_user: ASBD of the user's arguments, if an audio
+ // converter is needed outside that provided by the client side.
+
+ AudioStreamBasicDescription d_asbd_user;
public:
- osx_source(int sample_rate = 44100,
- const std::string device_name = "",
- bool do_block = true,
- int channel_config = -1,
- int max_sample_count = -1);
- ~osx_source();
+ osx_source(int sample_rate,
+ const std::string& device_name,
+ bool ok_to_block);
+
+ virtual inline ~osx_source() {
+ teardown();
+ }
- bool start();
- bool stop();
- bool IsRunning();
+ virtual bool start();
+ virtual bool stop();
- bool check_topology(int ninputs, int noutputs);
+ virtual 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);
+ virtual int work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+
+ inline void reset(bool hardware_changed) {
+ d_hardware_changed = hardware_changed;
+ d_do_reset = true;
+ }
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);
+
+ bool is_running();
+
+ void setup();
+
+ void teardown();
+
+ void alloc_audio_buffer_list
+ (AudioBufferList** t_abl,
+ UInt32 n_channels,
+ UInt32 input_buffer_size_bytes);
+
+ void free_audio_buffer_list
+ (AudioBufferList** t_abl);
+
+ static OSStatus converter_callback
+ (AudioConverterRef in_audio_converter,
+ UInt32* io_number_data_packets,
+ AudioBufferList* io_data,
+ AudioStreamPacketDescription** out_aspd,
+ void* in_user_data);
+
+ static OSStatus au_input_callback
+ (void *in_ref_con,
+ AudioUnitRenderActionFlags *io_action_flags,
+ const AudioTimeStamp *in_time_stamp,
+ UInt32 in_bus_number,
+ UInt32 in_number_frames,
+ AudioBufferList *io_data);
+
+#ifndef GR_USE_OLD_AUDIO_UNIT
+
+ // OSX 10.4 and newer
+
+ static OSStatus hardware_listener
+ (AudioObjectID in_object_id,
+ UInt32 in_num_addresses,
+ const AudioObjectPropertyAddress in_addresses[],
+ void* in_client_data);
+
+ static OSStatus default_listener
+ (AudioObjectID in_object_id,
+ UInt32 in_num_addresses,
+ const AudioObjectPropertyAddress in_addresses[],
+ void* in_client_data);
+
+#else
+
+ // OSX 10.6 and older; removed as of 10.7
+
+ static OSStatus hardware_listener
+ (AudioHardwarePropertyID in_property_id,
+ void* in_client_data);
+
+ static OSStatus default_listener
+ (AudioHardwarePropertyID in_property_id,
+ void* in_client_data);
+
#endif
- };
+ };
} /* namespace audio */
} /* namespace gr */
- [Commit-gnuradio] [gnuradio] branch master updated (8f75515 -> 24dc96a), git, 2014/03/08
- [Commit-gnuradio] [gnuradio] 05/06: qtgui: adds ability to set qss style sheet as a preference., git, 2014/03/08
- [Commit-gnuradio] [gnuradio] 03/06: Merge remote-tracking branch 'michaelld/fix_gr_audio_osx' into master_osx, git, 2014/03/08
- [Commit-gnuradio] [gnuradio] 04/06: runtime: updates prefs to preserve format of preference file options that are in quotes., git, 2014/03/08
- [Commit-gnuradio] [gnuradio] 06/06: Merge branch 'master_osx', git, 2014/03/08
- [Commit-gnuradio] [gnuradio] 02/06: fix typo in getting default source (output) audio device name from gr::prefs, git, 2014/03/08
- [Commit-gnuradio] [gnuradio] 01/06: fix gr-audio osx: + use GNU Radio preferences file to set default input and output audio device, if provided; + use gr::logger for all non-debug messages; + case-insensitive string find with desired audio device name; + fixes buffer allocation bug with low sample rates; + allows using a specific (named) audio device, or the default; + handles the case when the selected audio device becomes unavailable (e.g., a USB stick is removed while in use); + if no audio device name is provided, uses the default audio device as found in System Preferences::Sound; + handles the case when the default audio device is in use, and the user changes that audio device in System Preferences::Sound, by internally resetting to use the newly selected audio device; + all non-Apple names are now lower_case, not CamelCase; + move osx_impl functions to gr::audio::osx, and use them correctly; + install osx_impl.h to expose gr::audio::osx functions, but iff OSX audio is enabled.,
git <=