Imported Upstream version 3.2.2
[debian/gnuradio] / gr-audio-osx / src / audio_osx_source.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_source.h>
30 #include <gr_io_signature.h>
31 #include <stdexcept>
32 #include <audio_osx.h>
33
34 #define _OSX_AU_DEBUG_ 0
35 #define _OSX_DO_LISTENERS_ 0
36
37 void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
38 {
39   if (inDesc == NULL) {
40     fprintf (stderr, "PrintStreamDesc: Can't print a NULL desc!\n");
41     return;
42   }
43
44   fprintf (stderr, "  Sample Rate        : %g\n", inDesc->mSampleRate);
45   fprintf (stderr, "  Format ID          : %4s\n", (char*)&inDesc->mFormatID);
46   fprintf (stderr, "  Format Flags       : %lX\n", inDesc->mFormatFlags);
47   fprintf (stderr, "  Bytes per Packet   : %ld\n", inDesc->mBytesPerPacket);
48   fprintf (stderr, "  Frames per Packet  : %ld\n", inDesc->mFramesPerPacket);
49   fprintf (stderr, "  Bytes per Frame    : %ld\n", inDesc->mBytesPerFrame);
50   fprintf (stderr, "  Channels per Frame : %ld\n", inDesc->mChannelsPerFrame);
51   fprintf (stderr, "  Bits per Channel   : %ld\n", inDesc->mBitsPerChannel);
52 }
53
54 // FIXME these should query some kind of user preference
55
56 audio_osx_source::audio_osx_source (int sample_rate,
57                                     const std::string device_name,
58                                     bool do_block,
59                                     int channel_config,
60                                     int max_sample_count)
61   : gr_sync_block ("audio_osx_source",
62                    gr_make_io_signature (0, 0, 0),
63                    gr_make_io_signature (0, 0, 0)),
64     d_deviceSampleRate (0.0), d_outputSampleRate (0.0),
65     d_channel_config (0),
66     d_inputBufferSizeFrames (0), d_inputBufferSizeBytes (0),
67     d_outputBufferSizeFrames (0), d_outputBufferSizeBytes (0),
68     d_deviceBufferSizeFrames (0), d_deviceBufferSizeBytes (0),
69     d_leadSizeFrames (0), d_leadSizeBytes (0),
70     d_trailSizeFrames (0), d_trailSizeBytes (0),
71     d_extraBufferSizeFrames (0), d_extraBufferSizeBytes (0),
72     d_queueSampleCount (0), d_max_sample_count (0),
73     d_n_AvailableInputFrames (0), d_n_ActualInputFrames (0),
74     d_n_user_channels (0), d_n_max_channels (0), d_n_deviceChannels (0),
75     d_do_block (do_block), d_passThrough (false),
76     d_internal (0), d_cond_data (0),
77     d_buffers (0),
78     d_InputAU (0), d_InputBuffer (0), d_OutputBuffer (0),
79     d_AudioConverter (0)
80 {
81   if (sample_rate <= 0) {
82     fprintf (stderr, "Invalid Sample Rate: %d\n", sample_rate);
83     throw std::invalid_argument ("audio_osx_source::audio_osx_source");
84   } else
85     d_outputSampleRate = (Float64) sample_rate;
86
87   if (channel_config <= 0 & channel_config != -1) {
88     fprintf (stderr, "Invalid Channel Config: %d\n", channel_config);
89     throw std::invalid_argument ("audio_osx_source::audio_osx_source");
90   } else if (channel_config == -1) {
91 // no user input; try "device name" instead
92     int l_n_channels = (int) strtol (device_name.data(), (char **)NULL, 10);
93     if (l_n_channels == 0 & errno) {
94       fprintf (stderr, "Error Converting Device Name: %d\n", errno);
95       throw std::invalid_argument ("audio_osx_source::audio_osx_source");
96     }
97     if (l_n_channels <= 0)
98       channel_config = 2;
99     else
100       channel_config = l_n_channels;
101   }
102
103   d_channel_config = channel_config;
104
105 // check that the max # of samples to store is valid
106
107   if (max_sample_count == -1)
108     max_sample_count = sample_rate;
109   else if (max_sample_count <= 0) {
110     fprintf (stderr, "Invalid Max Sample Count: %d\n", max_sample_count);
111     throw std::invalid_argument ("audio_osx_source::audio_osx_source");
112   }
113
114   d_max_sample_count = max_sample_count;
115
116 #if _OSX_AU_DEBUG_
117   fprintf (stderr, "source(): max # samples = %ld\n", d_max_sample_count);
118 #endif
119
120   OSStatus err = noErr;
121
122 // create the default AudioUnit for input
123
124 // Open the default input unit
125   ComponentDescription InputDesc;
126
127   InputDesc.componentType = kAudioUnitType_Output;
128   InputDesc.componentSubType = kAudioUnitSubType_HALOutput;
129   InputDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
130   InputDesc.componentFlags = 0;
131   InputDesc.componentFlagsMask = 0;
132
133   Component comp = FindNextComponent (NULL, &InputDesc);
134   if (comp == NULL) {
135     fprintf (stderr, "FindNextComponent Error\n");
136     throw std::runtime_error ("audio_osx_source::audio_osx_source");
137   }
138
139   err = OpenAComponent (comp, &d_InputAU);
140   CheckErrorAndThrow (err, "OpenAComponent",
141                       "audio_osx_source::audio_osx_source");
142
143   UInt32 enableIO;
144
145 // must enable the AUHAL for input and disable output 
146 // before setting the AUHAL's current device
147
148 // Enable input on the AUHAL
149   enableIO = 1;
150   err = AudioUnitSetProperty (d_InputAU,
151                               kAudioOutputUnitProperty_EnableIO,
152                               kAudioUnitScope_Input,
153                               1, // input element
154                               &enableIO,
155                               sizeof (UInt32));
156   CheckErrorAndThrow (err, "AudioUnitSetProperty Input Enable",
157                       "audio_osx_source::audio_osx_source");
158
159 // Disable output on the AUHAL
160   enableIO = 0;
161   err = AudioUnitSetProperty (d_InputAU,
162                               kAudioOutputUnitProperty_EnableIO,
163                               kAudioUnitScope_Output,
164                               0, // output element
165                               &enableIO,
166                               sizeof (UInt32));
167   CheckErrorAndThrow (err, "AudioUnitSetProperty Output Disable",
168                       "audio_osx_source::audio_osx_source");
169
170 // set the default input device for our input AU
171
172   SetDefaultInputDeviceAsCurrent ();
173
174 #if _OSX_DO_LISTENERS_
175 // set up a listener if default hardware input device changes
176
177   err = AudioHardwareAddPropertyListener
178     (kAudioHardwarePropertyDefaultInputDevice,
179      (AudioHardwarePropertyListenerProc) HardwareListener,
180      this);
181
182   CheckErrorAndThrow (err, "AudioHardwareAddPropertyListener",
183                       "audio_osx_source::audio_osx_source");
184
185 // Add a listener for any changes in the input AU's output stream
186 // the function "UnitListener" will be called if the stream format
187 // changes for whatever reason
188
189   err = AudioUnitAddPropertyListener
190     (d_InputAU,
191      kAudioUnitProperty_StreamFormat,
192      (AudioUnitPropertyListenerProc) UnitListener,
193      this);
194   CheckErrorAndThrow (err, "Adding Unit Property Listener",
195                       "audio_osx_source::audio_osx_source");
196 #endif
197
198 // Now find out if it actually can do input.
199
200   UInt32 hasInput = 0;
201   UInt32 dataSize = sizeof (hasInput);
202   err = AudioUnitGetProperty (d_InputAU,
203                               kAudioOutputUnitProperty_HasIO,
204                               kAudioUnitScope_Input,
205                               1,
206                               &hasInput,
207                               &dataSize);
208   CheckErrorAndThrow (err, "AudioUnitGetProperty HasIO",
209                       "audio_osx_source::audio_osx_source");
210   if (hasInput == 0) {
211     fprintf (stderr, "Selected Audio Device does not support Input.\n");
212     throw std::runtime_error ("audio_osx_source::audio_osx_source");
213   }
214
215 // Set up a callback function to retrieve input from the Audio Device
216
217   AURenderCallbackStruct AUCallBack;
218
219   AUCallBack.inputProc = (AURenderCallback)(audio_osx_source::AUInputCallback);
220   AUCallBack.inputProcRefCon = this;
221
222   err = AudioUnitSetProperty (d_InputAU,
223                               kAudioOutputUnitProperty_SetInputCallback,
224                               kAudioUnitScope_Global,
225                               0,
226                               &AUCallBack,
227                               sizeof (AURenderCallbackStruct));
228   CheckErrorAndThrow (err, "AudioUnitSetProperty Input Callback",
229                       "audio_osx_source::audio_osx_source");
230
231   UInt32 propertySize;
232   AudioStreamBasicDescription asbd_device, asbd_client, asbd_user;
233
234 // asbd_device: ASBD of the device that is creating the input data stream
235 // asbd_client: ASBD of the client size (output) of the hardware device
236 // asbd_user:   ASBD of the user's arguments
237
238 // Get the Stream Format (device side)
239
240   propertySize = sizeof (asbd_device);
241   err = AudioUnitGetProperty (d_InputAU,
242                               kAudioUnitProperty_StreamFormat,
243                               kAudioUnitScope_Input,
244                               1,
245                               &asbd_device,
246                               &propertySize);
247   CheckErrorAndThrow (err, "AudioUnitGetProperty Device Input Stream Format",
248                       "audio_osx_source::audio_osx_source");
249
250 #if _OSX_AU_DEBUG_
251   fprintf (stderr, "\n---- Device Stream Format ----\n" );
252   PrintStreamDesc (&asbd_device);
253 #endif
254
255 // Get the Stream Format (client side)
256   propertySize = sizeof (asbd_client);
257   err = AudioUnitGetProperty (d_InputAU,
258                               kAudioUnitProperty_StreamFormat,
259                               kAudioUnitScope_Output,
260                               1,
261                               &asbd_client,
262                               &propertySize);
263   CheckErrorAndThrow (err, "AudioUnitGetProperty Device Ouput Stream Format",
264                       "audio_osx_source::audio_osx_source");
265
266 #if _OSX_AU_DEBUG_
267   fprintf (stderr, "\n---- Client Stream Format ----\n");
268   PrintStreamDesc (&asbd_client);
269 #endif
270
271 // Set the format of all the AUs to the input/output devices channel count
272
273 // get the max number of input (& thus output) channels supported by
274 // this device
275   d_n_max_channels = asbd_client.mChannelsPerFrame;
276
277 // create the output io signature;
278 // no input siganture to set (source is hardware)
279   set_output_signature (gr_make_io_signature (1,
280                                               d_n_max_channels,
281                                               sizeof (float)));
282
283 // allocate the output circular buffer(s), one per channel
284   d_buffers = (circular_buffer<float>**) new
285     circular_buffer<float>* [d_n_max_channels];
286   UInt32 n_alloc = (UInt32) ceil ((double) d_max_sample_count);
287   for (UInt32 n = 0; n < d_n_max_channels; n++) {
288     d_buffers[n] = new circular_buffer<float> (n_alloc, false, false);
289   }
290
291   d_deviceSampleRate = asbd_device.mSampleRate;
292   d_n_deviceChannels = asbd_device.mChannelsPerFrame;
293
294 // create an ASBD for the user's wants
295
296   asbd_user.mSampleRate = d_outputSampleRate;
297   asbd_user.mFormatID = kAudioFormatLinearPCM;
298   asbd_user.mFormatFlags = (kLinearPCMFormatFlagIsFloat |
299                             GR_PCM_ENDIANNESS |
300                             kLinearPCMFormatFlagIsPacked |
301                             kAudioFormatFlagIsNonInterleaved);
302   asbd_user.mBytesPerPacket = 4;
303   asbd_user.mFramesPerPacket = 1;
304   asbd_user.mBytesPerFrame = 4;
305   asbd_user.mChannelsPerFrame = d_n_max_channels;
306   asbd_user.mBitsPerChannel = 32;
307
308   if (d_deviceSampleRate == d_outputSampleRate) {
309 // no need to do conversion if asbd_client matches user wants
310     d_passThrough = true;
311     d_leadSizeFrames = d_trailSizeFrames = 0L;
312   } else {
313     d_passThrough = false;
314 // Create the audio converter
315
316     err = AudioConverterNew (&asbd_client, &asbd_user, &d_AudioConverter);
317     CheckErrorAndThrow (err, "AudioConverterNew",
318                         "audio_osx_source::audio_osx_source");
319
320 // Set the audio converter sample rate quality to "max" ...
321 // requires more samples, but should sound nicer
322
323     UInt32 ACQuality = kAudioConverterQuality_Max;
324     propertySize = sizeof (ACQuality);
325     err = AudioConverterSetProperty (d_AudioConverter,
326                                      kAudioConverterSampleRateConverterQuality,
327                                      propertySize,
328                                      &ACQuality);
329     CheckErrorAndThrow (err, "AudioConverterSetProperty "
330                         "SampleRateConverterQuality",
331                         "audio_osx_source::audio_osx_source");
332
333 // set the audio converter's prime method to "pre",
334 // which uses both leading and trailing frames
335 // from the "current input".  All of this is handled
336 // internally by the AudioConverter; we just supply
337 // the frames for conversion.
338
339 //   UInt32 ACPrimeMethod = kConverterPrimeMethod_None;
340     UInt32 ACPrimeMethod = kConverterPrimeMethod_Pre;
341     propertySize = sizeof (ACPrimeMethod);
342     err = AudioConverterSetProperty (d_AudioConverter, 
343                                      kAudioConverterPrimeMethod,
344                                      propertySize,
345                                      &ACPrimeMethod);
346     CheckErrorAndThrow (err, "AudioConverterSetProperty PrimeMethod",
347                         "audio_osx_source::audio_osx_source");
348
349 // Get the size of the I/O buffer(s) to allow for pre-allocated buffers
350       
351 // lead frame info (trail frame info is ignored)
352
353     AudioConverterPrimeInfo ACPrimeInfo = {0, 0};
354     propertySize = sizeof (ACPrimeInfo);
355     err = AudioConverterGetProperty (d_AudioConverter, 
356                                      kAudioConverterPrimeInfo,
357                                      &propertySize,
358                                      &ACPrimeInfo);
359     CheckErrorAndThrow (err, "AudioConverterGetProperty PrimeInfo",
360                         "audio_osx_source::audio_osx_source");
361
362     switch (ACPrimeMethod) {
363     case (kConverterPrimeMethod_None):
364       d_leadSizeFrames =
365         d_trailSizeFrames = 0L;
366       break;
367     case (kConverterPrimeMethod_Normal):
368       d_leadSizeFrames = 0L;
369       d_trailSizeFrames = ACPrimeInfo.trailingFrames;
370       break;
371     default:
372       d_leadSizeFrames = ACPrimeInfo.leadingFrames;
373       d_trailSizeFrames = ACPrimeInfo.trailingFrames;
374     }
375   }
376   d_leadSizeBytes = d_leadSizeFrames * sizeof (Float32);
377   d_trailSizeBytes = d_trailSizeFrames * sizeof (Float32);
378
379   propertySize = sizeof (d_deviceBufferSizeFrames);
380   err = AudioUnitGetProperty (d_InputAU,
381                               kAudioDevicePropertyBufferFrameSize,
382                               kAudioUnitScope_Global,
383                               0,
384                               &d_deviceBufferSizeFrames,
385                               &propertySize);
386   CheckErrorAndThrow (err, "AudioUnitGetProperty Buffer Frame Size",
387                       "audio_osx_source::audio_osx_source");
388
389   d_deviceBufferSizeBytes = d_deviceBufferSizeFrames * sizeof (Float32);
390   d_inputBufferSizeBytes = d_deviceBufferSizeBytes + d_leadSizeBytes;
391   d_inputBufferSizeFrames = d_deviceBufferSizeFrames + d_leadSizeFrames;
392
393 // outBufSizeBytes = floor (inBufSizeBytes * rate_out / rate_in)
394 // since this is rarely exact, we need another buffer to hold
395 // "extra" samples not processed at any given sampling period
396 // this buffer must be at least 4 floats in size, but generally
397 // follows the rule that
398 // extraBufSize =  ceil (rate_in / rate_out)*sizeof(float)
399
400   d_extraBufferSizeFrames = ((UInt32) ceil (d_deviceSampleRate
401                                             / d_outputSampleRate)
402                              * sizeof (float));
403   if (d_extraBufferSizeFrames < 4)
404     d_extraBufferSizeFrames = 4;
405   d_extraBufferSizeBytes = d_extraBufferSizeFrames * sizeof (float);
406
407   d_outputBufferSizeFrames = (UInt32) ceil (((Float64) d_inputBufferSizeFrames)
408                                             * d_outputSampleRate
409                                             / d_deviceSampleRate);
410   d_outputBufferSizeBytes = d_outputBufferSizeFrames * sizeof (float);
411   d_inputBufferSizeFrames += d_extraBufferSizeFrames;
412
413 // pre-alloc all buffers
414
415   AllocAudioBufferList (&d_InputBuffer, d_n_deviceChannels,
416                         d_inputBufferSizeBytes);
417   if (d_passThrough == false) {
418     AllocAudioBufferList (&d_OutputBuffer, d_n_max_channels,
419                           d_outputBufferSizeBytes);
420   } else {
421     d_OutputBuffer = d_InputBuffer;
422   }
423
424 // create the stuff to regulate I/O
425
426   d_cond_data = new mld_condition ();
427   if (d_cond_data == NULL)
428     CheckErrorAndThrow (errno, "new mld_condition (data)",
429                         "audio_osx_source::audio_osx_source");
430   d_internal = d_cond_data->mutex ();
431
432 // initialize the AU for input
433
434   err = AudioUnitInitialize (d_InputAU);
435   CheckErrorAndThrow (err, "AudioUnitInitialize",
436                       "audio_osx_source::audio_osx_source");
437
438 #if _OSX_AU_DEBUG_
439   fprintf (stderr, "audio_osx_source Parameters:\n");
440   fprintf (stderr, "  Device Sample Rate is %g\n", d_deviceSampleRate);
441   fprintf (stderr, "  User Sample Rate is %g\n", d_outputSampleRate);
442   fprintf (stderr, "  Max Sample Count is %ld\n", d_max_sample_count);
443   fprintf (stderr, "  # Device Channels is %ld\n", d_n_deviceChannels);
444   fprintf (stderr, "  # Max Channels is %ld\n", d_n_max_channels);
445   fprintf (stderr, "  Device Buffer Size is Frames = %ld\n",
446            d_deviceBufferSizeFrames);
447   fprintf (stderr, "  Lead Size is Frames = %ld\n",
448            d_leadSizeFrames);
449   fprintf (stderr, "  Trail Size is Frames = %ld\n",
450            d_trailSizeFrames);
451   fprintf (stderr, "  Input Buffer Size is Frames = %ld\n",
452            d_inputBufferSizeFrames);
453   fprintf (stderr, "  Output Buffer Size is Frames = %ld\n",
454            d_outputBufferSizeFrames);
455 #endif
456 }
457
458 void
459 audio_osx_source::AllocAudioBufferList (AudioBufferList** t_ABL,
460                                         UInt32 n_channels,
461                                         UInt32 bufferSizeBytes)
462 {
463   FreeAudioBufferList (t_ABL);
464   UInt32 propertySize = (offsetof (AudioBufferList, mBuffers[0]) +
465                          (sizeof (AudioBuffer) * n_channels));
466   *t_ABL = (AudioBufferList*) calloc (1, propertySize);
467   (*t_ABL)->mNumberBuffers = n_channels;
468
469   int counter = n_channels;
470
471   while (--counter >= 0) {
472     (*t_ABL)->mBuffers[counter].mNumberChannels = 1;
473     (*t_ABL)->mBuffers[counter].mDataByteSize = bufferSizeBytes;
474     (*t_ABL)->mBuffers[counter].mData = calloc (1, bufferSizeBytes);
475   }
476 }
477
478 void
479 audio_osx_source::FreeAudioBufferList (AudioBufferList** t_ABL)
480 {
481 // free pre-allocated audio buffer, if it exists
482   if (*t_ABL != NULL) {
483     int counter = (*t_ABL)->mNumberBuffers;
484     while (--counter >= 0)
485       free ((*t_ABL)->mBuffers[counter].mData);
486     free (*t_ABL);
487     (*t_ABL) = 0;
488   }
489 }
490
491 bool audio_osx_source::IsRunning ()
492 {
493   UInt32 AURunning = 0, AUSize = sizeof (UInt32);
494
495   OSStatus err = AudioUnitGetProperty (d_InputAU,
496                                        kAudioOutputUnitProperty_IsRunning,
497                                        kAudioUnitScope_Global,
498                                        0,
499                                        &AURunning,
500                                        &AUSize);
501   CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning",
502                       "audio_osx_source::IsRunning");
503
504   return (AURunning);
505 }
506
507 bool audio_osx_source::start ()
508 {
509   if (! IsRunning ()) {
510     OSStatus err = AudioOutputUnitStart (d_InputAU);
511     CheckErrorAndThrow (err, "AudioOutputUnitStart",
512                         "audio_osx_source::start");
513   }
514
515   return (true);
516 }
517
518 bool audio_osx_source::stop ()
519 {
520   if (IsRunning ()) {
521     OSStatus err = AudioOutputUnitStop (d_InputAU);
522     CheckErrorAndThrow (err, "AudioOutputUnitStart",
523                         "audio_osx_source::stop");
524     for (UInt32 n = 0; n < d_n_user_channels; n++) {
525       d_buffers[n]->abort ();
526     }
527   }
528
529   return (true);
530 }
531
532 audio_osx_source::~audio_osx_source ()
533 {
534   OSStatus err = noErr;
535
536 // stop the AudioUnit
537   stop();
538
539 #if _OSX_DO_LISTENERS_
540 // remove the listeners
541
542   err = AudioUnitRemovePropertyListener
543     (d_InputAU,
544      kAudioUnitProperty_StreamFormat,
545      (AudioUnitPropertyListenerProc) UnitListener);
546   CheckError (err, "~audio_osx_source: AudioUnitRemovePropertyListener");
547
548   err = AudioHardwareRemovePropertyListener
549     (kAudioHardwarePropertyDefaultInputDevice,
550      (AudioHardwarePropertyListenerProc) HardwareListener);
551   CheckError (err, "~audio_osx_source: AudioHardwareRemovePropertyListener");
552 #endif
553
554 // free pre-allocated audio buffers
555   FreeAudioBufferList (&d_InputBuffer);
556
557   if (d_passThrough == false) {
558     err = AudioConverterDispose (d_AudioConverter);
559     CheckError (err, "~audio_osx_source: AudioConverterDispose");
560     FreeAudioBufferList (&d_OutputBuffer);
561   }
562
563 // remove the audio unit
564   err = AudioUnitUninitialize (d_InputAU);
565   CheckError (err, "~audio_osx_source: AudioUnitUninitialize");
566
567   err = CloseComponent (d_InputAU);
568   CheckError (err, "~audio_osx_source: CloseComponent");
569
570 // empty and delete the queues
571   for (UInt32 n = 0; n < d_n_max_channels; n++) {
572     delete d_buffers[n];
573     d_buffers[n] = 0;
574   }
575   delete [] d_buffers;
576   d_buffers = 0;
577
578 // close and delete the control stuff
579   delete d_cond_data;
580 }
581
582 audio_osx_source_sptr
583 audio_osx_make_source (int sampling_freq,
584                        const std::string device_name,
585                        bool do_block,
586                        int channel_config,
587                        int max_sample_count)
588 {
589   return audio_osx_source_sptr (new audio_osx_source (sampling_freq,
590                                                       device_name,
591                                                       do_block,
592                                                       channel_config,
593                                                       max_sample_count));
594 }
595
596 bool
597 audio_osx_source::check_topology (int ninputs, int noutputs)
598 {
599 // check # inputs to make sure it's valid
600   if (ninputs != 0) {
601     fprintf (stderr, "audio_osx_source::check_topology(): "
602              "number of input streams provided (%d) should be 0.\n",
603              ninputs);
604     throw std::runtime_error ("audio_osx_source::check_topology()");
605   }
606
607 // check # outputs to make sure it's valid
608   if ((noutputs < 1) | (noutputs > (int) d_n_max_channels)) {
609     fprintf (stderr, "audio_osx_source::check_topology(): "
610              "number of output streams provided (%d) should be in "
611              "[1,%ld] for the selected audio device.\n",
612              noutputs, d_n_max_channels);
613     throw std::runtime_error ("audio_osx_source::check_topology()");
614   }
615
616 // save the actual number of output (user) channels
617   d_n_user_channels = noutputs;
618
619 #if _OSX_AU_DEBUG_
620   fprintf (stderr, "chk_topo: Actual # user output channels = %d\n",
621            noutputs);
622 #endif
623
624   return (true);
625 }
626
627 int
628 audio_osx_source::work
629 (int noutput_items,
630  gr_vector_const_void_star &input_items,
631  gr_vector_void_star &output_items)
632 {
633   // acquire control to do processing here only
634   d_internal->lock ();
635
636 #if _OSX_AU_DEBUG_
637   fprintf (stderr, "work1: SC = %4ld, #OI = %4d, #Chan = %ld\n",
638            d_queueSampleCount, noutput_items, output_items.size());
639 #endif
640
641   // set the actual # of output items to the 'desired' amount then
642   // verify that data is available; if not enough data is available,
643   // either wait until it is (is "do_block" is true), return (0) is no
644   // data is available and "do_block" is false, or process the actual
645   // amount of available data.
646
647   UInt32 actual_noutput_items = noutput_items;
648
649   if (d_queueSampleCount < actual_noutput_items) {
650     if (d_queueSampleCount == 0) {
651       // no data; do_block decides what to do
652       if (d_do_block == true) {
653         while (d_queueSampleCount == 0) {
654           // release control so-as to allow data to be retrieved;
655           // block until there is data to return
656           d_cond_data->wait ();
657           // the condition's signal() was called; acquire control to
658           // keep thread safe
659         }
660       } else {
661         // no data & not blocking; return nothing
662         // release control so-as to allow data to be retrieved
663         d_internal->unlock ();
664         return (0);
665       }
666     }
667     // use the actual amount of available data
668     actual_noutput_items = d_queueSampleCount;
669   }
670
671   // number of channels
672   int l_counter = (int) output_items.size();
673
674   // copy the items from the circular buffer(s) to 'work's output buffers
675   // verify that the number copied out is as expected.
676
677   while (--l_counter >= 0) {
678     UInt32 t_n_output_items = actual_noutput_items;
679     d_buffers[l_counter]->dequeue ((float*) output_items[l_counter],
680                                    &t_n_output_items);
681     if (t_n_output_items != actual_noutput_items) {
682       fprintf (stderr, "audio_osx_source::work(): "
683                "number of available items changing "
684                "unexpectedly; expecting %ld, got %ld.\n",
685                actual_noutput_items, t_n_output_items);
686       throw std::runtime_error ("audio_osx_source::work()");
687     }
688   }
689
690   // subtract the actual number of items removed from the buffer(s)
691   // from the local accounting of the number of available samples
692
693   d_queueSampleCount -= actual_noutput_items;
694
695 #if _OSX_AU_DEBUG_
696   fprintf (stderr, "work2: SC = %4ld, act#OI = %4ld\n",
697            d_queueSampleCount, actual_noutput_items);
698 #endif
699
700   // release control to allow for other processing parts to run
701
702   d_internal->unlock ();
703
704 #if _OSX_AU_DEBUG_
705   fprintf (stderr, "work3: Returning.\n");
706 #endif
707
708   return (actual_noutput_items);
709 }
710
711 OSStatus
712 audio_osx_source::ConverterCallback
713 (AudioConverterRef inAudioConverter,
714  UInt32* ioNumberDataPackets,
715  AudioBufferList* ioData,
716  AudioStreamPacketDescription** ioASPD,
717  void* inUserData)
718 {
719   // take current device buffers and copy them to the tail of the
720   // input buffers the lead buffer is already there in the first
721   // d_leadSizeFrames slots
722
723   audio_osx_source* This = static_cast<audio_osx_source*>(inUserData);
724   AudioBufferList* l_inputABL = This->d_InputBuffer;
725   UInt32 totalInputBufferSizeBytes = ((*ioNumberDataPackets) * sizeof (float));
726   int counter = This->d_n_deviceChannels;
727   ioData->mNumberBuffers = This->d_n_deviceChannels;
728   This->d_n_ActualInputFrames = (*ioNumberDataPackets);
729
730 #if _OSX_AU_DEBUG_
731   fprintf (stderr, "cc1: io#DP = %ld, TIBSB = %ld, #C = %d\n",
732            *ioNumberDataPackets, totalInputBufferSizeBytes, counter);
733 #endif
734
735   while (--counter >= 0)  {
736     AudioBuffer* l_ioD_AB = &(ioData->mBuffers[counter]);
737     l_ioD_AB->mNumberChannels = 1;
738     l_ioD_AB->mData = (float*)(l_inputABL->mBuffers[counter].mData);
739     l_ioD_AB->mDataByteSize = totalInputBufferSizeBytes;
740   }
741
742 #if _OSX_AU_DEBUG_
743   fprintf (stderr, "cc2: Returning.\n");
744 #endif
745
746   return (noErr);
747 }
748
749 OSStatus
750 audio_osx_source::AUInputCallback (void* inRefCon,
751                                    AudioUnitRenderActionFlags* ioActionFlags,
752                                    const AudioTimeStamp* inTimeStamp,
753                                    UInt32 inBusNumber,
754                                    UInt32 inNumberFrames,
755                                    AudioBufferList* ioData)
756 {
757   OSStatus err = noErr;
758   audio_osx_source* This = static_cast<audio_osx_source*>(inRefCon);
759
760   This->d_internal->lock ();
761
762 #if _OSX_AU_DEBUG_
763   fprintf (stderr, "cb0: in#F = %4ld, inBN = %ld, SC = %4ld\n",
764            inNumberFrames, inBusNumber, This->d_queueSampleCount);
765 #endif
766
767 // Get the new audio data from the input device
768
769   err = AudioUnitRender (This->d_InputAU,
770                          ioActionFlags,
771                          inTimeStamp,
772                          1, //inBusNumber,
773                          inNumberFrames,
774                          This->d_InputBuffer);
775   CheckErrorAndThrow (err, "AudioUnitRender",
776                       "audio_osx_source::AUInputCallback");
777
778   UInt32 AvailableInputFrames = inNumberFrames;
779   This->d_n_AvailableInputFrames = inNumberFrames;
780
781 // get the number of actual output frames,
782 // either via converting the buffer or not
783
784   UInt32 ActualOutputFrames;
785
786   if (This->d_passThrough == true) {
787     ActualOutputFrames = AvailableInputFrames;
788   } else {
789     UInt32 AvailableInputBytes = AvailableInputFrames * sizeof (float);
790     UInt32 AvailableOutputBytes = AvailableInputBytes;
791     UInt32 AvailableOutputFrames = AvailableOutputBytes / sizeof (float);
792     UInt32 propertySize = sizeof (AvailableOutputBytes);
793     err = AudioConverterGetProperty (This->d_AudioConverter,
794                    kAudioConverterPropertyCalculateOutputBufferSize,
795                                      &propertySize,
796                                      &AvailableOutputBytes);
797     CheckErrorAndThrow (err, "AudioConverterGetProperty CalculateOutputBufferSize", "audio_osx_source::audio_osx_source");
798
799     AvailableOutputFrames = AvailableOutputBytes / sizeof (float);
800
801 #if 0
802 // when decimating too much, the output sounds warbly due to
803 // fluctuating # of output frames
804 // This should not be a surprise, but there's probably some
805 // clever programming that could lessed the effect ...
806 // like finding the "ideal" # of output frames, and keeping
807 // that number constant no matter the # of input frames
808     UInt32 l_InputBytes = AvailableOutputBytes;
809     propertySize = sizeof (AvailableOutputBytes);
810     err = AudioConverterGetProperty (This->d_AudioConverter,
811                      kAudioConverterPropertyCalculateInputBufferSize,
812                                      &propertySize,
813                                      &l_InputBytes);
814     CheckErrorAndThrow (err, "AudioConverterGetProperty CalculateInputBufferSize", "audio_osx_source::audio_osx_source");
815
816     if (l_InputBytes < AvailableInputBytes) {
817 // OK to zero pad the input a little
818       AvailableOutputFrames += 1;
819       AvailableOutputBytes = AvailableOutputFrames * sizeof (float);
820     }
821 #endif
822
823 #if _OSX_AU_DEBUG_
824     fprintf (stderr, "cb1:  avail: #IF = %ld, #OF = %ld\n",
825              AvailableInputFrames, AvailableOutputFrames);
826 #endif
827     ActualOutputFrames = AvailableOutputFrames;
828
829 // convert the data to the correct rate
830 // on input, ActualOutputFrames is the number of available output frames
831
832     err = AudioConverterFillComplexBuffer (This->d_AudioConverter,
833            (AudioConverterComplexInputDataProc)(This->ConverterCallback),
834                                            inRefCon,
835                                            &ActualOutputFrames,
836                                            This->d_OutputBuffer,
837                                            NULL);
838     CheckErrorAndThrow (err, "AudioConverterFillComplexBuffer",
839                         "audio_osx_source::AUInputCallback");
840
841 // on output, ActualOutputFrames is the actual number of output frames
842
843 #if _OSX_AU_DEBUG_
844     fprintf (stderr, "cb2: actual: #IF = %ld, #OF = %ld\n",
845              This->d_n_ActualInputFrames, AvailableOutputFrames);
846     if (This->d_n_ActualInputFrames != AvailableInputFrames)
847       fprintf (stderr, "cb2.1: avail#IF = %ld, actual#IF = %ld\n",
848                AvailableInputFrames, This->d_n_ActualInputFrames);
849 #endif
850   }
851
852 // add the output frames to the buffers' queue, checking for overflow
853
854   int l_counter = This->d_n_user_channels;
855   int res = 0;
856
857   while (--l_counter >= 0) {
858     float* inBuffer = (float*) This->d_OutputBuffer->mBuffers[l_counter].mData;
859
860 #if _OSX_AU_DEBUG_
861   fprintf (stderr, "cb3: enqueuing audio data.\n");
862 #endif
863
864     int l_res = This->d_buffers[l_counter]->enqueue (inBuffer, ActualOutputFrames);
865     if (l_res == -1)
866       res = -1;
867   }
868
869   if (res == -1) {
870 // data coming in too fast
871 // drop oldest buffer
872     fputs ("aO", stderr);
873     fflush (stderr);
874 // set the local number of samples available to the max
875     This->d_queueSampleCount = This->d_buffers[0]->buffer_length_items ();
876   } else {
877 // keep up the local sample count
878     This->d_queueSampleCount += ActualOutputFrames;
879   }
880
881 #if _OSX_AU_DEBUG_
882   fprintf (stderr, "cb4: #OI = %4ld, #Cnt = %4ld, mSC = %ld, \n",
883            ActualOutputFrames, This->d_queueSampleCount,
884            This->d_max_sample_count);
885 #endif
886
887 // signal that data is available, if appropraite
888   This->d_cond_data->signal ();
889
890 #if _OSX_AU_DEBUG_
891   fprintf (stderr, "cb5: releasing internal mutex.\n");
892 #endif
893
894 // release control to allow for other processing parts to run
895   This->d_internal->unlock ();
896
897 #if _OSX_AU_DEBUG_
898   fprintf (stderr, "cb6: returning.\n");
899 #endif
900
901   return (err);
902 }
903
904 void
905 audio_osx_source::SetDefaultInputDeviceAsCurrent
906 ()
907 {
908 // set the default input device
909   AudioDeviceID deviceID;
910   UInt32 dataSize = sizeof (AudioDeviceID);
911   AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
912                             &dataSize,
913                             &deviceID);
914   OSStatus err = AudioUnitSetProperty (d_InputAU,
915                                        kAudioOutputUnitProperty_CurrentDevice,
916                                        kAudioUnitScope_Global,
917                                        0,
918                                        &deviceID,
919                                        sizeof (AudioDeviceID));
920   CheckErrorAndThrow (err, "AudioUnitSetProperty Current Device",
921                       "audio_osx_source::SetDefaultInputDeviceAsCurrent");
922 }
923
924 #if _OSX_DO_LISTENERS_
925 OSStatus
926 audio_osx_source::HardwareListener
927 (AudioHardwarePropertyID inPropertyID, 
928  void *inClientData)
929 {
930   OSStatus err = noErr;
931   audio_osx_source* This = static_cast<audio_osx_source*>(inClientData);
932
933   fprintf (stderr, "a_o_s::HardwareListener\n");
934
935 // set the new default hardware input device for use by our AU
936
937   This->SetDefaultInputDeviceAsCurrent ();
938
939 // reset the converter to tell it that the stream has changed
940
941   err = AudioConverterReset (This->d_AudioConverter);
942   CheckErrorAndThrow (err, "AudioConverterReset",
943                       "audio_osx_source::UnitListener");
944
945   return (err);
946 }
947
948 OSStatus
949 audio_osx_source::UnitListener
950 (void *inRefCon,
951  AudioUnit ci,
952  AudioUnitPropertyID inID,
953  AudioUnitScope inScope,
954  AudioUnitElement inElement)
955 {
956   OSStatus err = noErr;
957   audio_osx_source* This = static_cast<audio_osx_source*>(inRefCon);
958   AudioStreamBasicDescription asbd;                     
959
960   fprintf (stderr, "a_o_s::UnitListener\n");
961
962 // get the converter's input ASBD (for printing)
963
964   UInt32 propertySize = sizeof (asbd);
965   err = AudioConverterGetProperty (This->d_AudioConverter,
966                                    kAudioConverterCurrentInputStreamDescription,
967                                    &propertySize,
968                                    &asbd);
969   CheckErrorAndThrow (err, "AudioConverterGetProperty "
970                       "CurrentInputStreamDescription",
971                       "audio_osx_source::UnitListener");
972
973   fprintf (stderr, "UnitListener: Input Source changed.\n"
974            "Old Source Output Info:\n");
975   PrintStreamDesc (&asbd);
976
977 // get the new input unit's output ASBD
978
979   propertySize = sizeof (asbd);
980   err = AudioUnitGetProperty (This->d_InputAU,
981                               kAudioUnitProperty_StreamFormat,
982                               kAudioUnitScope_Output, 1,
983                               &asbd, &propertySize);
984   CheckErrorAndThrow (err, "AudioUnitGetProperty StreamFormat",
985                       "audio_osx_source::UnitListener");
986
987   fprintf (stderr, "New Source Output Info:\n");
988   PrintStreamDesc (&asbd);
989
990 // set the converter's input ASBD to this
991
992   err = AudioConverterSetProperty (This->d_AudioConverter,
993                                    kAudioConverterCurrentInputStreamDescription,
994                                    propertySize,
995                                    &asbd);
996   CheckErrorAndThrow (err, "AudioConverterSetProperty "
997                       "CurrentInputStreamDescription",
998                       "audio_osx_source::UnitListener");
999
1000 // reset the converter to tell it that the stream has changed
1001
1002   err = AudioConverterReset (This->d_AudioConverter);
1003   CheckErrorAndThrow (err, "AudioConverterReset",
1004                       "audio_osx_source::UnitListener");
1005
1006   return (err);
1007 }
1008 #endif