1 /* -*- c++ -*- */
2 /*
3 * Copyright 2014 Free Software Foundation, Inc.
4 *
5 * This file is part of GNU Radio.
6 *
7 * GNU Radio is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
10 * any later version.
11 *
12 * GNU Radio is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Radio; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "../audio_registry.h"
28
29 #include <gnuradio/audio/osx_impl.h>
30
31 #include <algorithm>
32 #include <iostream>
33 #include <locale>
34 #include <stdexcept>
35
operator <<(std::ostream & s,const AudioStreamBasicDescription & asbd)36 std::ostream& operator<<(std::ostream& s, const AudioStreamBasicDescription& asbd)
37 {
38 char format_id[sizeof(asbd.mFormatID) + 1];
39 memcpy(format_id, (void*)(&asbd.mFormatID), sizeof(asbd.mFormatID));
40 format_id[sizeof(asbd.mFormatID)] = 0;
41 s << " Sample Rate : " << asbd.mSampleRate << std::endl;
42 s << " Format ID : " << format_id << std::endl;
43 s << " Format Flags : " << asbd.mFormatFlags << std::endl;
44 s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsFloat) != 0) << " : Is Float"
45 << std::endl;
46 s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0)
47 << " : Is Big Endian" << std::endl;
48 s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
49 << " : Is Signed Integer" << std::endl;
50 s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsPacked) != 0) << " : Is Packed"
51 << std::endl;
52 s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsAlignedHigh) != 0)
53 << " : Is Aligned High" << std::endl;
54 s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0)
55 << " : Is Non-Interleaved" << std::endl;
56 s << " " << ((asbd.mFormatFlags & kAudioFormatFlagIsNonMixable) != 0)
57 << " : Is Non-Mixable" << std::endl;
58 s << " Bytes / Packet : " << asbd.mBytesPerPacket << std::endl;
59 s << " Frames / Packet : " << asbd.mFramesPerPacket << std::endl;
60 s << " Bytes / Frame : " << asbd.mBytesPerFrame << std::endl;
61 s << " Channels / Frame : " << asbd.mChannelsPerFrame << std::endl;
62 s << " Bits / Channel : " << asbd.mBitsPerChannel;
63 return (s);
64 };
65
66 namespace gr {
67 namespace audio {
68 namespace osx {
69
_get_num_channels(AudioDeviceID ad_id,AudioObjectPropertyScope scope)70 static UInt32 _get_num_channels(AudioDeviceID ad_id, AudioObjectPropertyScope scope)
71 {
72 // retrieve the AudioBufferList associated with this ID using
73 // the provided scope
74
75 UInt32 num_channels = 0;
76 UInt32 prop_size = 0;
77 AudioObjectPropertyAddress ao_address = { kAudioDevicePropertyStreamConfiguration,
78 scope,
79 0 };
80 OSStatus err = noErr;
81 if ((err = AudioObjectGetPropertyDataSize(ad_id, &ao_address, 0, NULL, &prop_size)) ==
82 noErr) {
83 boost::scoped_array<AudioBufferList> buf_list(
84 reinterpret_cast<AudioBufferList*>(new char[prop_size]));
85 if ((err = AudioObjectGetPropertyData(
86 ad_id, &ao_address, 0, NULL, &prop_size, buf_list.get())) == noErr) {
87 for (UInt32 mm = 0; mm < buf_list.get()->mNumberBuffers; ++mm) {
88 num_channels += buf_list.get()->mBuffers[mm].mNumberChannels;
89 }
90 } else {
91 // assume 2 channels
92 num_channels = 2;
93 }
94 } else {
95 // assume 2 channels
96 num_channels = 2;
97 }
98 return (num_channels);
99 }
100
101 // works with both char and wchar_t
102 template <typename charT>
103 struct ci_equal {
ci_equalgr::audio::osx::ci_equal104 ci_equal(const std::locale& loc) : loc_(loc) {}
operator ()gr::audio::osx::ci_equal105 bool operator()(charT ch1, charT ch2)
106 {
107 return std::tolower(ch1, loc_) == std::tolower(ch2, loc_);
108 }
109
110 private:
111 const std::locale& loc_;
112 };
113
114 // find substring (case insensitive)
ci_find_substr(const std::string & str1,const std::string & str2,const std::locale & loc=std::locale ())115 static std::string::size_type ci_find_substr(const std::string& str1,
116 const std::string& str2,
117 const std::locale& loc = std::locale())
118 {
119 std::string::const_iterator it = std::search(str1.begin(),
120 str1.end(),
121 str2.begin(),
122 str2.end(),
123 ci_equal<std::string::value_type>(loc));
124 if (it != str1.end()) {
125 return (it - str1.begin());
126 }
127 // not found
128 return (std::string::npos);
129 }
130
get_num_channels_for_audio_device_id(AudioDeviceID ad_id,UInt32 * n_input,UInt32 * n_output)131 void get_num_channels_for_audio_device_id(AudioDeviceID ad_id,
132 UInt32* n_input,
133 UInt32* n_output)
134 {
135 if (n_input) {
136 *n_input = _get_num_channels(ad_id, kAudioDevicePropertyScopeInput);
137 }
138 if (n_output) {
139 *n_output = _get_num_channels(ad_id, kAudioDevicePropertyScopeOutput);
140 }
141 }
142
find_audio_devices(const std::string & device_name,bool is_input,std::vector<AudioDeviceID> * all_ad_ids,std::vector<std::string> * all_names)143 void find_audio_devices(const std::string& device_name,
144 bool is_input,
145 std::vector<AudioDeviceID>* all_ad_ids,
146 std::vector<std::string>* all_names)
147 {
148 if ((!all_ad_ids) && (!all_names)) {
149 // if nothing is requested, no point in doing anything!
150 return;
151 }
152
153 OSStatus err = noErr;
154
155 // retrieve the size of the array of known audio device IDs
156
157 UInt32 prop_size = 0;
158
159 AudioObjectPropertyAddress ao_address = { kAudioHardwarePropertyDevices,
160 kAudioObjectPropertyScopeGlobal,
161 kAudioObjectPropertyElementMaster };
162
163 if ((err = AudioObjectGetPropertyDataSize(
164 kAudioObjectSystemObject, &ao_address, 0, NULL, &prop_size)) != noErr) {
165 #if _OSX_AU_DEBUG_
166 std::cerr << "audio_osx::find_audio_devices: "
167 << "Unable to retrieve number of audio objects: " << err << std::endl;
168 #endif
169 return;
170 }
171
172 // get the total number of audio devices (input and output)
173
174 UInt32 num_devices = prop_size / sizeof(AudioDeviceID);
175
176 // retrieve all audio device ids
177
178 boost::scoped_array<AudioDeviceID> all_dev_ids(new AudioDeviceID[num_devices]);
179
180 if ((err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
181 &ao_address,
182 0,
183 NULL,
184 &prop_size,
185 all_dev_ids.get())) != noErr) {
186 #if _OSX_AU_DEBUG_
187 std::cerr << "audio_osx::find_audio_devices: "
188 << "Unable to retrieve audio object ids: " << err << std::endl;
189 #endif
190 return;
191 }
192
193 // success; loop over all retrieved output device ids, retrieving
194 // the name for each and comparing with the desired name.
195
196 std::vector<std::string> valid_names(num_devices);
197 std::vector<UInt32> valid_indices(num_devices);
198 UInt32 num_found_devices = 0;
199 AudioObjectPropertyScope scope =
200 is_input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
201
202 for (UInt32 nn = 0; nn < num_devices; ++nn) {
203
204 // make sure this device has input / output channels (it might
205 // also have output / input channels, too, but we do not care
206 // about that here)
207
208 AudioDeviceID t_id = all_dev_ids[nn];
209
210 if (is_input) {
211 UInt32 n_input_channels = 0;
212 get_num_channels_for_audio_device_id(t_id, &n_input_channels, NULL);
213 if (n_input_channels == 0) {
214 // no input channels; must be output device; just continue
215 // to the next audio device.
216 continue;
217 }
218 } else {
219 UInt32 n_output_channels = 0;
220 get_num_channels_for_audio_device_id(t_id, NULL, &n_output_channels);
221 if (n_output_channels == 0) {
222 // no output channels; must be input device; just continue
223 // to the next audio device.
224 continue;
225 }
226 }
227
228 // retrieve the device name; max name length is 64 characters.
229
230 prop_size = 65;
231 char c_name_buf[prop_size];
232 bzero((void*)c_name_buf, prop_size);
233 --prop_size;
234
235 AudioObjectPropertyAddress ao_address = { kAudioDevicePropertyDeviceName,
236 scope,
237 0 };
238
239 if ((err = AudioObjectGetPropertyData(
240 t_id, &ao_address, 0, NULL, &prop_size, (void*)c_name_buf)) != noErr) {
241 #if _OSX_AU_DEBUG_
242 std::cerr << "audio_osx::find_audio_devices: "
243 << "Unable to retrieve audio device name #" << (nn + 1) << ": "
244 << err << std::endl;
245 #endif
246 continue;
247 }
248 std::string name_buf(c_name_buf);
249
250 // compare the retrieved name with the desired one, if
251 // provided; case insensitive.
252
253 if (device_name.length() > 0) {
254
255 std::string::size_type found = ci_find_substr(name_buf, device_name);
256 if (found == std::string::npos) {
257 // not found; continue to the next ID
258 continue;
259 }
260 }
261
262 // store this info
263
264 valid_names[nn] = name_buf;
265 valid_indices[num_found_devices++] = nn;
266 }
267
268 // resize valid function arguments, then copy found values
269
270 if (all_ad_ids) {
271 all_ad_ids->resize(num_found_devices);
272 for (UInt32 nn = 0; nn < num_found_devices; ++nn) {
273 (*all_ad_ids)[nn] = all_dev_ids[valid_indices[nn]];
274 }
275 }
276
277 if (all_names) {
278 all_names->resize(num_found_devices);
279 for (UInt32 nn = 0; nn < num_found_devices; ++nn) {
280 (*all_names)[nn] = valid_names[valid_indices[nn]];
281 }
282 }
283 }
284
285 } /* namespace osx */
286 } /* namespace audio */
287 } /* namespace gr */
288