summaryrefslogtreecommitdiff
path: root/gr-audio/lib/osx/osx_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gr-audio/lib/osx/osx_impl.cc')
-rw-r--r--gr-audio/lib/osx/osx_impl.cc313
1 files changed, 313 insertions, 0 deletions
diff --git a/gr-audio/lib/osx/osx_impl.cc b/gr-audio/lib/osx/osx_impl.cc
new file mode 100644
index 0000000000..c4e580b132
--- /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 */