Imported Upstream version 3.0
[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 2, 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", 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, "---- 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, "---- 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_internal = new mld_mutex ();
427   if (d_internal == NULL)
428     CheckErrorAndThrow (errno, "new mld_mutex (internal)",
429                         "audio_osx_source::audio_osx_source");
430
431   d_cond_data = new mld_condition ();
432   if (d_cond_data == NULL)
433     CheckErrorAndThrow (errno, "new mld_condition (data)",
434                         "audio_osx_source::audio_osx_source");
435
436 // initialize the AU for input
437
438   err = AudioUnitInitialize (d_InputAU);
439   CheckErrorAndThrow (err, "AudioUnitInitialize",
440                       "audio_osx_source::audio_osx_source");
441
442 #if _OSX_AU_DEBUG_
443   fprintf (stderr, "audio_osx_source Parameters:\n");
444   fprintf (stderr, "  Device Sample Rate is %g\n", d_deviceSampleRate);
445   fprintf (stderr, "  User Sample Rate is %g\n", d_outputSampleRate);
446   fprintf (stderr, "  Max Sample Count is %ld\n", d_max_sample_count);
447   fprintf (stderr, "  # Device Channels is %ld\n", d_n_deviceChannels);
448   fprintf (stderr, "  # Max Channels is %ld\n", d_n_max_channels);
449   fprintf (stderr, "  Device Buffer Size is Frames = %ld\n",
450            d_deviceBufferSizeFrames);
451   fprintf (stderr, "  Lead Size is Frames = %ld\n",
452            d_leadSizeFrames);
453   fprintf (stderr, "  Trail Size is Frames = %ld\n",
454            d_trailSizeFrames);
455   fprintf (stderr, "  Input Buffer Size is Frames = %ld\n",
456            d_inputBufferSizeFrames);
457   fprintf (stderr, "  Output Buffer Size is Frames = %ld\n",
458            d_outputBufferSizeFrames);
459 #endif
460 }
461
462 void
463 audio_osx_source::AllocAudioBufferList (AudioBufferList** t_ABL,
464                                         UInt32 n_channels,
465                                         UInt32 bufferSizeBytes)
466 {
467   FreeAudioBufferList (t_ABL);
468   UInt32 propertySize = (offsetof (AudioBufferList, mBuffers[0]) +
469                          (sizeof (AudioBuffer) * n_channels));
470   *t_ABL = (AudioBufferList*) calloc (1, propertySize);
471   (*t_ABL)->mNumberBuffers = n_channels;
472
473   int counter = n_channels;
474
475   while (--counter >= 0) {
476     (*t_ABL)->mBuffers[counter].mNumberChannels = 1;
477     (*t_ABL)->mBuffers[counter].mDataByteSize = bufferSizeBytes;
478     (*t_ABL)->mBuffers[counter].mData = calloc (1, bufferSizeBytes);
479   }
480 }
481
482 void
483 audio_osx_source::FreeAudioBufferList (AudioBufferList** t_ABL)
484 {
485 // free pre-allocated audio buffer, if it exists
486   if (*t_ABL != NULL) {
487     int counter = (*t_ABL)->mNumberBuffers;
488     while (--counter >= 0)
489       free ((*t_ABL)->mBuffers[counter].mData);
490     free (*t_ABL);
491     (*t_ABL) = 0;
492   }
493 }
494
495 bool audio_osx_source::IsRunning ()
496 {
497   UInt32 AURunning = 0, AUSize = sizeof (UInt32);
498
499   OSStatus err = AudioUnitGetProperty (d_InputAU,
500                                        kAudioOutputUnitProperty_IsRunning,
501                                        kAudioUnitScope_Global,
502                                        0,
503                                        &AURunning,
504                                        &AUSize);
505   CheckErrorAndThrow (err, "AudioUnitGetProperty IsRunning",
506                       "audio_osx_source::IsRunning");
507
508   return (AURunning);
509 }
510
511 bool audio_osx_source::start ()
512 {
513   if (! IsRunning ()) {
514     OSStatus err = AudioOutputUnitStart (d_InputAU);
515     CheckErrorAndThrow (err, "AudioOutputUnitStart",
516                         "audio_osx_source::start");
517   }
518
519   return (true);
520 }
521
522 bool audio_osx_source::stop ()
523 {
524   if (IsRunning ()) {
525     OSStatus err = AudioOutputUnitStop (d_InputAU);
526     CheckErrorAndThrow (err, "AudioOutputUnitStart",
527                         "audio_osx_source::stop");
528     for (UInt32 n = 0; n < d_n_user_channels; n++) {
529       d_buffers[n]->abort ();
530     }
531   }
532
533   return (true);
534 }
535
536 audio_osx_source::~audio_osx_source ()
537 {
538   OSStatus err = noErr;
539
540 // stop the AudioUnit
541   stop();
542
543 #if _OSX_DO_LISTENERS_
544 // remove the listeners
545
546   err = AudioUnitRemovePropertyListener
547     (d_InputAU,
548      kAudioUnitProperty_StreamFormat,
549      (AudioUnitPropertyListenerProc) UnitListener);
550   CheckError (err, "~audio_osx_source: AudioUnitRemovePropertyListener");
551
552   err = AudioHardwareRemovePropertyListener
553     (kAudioHardwarePropertyDefaultInputDevice,
554      (AudioHardwarePropertyListenerProc) HardwareListener);
555   CheckError (err, "~audio_osx_source: AudioHardwareRemovePropertyListener");
556 #endif
557
558 // free pre-allocated audio buffers
559   FreeAudioBufferList (&d_InputBuffer);
560
561   if (d_passThrough == false) {
562     err = AudioConverterDispose (d_AudioConverter);
563     CheckError (err, "~audio_osx_source: AudioConverterDispose");
564     FreeAudioBufferList (&d_OutputBuffer);
565   }
566
567 // remove the audio unit
568   err = AudioUnitUninitialize (d_InputAU);
569   CheckError (err, "~audio_osx_source: AudioUnitUninitialize");
570
571   err = CloseComponent (d_InputAU);
572   CheckError (err, "~audio_osx_source: CloseComponent");
573
574 // empty and delete the queues
575   for (UInt32 n = 0; n < d_n_max_channels; n++) {
576     delete d_buffers[n];
577     d_buffers[n] = 0;
578   }
579   delete [] d_buffers;
580   d_buffers = 0;
581
582 // close and delete the control stuff
583   delete d_internal;
584   delete d_cond_data;
585 }
586
587 audio_osx_source_sptr
588 audio_osx_make_source (int sampling_freq,
589                        const std::string device_name,
590                        bool do_block,
591                        int channel_config,
592                        int max_sample_count)
593 {
594   return audio_osx_source_sptr (new audio_osx_source (sampling_freq,
595                                                       device_name,
596                                                       do_block,
597                                                       channel_config,
598                                                       max_sample_count));
599 }
600
601 bool
602 audio_osx_source::check_topology (int ninputs, int noutputs)
603 {
604 // check # inputs to make sure it's valid
605   if (ninputs != 0) {
606     fprintf (stderr, "audio_osx_source::check_topology(): "
607              "number of input streams provided (%d) should be 0.\n",
608              ninputs);
609     throw std::runtime_error ("audio_osx_source::check_topology()");
610   }
611
612 // check # outputs to make sure it's valid
613   if ((noutputs < 1) | (noutputs > (int) d_n_max_channels)) {
614     fprintf (stderr, "audio_osx_source::check_topology(): "
615              "number of output streams provided (%d) should be in "
616              "[1,%ld] for the selected audio device.\n",
617              noutputs, d_n_max_channels);
618     throw std::runtime_error ("audio_osx_source::check_topology()");
619   }
620
621 // save the actual number of output (user) channels
622   d_n_user_channels = noutputs;
623
624 #if _OSX_AU_DEBUG_
625   fprintf (stderr, "chk_topo: Actual # user output channels = %d\n",
626            noutputs);
627 #endif
628
629   return (true);
630 }
631
632 int
633 audio_osx_source::work (int noutput_items,
634                         gr_vector_const_void_star &input_items,
635                         gr_vector_void_star &output_items)
636 {
637 // acquire control to do processing here only
638   d_internal->wait ();
639
640 #if _OSX_AU_DEBUG_
641   fprintf (stderr, "work1: SC = %4ld, #OI = %4d, #Chan = %ld\n",
642            d_queueSampleCount, noutput_items, output_items.size());
643 #endif
644
645 // ?: always block until there is something to output from the source
646 //    or return anything that is available, even if it's less than desired?
647
648   UInt32 actual_noutput_items = noutput_items;
649
650   if (d_queueSampleCount < actual_noutput_items) {
651     if (d_queueSampleCount == 0) {
652 // no data; do_block decides what to do
653       if (d_do_block == true) {
654         while (d_queueSampleCount == 0) {
655 // release control so-as to allow data to be retrieved
656           d_internal->post ();
657 // block until there is data to return
658           d_cond_data->wait ();
659 // the condition's signal() was called; acquire control
660 // to keep thread safe
661           d_internal->wait ();
662         }
663       } else {
664 // not enough data & not blocking; return nothing
665         return (0);
666       }
667     }
668     actual_noutput_items = d_queueSampleCount;
669   }
670
671   int l_counter = (int) output_items.size();
672
673 // get the items from the circular buffers
674   while (--l_counter >= 0) {
675     UInt32 t_n_output_items = actual_noutput_items;
676     d_buffers[l_counter]->dequeue ((float*) output_items[l_counter],
677                                    &t_n_output_items);
678     if (t_n_output_items != actual_noutput_items) {
679       fprintf (stderr, "audio_osx_source::work(): "
680                "number of available items changing "
681                "unexpectedly; expecting %ld, got %ld.\n",
682                actual_noutput_items, t_n_output_items);
683       throw std::runtime_error ("audio_osx_source::work()");
684     }
685   }
686
687   d_queueSampleCount -= actual_noutput_items;
688
689 #if _OSX_AU_DEBUG_
690   fprintf (stderr, "work2: SC = %4ld, act#OI = %4ld\n",
691            d_queueSampleCount, actual_noutput_items);
692 #endif
693
694 // release control to allow for other processing parts to run
695   d_internal->post ();
696
697   return (actual_noutput_items);
698 }
699
700 OSStatus
701 audio_osx_source::ConverterCallback (AudioConverterRef inAudioConverter,
702                                      UInt32* ioNumberDataPackets,
703                                      AudioBufferList* ioData,
704                                      AudioStreamPacketDescription** ioASPD,
705                                      void* inUserData)
706 {
707 // take current device buffers and copy them to the tail of the input buffers
708 // the lead buffer is already there in the first d_leadSizeFrames slots
709
710   audio_osx_source* This = static_cast<audio_osx_source*>(inUserData);
711   AudioBufferList* l_inputABL = This->d_InputBuffer;
712   UInt32 totalInputBufferSizeBytes = ((*ioNumberDataPackets) * sizeof (float));
713   int counter = This->d_n_deviceChannels;
714   ioData->mNumberBuffers = This->d_n_deviceChannels;
715   This->d_n_ActualInputFrames = (*ioNumberDataPackets);
716
717 #if _OSX_AU_DEBUG_
718   fprintf (stderr, "cc1: io#DP = %ld, TIBSB = %ld, #C = %d\n",
719            *ioNumberDataPackets, totalInputBufferSizeBytes, counter);
720 #endif
721
722   while (--counter >= 0)  {
723     AudioBuffer* l_ioD_AB = &(ioData->mBuffers[counter]);
724     l_ioD_AB->mNumberChannels = 1;
725     l_ioD_AB->mData = (float*)(l_inputABL->mBuffers[counter].mData);
726     l_ioD_AB->mDataByteSize = totalInputBufferSizeBytes;
727   }
728
729   return (noErr);
730 }
731
732 OSStatus
733 audio_osx_source::AUInputCallback (void* inRefCon,
734                                    AudioUnitRenderActionFlags* ioActionFlags,
735                                    const AudioTimeStamp* inTimeStamp,
736                                    UInt32 inBusNumber,
737                                    UInt32 inNumberFrames,
738                                    AudioBufferList* ioData)
739 {
740   OSStatus err = noErr;
741   audio_osx_source* This = static_cast<audio_osx_source*>(inRefCon);
742
743   This->d_internal->wait ();
744
745 #if _OSX_AU_DEBUG_
746   fprintf (stderr, "cb0: in#F = %4ld, inBN = %ld, SC = %4ld\n",
747            inNumberFrames, inBusNumber, This->d_queueSampleCount);
748 #endif
749
750 // Get the new audio data from the input device
751
752   err = AudioUnitRender (This->d_InputAU,
753                          ioActionFlags,
754                          inTimeStamp,
755                          1, //inBusNumber,
756                          inNumberFrames,
757                          This->d_InputBuffer);
758   CheckErrorAndThrow (err, "AudioUnitRender",
759                       "audio_osx_source::AUInputCallback");
760
761   UInt32 AvailableInputFrames = inNumberFrames;
762   This->d_n_AvailableInputFrames = inNumberFrames;
763
764 // get the number of actual output frames,
765 // either via converting the buffer or not
766
767   UInt32 ActualOutputFrames;
768
769   if (This->d_passThrough == true) {
770     ActualOutputFrames = AvailableInputFrames;
771   } else {
772     UInt32 AvailableInputBytes = AvailableInputFrames * sizeof (float);
773     UInt32 AvailableOutputBytes = AvailableInputBytes;
774     UInt32 AvailableOutputFrames = AvailableOutputBytes / sizeof (float);
775     UInt32 propertySize = sizeof (AvailableOutputBytes);
776     err = AudioConverterGetProperty (This->d_AudioConverter,
777                    kAudioConverterPropertyCalculateOutputBufferSize,
778                                      &propertySize,
779                                      &AvailableOutputBytes);
780     CheckErrorAndThrow (err, "AudioConverterGetProperty CalculateOutputBufferSize", "audio_osx_source::audio_osx_source");
781
782     AvailableOutputFrames = AvailableOutputBytes / sizeof (float);
783
784 #if 0
785 // when decimating too much, the output sounds warbly due to
786 // fluctuating # of output frames
787 // This should not be a surprise, but there's probably some
788 // clever programming that could lessed the effect ...
789 // like finding the "ideal" # of output frames, and keeping
790 // that number constant no matter the # of input frames
791     UInt32 l_InputBytes = AvailableOutputBytes;
792     propertySize = sizeof (AvailableOutputBytes);
793     err = AudioConverterGetProperty (This->d_AudioConverter,
794                      kAudioConverterPropertyCalculateInputBufferSize,
795                                      &propertySize,
796                                      &l_InputBytes);
797     CheckErrorAndThrow (err, "AudioConverterGetProperty CalculateInputBufferSize", "audio_osx_source::audio_osx_source");
798
799     if (l_InputBytes < AvailableInputBytes) {
800 // OK to zero pad the input a little
801       AvailableOutputFrames += 1;
802       AvailableOutputBytes = AvailableOutputFrames * sizeof (float);
803     }
804 #endif
805
806 #if _OSX_AU_DEBUG_
807     fprintf (stderr, "cb1:  avail: #IF = %ld, #OF = %ld\n",
808              AvailableInputFrames, AvailableOutputFrames);
809 #endif
810     ActualOutputFrames = AvailableOutputFrames;
811
812 // convert the data to the correct rate
813 // on input, ActualOutputFrames is the number of available output frames
814
815     err = AudioConverterFillComplexBuffer (This->d_AudioConverter,
816            (AudioConverterComplexInputDataProc)(This->ConverterCallback),
817                                            inRefCon,
818                                            &ActualOutputFrames,
819                                            This->d_OutputBuffer,
820                                            NULL);
821     CheckErrorAndThrow (err, "AudioConverterFillComplexBuffer",
822                         "audio_osx_source::AUInputCallback");
823
824 // on output, ActualOutputFrames is the actual number of output frames
825
826 #if _OSX_AU_DEBUG_
827     fprintf (stderr, "cb2: actual: #IF = %ld, #OF = %ld\n",
828              This->d_n_ActualInputFrames, AvailableOutputFrames);
829     if (This->d_n_ActualInputFrames != AvailableInputFrames)
830       fprintf (stderr, "cb2.1: avail#IF = %ld, actual#IF = %ld\n",
831                AvailableInputFrames, This->d_n_ActualInputFrames);
832 #endif
833   }
834
835 // add the output frames to the buffers' queue, checking for overflow
836
837   int l_counter = This->d_n_user_channels;
838   int res = 0;
839
840   while (--l_counter >= 0) {
841     float* inBuffer = (float*) This->d_OutputBuffer->mBuffers[l_counter].mData;
842     int l_res = This->d_buffers[l_counter]->enqueue (inBuffer, ActualOutputFrames);
843     if (l_res == -1)
844       res = -1;
845   }
846
847   if (res == -1) {
848 // data coming in too fast
849 // drop oldest buffer
850     fputs ("aO", stderr);
851     fflush (stderr);
852 // set the local number of samples available to the max
853     This->d_queueSampleCount = This->d_buffers[0]->buffer_length_items ();
854   } else {
855 // keep up the local sample count
856     This->d_queueSampleCount += ActualOutputFrames;
857   }
858
859 #if _OSX_AU_DEBUG_
860   fprintf (stderr, "cb5: #OI = %4ld, #Cnt = %4ld, mSC = %ld, \n",
861            ActualOutputFrames, This->d_queueSampleCount,
862            This->d_max_sample_count);
863 #endif
864
865 // signal that data is available, if appropraite
866   This->d_cond_data->signal ();
867
868 // release control to allow for other processing parts to run
869   This->d_internal->post ();
870
871   return (err);
872 }
873
874 void
875 audio_osx_source::SetDefaultInputDeviceAsCurrent
876 ()
877 {
878 // set the default input device
879   AudioDeviceID deviceID;
880   UInt32 dataSize = sizeof (AudioDeviceID);
881   AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
882                             &dataSize,
883                             &deviceID);
884   OSStatus err = AudioUnitSetProperty (d_InputAU,
885                                        kAudioOutputUnitProperty_CurrentDevice,
886                                        kAudioUnitScope_Global,
887                                        0,
888                                        &deviceID,
889                                        sizeof (AudioDeviceID));
890   CheckErrorAndThrow (err, "AudioUnitSetProperty Current Device",
891                       "audio_osx_source::SetDefaultInputDeviceAsCurrent");
892 }
893
894 #if _OSX_DO_LISTENERS_
895 OSStatus
896 audio_osx_source::HardwareListener
897 (AudioHardwarePropertyID inPropertyID, 
898  void *inClientData)
899 {
900   OSStatus err = noErr;
901   audio_osx_source* This = static_cast<audio_osx_source*>(inClientData);
902
903   fprintf (stderr, "a_o_s::HardwareListener\n");
904
905 // set the new default hardware input device for use by our AU
906
907   This->SetDefaultInputDeviceAsCurrent ();
908
909 // reset the converter to tell it that the stream has changed
910
911   err = AudioConverterReset (This->d_AudioConverter);
912   CheckErrorAndThrow (err, "AudioConverterReset",
913                       "audio_osx_source::UnitListener");
914
915   return (err);
916 }
917
918 OSStatus
919 audio_osx_source::UnitListener
920 (void *inRefCon,
921  AudioUnit ci,
922  AudioUnitPropertyID inID,
923  AudioUnitScope inScope,
924  AudioUnitElement inElement)
925 {
926   OSStatus err = noErr;
927   audio_osx_source* This = static_cast<audio_osx_source*>(inRefCon);
928   AudioStreamBasicDescription asbd;                     
929
930   fprintf (stderr, "a_o_s::UnitListener\n");
931
932 // get the converter's input ASBD (for printing)
933
934   UInt32 propertySize = sizeof (asbd);
935   err = AudioConverterGetProperty (This->d_AudioConverter,
936                                    kAudioConverterCurrentInputStreamDescription,
937                                    &propertySize,
938                                    &asbd);
939   CheckErrorAndThrow (err, "AudioConverterGetProperty "
940                       "CurrentInputStreamDescription",
941                       "audio_osx_source::UnitListener");
942
943   fprintf (stderr, "UnitListener: Input Source changed.\n"
944            "Old Source Output Info:\n");
945   PrintStreamDesc (&asbd);
946
947 // get the new input unit's output ASBD
948
949   propertySize = sizeof (asbd);
950   err = AudioUnitGetProperty (This->d_InputAU,
951                               kAudioUnitProperty_StreamFormat,
952                               kAudioUnitScope_Output, 1,
953                               &asbd, &propertySize);
954   CheckErrorAndThrow (err, "AudioUnitGetProperty StreamFormat",
955                       "audio_osx_source::UnitListener");
956
957   fprintf (stderr, "New Source Output Info:\n");
958   PrintStreamDesc (&asbd);
959
960 // set the converter's input ASBD to this
961
962   err = AudioConverterSetProperty (This->d_AudioConverter,
963                                    kAudioConverterCurrentInputStreamDescription,
964                                    propertySize,
965                                    &asbd);
966   CheckErrorAndThrow (err, "AudioConverterSetProperty "
967                       "CurrentInputStreamDescription",
968                       "audio_osx_source::UnitListener");
969
970 // reset the converter to tell it that the stream has changed
971
972   err = AudioConverterReset (This->d_AudioConverter);
973   CheckErrorAndThrow (err, "AudioConverterReset",
974                       "audio_osx_source::UnitListener");
975
976   return (err);
977 }
978 #endif