Imported Upstream version 3.2.2
[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     fprintf (stderr, "Invalid Sample Rate: %d\n", sample_rate);
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     fprintf (stderr, "Invalid Channel Config: %d\n", channel_config);
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       fprintf (stderr, "Error Converting Device Name: %d\n", errno);
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     fprintf (stderr, "Invalid Max Sample Count: %d\n", max_sample_count);
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   ComponentDescription desc;
102   desc.componentType = kAudioUnitType_Output;
103   desc.componentSubType = kAudioUnitSubType_DefaultOutput;
104   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
105   desc.componentFlags = 0;
106   desc.componentFlagsMask = 0;
107
108   Component comp = FindNextComponent (NULL, &desc);
109   if (comp == NULL) {
110     fprintf (stderr, "FindNextComponent Error\n");
111     throw std::runtime_error ("audio_osx_sink::audio_osx_sink");
112   }
113
114   err = OpenAComponent (comp, &d_OutputAU);
115   CheckErrorAndThrow (err, "OpenAComponent", "audio_osx_sink::audio_osx_sink");
116
117 // Set up a callback function to generate output to the output unit
118
119   AURenderCallbackStruct input;
120   input.inputProc = (AURenderCallback)(audio_osx_sink::AUOutputCallback);
121   input.inputProcRefCon = this;
122
123   err = AudioUnitSetProperty (d_OutputAU,
124                               kAudioUnitProperty_SetRenderCallback, 
125                               kAudioUnitScope_Input,
126                               0, 
127                               &input, 
128                               sizeof (input));
129   CheckErrorAndThrow (err, "AudioUnitSetProperty Render Callback", "audio_osx_sink::audio_osx_sink");
130
131 // tell the Output Unit what format data will be supplied to it
132 // so that it handles any format conversions
133
134   AudioStreamBasicDescription streamFormat;
135   streamFormat.mSampleRate = (Float64)(sample_rate);
136   streamFormat.mFormatID = kAudioFormatLinearPCM;
137   streamFormat.mFormatFlags = (kLinearPCMFormatFlagIsFloat |
138                                GR_PCM_ENDIANNESS |
139                                kLinearPCMFormatFlagIsPacked |
140                                kAudioFormatFlagIsNonInterleaved);
141   streamFormat.mBytesPerPacket = 4;
142   streamFormat.mFramesPerPacket = 1;
143   streamFormat.mBytesPerFrame = 4;
144   streamFormat.mChannelsPerFrame = d_n_channels;
145   streamFormat.mBitsPerChannel = 32;
146
147   err = AudioUnitSetProperty (d_OutputAU,
148                               kAudioUnitProperty_StreamFormat,
149                               kAudioUnitScope_Input,
150                               0,
151                               &streamFormat,
152                               sizeof (AudioStreamBasicDescription));
153   CheckErrorAndThrow (err, "AudioUnitSetProperty StreamFormat", "audio_osx_sink::audio_osx_sink");
154
155 // create the stuff to regulate I/O
156
157   d_cond_data = new mld_condition ();
158   if (d_cond_data == NULL)
159     CheckErrorAndThrow (errno, "new mld_condition (data)",
160                         "audio_osx_source::audio_osx_source");
161   d_internal = d_cond_data->mutex ();
162
163 // initialize the AU for output
164
165   err = AudioUnitInitialize (d_OutputAU);
166   CheckErrorAndThrow (err, "AudioUnitInitialize",
167                       "audio_osx_sink::audio_osx_sink");
168
169 #if _OSX_AU_DEBUG_
170   fprintf (stderr, "audio_osx_sink Parameters:\n");
171   fprintf (stderr, "  Sample Rate is %g\n", d_sample_rate);
172   fprintf (stderr, "  Number of Channels is %ld\n", d_n_channels);
173   fprintf (stderr, "  Max # samples to store per channel is %ld",
174            d_max_sample_count);
175 #endif
176 }
177
178 bool audio_osx_sink::IsRunning ()
179 {
180   UInt32 AURunning = 0, AUSize = sizeof (UInt32);
181
182   OSStatus err = AudioUnitGetProperty (d_OutputAU,
183                                        kAudioOutputUnitProperty_IsRunning,
184                                        kAudioUnitScope_Global,
185                                        0,
186                                        &AURunning,
187                                        &AUSize);
188   CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning",
189                       "audio_osx_sink::IsRunning");
190
191   return (AURunning);
192 }
193
194 bool audio_osx_sink::start ()
195 {
196   if (! IsRunning ()) {
197     OSStatus err = AudioOutputUnitStart (d_OutputAU);
198     CheckErrorAndThrow (err, "AudioOutputUnitStart", "audio_osx_sink::start");
199   }
200
201   return (true);
202 }
203
204 bool audio_osx_sink::stop ()
205 {
206   if (IsRunning ()) {
207     OSStatus err = AudioOutputUnitStop (d_OutputAU);
208     CheckErrorAndThrow (err, "AudioOutputUnitStop", "audio_osx_sink::stop");
209
210     for (UInt32 n = 0; n < d_n_channels; n++) {
211       d_buffers[n]->abort ();
212     }
213   }
214
215   return (true);
216 }
217
218 audio_osx_sink::~audio_osx_sink ()
219 {
220 // stop and close the AudioUnit
221   stop ();
222   AudioUnitUninitialize (d_OutputAU);
223   CloseComponent (d_OutputAU);
224
225 // empty and delete the queues
226   for (UInt32 n = 0; n < d_n_channels; n++) {
227     delete d_buffers[n];
228     d_buffers[n] = 0;
229   }
230   delete [] d_buffers;
231   d_buffers = 0;
232
233 // close and delete control stuff
234   delete d_cond_data;
235 }
236
237 audio_osx_sink_sptr
238 audio_osx_make_sink (int sampling_freq,
239                      const std::string dev,
240                      bool do_block,
241                      int channel_config,
242                      int max_sample_count)
243 {
244   return audio_osx_sink_sptr (new audio_osx_sink (sampling_freq,
245                                                   dev,
246                                                   do_block,
247                                                   channel_config,
248                                                   max_sample_count));
249 }
250
251 int
252 audio_osx_sink::work (int noutput_items,
253                       gr_vector_const_void_star &input_items,
254                       gr_vector_void_star &output_items)
255 {
256   d_internal->lock ();
257
258   /* take the input data, copy it, and push it to the bottom of the queue
259      mono input are pushed onto queue[0];
260      stereo input are pushed onto queue[1].
261      Start the AudioUnit if necessary. */
262
263   UInt32 l_max_count;
264   int diff_count = d_max_sample_count - noutput_items;
265   if (diff_count < 0)
266     l_max_count = 0;
267   else
268     l_max_count = (UInt32) diff_count;
269
270 #if 0
271   if (l_max_count < d_queueItemLength->back()) {
272 //  allow 2 buffers at a time, regardless of length
273     l_max_count = d_queueItemLength->back();
274   }
275 #endif
276
277 #if _OSX_AU_DEBUG_
278   fprintf (stderr, "work1: qSC = %ld, lMC = %ld, dmSC = %ld, nOI = %d\n",
279            d_queueSampleCount, l_max_count, d_max_sample_count, noutput_items);
280 #endif
281
282   if (d_queueSampleCount > l_max_count) {
283 // data coming in too fast; do_block decides what to do
284     if (d_do_block == true) {
285 // block until there is data to return
286       while (d_queueSampleCount > l_max_count) {
287 // release control so-as to allow data to be retrieved;
288 // block until there is data to return
289         d_cond_data->wait ();
290 // the condition's signal() was called; acquire control
291 // to keep thread safe
292       }
293     }
294   }
295 // not blocking case and overflow is handled by the circular buffer
296
297 // add the input frames to the buffers' queue, checking for overflow
298
299   UInt32 l_counter;
300   int res = 0;
301   float* inBuffer = (float*) input_items[0];
302   const UInt32 l_size = input_items.size();
303   for (l_counter = 0; l_counter < l_size; l_counter++) {
304     inBuffer = (float*) input_items[l_counter];
305     int l_res = d_buffers[l_counter]->enqueue (inBuffer,
306                                                noutput_items);
307     if (l_res == -1)
308       res = -1;
309   }
310   while (l_counter < d_n_channels) {
311 // for extra channels, copy the last input's data
312     int l_res = d_buffers[l_counter++]->enqueue (inBuffer,
313                                                  noutput_items);
314     if (l_res == -1)
315       res = -1;
316   }
317
318   if (res == -1) {
319 // data coming in too fast
320 // drop oldest buffer
321     fputs ("oX", stderr);
322     fflush (stderr);
323 // set the local number of samples available to the max
324     d_queueSampleCount = d_buffers[0]->buffer_length_items ();
325   } else {
326 // keep up the local sample count
327     d_queueSampleCount += noutput_items;
328   }
329
330 #if _OSX_AU_DEBUG_
331   fprintf (stderr, "work2: #OI = %4d, #Cnt = %4ld, mSC = %ld\n",
332            noutput_items, d_queueSampleCount, d_max_sample_count);
333 #endif
334
335 // release control to allow for other processing parts to run
336   d_internal->unlock ();
337
338   return (noutput_items);
339 }
340
341 OSStatus audio_osx_sink::AUOutputCallback
342 (void *inRefCon, 
343  AudioUnitRenderActionFlags *ioActionFlags, 
344  const AudioTimeStamp *inTimeStamp, 
345  UInt32 inBusNumber, 
346  UInt32 inNumberFrames, 
347  AudioBufferList *ioData)
348 {
349   audio_osx_sink* This = (audio_osx_sink*) inRefCon;
350   OSStatus err = noErr;
351
352   This->d_internal->lock ();
353
354 #if _OSX_AU_DEBUG_
355   fprintf (stderr, "cb_in: SC = %4ld, in#F = %4ld\n",
356            This->d_queueSampleCount, inNumberFrames);
357 #endif
358
359   if (This->d_queueSampleCount < inNumberFrames) {
360 // not enough data to fill request
361     err = -1;
362   } else {
363 // enough data; remove data from our buffers into the AU's buffers
364     int l_counter = This->d_n_channels;
365
366     while (--l_counter >= 0) {
367       UInt32 t_n_output_items = inNumberFrames;
368       float* outBuffer = (float*) ioData->mBuffers[l_counter].mData;
369       This->d_buffers[l_counter]->dequeue (outBuffer, &t_n_output_items);
370       if (t_n_output_items != inNumberFrames) {
371         throw std::runtime_error ("audio_osx_sink::AUOutputCallback(): "
372                                   "number of available items changing "
373                                   "unexpectedly.\n");
374       }
375     }
376
377     This->d_queueSampleCount -= inNumberFrames;
378   }
379
380 #if _OSX_AU_DEBUG_
381   fprintf (stderr, "cb_out: SC = %4ld\n", This->d_queueSampleCount);
382 #endif
383
384 // signal that data is available
385   This->d_cond_data->signal ();
386
387 // release control to allow for other processing parts to run
388   This->d_internal->unlock ();
389
390   return (err);
391 }