diff options
author | Michael Dickens <mlk@alum.mit.edu> | 2014-03-07 11:10:07 -0500 |
---|---|---|
committer | Michael Dickens <mlk@alum.mit.edu> | 2014-03-07 11:10:07 -0500 |
commit | 6e0895cb3cbc355060eab037ef74b8e237dcf133 (patch) | |
tree | 41d7f98abee78a8154bb4841761de8850f1ae607 /gr-audio/lib/osx/osx_source.cc | |
parent | ca69ec5d64b67dfc714917bd94162a5d1f131d69 (diff) |
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.
Diffstat (limited to 'gr-audio/lib/osx/osx_source.cc')
-rw-r--r-- | gr-audio/lib/osx/osx_source.cc | 1881 |
1 files changed, 1163 insertions, 718 deletions
diff --git a/gr-audio/lib/osx/osx_source.cc b/gr-audio/lib/osx/osx_source.cc index 3e2dcef4fe..93d857e4be 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 */ |