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