1 /* -*- c++ -*- */
2 /*
3  * Copyright 2006-2011,2013-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 #include "osx_source.h"
29 
30 #include <gnuradio/io_signature.h>
31 #include <gnuradio/prefs.h>
32 #include <stdexcept>
33 
34 namespace gr {
35 namespace audio {
36 
37 source::sptr
osx_source_fcn(int sampling_rate,const std::string & device_name,bool ok_to_block)38 osx_source_fcn(int sampling_rate, const std::string& device_name, bool ok_to_block)
39 {
40     return source::sptr(new osx_source(sampling_rate, device_name, ok_to_block));
41 }
42 
default_device_name()43 static std::string default_device_name()
44 {
45     return prefs::singleton()->get_string(
46         "audio_osx", "default_input_device", "built-in");
47 }
48 
osx_source(int sample_rate,const std::string & device_name,bool ok_to_block)49 osx_source::osx_source(int sample_rate, const std::string& device_name, bool ok_to_block)
50     : sync_block(
51           "audio_osx_source", io_signature::make(0, 0, 0), io_signature::make(0, 0, 0)),
52       d_device_sample_rate(0.0),
53       d_output_sample_rate(0.0),
54       d_input_buffer_size_frames(0),
55       d_input_buffer_size_bytes(0),
56       d_output_buffer_size_frames(0),
57       d_output_buffer_size_bytes(0),
58       d_device_buffer_size_frames(0),
59       d_device_buffer_size_bytes(0),
60       d_lead_size_frames(0),
61       d_lead_size_bytes(0),
62       d_trail_size_frames(0),
63       d_trail_size_bytes(0),
64       d_extra_buffer_size_frames(0),
65       d_extra_buffer_size_bytes(0),
66       d_queue_sample_count(0),
67       d_buffer_sample_count(0),
68       d_n_available_input_frames(0),
69       d_n_actual_input_frames(0),
70       d_n_user_channels(0),
71       d_n_dev_channels(0),
72       d_ok_to_block(ok_to_block),
73       d_pass_through(false),
74       d_waiting_for_data(false),
75       d_do_reset(false),
76       d_hardware_changed(false),
77       d_using_default_device(false),
78       d_desired_name(device_name.empty() ? default_device_name() : device_name),
79       d_input_ad_id(0),
80       d_input_au(0),
81       d_input_buffer(0),
82       d_output_buffer(0),
83       d_audio_converter(0)
84 {
85     // set the desired output sample rate
86 
87     if (sample_rate <= 0) {
88         GR_LOG_ERROR(d_logger, boost::format("Invalid Sample Rate: %d") % sample_rate);
89         throw std::invalid_argument("audio_osx_source");
90     } else {
91         d_output_sample_rate = (Float64)sample_rate;
92     }
93 
94     // set up for audio input using the stored desired parameters
95 
96     setup();
97 }
98 
setup()99 void osx_source::setup()
100 {
101     OSStatus err = noErr;
102 
103     // set the default input audio device id to "unknown"
104 
105     d_input_ad_id = kAudioDeviceUnknown;
106 
107     // try to find the input audio device, if specified
108 
109     std::vector<AudioDeviceID> all_ad_ids;
110     std::vector<std::string> all_names;
111 
112     osx::find_audio_devices(d_desired_name, true, &all_ad_ids, &all_names);
113 
114     // check number of device(s) returned
115 
116     if (d_desired_name.length() != 0) {
117         if (all_ad_ids.size() == 1) {
118 
119             // exactly 1 match was found; see if it was partial
120 
121             if (all_names[0].compare(d_desired_name) != 0) {
122 
123                 // yes: log the full device name
124                 GR_LOG_INFO(d_logger,
125                             boost::format("Using input audio device '%s'.") %
126                                 all_names[0]);
127             }
128 
129             // store info on this device
130 
131             d_input_ad_id = all_ad_ids[0];
132             d_selected_name = all_names[0];
133 
134         } else {
135 
136             // either 0 or more than 1 device was found; get all input
137             // device names, print those, and error out.
138 
139             osx::find_audio_devices("", true, NULL, &all_names);
140 
141             std::string err_str("\n\nA unique input audio device name "
142                                 "matching the string '");
143             err_str += d_desired_name;
144             err_str += "' was not found.\n\n";
145             err_str += "The current known input audio device name";
146             err_str += ((all_names.size() > 1) ? "s are" : " is");
147             err_str += ":\n";
148             for (UInt32 nn = 0; nn < all_names.size(); ++nn) {
149                 err_str += "    " + all_names[nn] + "\n";
150             }
151             GR_LOG_ERROR(d_logger, boost::format(err_str));
152             throw std::runtime_error("audio_osx_source::setup");
153         }
154     }
155 
156     // if no input audio device id was found or no specific device
157     // name was provided, use the default input audio device id as
158     // set in System Preferences.
159 
160     if (d_input_ad_id == kAudioDeviceUnknown) {
161 
162         UInt32 prop_size = (UInt32)sizeof(AudioDeviceID);
163         AudioObjectPropertyAddress ao_address = {
164             kAudioHardwarePropertyDefaultInputDevice,
165             kAudioObjectPropertyScopeGlobal,
166             kAudioObjectPropertyElementMaster
167         };
168 
169         err = AudioObjectGetPropertyData(
170             kAudioObjectSystemObject, &ao_address, 0, NULL, &prop_size, &d_input_ad_id);
171         check_error_and_throw(err,
172                               "Getting the default input audio device ID failed",
173                               "audio_osx_source::setup");
174 
175         {
176             // retrieve the device name; max name length is 64 characters.
177 
178             UInt32 prop_size = 65;
179             char c_name_buf[prop_size];
180             memset((void*)c_name_buf, 0, (size_t)prop_size);
181             --prop_size;
182 
183             AudioObjectPropertyAddress ao_address = { kAudioDevicePropertyDeviceName,
184                                                       kAudioDevicePropertyScopeInput,
185                                                       0 };
186 
187             if ((err = AudioObjectGetPropertyData(d_input_ad_id,
188                                                   &ao_address,
189                                                   0,
190                                                   NULL,
191                                                   &prop_size,
192                                                   (void*)c_name_buf)) != noErr) {
193 
194                 check_error(err, "Unable to retrieve input audio device name");
195 
196             } else {
197 
198                 GR_LOG_INFO(d_logger,
199                             boost::format("\n\nUsing input audio device '%s'.\n  ... "
200                                           "which is the current default input audio"
201                                           " device.\n  Changing the default input"
202                                           " audio device in the System Preferences"
203                                           " will \n  result in changing it here, too "
204                                           "(with an internal reconfiguration).\n") %
205                                 std::string(c_name_buf));
206             }
207 
208             d_selected_name = c_name_buf;
209         }
210 
211         d_using_default_device = true;
212     }
213 
214     // retrieve the total number of channels for the selected input
215     // audio device
216 
217     osx::get_num_channels_for_audio_device_id(d_input_ad_id, &d_n_dev_channels, NULL);
218 
219     // set the block output signature, if not already set
220     // (d_n_user_channels is set in check_topology, which is called
221     // before the flow-graph is running)
222 
223     if (d_n_user_channels == 0) {
224         set_output_signature(io_signature::make(1, d_n_dev_channels, sizeof(float)));
225     }
226 
227     // set the interim buffer size; to work with the GR scheduler,
228     // must be at least 16kB.  Pick 50 kB since that's plenty yet
229     // not very much.
230 
231     d_buffer_sample_count =
232         (d_output_sample_rate < 50000.0 ? 50000 : (UInt32)d_output_sample_rate);
233 
234 #if _OSX_AU_DEBUG_
235     std::cerr << "source(): max # samples = " << d_buffer_sample_count << std::endl;
236 #endif
237 
238     // create the default AudioUnit for input
239 
240     // Open the default input unit
241 
242 #ifndef GR_USE_OLD_AUDIO_UNIT
243     AudioComponentDescription desc;
244 #else
245     ComponentDescription desc;
246 #endif
247 
248     desc.componentType = kAudioUnitType_Output;
249     desc.componentSubType = kAudioUnitSubType_HALOutput;
250     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
251     desc.componentFlags = 0;
252     desc.componentFlagsMask = 0;
253 
254 #ifndef GR_USE_OLD_AUDIO_UNIT
255 
256     AudioComponent comp = AudioComponentFindNext(NULL, &desc);
257     if (!comp) {
258         GR_LOG_FATAL(d_logger, boost::format("AudioComponentFindNext Failed"));
259         throw std::runtime_error("audio_osx_source::setup");
260     }
261     err = AudioComponentInstanceNew(comp, &d_input_au);
262     check_error_and_throw(
263         err, "AudioComponentInstanceNew Failed", "audio_osx_source::setup");
264 
265 #else
266 
267     Component comp = FindNextComponent(NULL, &desc);
268     if (!comp) {
269         GR_LOG_FATAL(d_logger, boost::format("FindNextComponent Failed"));
270         throw std::runtime_error("audio_osx_source::setup");
271     }
272     err = OpenAComponent(comp, &d_input_au);
273     check_error_and_throw(err, "OpenAComponent Failed", "audio_osx_source::setup");
274 
275 #endif
276 
277     // must enable the AUHAL for input and disable output
278     // before setting the AUHAL's current device
279 
280     // Enable input on the AUHAL
281 
282     UInt32 enable_io = 1;
283     err = AudioUnitSetProperty(d_input_au,
284                                kAudioOutputUnitProperty_EnableIO,
285                                kAudioUnitScope_Input,
286                                1, // input element
287                                &enable_io,
288                                sizeof(enable_io));
289     check_error_and_throw(
290         err, "AudioUnitSetProperty Input Enable", "audio_osx_source::setup");
291 
292     // Disable output on the AUHAL
293 
294     enable_io = 0;
295     err = AudioUnitSetProperty(d_input_au,
296                                kAudioOutputUnitProperty_EnableIO,
297                                kAudioUnitScope_Output,
298                                0, // output element
299                                &enable_io,
300                                sizeof(enable_io));
301     check_error_and_throw(
302         err, "AudioUnitSetProperty Output Disable", "audio_osx_source::setup");
303 
304     // set the selected device ID as the current input device
305 
306     err = AudioUnitSetProperty(d_input_au,
307                                kAudioOutputUnitProperty_CurrentDevice,
308                                kAudioUnitScope_Global,
309                                0,
310                                &d_input_ad_id,
311                                sizeof(d_input_ad_id));
312     check_error_and_throw(err,
313                           "Setting selected input device as current failed",
314                           "audio_osx_source::setup");
315 
316     // Set up a callback function to retrieve input from the Audio Device
317 
318     AURenderCallbackStruct au_callback = { reinterpret_cast<AURenderCallback>(
319                                                &osx_source::au_input_callback),
320                                            reinterpret_cast<void*>(this) };
321     UInt32 prop_size = (UInt32)sizeof(au_callback);
322 
323     err = AudioUnitSetProperty(d_input_au,
324                                kAudioOutputUnitProperty_SetInputCallback,
325                                kAudioUnitScope_Global,
326                                0,
327                                &au_callback,
328                                prop_size);
329     check_error_and_throw(err, "Set Input Callback", "audio_osx_source::setup");
330 
331     // Get the Stream Format (device side; cannot generally be changed)
332 
333     prop_size = (UInt32)sizeof(d_asbd_device);
334     memset((void*)(&d_asbd_device), 0, (size_t)prop_size);
335     err = AudioUnitGetProperty(d_input_au,
336                                kAudioUnitProperty_StreamFormat,
337                                kAudioUnitScope_Input,
338                                1,
339                                &d_asbd_device,
340                                &prop_size);
341     check_error_and_throw(
342         err, "Get Device Input Stream Format (before) failed", "audio_osx_source::setup");
343 
344 #if _OSX_AU_DEBUG_
345     std::cerr << std::endl
346               << "---- Device Stream Format (before) ----" << std::endl
347               << d_asbd_device << std::endl
348               << std::endl;
349 #endif
350 
351     // try to set the device (input) side of the audio device to the
352     // sample rate of this source.  This will likely fail, and
353     // that's OK; just ignore the error since we can accomplish
354     // audio input in other ways.
355 
356     prop_size = (UInt32)sizeof(d_asbd_device);
357     d_asbd_device.mSampleRate = d_output_sample_rate;
358     err = AudioUnitSetProperty(d_input_au,
359                                kAudioUnitProperty_StreamFormat,
360                                kAudioUnitScope_Input,
361                                1,
362                                &d_asbd_device,
363                                prop_size);
364 #if _OSX_AU_DEBUG_
365     check_error(err, "Set Device Input Stream Format failed (expected)");
366 #endif
367 
368     memset((void*)(&d_asbd_device), 0, (size_t)prop_size);
369     err = AudioUnitGetProperty(d_input_au,
370                                kAudioUnitProperty_StreamFormat,
371                                kAudioUnitScope_Input,
372                                1,
373                                &d_asbd_device,
374                                &prop_size);
375     check_error_and_throw(
376         err, "Get Device Input Stream Format (after) failed", "audio_osx_source::setup");
377 
378 #if _OSX_AU_DEBUG_
379     std::cerr << std::endl
380               << "---- Device Stream Format (after) ----" << std::endl
381               << d_asbd_device << std::endl
382               << std::endl;
383 #endif
384 
385     d_device_sample_rate = d_asbd_device.mSampleRate;
386 
387     // Get the Stream Format (client side; might be changeable)
388 
389     prop_size = (UInt32)sizeof(d_asbd_client);
390     memset((void*)(&d_asbd_client), 0, (size_t)prop_size);
391     err = AudioUnitGetProperty(d_input_au,
392                                kAudioUnitProperty_StreamFormat,
393                                kAudioUnitScope_Output,
394                                1,
395                                &d_asbd_client,
396                                &prop_size);
397     check_error_and_throw(err,
398                           "Get Device Output Stream Format (before) failed",
399                           "audio_osx_source::setup");
400 
401 #if _OSX_AU_DEBUG_
402     std::cerr << "---- Client Stream Format (Before) ----" << std::endl
403               << d_asbd_client << std::endl
404               << std::endl;
405 #endif
406 
407     // Set the format of all the AUs to the
408     // input/output devices channel count
409 
410     d_asbd_client.mFormatID = kAudioFormatLinearPCM;
411     d_asbd_client.mFormatFlags = (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked |
412                                   kAudioFormatFlagIsNonInterleaved);
413     if ((d_asbd_client.mFormatID == kAudioFormatLinearPCM) && (d_n_dev_channels == 1)) {
414         d_asbd_client.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
415     }
416     d_asbd_client.mBytesPerFrame = (UInt32)sizeof(float);
417     d_asbd_client.mFramesPerPacket = 1;
418     d_asbd_client.mBitsPerChannel = d_asbd_client.mBytesPerFrame * 8;
419     d_asbd_client.mChannelsPerFrame = d_n_dev_channels;
420     d_asbd_client.mBytesPerPacket = d_asbd_client.mBytesPerFrame;
421 
422     // according to Apple docs [see, e.g., Apple Technical Note
423     // TN2091 "Device input using the HAL Output Audio Unit"], the
424     // device input and output sample rate must be the same; do
425     // sample rate conversion elsewhere.
426 
427     d_asbd_client.mSampleRate = d_asbd_device.mSampleRate;
428     err = AudioUnitSetProperty(d_input_au,
429                                kAudioUnitProperty_StreamFormat,
430                                kAudioUnitScope_Output,
431                                1,
432                                &d_asbd_client,
433                                prop_size);
434     check_error_and_throw(
435         err, "Set Device Output Stream Format failed", "audio_osx_source::setup");
436 
437     // Get the Stream Format (client side), again
438 
439     prop_size = (UInt32)sizeof(d_asbd_client);
440     memset((void*)(&d_asbd_client), 0, (size_t)prop_size);
441     err = AudioUnitGetProperty(d_input_au,
442                                kAudioUnitProperty_StreamFormat,
443                                kAudioUnitScope_Output,
444                                1,
445                                &d_asbd_client,
446                                &prop_size);
447     check_error_and_throw(
448         err, "Get Device Output Stream Format (after) failed", "audio_osx_source::setup");
449 
450 #if _OSX_AU_DEBUG_
451     std::cerr << "---- Client Stream Format (After) ----" << std::endl
452               << d_asbd_client << std::endl
453               << std::endl;
454 #endif
455 
456     d_pass_through = (d_asbd_client.mSampleRate == d_output_sample_rate);
457 
458     if (d_pass_through) {
459 
460         // no need to do conversion if d_asbd_client matches user wants
461         d_lead_size_frames = d_trail_size_frames = 0L;
462 
463     } else {
464 
465         // create an ASBD for the user's wants
466 
467         memset((void*)(&d_asbd_user), 0, sizeof(d_asbd_user));
468         d_asbd_user.mSampleRate = d_output_sample_rate;
469         d_asbd_user.mFormatID = kAudioFormatLinearPCM;
470         d_asbd_user.mFormatFlags =
471             (kLinearPCMFormatFlagIsFloat | GR_PCM_ENDIANNESS |
472              kLinearPCMFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved);
473         d_asbd_user.mBytesPerPacket = (UInt32)sizeof(float);
474         d_asbd_user.mFramesPerPacket = 1;
475         d_asbd_user.mBytesPerFrame = d_asbd_user.mBytesPerPacket;
476         d_asbd_user.mChannelsPerFrame = d_n_dev_channels;
477         d_asbd_user.mBitsPerChannel = d_asbd_user.mBytesPerPacket * 8;
478 
479         // Create the audio converter
480 
481         err = AudioConverterNew(&d_asbd_client, &d_asbd_user, &d_audio_converter);
482         check_error_and_throw(err, "AudioConverterNew failed", "audio_osx_source::setup");
483 
484         // Set the audio converter sample rate quality to "max" ...
485         // requires more samples, but should sound nicer
486 
487         UInt32 ac_quality = kAudioConverterQuality_Max;
488         prop_size = (UInt32)sizeof(ac_quality);
489         err = AudioConverterSetProperty(d_audio_converter,
490                                         kAudioConverterSampleRateConverterQuality,
491                                         prop_size,
492                                         &ac_quality);
493         check_error_and_throw(
494             err, "Set Sample Rate Converter Quality failed", "audio_osx_source::setup");
495 
496         // set the audio converter's prime method to "pre",
497         // which uses both leading and trailing frames
498         // from the "current input".  All of this is handled
499         // internally by the AudioConverter; we just supply
500         // the frames for conversion.
501 
502         UInt32 ac_prime_method = kConverterPrimeMethod_Pre;
503         prop_size = (UInt32)sizeof(ac_prime_method);
504         err = AudioConverterSetProperty(
505             d_audio_converter, kAudioConverterPrimeMethod, prop_size, &ac_prime_method);
506         check_error_and_throw(err, "Set Prime Method failed", "audio_osx_source::setup");
507 
508         // Get the size of the priming I/O buffer space to allow for
509         // pre-allocated buffers
510 
511         AudioConverterPrimeInfo ac_prime_info = { 0, 0 };
512         prop_size = (UInt32)sizeof(ac_prime_info);
513         err = AudioConverterGetProperty(
514             d_audio_converter, kAudioConverterPrimeInfo, &prop_size, &ac_prime_info);
515         check_error_and_throw(err, "Get Prime Info failed", "audio_osx_source::setup");
516 
517         d_lead_size_frames = ac_prime_info.leadingFrames;
518         d_trail_size_frames = ac_prime_info.trailingFrames;
519     }
520 
521     d_lead_size_bytes = d_lead_size_frames * sizeof(float);
522     d_trail_size_bytes = d_trail_size_frames * sizeof(float);
523 
524     prop_size = (UInt32)sizeof(d_device_buffer_size_frames);
525     err = AudioUnitGetProperty(d_input_au,
526                                kAudioDevicePropertyBufferFrameSize,
527                                kAudioUnitScope_Global,
528                                0,
529                                &d_device_buffer_size_frames,
530                                &prop_size);
531     check_error_and_throw(err, "Get Buffer Frame Size failed", "audio_osx_source::setup");
532 
533     d_device_buffer_size_bytes = (d_device_buffer_size_frames * sizeof(float));
534     d_input_buffer_size_bytes = (d_device_buffer_size_bytes + d_lead_size_bytes);
535     d_input_buffer_size_frames = (d_device_buffer_size_frames + d_lead_size_frames);
536 
537     // outBufSizeBytes = floor (inBufSizeBytes * rate_out / rate_in)
538     // since this is rarely exact, we need another buffer to hold
539     // "extra" samples not processed at any given sampling period
540     // this buffer must be at least 4 floats in size, but generally
541     // follows the rule that
542     // extraBufSize =  ceil (rate_in / rate_out)*sizeof(float)
543 
544     d_extra_buffer_size_frames =
545         ((UInt32)ceil(d_device_sample_rate / d_output_sample_rate) * sizeof(float));
546     if (d_extra_buffer_size_frames < 4)
547         d_extra_buffer_size_frames = 4;
548     d_extra_buffer_size_bytes = d_extra_buffer_size_frames * sizeof(float);
549 
550     d_output_buffer_size_frames =
551         (UInt32)ceil(((Float64)d_input_buffer_size_frames) * d_output_sample_rate /
552                      d_device_sample_rate);
553     d_output_buffer_size_bytes = d_output_buffer_size_frames * sizeof(float);
554     d_input_buffer_size_frames += d_extra_buffer_size_frames;
555 
556     // pre-alloc all CoreAudio buffers
557 
558     alloc_audio_buffer_list(&d_input_buffer, d_n_dev_channels, d_input_buffer_size_bytes);
559     if (!d_pass_through) {
560         alloc_audio_buffer_list(
561             &d_output_buffer, d_n_dev_channels, d_output_buffer_size_bytes);
562     } else {
563         d_output_buffer = d_input_buffer;
564     }
565 
566     // allocate the output circular buffer(s), one per device
567     // channel (the user may select fewer channels; those buffers
568     // just won't get used).
569 
570     d_buffers.resize(d_n_dev_channels);
571     for (UInt32 nn = 0; nn < d_n_dev_channels; ++nn) {
572         d_buffers[nn] = new circular_buffer<float>(d_buffer_sample_count, false, false);
573     }
574 
575     // clear the RunLoop (whatever that is); needed, for some
576     // reason, before a listener will work.
577 
578     {
579         CFRunLoopRef the_run_loop = NULL;
580         AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,
581                                                 kAudioObjectPropertyScopeGlobal,
582                                                 kAudioObjectPropertyElementMaster };
583         prop_size = (UInt32)sizeof(the_run_loop);
584         err = AudioObjectSetPropertyData(
585             kAudioObjectSystemObject, &property, 0, NULL, prop_size, &the_run_loop);
586         check_error(err,
587                     "Clearing RunLoop failed; "
588                     "Audio Input Device Listener might not work.");
589     }
590 
591     // set up listeners
592 
593 #ifndef GR_USE_OLD_AUDIO_UNIT
594 
595     // 10.4 and newer
596 
597     {
598 
599         // set up a listener if hardware changes (at all)
600 
601         AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
602                                                 kAudioObjectPropertyScopeGlobal,
603                                                 kAudioObjectPropertyElementMaster };
604 
605         err = AudioObjectAddPropertyListener(
606             kAudioObjectSystemObject,
607             &property,
608             reinterpret_cast<AudioObjectPropertyListenerProc>(
609                 &osx_source::hardware_listener),
610             reinterpret_cast<void*>(this));
611         check_error(err, "Adding Audio Hardware Listener failed");
612     }
613 
614     if (d_using_default_device) {
615 
616         // set up a listener if default hardware input device changes
617 
618         AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice,
619                                                 kAudioObjectPropertyScopeGlobal,
620                                                 kAudioObjectPropertyElementMaster };
621 
622         err = AudioObjectAddPropertyListener(
623             kAudioObjectSystemObject,
624             &property,
625             reinterpret_cast<AudioObjectPropertyListenerProc>(
626                 &osx_source::default_listener),
627             reinterpret_cast<void*>(this));
628         check_error(err, "Adding Default Input Audio Listener failed");
629     }
630 
631 #else
632 
633     // 10.5 and older
634 
635     err = AudioHardwareAddPropertyListener(
636         kAudioHardwarePropertyDevices,
637         reinterpret_cast<AudioHardwarePropertyListenerProc>(
638             &osx_source::hardware_listener),
639         reinterpret_cast<void*>(this));
640     check_error(err, "Adding Audio Hardware Listener failed");
641 
642     if (d_using_default_device) {
643 
644         err = AudioHardwareAddPropertyListener(
645             kAudioHardwarePropertyDefaultInputDevice,
646             reinterpret_cast<AudioHardwarePropertyListenerProc>(
647                 &osx_source::default_listener),
648             reinterpret_cast<void*>(this));
649         check_error(err, "Adding Default Input Audio Listener failed");
650     }
651 
652 #endif
653 
654     // initialize the AU for input, so that it is ready to be used
655 
656     err = AudioUnitInitialize(d_input_au);
657     check_error_and_throw(err, "AudioUnitInitialize", "audio_osx_source::check_channels");
658 
659 #if _OSX_AU_DEBUG_
660     std::cerr << std::endl
661               << "audio_osx_source Parameters:" << std::endl
662               << "  Device Sample Rate is " << d_device_sample_rate << std::endl
663               << "  Client Sample Rate is "
664               << (d_pass_through ? d_output_sample_rate : d_device_sample_rate)
665               << std::endl
666               << "  User Sample Rate is " << d_output_sample_rate << std::endl
667               << "  Do Passthrough is " << (d_pass_through ? "true" : "false")
668               << std::endl
669               << "  Max Sample Count is " << d_buffer_sample_count << std::endl
670               << "  # Device Channels is " << d_n_dev_channels << std::endl
671               << "  Device Buffer Size in Frames = " << d_device_buffer_size_frames
672               << std::endl
673               << "  Lead Size in Frames = " << d_lead_size_frames << std::endl
674               << "  Trail Size in Frames = " << d_trail_size_frames << std::endl
675               << "  Input Buffer Size in Frames = " << d_input_buffer_size_frames
676               << std::endl
677               << "  Output Buffer Size in Frames = " << d_output_buffer_size_frames
678               << std::endl
679               << std::endl;
680 #endif
681 }
682 
alloc_audio_buffer_list(AudioBufferList ** t_abl,UInt32 n_channels,UInt32 input_buffer_size_bytes)683 void osx_source::alloc_audio_buffer_list(AudioBufferList** t_abl,
684                                          UInt32 n_channels,
685                                          UInt32 input_buffer_size_bytes)
686 {
687     free_audio_buffer_list(t_abl);
688     UInt32 prop_size =
689         (offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * n_channels));
690     *t_abl = (AudioBufferList*)calloc(1, prop_size);
691     (*t_abl)->mNumberBuffers = n_channels;
692 
693     int counter = n_channels;
694 
695 #if _OSX_AU_DEBUG_
696     std::cerr << "alloc_audio_buffer_list: (#chan, #bytes) == (" << n_channels << ", "
697               << input_buffer_size_bytes << ")" << std::endl;
698 #endif
699 
700     while (--counter >= 0) {
701         AudioBuffer* t_ab = &((*t_abl)->mBuffers[counter]);
702         t_ab->mNumberChannels = 1;
703         t_ab->mDataByteSize = input_buffer_size_bytes;
704         t_ab->mData = calloc(1, input_buffer_size_bytes);
705     }
706 }
707 
free_audio_buffer_list(AudioBufferList ** t_abl)708 void osx_source::free_audio_buffer_list(AudioBufferList** t_abl)
709 {
710     // free pre-allocated audio buffer, if it exists
711     if (*t_abl) {
712         int counter = (*t_abl)->mNumberBuffers;
713         while (--counter >= 0) {
714             AudioBuffer* t_ab = &((*t_abl)->mBuffers[counter]);
715             free(t_ab->mData);
716             t_ab->mData = 0;
717         }
718         free(*t_abl);
719         (*t_abl) = 0;
720     }
721 }
722 
is_running()723 bool osx_source::is_running()
724 {
725     UInt32 au_running = 0;
726     if (d_input_au) {
727 
728         UInt32 prop_size = (UInt32)sizeof(au_running);
729         OSStatus err = AudioUnitGetProperty(d_input_au,
730                                             kAudioOutputUnitProperty_IsRunning,
731                                             kAudioUnitScope_Global,
732                                             0,
733                                             &au_running,
734                                             &prop_size);
735         check_error_and_throw(
736             err, "AudioUnitGetProperty IsRunning", "audio_osx_source::is_running");
737     }
738 
739     return (au_running != 0);
740 }
741 
start()742 bool osx_source::start()
743 {
744 #if _OSX_AU_DEBUG_
745     std::cerr << ((void*)(pthread_self())) << " : audio_osx_source::start: Starting."
746               << std::endl;
747 #endif
748 
749     if ((!is_running()) && d_input_au) {
750 
751 #if _OSX_AU_DEBUG_
752         std::cerr << ((void*)(pthread_self()))
753                   << " : audio_osx_source::start: Starting Audio Unit." << std::endl;
754 #endif
755 
756         // reset buffers before starting
757 
758         for (UInt32 nn = 0; nn < d_buffers.size(); ++nn) {
759             d_buffers[nn]->reset();
760         }
761 
762         // start the audio unit
763 
764         OSStatus err = AudioOutputUnitStart(d_input_au);
765         check_error_and_throw(err, "AudioOutputUnitStart", "audio_osx_source::start");
766 
767         // clear reset (will sometimes be necessary, and it has to
768         // happen after AudioOutputUnitStart)
769 
770         d_do_reset = false;
771     }
772 
773 #if _OSX_AU_DEBUG_
774     std::cerr << ((void*)(pthread_self())) << " : audio_osx_source::start: Returning."
775               << std::endl;
776 #endif
777 
778     return (true);
779 }
780 
stop()781 bool osx_source::stop()
782 {
783 #if _OSX_AU_DEBUG_
784     std::cerr << ((void*)(pthread_self())) << " : audio_osx_source::stop: Starting."
785               << std::endl;
786 #endif
787     if (is_running()) {
788 
789 #if _OSX_AU_DEBUG_
790         std::cerr << ((void*)(pthread_self()))
791                   << " : audio_osx_source::stop: stopping audio unit." << std::endl;
792 #endif
793 
794         // stop the audio unit
795 
796         OSStatus err = AudioOutputUnitStop(d_input_au);
797         check_error_and_throw(err, "AudioOutputUnitStart", "audio_osx_source::stop");
798 
799         // abort all buffers
800 
801         for (UInt32 nn = 0; nn < d_n_user_channels; ++nn) {
802             d_buffers[nn]->abort();
803         }
804     }
805 
806 #if _OSX_AU_DEBUG_
807     std::cerr << ((void*)(pthread_self())) << " : audio_osx_source::stop: Returning."
808               << std::endl;
809 #endif
810 
811     return (true);
812 }
813 
teardown()814 void osx_source::teardown()
815 {
816 #if _OSX_AU_DEBUG_
817     std::cerr << ((void*)(pthread_self())) << " : audio_osx_source::teardown: Starting."
818               << std::endl;
819 #endif
820 
821     OSStatus err = noErr;
822 
823     // stop the AudioUnit
824 
825     stop();
826 
827     // remove the listeners
828 
829 #ifndef GR_USE_OLD_AUDIO_UNIT
830 
831     // 10.4 and newer
832     {
833         AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
834                                                 kAudioObjectPropertyScopeGlobal,
835                                                 kAudioObjectPropertyElementMaster };
836 
837         err = AudioObjectRemovePropertyListener(
838             kAudioObjectSystemObject,
839             &property,
840             reinterpret_cast<AudioObjectPropertyListenerProc>(
841                 &osx_source::hardware_listener),
842             reinterpret_cast<void*>(this));
843 #if _OSX_AU_DEBUG_
844         check_error(err,
845                     "teardown: AudioObjectRemovePropertyListener "
846                     "hardware failed");
847 #endif
848     }
849 
850     if (d_using_default_device) {
851 
852         AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice,
853                                                 kAudioObjectPropertyScopeGlobal,
854                                                 kAudioObjectPropertyElementMaster };
855 
856         err = AudioObjectRemovePropertyListener(
857             kAudioObjectSystemObject,
858             &property,
859             reinterpret_cast<AudioObjectPropertyListenerProc>(
860                 &osx_source::default_listener),
861             reinterpret_cast<void*>(this));
862 #if _OSX_AU_DEBUG_
863         check_error(err, "AudioObjectRemovePropertyListener default");
864 #endif
865 
866         d_using_default_device = false;
867     }
868 #else
869 
870     // 10.5 and older
871 
872     err = AudioHardwareRemovePropertyListener(
873         kAudioHardwarePropertyDevices,
874         reinterpret_cast<AudioHardwarePropertyListenerProc>(
875             &osx_source::hardware_listener));
876 #if _OSX_AU_DEBUG_
877     check_error(err, "AudioObjectRemovePropertyListener hardware");
878 #endif
879 
880     if (d_using_default_device) {
881         err = AudioHardwareRemovePropertyListener(
882             kAudioHardwarePropertyDefaultInputDevice,
883             reinterpret_cast<AudioHardwarePropertyListenerProc>(
884                 &osx_source::default_listener));
885 #if _OSX_AU_DEBUG_
886         check_error(err, "AudioObjectRemovePropertyListener default");
887 #endif
888         d_using_default_device = false;
889     }
890 
891 #endif // GR_USE_OLD_AUDIO_UNIT
892 
893     // free pre-allocated audio buffers
894     free_audio_buffer_list(&d_input_buffer);
895 
896     if (!d_pass_through) {
897         err = AudioConverterDispose(d_audio_converter);
898 #if _OSX_AU_DEBUG_
899         check_error(err, "~audio_osx_source: AudioConverterDispose");
900 #endif
901         free_audio_buffer_list(&d_output_buffer);
902     }
903 
904     // remove the audio unit
905     err = AudioUnitUninitialize(d_input_au);
906 #if _OSX_AU_DEBUG_
907     check_error(err, "~audio_osx_source: AudioUnitUninitialize");
908 #endif
909 
910 #ifndef GR_USE_OLD_AUDIO_UNIT
911     err = AudioComponentInstanceDispose(d_input_au);
912 #if _OSX_AU_DEBUG_
913     check_error(err, "~audio_osx_source: AudioComponentInstanceDispose");
914 #endif
915 #else
916     err = CloseComponent(d_input_au);
917 #if _OSX_AU_DEBUG_
918     check_error(err, "~audio_osx_source: CloseComponent");
919 #endif
920 #endif
921 
922     // empty and delete the queues
923 
924     for (UInt32 nn = 0; nn < d_buffers.size(); ++nn) {
925         delete d_buffers[nn];
926         d_buffers[nn] = 0;
927     }
928     d_buffers.resize(0);
929 
930     // clear important variables; not # user channels
931 
932     d_queue_sample_count = 0;
933     d_device_sample_rate = 0;
934     d_n_dev_channels = 0;
935     d_input_ad_id = 0;
936     d_input_au = 0;
937     d_input_buffer = d_output_buffer = 0;
938     d_audio_converter = 0;
939     d_using_default_device = false;
940 
941 #if _OSX_AU_DEBUG_
942     std::cerr << ((void*)(pthread_self())) << " : audio_osx_source::teardown: Returning."
943               << std::endl;
944 #endif
945 }
946 
check_topology(int ninputs,int noutputs)947 bool osx_source::check_topology(int ninputs, int noutputs)
948 {
949     // check # inputs to make sure it's valid
950     if (ninputs != 0) {
951 
952         GR_LOG_FATAL(d_logger,
953                      boost::format("check_topology(): number of input "
954                                    "streams provided (%d) should be 0.") %
955                          ninputs);
956         throw std::runtime_error("audio_osx_source::check_topology");
957     }
958 
959     // check # outputs to make sure it's valid
960     if ((noutputs < 1) | (noutputs > (int)d_n_dev_channels)) {
961 
962         GR_LOG_FATAL(d_logger,
963                      boost::format("check_topology(): number of output "
964                                    "streams provided (%d) should be in [1,%d] "
965                                    "for the selected input audio device.") %
966                          noutputs % d_n_dev_channels);
967         throw std::runtime_error("audio_osx_source::check_topology");
968     }
969 
970     // save the actual number of output (user) channels
971 
972     d_n_user_channels = noutputs;
973 
974 #if _OSX_AU_DEBUG_
975     std::cerr << "chk_topo: Actual # user output channels = " << noutputs << std::endl;
976 #endif
977 
978     return (true);
979 }
980 
work(int noutput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)981 int osx_source::work(int noutput_items,
982                      gr_vector_const_void_star& input_items,
983                      gr_vector_void_star& output_items)
984 {
985 #if _OSX_AU_DEBUG_RENDER_
986     std::cerr << ((void*)(pthread_self())) << " : audio_osx_source::work: Starting."
987               << std::endl;
988 #endif
989     if (d_do_reset) {
990         if (d_hardware_changed) {
991 
992             // see if the current AudioDeviceID is still available
993 
994             std::vector<AudioDeviceID> all_ad_ids;
995             osx::find_audio_devices(d_desired_name, true, &all_ad_ids, NULL);
996             bool found = false;
997             for (UInt32 nn = 0; (nn < all_ad_ids.size()) && (!found); ++nn) {
998                 found = (all_ad_ids[nn] == d_input_ad_id);
999             }
1000             if (!found) {
1001 
1002                 GR_LOG_FATAL(d_logger,
1003                              boost::format("The selected input audio device ('%s') "
1004                                            "is no longer available.\n") %
1005                                  d_selected_name);
1006                 return (gr::block::WORK_DONE);
1007             }
1008 
1009             d_do_reset = d_hardware_changed = false;
1010 
1011         } else {
1012 
1013 #if _OSX_AU_DEBUG_RENDER_
1014             std::cerr << "audio_osx_source::work: doing reset." << std::endl;
1015 #endif
1016 
1017             GR_LOG_WARN(d_logger,
1018                         boost::format("\n\nThe default input audio device has "
1019                                       "changed; resetting audio.\nThere may "
1020                                       "be a sound glitch while resetting.\n"));
1021 
1022             // for any changes, just tear down the current
1023             // configuration, then set it up again using the user's
1024             // parameters to try to make selections.
1025 
1026             teardown();
1027 
1028             gr::thread::scoped_lock l(d_internal);
1029 
1030 #if _OSX_AU_DEBUG_RENDER_
1031             std::cerr << "audio_osx_source::work: mutex locked." << std::endl;
1032 #endif
1033 
1034             setup();
1035             start();
1036 
1037 #if _OSX_AU_DEBUG_RENDER_
1038             std::cerr << "audio_osx_source::work: returning after reset." << std::endl;
1039 #endif
1040             return (0);
1041         }
1042     }
1043 
1044     gr::thread::scoped_lock l(d_internal);
1045 
1046 #if _OSX_AU_DEBUG_RENDER_
1047     std::cerr << "audio_osx_source::work: mutex locked." << std::endl;
1048 #endif
1049 
1050 #if _OSX_AU_DEBUG_RENDER_
1051     std::cerr << "work1: SC = " << d_queue_sample_count << ", #OI = " << noutput_items
1052               << ", #Chan = " << output_items.size() << std::endl;
1053 #endif
1054 
1055     // set the actual # of output items to the 'desired' amount then
1056     // verify that data is available; if not enough data is
1057     // available, either wait until it is (is "ok_to_block" is
1058     // true), return (0) is no data is available and "ok_to_block"
1059     // is false, or process the actual amount of available data.
1060 
1061     UInt32 actual_noutput_items = noutput_items;
1062 
1063     if (d_queue_sample_count < actual_noutput_items) {
1064         if (d_queue_sample_count == 0) {
1065             // no data; ok_to_block decides what to do
1066             if (d_ok_to_block == true) {
1067                 // block until there is data to return, or on reset
1068                 while (d_queue_sample_count == 0) {
1069                     // release control so-as to allow data to be retrieved;
1070                     // block until there is data to return
1071 #if _OSX_AU_DEBUG_RENDER_
1072                     std::cerr << "audio_osx_source::work: waiting." << std::endl;
1073 #endif
1074                     d_waiting_for_data = true;
1075                     d_cond_data.wait(l);
1076                     d_waiting_for_data = false;
1077 #if _OSX_AU_DEBUG_RENDER_
1078                     std::cerr << "audio_osx_source::work: done waiting." << std::endl;
1079 #endif
1080                     // the condition's 'notify' was called; acquire control to
1081                     // keep thread safe
1082 
1083                     // if doing a reset, just return here; reset will pick
1084                     // up the next time this method is called.
1085                     if (d_do_reset) {
1086 #if _OSX_AU_DEBUG_RENDER_
1087                         std::cerr << "audio_osx_source::work: "
1088                                      "returning for reset."
1089                                   << std::endl;
1090 #endif
1091                         return (0);
1092                     }
1093                 }
1094             } else {
1095                 // no data & not blocking; return nothing
1096 #if _OSX_AU_DEBUG_RENDER_
1097                 std::cerr << "audio_osx_source::work: no data "
1098                              "& not blocking; returning 0."
1099                           << std::endl;
1100 #endif
1101                 return (0);
1102             }
1103         }
1104 
1105         // use the actual amount of available data
1106         actual_noutput_items = d_queue_sample_count;
1107     }
1108 
1109 #if _OSX_AU_DEBUG_RENDER_
1110     std::cerr << "audio_osx_source::work: copying " << actual_noutput_items
1111               << " items per channel" << std::endl;
1112 #endif
1113 
1114     // number of channels
1115     int l_counter = (int)output_items.size();
1116 
1117     // copy the items from the circular buffer(s) to 'work's output
1118     // buffers; verify that the number copied out is as expected.
1119 
1120     while (--l_counter >= 0) {
1121 
1122         size_t t_n_output_items = actual_noutput_items;
1123         d_buffers[l_counter]->dequeue((float*)output_items[l_counter], &t_n_output_items);
1124 
1125         if (t_n_output_items != actual_noutput_items) {
1126 
1127             GR_LOG_FATAL(d_logger,
1128                          boost::format("work(): ERROR: number of available "
1129                                        "items changing unexpectedly; expecting %d"
1130                                        ", got %d.") %
1131                              actual_noutput_items % t_n_output_items);
1132             throw std::runtime_error("audio_osx_source::work()");
1133         }
1134     }
1135 
1136     // subtract the actual number of items removed from the buffer(s)
1137     // from the local accounting of the number of available samples
1138 
1139     d_queue_sample_count -= actual_noutput_items;
1140 
1141 #if _OSX_AU_DEBUG_RENDER_
1142     std::cerr << "work2: SC = " << d_queue_sample_count
1143               << ", act#OI = " << actual_noutput_items << std::endl
1144               << "Returning." << std::endl;
1145 #endif
1146 #if _OSX_AU_DEBUG_RENDER_
1147     std::cerr << "audio_osx_source::work: returning." << std::endl;
1148 #endif
1149 
1150     return (actual_noutput_items);
1151 }
1152 
converter_callback(AudioConverterRef in_audio_converter,UInt32 * io_number_data_packets,AudioBufferList * io_data,AudioStreamPacketDescription ** out_aspd,void * in_user_data)1153 OSStatus osx_source::converter_callback(AudioConverterRef in_audio_converter,
1154                                         UInt32* io_number_data_packets,
1155                                         AudioBufferList* io_data,
1156                                         AudioStreamPacketDescription** out_aspd,
1157                                         void* in_user_data)
1158 {
1159     // This callback is for us to provide the buffers to CoreAudio
1160     // for conversion.  We need to set the buffers in the provided
1161     // buffer list (io_data) to the buffers we know about and use to
1162     // do data input (d_input_buffers).
1163 
1164     osx_source* This = static_cast<osx_source*>(in_user_data);
1165     AudioBufferList* l_input_abl = This->d_input_buffer;
1166     UInt32 total_input_buffer_size_bytes = ((*io_number_data_packets) * sizeof(float));
1167     int counter = This->d_n_dev_channels;
1168     io_data->mNumberBuffers = This->d_n_dev_channels;
1169     This->d_n_actual_input_frames = (*io_number_data_packets);
1170 
1171 #if _OSX_AU_DEBUG_RENDER_
1172     std::cerr << "cc1: io#DP = " << (*io_number_data_packets)
1173               << ", TIBSB = " << total_input_buffer_size_bytes << ", #C = " << counter
1174               << std::endl;
1175 #endif
1176 
1177     while (--counter >= 0) {
1178         AudioBuffer* t_ab = &(io_data->mBuffers[counter]);
1179         t_ab->mNumberChannels = 1;
1180         t_ab->mData = l_input_abl->mBuffers[counter].mData;
1181         t_ab->mDataByteSize = total_input_buffer_size_bytes;
1182     }
1183 
1184 #if _OSX_AU_DEBUG_RENDER_
1185     std::cerr << "cc2: Returning." << std::endl;
1186 #endif
1187 
1188     return (noErr);
1189 }
1190 
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)1191 OSStatus osx_source::au_input_callback(void* in_ref_con,
1192                                        AudioUnitRenderActionFlags* io_action_flags,
1193                                        const AudioTimeStamp* in_time_stamp,
1194                                        UInt32 in_bus_number,
1195                                        UInt32 in_number_frames,
1196                                        AudioBufferList* io_data)
1197 {
1198 #if _OSX_AU_DEBUG_RENDER_
1199     std::cerr << ((void*)(pthread_self()))
1200               << " : audio_osx_source::au_input_callback: Starting." << std::endl;
1201 #endif
1202 
1203     osx_source* This = reinterpret_cast<osx_source*>(in_ref_con);
1204     gr::thread::scoped_lock l(This->d_internal);
1205     gr::logger_ptr d_logger = This->d_logger;
1206 
1207 #if _OSX_AU_DEBUG_RENDER_
1208     std::cerr << "audio_osx_source::au_input_callback: mutex locked." << std::endl;
1209 #endif
1210 
1211     OSStatus err = noErr;
1212 
1213 #if _OSX_AU_DEBUG_RENDER_
1214     std::cerr << "cb0: in#Fr = " << in_number_frames << ", inBus# = " << in_bus_number
1215               << ", idD = " << ((void*)io_data)
1216               << ", d_ib = " << ((void*)(This->d_input_buffer))
1217               << ", d_ib#c = " << This->d_input_buffer->mNumberBuffers
1218               << ", SC = " << This->d_queue_sample_count << std::endl;
1219 #endif
1220 
1221     if (This->d_do_reset) {
1222 
1223         // clear audio data; do not render since it will generate an error
1224 
1225         AudioBufferList* t_abl = This->d_input_buffer;
1226         for (UInt32 nn = 0; nn < t_abl->mNumberBuffers; ++nn) {
1227             AudioBuffer* t_ab = &(t_abl->mBuffers[nn]);
1228             memset(t_ab->mData,
1229                    0,
1230                    (size_t)((t_ab->mDataByteSize) * (t_ab->mNumberChannels)));
1231         }
1232     } else {
1233 
1234         // Get the new audio data from the input device
1235 
1236         err = AudioUnitRender(This->d_input_au,
1237                               io_action_flags,
1238                               in_time_stamp,
1239                               1, // inBusNumber,
1240                               in_number_frames,
1241                               This->d_input_buffer);
1242         check_error_and_throw(
1243             err, "AudioUnitRender", "audio_osx_source::au_input_callback");
1244     }
1245 
1246     UInt32 available_input_frames = This->d_n_available_input_frames = in_number_frames;
1247 
1248     // get the number of actual output frames,
1249     // either via converting the buffer or not
1250 
1251     UInt32 actual_output_frames = available_input_frames;
1252 
1253     if (!This->d_pass_through) {
1254         UInt32 available_input_bytes = available_input_frames * sizeof(float);
1255         UInt32 available_output_bytes = available_input_bytes;
1256         UInt32 prop_size = sizeof(available_output_bytes);
1257         err = AudioConverterGetProperty(This->d_audio_converter,
1258                                         kAudioConverterPropertyCalculateOutputBufferSize,
1259                                         &prop_size,
1260                                         &available_output_bytes);
1261         check_error_and_throw(
1262             err, "Get Output Buffer Size failed", "audio_osx_source::au_input_callback");
1263 
1264         UInt32 available_output_frames = available_output_bytes / sizeof(float);
1265 
1266 #if 0
1267         // when decimating too much, the output sounds warbly due to
1268         // fluctuating # of output frames
1269         // This should not be a surprise, but there's probably some
1270         // clever programming that could lessed the effect ...
1271         // like finding the "ideal" # of output frames, and keeping
1272         // that number constant no matter the # of input frames
1273 
1274         UInt32 l_input_bytes = available_output_bytes;
1275         prop_size = sizeof(available_output_bytes);
1276         err = AudioConverterGetProperty
1277 	  (This->d_audio_converter,
1278 	   kAudioConverterPropertyCalculateInputBufferSize,
1279 	   &prop_size, &l_input_bytes);
1280         check_error_and_throw
1281 	  (err, "Get Input Buffer Size failed",
1282 	   "audio_osx_source::au_input_callback");
1283 
1284         if(l_input_bytes < available_input_bytes) {
1285           // OK to zero pad the input a little
1286           ++available_output_frames;
1287           available_output_bytes = available_output_frames * sizeof(float);
1288         }
1289 
1290 #endif
1291 
1292 #if _OSX_AU_DEBUG_RENDER_
1293         std::cerr << "cb1:  avail: #IF = " << available_input_frames
1294                   << ", #OF = " << available_output_frames << std::endl;
1295 #endif
1296         actual_output_frames = available_output_frames;
1297 
1298         // convert the data to the correct rate; on input,
1299         // actual_output_frames is the number of available output frames
1300 
1301         err = AudioConverterFillComplexBuffer(
1302             This->d_audio_converter,
1303             reinterpret_cast<AudioConverterComplexInputDataProc>(
1304                 &(This->converter_callback)),
1305             in_ref_con,
1306             &actual_output_frames,
1307             This->d_output_buffer,
1308             NULL);
1309         check_error_and_throw(err,
1310                               "AudioConverterFillComplexBuffer failed",
1311                               "audio_osx_source::au_input_callback");
1312 
1313         // on output, actual_output_frames is the actual number of
1314         // output frames
1315 
1316 #if _OSX_AU_DEBUG_RENDER_
1317         std::cerr << "cb2: actual: #IF = " << This->d_n_actual_input_frames
1318                   << ", #OF = " << actual_output_frames << std::endl;
1319         if (This->d_n_actual_input_frames != available_input_frames)
1320             std::cerr << "cb2.1: avail#IF = " << available_input_frames
1321                       << ", actual#IF = " << This->d_n_actual_input_frames << std::endl;
1322 #endif
1323     }
1324 
1325     // add the output frames to the buffers' queue, checking for overflow
1326 
1327     int counter = This->d_n_user_channels;
1328     int res = 0;
1329 
1330     while (--counter >= 0) {
1331         float* in_buffer = (float*)This->d_output_buffer->mBuffers[counter].mData;
1332 
1333 #if _OSX_AU_DEBUG_RENDER_
1334         std::cerr << "cb3: enqueuing audio data." << std::endl;
1335 #endif
1336 
1337         int l_res = This->d_buffers[counter]->enqueue(in_buffer, actual_output_frames);
1338         if (l_res == -1)
1339             res = -1;
1340     }
1341 
1342     if (res == -1) {
1343         // data coming in too fast
1344         // drop oldest buffer
1345         fputs("aO", stderr);
1346         fflush(stderr);
1347         // set the local number of samples available to the max
1348         This->d_queue_sample_count = This->d_buffers[0]->buffer_length_items();
1349     } else {
1350         // keep up the local sample count
1351         This->d_queue_sample_count += actual_output_frames;
1352     }
1353 
1354 #if _OSX_AU_DEBUG_RENDER_
1355     std::cerr << "cb4: #OI = " << actual_output_frames
1356               << ", #Cnt = " << This->d_queue_sample_count
1357               << ", mSC = " << This->d_buffer_sample_count << std::endl;
1358 #endif
1359 
1360     // signal that data is available, if appropriate
1361 
1362     if (This->d_waiting_for_data) {
1363         This->d_cond_data.notify_one();
1364     }
1365 
1366 #if _OSX_AU_DEBUG_RENDER_
1367     std::cerr << "cb5: returning." << std::endl;
1368 #endif
1369 
1370     return (err);
1371 }
1372 
1373 #ifndef GR_USE_OLD_AUDIO_UNIT
1374 
hardware_listener(AudioObjectID in_object_id,UInt32 in_num_addresses,const AudioObjectPropertyAddress in_addresses[],void * in_client_data)1375 OSStatus osx_source::hardware_listener(AudioObjectID in_object_id,
1376                                        UInt32 in_num_addresses,
1377                                        const AudioObjectPropertyAddress in_addresses[],
1378                                        void* in_client_data)
1379 
1380 #else
1381 
1382 OSStatus osx_source::hardware_listener(AudioHardwarePropertyID in_property_id,
1383                                        void* in_client_data)
1384 
1385 #endif
1386 {
1387     osx_source* This = reinterpret_cast<osx_source*>(in_client_data);
1388     This->reset(true);
1389     return (noErr);
1390 }
1391 
1392 #ifndef GR_USE_OLD_AUDIO_UNIT
1393 
default_listener(AudioObjectID in_object_id,UInt32 in_num_addresses,const AudioObjectPropertyAddress in_addresses[],void * in_client_data)1394 OSStatus osx_source::default_listener(AudioObjectID in_object_id,
1395                                       UInt32 in_num_addresses,
1396                                       const AudioObjectPropertyAddress in_addresses[],
1397                                       void* in_client_data)
1398 
1399 #else
1400 
1401 OSStatus osx_source::default_listener(AudioHardwarePropertyID in_property_id,
1402                                       void* in_client_data)
1403 
1404 #endif
1405 {
1406     osx_source* This = reinterpret_cast<osx_source*>(in_client_data);
1407     This->reset(false);
1408     return (noErr);
1409 }
1410 
1411 } /* namespace audio */
1412 } /* namespace gr */
1413