3 * Copyright 2006 Free Software Foundation, Inc.
5 * This file is part of GNU Radio.
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)
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.
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.
27 #define _USE_OMNI_THREADS_
29 #include <audio_osx_sink.h>
30 #include <gr_io_signature.h>
32 #include <audio_osx.h>
34 #define _OSX_AU_DEBUG_ 0
36 audio_osx_sink::audio_osx_sink (int sample_rate,
37 const std::string device_name,
41 : gr_sync_block ("audio_osx_sink",
42 gr_make_io_signature (0, 0, 0),
43 gr_make_io_signature (0, 0, 0)),
44 d_sample_rate (0.0), d_channel_config (0), d_n_channels (0),
45 d_queueSampleCount (0), d_max_sample_count (0),
46 d_do_block (do_block), d_internal (0), d_cond_data (0),
49 if (sample_rate <= 0) {
50 std::cerr << "Invalid Sample Rate: " << sample_rate << std::endl;
51 throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
53 d_sample_rate = (Float64) sample_rate;
55 if (channel_config <= 0 & channel_config != -1) {
56 std::cerr << "Invalid Channel Config: " << channel_config << std::endl;
57 throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
58 } else if (channel_config == -1) {
59 // no user input; try "device name" instead
60 int l_n_channels = (int) strtol (device_name.data(), (char **)NULL, 10);
61 if (l_n_channels == 0 & errno) {
62 std::cerr << "Error Converting Device Name: " << errno << std::endl;
63 throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
65 if (l_n_channels <= 0)
68 channel_config = l_n_channels;
71 d_n_channels = d_channel_config = channel_config;
73 // set the input signature
75 set_input_signature (gr_make_io_signature (1, d_n_channels, sizeof (float)));
77 // check that the max # of samples to store is valid
79 if (max_sample_count == -1)
80 max_sample_count = sample_rate;
81 else if (max_sample_count <= 0) {
82 std::cerr << "Invalid Max Sample Count: " << max_sample_count << std::endl;
83 throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
86 d_max_sample_count = max_sample_count;
88 // allocate the output circular buffer(s), one per channel
90 d_buffers = (circular_buffer<float>**) new
91 circular_buffer<float>* [d_n_channels];
92 UInt32 n_alloc = (UInt32) ceil ((double) d_max_sample_count);
93 for (UInt32 n = 0; n < d_n_channels; n++) {
94 d_buffers[n] = new circular_buffer<float> (n_alloc, false, false);
97 // create the default AudioUnit for output
100 // Open the default output unit
101 #ifndef GR_USE_OLD_AUDIO_UNIT
102 AudioComponentDescription desc;
104 ComponentDescription desc;
107 desc.componentType = kAudioUnitType_Output;
108 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
109 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
110 desc.componentFlags = 0;
111 desc.componentFlagsMask = 0;
113 #ifndef GR_USE_OLD_AUDIO_UNIT
114 AudioComponent comp = AudioComponentFindNext(NULL, &desc);
116 std::cerr << "AudioComponentFindNext Error" << std::endl;
117 throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
120 Component comp = FindNextComponent (NULL, &desc);
122 std::cerr << "FindNextComponent Error" << std::endl;
123 throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
127 #ifndef GR_USE_OLD_AUDIO_UNIT
128 err = AudioComponentInstanceNew (comp, &d_OutputAU);
129 CheckErrorAndThrow (err, "AudioComponentInstanceNew", "audio_osx_sink::audio_osx_sink");
131 err = OpenAComponent (comp, &d_OutputAU);
132 CheckErrorAndThrow (err, "OpenAComponent", "audio_osx_sink::audio_osx_sink");
135 // Set up a callback function to generate output to the output unit
137 AURenderCallbackStruct input;
138 input.inputProc = (AURenderCallback)(audio_osx_sink::AUOutputCallback);
139 input.inputProcRefCon = this;
141 err = AudioUnitSetProperty (d_OutputAU,
142 kAudioUnitProperty_SetRenderCallback,
143 kAudioUnitScope_Input,
147 CheckErrorAndThrow (err, "AudioUnitSetProperty Render Callback", "audio_osx_sink::audio_osx_sink");
149 // tell the Output Unit what format data will be supplied to it
150 // so that it handles any format conversions
152 AudioStreamBasicDescription streamFormat;
153 streamFormat.mSampleRate = (Float64)(sample_rate);
154 streamFormat.mFormatID = kAudioFormatLinearPCM;
155 streamFormat.mFormatFlags = (kLinearPCMFormatFlagIsFloat |
157 kLinearPCMFormatFlagIsPacked |
158 kAudioFormatFlagIsNonInterleaved);
159 streamFormat.mBytesPerPacket = 4;
160 streamFormat.mFramesPerPacket = 1;
161 streamFormat.mBytesPerFrame = 4;
162 streamFormat.mChannelsPerFrame = d_n_channels;
163 streamFormat.mBitsPerChannel = 32;
165 err = AudioUnitSetProperty (d_OutputAU,
166 kAudioUnitProperty_StreamFormat,
167 kAudioUnitScope_Input,
170 sizeof (AudioStreamBasicDescription));
171 CheckErrorAndThrow (err, "AudioUnitSetProperty StreamFormat", "audio_osx_sink::audio_osx_sink");
173 // create the stuff to regulate I/O
175 d_cond_data = new mld_condition ();
176 if (d_cond_data == NULL)
177 CheckErrorAndThrow (errno, "new mld_condition (data)",
178 "audio_osx_source::audio_osx_source");
179 d_internal = d_cond_data->mutex ();
181 // initialize the AU for output
183 err = AudioUnitInitialize (d_OutputAU);
184 CheckErrorAndThrow (err, "AudioUnitInitialize",
185 "audio_osx_sink::audio_osx_sink");
188 std::cerr << "audio_osx_sink Parameters:" << std::endl;
189 std::cerr << " Sample Rate is " << d_sample_rate << std::endl;
190 std::cerr << " Number of Channels is " << d_n_channels << std::endl;
191 std::cerr << " Max # samples to store per channel is " << d_max_sample_count << std::endl;
195 bool audio_osx_sink::IsRunning ()
197 UInt32 AURunning = 0, AUSize = sizeof (UInt32);
199 OSStatus err = AudioUnitGetProperty (d_OutputAU,
200 kAudioOutputUnitProperty_IsRunning,
201 kAudioUnitScope_Global,
205 CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning",
206 "audio_osx_sink::IsRunning");
211 bool audio_osx_sink::start ()
213 if (! IsRunning ()) {
214 OSStatus err = AudioOutputUnitStart (d_OutputAU);
215 CheckErrorAndThrow (err, "AudioOutputUnitStart", "audio_osx_sink::start");
221 bool audio_osx_sink::stop ()
224 OSStatus err = AudioOutputUnitStop (d_OutputAU);
225 CheckErrorAndThrow (err, "AudioOutputUnitStop", "audio_osx_sink::stop");
227 for (UInt32 n = 0; n < d_n_channels; n++) {
228 d_buffers[n]->abort ();
235 audio_osx_sink::~audio_osx_sink ()
237 // stop and close the AudioUnit
239 AudioUnitUninitialize (d_OutputAU);
240 #ifndef GR_USE_OLD_AUDIO_UNIT
241 AudioComponentInstanceDispose (d_OutputAU);
243 CloseComponent (d_OutputAU);
246 // empty and delete the queues
247 for (UInt32 n = 0; n < d_n_channels; n++) {
254 // close and delete control stuff
259 audio_osx_make_sink (int sampling_freq,
260 const std::string dev,
263 int max_sample_count)
265 return audio_osx_sink_sptr (new audio_osx_sink (sampling_freq,
273 audio_osx_sink::work (int noutput_items,
274 gr_vector_const_void_star &input_items,
275 gr_vector_void_star &output_items)
279 /* take the input data, copy it, and push it to the bottom of the queue
280 mono input are pushed onto queue[0];
281 stereo input are pushed onto queue[1].
282 Start the AudioUnit if necessary. */
285 int diff_count = d_max_sample_count - noutput_items;
289 l_max_count = (UInt32) diff_count;
292 if (l_max_count < d_queueItemLength->back()) {
293 // allow 2 buffers at a time, regardless of length
294 l_max_count = d_queueItemLength->back();
299 std::cerr << "work1: qSC = " << d_queueSampleCount << ", lMC = "<< l_max_count
300 << ", dmSC = " << d_max_sample_count << ", nOI = " << noutput_items << std::endl;
303 if (d_queueSampleCount > l_max_count) {
304 // data coming in too fast; do_block decides what to do
305 if (d_do_block == true) {
306 // block until there is data to return
307 while (d_queueSampleCount > l_max_count) {
308 // release control so-as to allow data to be retrieved;
309 // block until there is data to return
310 d_cond_data->wait ();
311 // the condition's signal() was called; acquire control
312 // to keep thread safe
316 // not blocking case and overflow is handled by the circular buffer
318 // add the input frames to the buffers' queue, checking for overflow
322 float* inBuffer = (float*) input_items[0];
323 const UInt32 l_size = input_items.size();
324 for (l_counter = 0; l_counter < l_size; l_counter++) {
325 inBuffer = (float*) input_items[l_counter];
326 int l_res = d_buffers[l_counter]->enqueue (inBuffer,
331 while (l_counter < d_n_channels) {
332 // for extra channels, copy the last input's data
333 int l_res = d_buffers[l_counter++]->enqueue (inBuffer,
340 // data coming in too fast
341 // drop oldest buffer
342 fputs ("aO", stderr);
344 // set the local number of samples available to the max
345 d_queueSampleCount = d_buffers[0]->buffer_length_items ();
347 // keep up the local sample count
348 d_queueSampleCount += noutput_items;
352 std::cerr << "work2: #OI = " << noutput_items << ", #Cnt = "
353 << d_queueSampleCount << ", mSC = " << d_max_sample_count << std::endl;
356 // release control to allow for other processing parts to run
357 d_internal->unlock ();
359 return (noutput_items);
362 OSStatus audio_osx_sink::AUOutputCallback
364 AudioUnitRenderActionFlags *ioActionFlags,
365 const AudioTimeStamp *inTimeStamp,
367 UInt32 inNumberFrames,
368 AudioBufferList *ioData)
370 audio_osx_sink* This = (audio_osx_sink*) inRefCon;
371 OSStatus err = noErr;
373 This->d_internal->lock ();
376 std::cerr << "cb_in: SC = " << This->d_queueSampleCount
377 << ", in#F = " << inNumberFrames << std::endl;
380 if (This->d_queueSampleCount < inNumberFrames) {
381 // not enough data to fill request
384 // enough data; remove data from our buffers into the AU's buffers
385 int l_counter = This->d_n_channels;
387 while (--l_counter >= 0) {
388 size_t t_n_output_items = inNumberFrames;
389 float* outBuffer = (float*) ioData->mBuffers[l_counter].mData;
390 This->d_buffers[l_counter]->dequeue (outBuffer, &t_n_output_items);
391 if (t_n_output_items != inNumberFrames) {
392 throw std::runtime_error ("audio_osx_sink::AUOutputCallback(): "
393 "number of available items changing "
398 This->d_queueSampleCount -= inNumberFrames;
402 std::cerr << "cb_out: SC = " << This->d_queueSampleCount << std::endl;
405 // signal that data is available
406 This->d_cond_data->signal ();
408 // release control to allow for other processing parts to run
409 This->d_internal->unlock ();