3 * Copyright 2006,2010 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 #include <audio_osx_sink.h>
28 #include <gr_io_signature.h>
30 #include <audio_osx.h>
32 #define _OSX_AU_DEBUG_ 0
34 audio_osx_sink::audio_osx_sink (int sample_rate,
35 const std::string device_name,
39 : gr_sync_block ("audio_osx_sink",
40 gr_make_io_signature (0, 0, 0),
41 gr_make_io_signature (0, 0, 0)),
42 d_sample_rate (0.0), d_channel_config (0), d_n_channels (0),
43 d_queueSampleCount (0), d_max_sample_count (0),
44 d_do_block (do_block), d_internal (0), d_cond_data (0),
47 if (sample_rate <= 0) {
48 std::cerr << "Invalid Sample Rate: " << sample_rate << std::endl;
49 throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
51 d_sample_rate = (Float64) sample_rate;
53 if (channel_config <= 0 & channel_config != -1) {
54 std::cerr << "Invalid Channel Config: " << channel_config << std::endl;
55 throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
56 } else if (channel_config == -1) {
57 // no user input; try "device name" instead
58 int l_n_channels = (int) strtol (device_name.data(), (char **)NULL, 10);
59 if (l_n_channels == 0 & errno) {
60 std::cerr << "Error Converting Device Name: " << errno << std::endl;
61 throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
63 if (l_n_channels <= 0)
66 channel_config = l_n_channels;
69 d_n_channels = d_channel_config = channel_config;
71 // set the input signature
73 set_input_signature (gr_make_io_signature (1, d_n_channels, sizeof (float)));
75 // check that the max # of samples to store is valid
77 if (max_sample_count == -1)
78 max_sample_count = sample_rate;
79 else if (max_sample_count <= 0) {
80 std::cerr << "Invalid Max Sample Count: " << max_sample_count << std::endl;
81 throw std::invalid_argument ("audio_osx_sink::audio_osx_sink");
84 d_max_sample_count = max_sample_count;
86 // allocate the output circular buffer(s), one per channel
88 d_buffers = (circular_buffer<float>**) new
89 circular_buffer<float>* [d_n_channels];
90 UInt32 n_alloc = (UInt32) ceil ((double) d_max_sample_count);
91 for (UInt32 n = 0; n < d_n_channels; n++) {
92 d_buffers[n] = new circular_buffer<float> (n_alloc, false, false);
95 // create the default AudioUnit for output
98 // Open the default output unit
99 #ifndef GR_USE_OLD_AUDIO_UNIT
100 AudioComponentDescription desc;
102 ComponentDescription desc;
105 desc.componentType = kAudioUnitType_Output;
106 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
107 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
108 desc.componentFlags = 0;
109 desc.componentFlagsMask = 0;
111 #ifndef GR_USE_OLD_AUDIO_UNIT
112 AudioComponent comp = AudioComponentFindNext(NULL, &desc);
114 std::cerr << "AudioComponentFindNext Error" << std::endl;
115 throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
118 Component comp = FindNextComponent (NULL, &desc);
120 std::cerr << "FindNextComponent Error" << std::endl;
121 throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
125 #ifndef GR_USE_OLD_AUDIO_UNIT
126 err = AudioComponentInstanceNew (comp, &d_OutputAU);
127 CheckErrorAndThrow (err, "AudioComponentInstanceNew", "audio_osx_sink::audio_osx_sink");
129 err = OpenAComponent (comp, &d_OutputAU);
130 CheckErrorAndThrow (err, "OpenAComponent", "audio_osx_sink::audio_osx_sink");
133 // Set up a callback function to generate output to the output unit
135 AURenderCallbackStruct input;
136 input.inputProc = (AURenderCallback)(audio_osx_sink::AUOutputCallback);
137 input.inputProcRefCon = this;
139 err = AudioUnitSetProperty (d_OutputAU,
140 kAudioUnitProperty_SetRenderCallback,
141 kAudioUnitScope_Input,
145 CheckErrorAndThrow (err, "AudioUnitSetProperty Render Callback", "audio_osx_sink::audio_osx_sink");
147 // tell the Output Unit what format data will be supplied to it
148 // so that it handles any format conversions
150 AudioStreamBasicDescription streamFormat;
151 streamFormat.mSampleRate = (Float64)(sample_rate);
152 streamFormat.mFormatID = kAudioFormatLinearPCM;
153 streamFormat.mFormatFlags = (kLinearPCMFormatFlagIsFloat |
155 kLinearPCMFormatFlagIsPacked |
156 kAudioFormatFlagIsNonInterleaved);
157 streamFormat.mBytesPerPacket = 4;
158 streamFormat.mFramesPerPacket = 1;
159 streamFormat.mBytesPerFrame = 4;
160 streamFormat.mChannelsPerFrame = d_n_channels;
161 streamFormat.mBitsPerChannel = 32;
163 err = AudioUnitSetProperty (d_OutputAU,
164 kAudioUnitProperty_StreamFormat,
165 kAudioUnitScope_Input,
168 sizeof (AudioStreamBasicDescription));
169 CheckErrorAndThrow (err, "AudioUnitSetProperty StreamFormat", "audio_osx_sink::audio_osx_sink");
171 // create the stuff to regulate I/O
173 d_cond_data = new gruel::condition_variable ();
174 if (d_cond_data == NULL)
175 CheckErrorAndThrow (errno, "new condition (data)",
176 "audio_osx_sink::audio_osx_sink");
178 d_internal = new gruel::mutex ();
179 if (d_internal == NULL)
180 CheckErrorAndThrow (errno, "new mutex (internal)",
181 "audio_osx_sink::audio_osx_sink");
183 // initialize the AU for output
185 err = AudioUnitInitialize (d_OutputAU);
186 CheckErrorAndThrow (err, "AudioUnitInitialize",
187 "audio_osx_sink::audio_osx_sink");
190 std::cerr << "audio_osx_sink Parameters:" << std::endl;
191 std::cerr << " Sample Rate is " << d_sample_rate << std::endl;
192 std::cerr << " Number of Channels is " << d_n_channels << std::endl;
193 std::cerr << " Max # samples to store per channel is " << d_max_sample_count << std::endl;
197 bool audio_osx_sink::IsRunning ()
199 UInt32 AURunning = 0, AUSize = sizeof (UInt32);
201 OSStatus err = AudioUnitGetProperty (d_OutputAU,
202 kAudioOutputUnitProperty_IsRunning,
203 kAudioUnitScope_Global,
207 CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning",
208 "audio_osx_sink::IsRunning");
213 bool audio_osx_sink::start ()
215 if (! IsRunning ()) {
216 OSStatus err = AudioOutputUnitStart (d_OutputAU);
217 CheckErrorAndThrow (err, "AudioOutputUnitStart", "audio_osx_sink::start");
223 bool audio_osx_sink::stop ()
226 OSStatus err = AudioOutputUnitStop (d_OutputAU);
227 CheckErrorAndThrow (err, "AudioOutputUnitStop", "audio_osx_sink::stop");
229 for (UInt32 n = 0; n < d_n_channels; n++) {
230 d_buffers[n]->abort ();
237 audio_osx_sink::~audio_osx_sink ()
239 // stop and close the AudioUnit
241 AudioUnitUninitialize (d_OutputAU);
242 #ifndef GR_USE_OLD_AUDIO_UNIT
243 AudioComponentInstanceDispose (d_OutputAU);
245 CloseComponent (d_OutputAU);
248 // empty and delete the queues
249 for (UInt32 n = 0; n < d_n_channels; n++) {
256 // close and delete control stuff
264 audio_osx_make_sink (int sampling_freq,
265 const std::string dev,
268 int max_sample_count)
270 return audio_osx_sink_sptr (new audio_osx_sink (sampling_freq,
278 audio_osx_sink::work (int noutput_items,
279 gr_vector_const_void_star &input_items,
280 gr_vector_void_star &output_items)
282 gruel::scoped_lock l (*d_internal);
284 /* take the input data, copy it, and push it to the bottom of the queue
285 mono input are pushed onto queue[0];
286 stereo input are pushed onto queue[1].
287 Start the AudioUnit if necessary. */
290 int diff_count = d_max_sample_count - noutput_items;
294 l_max_count = (UInt32) diff_count;
297 if (l_max_count < d_queueItemLength->back()) {
298 // allow 2 buffers at a time, regardless of length
299 l_max_count = d_queueItemLength->back();
304 std::cerr << "work1: qSC = " << d_queueSampleCount << ", lMC = "<< l_max_count
305 << ", dmSC = " << d_max_sample_count << ", nOI = " << noutput_items << std::endl;
308 if (d_queueSampleCount > l_max_count) {
309 // data coming in too fast; do_block decides what to do
310 if (d_do_block == true) {
311 // block until there is data to return
312 while (d_queueSampleCount > l_max_count) {
313 // release control so-as to allow data to be retrieved;
314 // block until there is data to return
315 d_cond_data->wait (l);
316 // the condition's 'notify' was called; acquire control
317 // to keep thread safe
321 // not blocking case and overflow is handled by the circular buffer
323 // add the input frames to the buffers' queue, checking for overflow
327 float* inBuffer = (float*) input_items[0];
328 const UInt32 l_size = input_items.size();
329 for (l_counter = 0; l_counter < l_size; l_counter++) {
330 inBuffer = (float*) input_items[l_counter];
331 int l_res = d_buffers[l_counter]->enqueue (inBuffer,
336 while (l_counter < d_n_channels) {
337 // for extra channels, copy the last input's data
338 int l_res = d_buffers[l_counter++]->enqueue (inBuffer,
345 // data coming in too fast
346 // drop oldest buffer
347 fputs ("aO", stderr);
349 // set the local number of samples available to the max
350 d_queueSampleCount = d_buffers[0]->buffer_length_items ();
352 // keep up the local sample count
353 d_queueSampleCount += noutput_items;
357 std::cerr << "work2: #OI = " << noutput_items << ", #Cnt = "
358 << d_queueSampleCount << ", mSC = " << d_max_sample_count << std::endl;
361 return (noutput_items);
364 OSStatus audio_osx_sink::AUOutputCallback
366 AudioUnitRenderActionFlags *ioActionFlags,
367 const AudioTimeStamp *inTimeStamp,
369 UInt32 inNumberFrames,
370 AudioBufferList *ioData)
372 audio_osx_sink* This = (audio_osx_sink*) inRefCon;
373 OSStatus err = noErr;
375 gruel::scoped_lock l (*This->d_internal);
378 std::cerr << "cb_in: SC = " << This->d_queueSampleCount
379 << ", in#F = " << inNumberFrames << std::endl;
382 if (This->d_queueSampleCount < inNumberFrames) {
383 // not enough data to fill request
386 // enough data; remove data from our buffers into the AU's buffers
387 int l_counter = This->d_n_channels;
389 while (--l_counter >= 0) {
390 size_t t_n_output_items = inNumberFrames;
391 float* outBuffer = (float*) ioData->mBuffers[l_counter].mData;
392 This->d_buffers[l_counter]->dequeue (outBuffer, &t_n_output_items);
393 if (t_n_output_items != inNumberFrames) {
394 throw std::runtime_error ("audio_osx_sink::AUOutputCallback(): "
395 "number of available items changing "
400 This->d_queueSampleCount -= inNumberFrames;
404 std::cerr << "cb_out: SC = " << This->d_queueSampleCount << std::endl;
407 // signal that data is available
408 This->d_cond_data->notify_one ();