e91716c0acacc44dfb32a39614d61d5735afed65
[debian/gnuradio] / gr-audio-osx / src / audio_osx_sink.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2006 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 #define _USE_OMNI_THREADS_
28
29 #include <audio_osx_sink.h>
30 #include <gr_io_signature.h>
31 #include <stdexcept>
32 #include <audio_osx.h>
33
34 #define _OSX_AU_DEBUG_ 0
35
36 audio_osx_sink::audio_osx_sink (int sample_rate,
37                                 const std::string device_name,
38                                 bool do_block,
39                                 int channel_config,
40                                 int max_sample_count)
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),
47     d_OutputAU (0)
48 {
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");
52   } else
53     d_sample_rate = (Float64) sample_rate;
54
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");
64     }
65     if (l_n_channels <= 0)
66       channel_config = 2;
67     else
68       channel_config = l_n_channels;
69   }
70
71   d_n_channels = d_channel_config = channel_config;
72
73 // set the input signature
74
75   set_input_signature (gr_make_io_signature (1, d_n_channels, sizeof (float)));
76
77 // check that the max # of samples to store is valid
78
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");
84   }
85
86   d_max_sample_count = max_sample_count;
87
88 // allocate the output circular buffer(s), one per channel
89
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);
95   }
96
97 // create the default AudioUnit for output
98   OSStatus err = noErr;
99
100 // Open the default output unit
101 #ifndef GR_USE_OLD_AUDIO_UNIT
102   AudioComponentDescription desc;
103 #else
104   ComponentDescription desc;
105 #endif
106
107   desc.componentType = kAudioUnitType_Output;
108   desc.componentSubType = kAudioUnitSubType_DefaultOutput;
109   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
110   desc.componentFlags = 0;
111   desc.componentFlagsMask = 0;
112
113 #ifndef GR_USE_OLD_AUDIO_UNIT
114   AudioComponent comp = AudioComponentFindNext(NULL, &desc);
115   if (comp == NULL) {
116     std::cerr << "AudioComponentFindNext Error" << std::endl;
117     throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
118   }
119 #else
120   Component comp = FindNextComponent (NULL, &desc);
121   if (comp == NULL) {
122     std::cerr << "FindNextComponent Error" << std::endl;
123     throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
124   }
125 #endif
126
127 #ifndef GR_USE_OLD_AUDIO_UNIT
128   err = AudioComponentInstanceNew (comp, &d_OutputAU);
129   CheckErrorAndThrow (err, "AudioComponentInstanceNew", "audio_osx_sink::audio_osx_sink");
130 #else
131   err = OpenAComponent (comp, &d_OutputAU);
132   CheckErrorAndThrow (err, "OpenAComponent", "audio_osx_sink::audio_osx_sink");
133 #endif
134
135 // Set up a callback function to generate output to the output unit
136
137   AURenderCallbackStruct input;
138   input.inputProc = (AURenderCallback)(audio_osx_sink::AUOutputCallback);
139   input.inputProcRefCon = this;
140
141   err = AudioUnitSetProperty (d_OutputAU,
142                               kAudioUnitProperty_SetRenderCallback, 
143                               kAudioUnitScope_Input,
144                               0, 
145                               &input, 
146                               sizeof (input));
147   CheckErrorAndThrow (err, "AudioUnitSetProperty Render Callback", "audio_osx_sink::audio_osx_sink");
148
149 // tell the Output Unit what format data will be supplied to it
150 // so that it handles any format conversions
151
152   AudioStreamBasicDescription streamFormat;
153   streamFormat.mSampleRate = (Float64)(sample_rate);
154   streamFormat.mFormatID = kAudioFormatLinearPCM;
155   streamFormat.mFormatFlags = (kLinearPCMFormatFlagIsFloat |
156                                GR_PCM_ENDIANNESS |
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;
164
165   err = AudioUnitSetProperty (d_OutputAU,
166                               kAudioUnitProperty_StreamFormat,
167                               kAudioUnitScope_Input,
168                               0,
169                               &streamFormat,
170                               sizeof (AudioStreamBasicDescription));
171   CheckErrorAndThrow (err, "AudioUnitSetProperty StreamFormat", "audio_osx_sink::audio_osx_sink");
172
173 // create the stuff to regulate I/O
174
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 ();
180
181 // initialize the AU for output
182
183   err = AudioUnitInitialize (d_OutputAU);
184   CheckErrorAndThrow (err, "AudioUnitInitialize",
185                       "audio_osx_sink::audio_osx_sink");
186
187 #if _OSX_AU_DEBUG_
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;
192 #endif
193 }
194
195 bool audio_osx_sink::IsRunning ()
196 {
197   UInt32 AURunning = 0, AUSize = sizeof (UInt32);
198
199   OSStatus err = AudioUnitGetProperty (d_OutputAU,
200                                        kAudioOutputUnitProperty_IsRunning,
201                                        kAudioUnitScope_Global,
202                                        0,
203                                        &AURunning,
204                                        &AUSize);
205   CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning",
206                       "audio_osx_sink::IsRunning");
207
208   return (AURunning);
209 }
210
211 bool audio_osx_sink::start ()
212 {
213   if (! IsRunning ()) {
214     OSStatus err = AudioOutputUnitStart (d_OutputAU);
215     CheckErrorAndThrow (err, "AudioOutputUnitStart", "audio_osx_sink::start");
216   }
217
218   return (true);
219 }
220
221 bool audio_osx_sink::stop ()
222 {
223   if (IsRunning ()) {
224     OSStatus err = AudioOutputUnitStop (d_OutputAU);
225     CheckErrorAndThrow (err, "AudioOutputUnitStop", "audio_osx_sink::stop");
226
227     for (UInt32 n = 0; n < d_n_channels; n++) {
228       d_buffers[n]->abort ();
229     }
230   }
231
232   return (true);
233 }
234
235 audio_osx_sink::~audio_osx_sink ()
236 {
237 // stop and close the AudioUnit
238   stop ();
239   AudioUnitUninitialize (d_OutputAU);
240 #ifndef GR_USE_OLD_AUDIO_UNIT
241   AudioComponentInstanceDispose (d_OutputAU);
242 #else
243   CloseComponent (d_OutputAU);
244 #endif
245
246 // empty and delete the queues
247   for (UInt32 n = 0; n < d_n_channels; n++) {
248     delete d_buffers[n];
249     d_buffers[n] = 0;
250   }
251   delete [] d_buffers;
252   d_buffers = 0;
253
254 // close and delete control stuff
255   delete d_cond_data;
256 }
257
258 audio_osx_sink_sptr
259 audio_osx_make_sink (int sampling_freq,
260                      const std::string dev,
261                      bool do_block,
262                      int channel_config,
263                      int max_sample_count)
264 {
265   return audio_osx_sink_sptr (new audio_osx_sink (sampling_freq,
266                                                   dev,
267                                                   do_block,
268                                                   channel_config,
269                                                   max_sample_count));
270 }
271
272 int
273 audio_osx_sink::work (int noutput_items,
274                       gr_vector_const_void_star &input_items,
275                       gr_vector_void_star &output_items)
276 {
277   d_internal->lock ();
278
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. */
283
284   UInt32 l_max_count;
285   int diff_count = d_max_sample_count - noutput_items;
286   if (diff_count < 0)
287     l_max_count = 0;
288   else
289     l_max_count = (UInt32) diff_count;
290
291 #if 0
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();
295   }
296 #endif
297
298 #if _OSX_AU_DEBUG_
299   std::cerr << "work1: qSC = " << d_queueSampleCount << ", lMC = "<< l_max_count
300             << ", dmSC = " << d_max_sample_count << ", nOI = " << noutput_items << std::endl;
301 #endif
302
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
313       }
314     }
315   }
316 // not blocking case and overflow is handled by the circular buffer
317
318 // add the input frames to the buffers' queue, checking for overflow
319
320   UInt32 l_counter;
321   int res = 0;
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,
327                                                noutput_items);
328     if (l_res == -1)
329       res = -1;
330   }
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,
334                                                  noutput_items);
335     if (l_res == -1)
336       res = -1;
337   }
338
339   if (res == -1) {
340 // data coming in too fast
341 // drop oldest buffer
342     fputs ("aO", stderr);
343     fflush (stderr);
344 // set the local number of samples available to the max
345     d_queueSampleCount = d_buffers[0]->buffer_length_items ();
346   } else {
347 // keep up the local sample count
348     d_queueSampleCount += noutput_items;
349   }
350
351 #if _OSX_AU_DEBUG_
352   std::cerr << "work2: #OI = " << noutput_items << ", #Cnt = "
353             << d_queueSampleCount << ", mSC = " << d_max_sample_count << std::endl;
354 #endif
355
356 // release control to allow for other processing parts to run
357   d_internal->unlock ();
358
359   return (noutput_items);
360 }
361
362 OSStatus audio_osx_sink::AUOutputCallback
363 (void *inRefCon, 
364  AudioUnitRenderActionFlags *ioActionFlags, 
365  const AudioTimeStamp *inTimeStamp, 
366  UInt32 inBusNumber, 
367  UInt32 inNumberFrames, 
368  AudioBufferList *ioData)
369 {
370   audio_osx_sink* This = (audio_osx_sink*) inRefCon;
371   OSStatus err = noErr;
372
373   This->d_internal->lock ();
374
375 #if _OSX_AU_DEBUG_
376   std::cerr << "cb_in: SC = " << This->d_queueSampleCount
377             << ", in#F = " << inNumberFrames << std::endl;
378 #endif
379
380   if (This->d_queueSampleCount < inNumberFrames) {
381 // not enough data to fill request
382     err = -1;
383   } else {
384 // enough data; remove data from our buffers into the AU's buffers
385     int l_counter = This->d_n_channels;
386
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 "
394                                   "unexpectedly.\n");
395       }
396     }
397
398     This->d_queueSampleCount -= inNumberFrames;
399   }
400
401 #if _OSX_AU_DEBUG_
402   std::cerr << "cb_out: SC = " << This->d_queueSampleCount << std::endl;
403 #endif
404
405 // signal that data is available
406   This->d_cond_data->signal ();
407
408 // release control to allow for other processing parts to run
409   This->d_internal->unlock ();
410
411   return (err);
412 }