~ubuntu-branches/ubuntu/trusty/teeworlds/trusty-updates

« back to all changes in this revision

Viewing changes to src/engine/external/portaudio/src/hostapi/coreaudio/pa_mac_core.c

  • Committer: Bazaar Package Importer
  • Author(s): Jack Coulter
  • Date: 2008-04-13 18:48:12 UTC
  • Revision ID: james.westby@ubuntu.com-20080413184812-efc80waq2er6p1bs
Tags: upstream-0.4.2
ImportĀ upstreamĀ versionĀ 0.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Implementation of the PortAudio API for Apple AUHAL
 
3
 *
 
4
 * PortAudio Portable Real-Time Audio Library
 
5
 * Latest Version at: http://www.portaudio.com
 
6
 *
 
7
 * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
 
8
 * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
 
9
 *
 
10
 * Dominic's code was based on code by Phil Burk, Darren Gibbs,
 
11
 * Gord Peters, Stephane Letz, and Greg Pfiel.
 
12
 *
 
13
 * The following people also deserve acknowledgements:
 
14
 *
 
15
 * Olivier Tristan for feedback and testing
 
16
 * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
 
17
 * interface.
 
18
 * 
 
19
 *
 
20
 * Based on the Open Source API proposed by Ross Bencina
 
21
 * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
 
22
 *
 
23
 * Permission is hereby granted, free of charge, to any person obtaining
 
24
 * a copy of this software and associated documentation files
 
25
 * (the "Software"), to deal in the Software without restriction,
 
26
 * including without limitation the rights to use, copy, modify, merge,
 
27
 * publish, distribute, sublicense, and/or sell copies of the Software,
 
28
 * and to permit persons to whom the Software is furnished to do so,
 
29
 * subject to the following conditions:
 
30
 *
 
31
 * The above copyright notice and this permission notice shall be
 
32
 * included in all copies or substantial portions of the Software.
 
33
 *
 
34
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
35
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
36
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 
37
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 
38
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 
39
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
40
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
41
 */
 
42
 
 
43
/*
 
44
 * The text above constitutes the entire PortAudio license; however, 
 
45
 * the PortAudio community also makes the following non-binding requests:
 
46
 *
 
47
 * Any person wishing to distribute modifications to the Software is
 
48
 * requested to send the modifications to the original developer so that
 
49
 * they can be incorporated into the canonical version. It is also 
 
50
 * requested that these non-binding requests be included along with the 
 
51
 * license above.
 
52
 */
 
53
 
 
54
/**
 
55
 @file pa_mac_core
 
56
 @ingroup hostapi_src
 
57
 @author Bjorn Roche
 
58
 @brief AUHAL implementation of PortAudio
 
59
*/
 
60
 
 
61
/* FIXME: not all error conditions call PaUtil_SetLastHostErrorInfo()
 
62
 * PaMacCore_SetError() will do this.
 
63
 */
 
64
 
 
65
#include "pa_mac_core_internal.h"
 
66
 
 
67
#include <string.h> /* strlen(), memcmp() etc. */
 
68
#include <libkern/OSAtomic.h>
 
69
 
 
70
#include "pa_mac_core.h"
 
71
#include "pa_mac_core_utilities.h"
 
72
#include "pa_mac_core_blocking.h"
 
73
 
 
74
 
 
75
#ifdef __cplusplus
 
76
extern "C"
 
77
{
 
78
#endif /* __cplusplus */
 
79
 
 
80
/* prototypes for functions declared in this file */
 
81
 
 
82
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
 
83
 
 
84
/*
 
85
 * Function declared in pa_mac_core.h. Sets up a PaMacCoreStreamInfoStruct
 
86
 * with the requested flags and initializes channel map.
 
87
 */
 
88
void PaMacCore_SetupStreamInfo(  PaMacCoreStreamInfo *data, const unsigned long flags )
 
89
{
 
90
   bzero( data, sizeof( PaMacCoreStreamInfo ) );
 
91
   data->size = sizeof( PaMacCoreStreamInfo );
 
92
   data->hostApiType = paCoreAudio;
 
93
   data->version = 0x01;
 
94
   data->flags = flags;
 
95
   data->channelMap = NULL;
 
96
   data->channelMapSize = 0;
 
97
}
 
98
 
 
99
/*
 
100
 * Function declared in pa_mac_core.h. Adds channel mapping to a PaMacCoreStreamInfoStruct
 
101
 */
 
102
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, const unsigned long channelMapSize )
 
103
{
 
104
   data->channelMap = channelMap;
 
105
   data->channelMapSize = channelMapSize;
 
106
}
 
107
static char *channelName = NULL;
 
108
static int channelNameSize = 0;
 
109
static bool ensureChannelNameSize( int size )
 
110
{
 
111
   if( size >= channelNameSize ) {
 
112
      free( channelName );
 
113
      channelName = (char *) malloc( ( channelNameSize = size ) + 1 );
 
114
      if( !channelName ) {
 
115
         channelNameSize = 0;
 
116
         return false;
 
117
      }
 
118
   }
 
119
   return true;
 
120
}
 
121
/*
 
122
 * Function declared in pa_mac_core.h. retrives channel names.
 
123
 */
 
124
const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input )
 
125
{
 
126
   struct PaUtilHostApiRepresentation *hostApi;
 
127
   PaError err;
 
128
   OSStatus error;
 
129
   err = PaUtil_GetHostApiRepresentation( &hostApi, paCoreAudio );
 
130
   assert(err == paNoError);
 
131
   if( err != paNoError )
 
132
      return NULL;
 
133
   PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi;
 
134
   AudioDeviceID hostApiDevice = macCoreHostApi->devIds[device];
 
135
 
 
136
   UInt32 size = 0;
 
137
 
 
138
   error = AudioDeviceGetPropertyInfo( hostApiDevice,
 
139
                                       channelIndex + 1,
 
140
                                       input,
 
141
                                       kAudioDevicePropertyChannelName,
 
142
                                       &size,
 
143
                                       NULL );
 
144
   if( error ) {
 
145
      //try the CFString
 
146
      CFStringRef name;
 
147
      bool isDeviceName = false;
 
148
      size = sizeof( name );
 
149
      error = AudioDeviceGetProperty( hostApiDevice,
 
150
                                      channelIndex + 1,
 
151
                                      input,
 
152
                                      kAudioDevicePropertyChannelNameCFString,
 
153
                                      &size,
 
154
                                      &name );
 
155
      if( error ) { //as a last-ditch effort, get the device name. Later we'll append the channel number.
 
156
         size = sizeof( name );
 
157
         error = AudioDeviceGetProperty( hostApiDevice,
 
158
                                      channelIndex + 1,
 
159
                                      input,
 
160
                                      kAudioDevicePropertyDeviceNameCFString,
 
161
                                      &size,
 
162
                                      &name );
 
163
         if( error )
 
164
            return NULL;
 
165
         isDeviceName = true;
 
166
      }
 
167
      if( isDeviceName ) {
 
168
         name = CFStringCreateWithFormat( NULL, NULL, CFSTR( "%@: %d"), name, channelIndex + 1 );
 
169
      }
 
170
 
 
171
      CFIndex length = CFStringGetLength(name);
 
172
      while( ensureChannelNameSize( length * sizeof(UniChar) + 1 ) ) {
 
173
         if( CFStringGetCString( name, channelName, channelNameSize, kCFStringEncodingUTF8 ) ) {
 
174
            if( isDeviceName )
 
175
               CFRelease( name );
 
176
            return channelName;
 
177
         }
 
178
         if( length == 0 )
 
179
            ++length;
 
180
         length *= 2;
 
181
      }
 
182
      if( isDeviceName )
 
183
         CFRelease( name );
 
184
      return NULL;
 
185
   }
 
186
 
 
187
   //continue with C string:
 
188
   if( !ensureChannelNameSize( size ) )
 
189
      return NULL;
 
190
 
 
191
   error = AudioDeviceGetProperty( hostApiDevice,
 
192
                                   channelIndex + 1,
 
193
                                   input,
 
194
                                   kAudioDevicePropertyChannelName,
 
195
                                   &size,
 
196
                                   channelName );
 
197
 
 
198
   if( error ) {
 
199
      ERR( error );
 
200
      return NULL;
 
201
   }
 
202
   return channelName;
 
203
}
 
204
 
 
205
 
 
206
 
 
207
 
 
208
 
 
209
AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s )
 
210
{
 
211
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
212
    VVDBUG(("PaMacCore_GetStreamInputHandle()\n"));
 
213
 
 
214
    return ( stream->inputDevice );
 
215
}
 
216
 
 
217
AudioDeviceID PaMacCore_GetStreamOutputDevice( PaStream* s )
 
218
{
 
219
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
220
    VVDBUG(("PaMacCore_GetStreamOutputHandle()\n"));
 
221
 
 
222
    return ( stream->outputDevice );
 
223
}
 
224
 
 
225
#ifdef __cplusplus
 
226
}
 
227
#endif /* __cplusplus */
 
228
 
 
229
#define RING_BUFFER_ADVANCE_DENOMINATOR (4)
 
230
 
 
231
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
 
232
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
233
                                  const PaStreamParameters *inputParameters,
 
234
                                  const PaStreamParameters *outputParameters,
 
235
                                  double sampleRate );
 
236
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
237
                           PaStream** s,
 
238
                           const PaStreamParameters *inputParameters,
 
239
                           const PaStreamParameters *outputParameters,
 
240
                           double sampleRate,
 
241
                           unsigned long framesPerBuffer,
 
242
                           PaStreamFlags streamFlags,
 
243
                           PaStreamCallback *streamCallback,
 
244
                           void *userData );
 
245
static PaError CloseStream( PaStream* stream );
 
246
static PaError StartStream( PaStream *stream );
 
247
static PaError StopStream( PaStream *stream );
 
248
static PaError AbortStream( PaStream *stream );
 
249
static PaError IsStreamStopped( PaStream *s );
 
250
static PaError IsStreamActive( PaStream *stream );
 
251
static PaTime GetStreamTime( PaStream *stream );
 
252
static void setStreamStartTime( PaStream *stream );
 
253
static OSStatus AudioIOProc( void *inRefCon,
 
254
                               AudioUnitRenderActionFlags *ioActionFlags,
 
255
                               const AudioTimeStamp *inTimeStamp,
 
256
                               UInt32 inBusNumber,
 
257
                               UInt32 inNumberFrames,
 
258
                               AudioBufferList *ioData );
 
259
static double GetStreamCpuLoad( PaStream* stream );
 
260
 
 
261
static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
 
262
                               PaDeviceInfo *deviceInfo,
 
263
                               AudioDeviceID macCoreDeviceId,
 
264
                               int isInput);
 
265
 
 
266
static PaError OpenAndSetupOneAudioUnit(
 
267
                                   const PaMacCoreStream *stream,
 
268
                                   const PaStreamParameters *inStreamParams,
 
269
                                   const PaStreamParameters *outStreamParams,
 
270
                                   const UInt32 requestedFramesPerBuffer,
 
271
                                   UInt32 *actualInputFramesPerBuffer,
 
272
                                   UInt32 *actualOutputFramesPerBuffer,
 
273
                                   const PaMacAUHAL *auhalHostApi,
 
274
                                   AudioUnit *audioUnit,
 
275
                                   AudioConverterRef *srConverter,
 
276
                                   AudioDeviceID *audioDevice,
 
277
                                   const double sampleRate,
 
278
                                   void *refCon );
 
279
 
 
280
/* for setting errors. */
 
281
#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
 
282
    PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
 
283
 
 
284
/*
 
285
 * Callback called when starting or stopping a stream.
 
286
 */
 
287
static void startStopCallback(
 
288
   void *               inRefCon,
 
289
   AudioUnit            ci,
 
290
   AudioUnitPropertyID  inID,
 
291
   AudioUnitScope       inScope,
 
292
   AudioUnitElement     inElement )
 
293
{
 
294
   PaMacCoreStream *stream = (PaMacCoreStream *) inRefCon;
 
295
   UInt32 isRunning;
 
296
   UInt32 size = sizeof( isRunning );
 
297
   OSStatus err;
 
298
   err = AudioUnitGetProperty( ci, kAudioOutputUnitProperty_IsRunning, inScope, inElement, &isRunning, &size );
 
299
   assert( !err );
 
300
   if( err )
 
301
      isRunning = false; //it's very unclear what to do in case of error here. There's no real way to notify the user, and crashing seems unreasonable.
 
302
   if( isRunning )
 
303
      return; //We are only interested in when we are stopping
 
304
   // -- if we are using 2 I/O units, we only need one notification!
 
305
   if( stream->inputUnit && stream->outputUnit && stream->inputUnit != stream->outputUnit && ci == stream->inputUnit )
 
306
      return;
 
307
   PaStreamFinishedCallback *sfc = stream->streamRepresentation.streamFinishedCallback;
 
308
   if( stream->state == STOPPING )
 
309
      stream->state = STOPPED ;
 
310
   if( sfc )
 
311
      sfc( stream->streamRepresentation.userData );
 
312
}
 
313
 
 
314
 
 
315
/*currently, this is only used in initialization, but it might be modified
 
316
  to be used when the list of devices changes.*/
 
317
static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
 
318
{
 
319
    UInt32 size;
 
320
    UInt32 propsize;
 
321
    VVDBUG(("gatherDeviceInfo()\n"));
 
322
    /* -- free any previous allocations -- */
 
323
    if( auhalHostApi->devIds )
 
324
        PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds);
 
325
    auhalHostApi->devIds = NULL;
 
326
 
 
327
    /* -- figure out how many devices there are -- */
 
328
    AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
 
329
                                  &propsize,
 
330
                                  NULL );
 
331
    auhalHostApi->devCount = propsize / sizeof( AudioDeviceID );
 
332
 
 
333
    VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) );
 
334
 
 
335
    /* -- copy the device IDs -- */
 
336
    auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory(
 
337
                             auhalHostApi->allocations,
 
338
                             propsize );
 
339
    if( !auhalHostApi->devIds )
 
340
        return paInsufficientMemory;
 
341
    AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
 
342
                                  &propsize,
 
343
                                  auhalHostApi->devIds );
 
344
#ifdef MAC_CORE_VERBOSE_DEBUG
 
345
    {
 
346
       int i;
 
347
       for( i=0; i<auhalHostApi->devCount; ++i )
 
348
          printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] );
 
349
    }
 
350
#endif
 
351
 
 
352
    size = sizeof(AudioDeviceID);
 
353
    auhalHostApi->defaultIn  = kAudioDeviceUnknown;
 
354
    auhalHostApi->defaultOut = kAudioDeviceUnknown;
 
355
 
 
356
    /* determine the default device. */
 
357
    /* I am not sure how these calls to AudioHardwareGetProperty()
 
358
       could fail, but in case they do, we use the first available
 
359
       device as the default. */
 
360
    if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
 
361
                     &size,
 
362
                     &auhalHostApi->defaultIn) ) {
 
363
       int i;
 
364
       auhalHostApi->defaultIn  = kAudioDeviceUnknown;
 
365
       VDBUG(("Failed to get default input device from OS."));
 
366
       VDBUG((" I will substitute the first available input Device."));
 
367
       for( i=0; i<auhalHostApi->devCount; ++i ) {
 
368
          PaDeviceInfo devInfo;
 
369
          if( 0 != GetChannelInfo( auhalHostApi, &devInfo,
 
370
                                   auhalHostApi->devIds[i], TRUE ) )
 
371
             if( devInfo.maxInputChannels ) {
 
372
                auhalHostApi->defaultIn = auhalHostApi->devIds[i];
 
373
                break;
 
374
             }
 
375
       }
 
376
    }   
 
377
    if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
 
378
                     &size,
 
379
                     &auhalHostApi->defaultOut) ) {
 
380
       int i;
 
381
       auhalHostApi->defaultIn  = kAudioDeviceUnknown;
 
382
       VDBUG(("Failed to get default output device from OS."));
 
383
       VDBUG((" I will substitute the first available output Device."));
 
384
       for( i=0; i<auhalHostApi->devCount; ++i ) {
 
385
          PaDeviceInfo devInfo;
 
386
          if( 0 != GetChannelInfo( auhalHostApi, &devInfo,
 
387
                                   auhalHostApi->devIds[i], FALSE ) )
 
388
             if( devInfo.maxOutputChannels ) {
 
389
                auhalHostApi->defaultOut = auhalHostApi->devIds[i];
 
390
                break;
 
391
             }
 
392
       }
 
393
    }   
 
394
 
 
395
    VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn  ) );
 
396
    VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) );
 
397
 
 
398
    return paNoError;
 
399
}
 
400
 
 
401
static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
 
402
                               PaDeviceInfo *deviceInfo,
 
403
                               AudioDeviceID macCoreDeviceId,
 
404
                               int isInput)
 
405
{
 
406
    UInt32 propSize;
 
407
    PaError err = paNoError;
 
408
    UInt32 i;
 
409
    int numChannels = 0;
 
410
    AudioBufferList *buflist = NULL;
 
411
    UInt32 frameLatency;
 
412
 
 
413
    VVDBUG(("GetChannelInfo()\n"));
 
414
 
 
415
    /* Get the number of channels from the stream configuration.
 
416
       Fail if we can't get this. */
 
417
 
 
418
    err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
 
419
    if (err)
 
420
        return err;
 
421
 
 
422
    buflist = PaUtil_AllocateMemory(propSize);
 
423
    if( !buflist )
 
424
       return paInsufficientMemory;
 
425
    err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
 
426
    if (err)
 
427
        goto error;
 
428
 
 
429
    for (i = 0; i < buflist->mNumberBuffers; ++i)
 
430
        numChannels += buflist->mBuffers[i].mNumberChannels;
 
431
 
 
432
    if (isInput)
 
433
        deviceInfo->maxInputChannels = numChannels;
 
434
    else
 
435
        deviceInfo->maxOutputChannels = numChannels;
 
436
      
 
437
    if (numChannels > 0) /* do not try to retrieve the latency if there is no channels. */
 
438
    {
 
439
       /* Get the latency.  Don't fail if we can't get this. */
 
440
       /* default to something reasonable */
 
441
       deviceInfo->defaultLowInputLatency = .01;
 
442
       deviceInfo->defaultHighInputLatency = .10;
 
443
       deviceInfo->defaultLowOutputLatency = .01;
 
444
       deviceInfo->defaultHighOutputLatency = .10;
 
445
       propSize = sizeof(UInt32);
 
446
       err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
 
447
       if (!err)
 
448
       {
 
449
          /** FEEDBACK:
 
450
           * This code was arrived at by trial and error, and some extentive, but not exhaustive
 
451
           * testing. Sebastien Beaulieu <seb@plogue.com> has suggested using
 
452
           * kAudioDevicePropertyLatency + kAudioDevicePropertySafetyOffset + buffer size instead.
 
453
           * At the time this code was written, many users were reporting dropouts with audio
 
454
           * programs that probably used this formula. This was probably
 
455
           * around 10.4.4, and the problem is probably fixed now. So perhaps
 
456
           * his formula should be reviewed and used.
 
457
           * */
 
458
          double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
 
459
          if (isInput)
 
460
          {
 
461
             deviceInfo->defaultLowInputLatency = 3 * secondLatency;
 
462
             deviceInfo->defaultHighInputLatency = 3 * 10 * secondLatency;
 
463
          }
 
464
          else
 
465
          {
 
466
             deviceInfo->defaultLowOutputLatency = 3 * secondLatency;
 
467
             deviceInfo->defaultHighOutputLatency = 3 * 10 * secondLatency;
 
468
          }
 
469
       }
 
470
    }
 
471
    PaUtil_FreeMemory( buflist );
 
472
    return paNoError;
 
473
 error:
 
474
    PaUtil_FreeMemory( buflist );
 
475
    return err;
 
476
}
 
477
 
 
478
static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
 
479
                                     PaDeviceInfo *deviceInfo,
 
480
                                     AudioDeviceID macCoreDeviceId,
 
481
                                     PaHostApiIndex hostApiIndex )
 
482
{
 
483
    Float64 sampleRate;
 
484
    char *name;
 
485
    PaError err = paNoError;
 
486
    UInt32 propSize;
 
487
 
 
488
    VVDBUG(("InitializeDeviceInfo(): macCoreDeviceId=%ld\n", macCoreDeviceId));
 
489
 
 
490
    memset(deviceInfo, 0, sizeof(deviceInfo));
 
491
 
 
492
    deviceInfo->structVersion = 2;
 
493
    deviceInfo->hostApi = hostApiIndex;
 
494
 
 
495
    /* Get the device name.  Fail if we can't get it. */
 
496
    err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
 
497
    if (err)
 
498
        return err;
 
499
 
 
500
    name = PaUtil_GroupAllocateMemory(auhalHostApi->allocations,propSize);
 
501
    if ( !name )
 
502
        return paInsufficientMemory;
 
503
    err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name));
 
504
    if (err)
 
505
        return err;
 
506
    deviceInfo->name = name;
 
507
 
 
508
    /* Try to get the default sample rate.  Don't fail if we can't get this. */
 
509
    propSize = sizeof(Float64);
 
510
    err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
 
511
    if (err)
 
512
        deviceInfo->defaultSampleRate = 0.0;
 
513
    else
 
514
        deviceInfo->defaultSampleRate = sampleRate;
 
515
 
 
516
    /* Get the maximum number of input and output channels.  Fail if we can't get this. */
 
517
 
 
518
    err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 1);
 
519
    if (err)
 
520
        return err;
 
521
 
 
522
    err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 0);
 
523
    if (err)
 
524
        return err;
 
525
 
 
526
    return paNoError;
 
527
}
 
528
 
 
529
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
 
530
{
 
531
    PaError result = paNoError;
 
532
    int i;
 
533
    PaMacAUHAL *auhalHostApi;
 
534
    PaDeviceInfo *deviceInfoArray;
 
535
    int unixErr;
 
536
 
 
537
    VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex));
 
538
 
 
539
    unixErr = initializeXRunListenerList();
 
540
    if( 0 != unixErr ) {
 
541
       return UNIX_ERR(unixErr);
 
542
    }
 
543
 
 
544
    auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) );
 
545
    if( !auhalHostApi )
 
546
    {
 
547
        result = paInsufficientMemory;
 
548
        goto error;
 
549
    }
 
550
 
 
551
    auhalHostApi->allocations = PaUtil_CreateAllocationGroup();
 
552
    if( !auhalHostApi->allocations )
 
553
    {
 
554
        result = paInsufficientMemory;
 
555
        goto error;
 
556
    }
 
557
 
 
558
    auhalHostApi->devIds = NULL;
 
559
    auhalHostApi->devCount = 0;
 
560
 
 
561
    /* get the info we need about the devices */
 
562
    result = gatherDeviceInfo( auhalHostApi );
 
563
    if( result != paNoError )
 
564
       goto error;
 
565
 
 
566
    *hostApi = &auhalHostApi->inheritedHostApiRep;
 
567
    (*hostApi)->info.structVersion = 1;
 
568
    (*hostApi)->info.type = paCoreAudio;
 
569
    (*hostApi)->info.name = "Core Audio";
 
570
 
 
571
    (*hostApi)->info.defaultInputDevice = paNoDevice;
 
572
    (*hostApi)->info.defaultOutputDevice = paNoDevice;
 
573
 
 
574
    (*hostApi)->info.deviceCount = 0;  
 
575
 
 
576
    if( auhalHostApi->devCount > 0 )
 
577
    {
 
578
        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
 
579
                auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount);
 
580
        if( !(*hostApi)->deviceInfos )
 
581
        {
 
582
            result = paInsufficientMemory;
 
583
            goto error;
 
584
        }
 
585
 
 
586
        /* allocate all device info structs in a contiguous block */
 
587
        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
 
588
                auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount );
 
589
        if( !deviceInfoArray )
 
590
        {
 
591
            result = paInsufficientMemory;
 
592
            goto error;
 
593
        }
 
594
 
 
595
        for( i=0; i < auhalHostApi->devCount; ++i )
 
596
        {
 
597
            int err;
 
598
            err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i],
 
599
                                      auhalHostApi->devIds[i],
 
600
                                      hostApiIndex );
 
601
            if (err == paNoError)
 
602
            { /* copy some info and set the defaults */
 
603
                (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i];
 
604
                if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn)
 
605
                    (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
 
606
                if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut)
 
607
                    (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
 
608
                (*hostApi)->info.deviceCount++;
 
609
            }
 
610
            else
 
611
            { /* there was an error. we need to shift the devices down, so we ignore this one */
 
612
                int j;
 
613
                auhalHostApi->devCount--;
 
614
                for( j=i; j<auhalHostApi->devCount; ++j )
 
615
                   auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1];
 
616
                i--;
 
617
            }
 
618
        }
 
619
    }
 
620
 
 
621
    (*hostApi)->Terminate = Terminate;
 
622
    (*hostApi)->OpenStream = OpenStream;
 
623
    (*hostApi)->IsFormatSupported = IsFormatSupported;
 
624
 
 
625
    PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface,
 
626
                                      CloseStream, StartStream,
 
627
                                      StopStream, AbortStream, IsStreamStopped,
 
628
                                      IsStreamActive,
 
629
                                      GetStreamTime, GetStreamCpuLoad,
 
630
                                      PaUtil_DummyRead, PaUtil_DummyWrite,
 
631
                                      PaUtil_DummyGetReadAvailable,
 
632
                                      PaUtil_DummyGetWriteAvailable );
 
633
 
 
634
    PaUtil_InitializeStreamInterface( &auhalHostApi->blockingStreamInterface,
 
635
                                      CloseStream, StartStream,
 
636
                                      StopStream, AbortStream, IsStreamStopped,
 
637
                                      IsStreamActive,
 
638
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
 
639
                                      ReadStream, WriteStream,
 
640
                                      GetStreamReadAvailable,
 
641
                                      GetStreamWriteAvailable );
 
642
 
 
643
    return result;
 
644
 
 
645
error:
 
646
    if( auhalHostApi )
 
647
    {
 
648
        if( auhalHostApi->allocations )
 
649
        {
 
650
            PaUtil_FreeAllAllocations( auhalHostApi->allocations );
 
651
            PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
 
652
        }
 
653
                
 
654
        PaUtil_FreeMemory( auhalHostApi );
 
655
    }
 
656
    return result;
 
657
}
 
658
 
 
659
 
 
660
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
 
661
{
 
662
    int unixErr;
 
663
 
 
664
    PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
 
665
 
 
666
    VVDBUG(("Terminate()\n"));
 
667
 
 
668
    unixErr = destroyXRunListenerList();
 
669
    if( 0 != unixErr )
 
670
       UNIX_ERR(unixErr);
 
671
 
 
672
    /*
 
673
        IMPLEMENT ME:
 
674
            - clean up any resources not handled by the allocation group
 
675
        TODO: Double check that everything is handled by alloc group
 
676
    */
 
677
 
 
678
    if( auhalHostApi->allocations )
 
679
    {
 
680
        PaUtil_FreeAllAllocations( auhalHostApi->allocations );
 
681
        PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
 
682
    }
 
683
 
 
684
    PaUtil_FreeMemory( auhalHostApi );
 
685
}
 
686
 
 
687
 
 
688
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
689
                                  const PaStreamParameters *inputParameters,
 
690
                                  const PaStreamParameters *outputParameters,
 
691
                                  double sampleRate )
 
692
{
 
693
    int inputChannelCount, outputChannelCount;
 
694
    PaSampleFormat inputSampleFormat, outputSampleFormat;
 
695
 
 
696
    VVDBUG(("IsFormatSupported(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld sampleRate=%g\n",
 
697
                inputParameters  ? inputParameters->channelCount  : -1,
 
698
                inputParameters  ? inputParameters->sampleFormat  : -1,
 
699
                outputParameters ? outputParameters->channelCount : -1,
 
700
                outputParameters ? outputParameters->sampleFormat : -1,
 
701
                (float) sampleRate ));
 
702
 
 
703
    /** These first checks are standard PA checks. We do some fancier checks
 
704
        later. */
 
705
    if( inputParameters )
 
706
    {
 
707
        inputChannelCount = inputParameters->channelCount;
 
708
        inputSampleFormat = inputParameters->sampleFormat;
 
709
 
 
710
        /* all standard sample formats are supported by the buffer adapter,
 
711
            this implementation doesn't support any custom sample formats */
 
712
        if( inputSampleFormat & paCustomFormat )
 
713
            return paSampleFormatNotSupported;
 
714
            
 
715
        /* unless alternate device specification is supported, reject the use of
 
716
            paUseHostApiSpecificDeviceSpecification */
 
717
 
 
718
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
719
            return paInvalidDevice;
 
720
 
 
721
        /* check that input device can support inputChannelCount */
 
722
        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
 
723
            return paInvalidChannelCount;
 
724
    }
 
725
    else
 
726
    {
 
727
        inputChannelCount = 0;
 
728
    }
 
729
 
 
730
    if( outputParameters )
 
731
    {
 
732
        outputChannelCount = outputParameters->channelCount;
 
733
        outputSampleFormat = outputParameters->sampleFormat;
 
734
 
 
735
        /* all standard sample formats are supported by the buffer adapter,
 
736
            this implementation doesn't support any custom sample formats */
 
737
        if( outputSampleFormat & paCustomFormat )
 
738
            return paSampleFormatNotSupported;
 
739
            
 
740
        /* unless alternate device specification is supported, reject the use of
 
741
            paUseHostApiSpecificDeviceSpecification */
 
742
 
 
743
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
744
            return paInvalidDevice;
 
745
 
 
746
        /* check that output device can support outputChannelCount */
 
747
        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
 
748
            return paInvalidChannelCount;
 
749
 
 
750
    }
 
751
    else
 
752
    {
 
753
        outputChannelCount = 0;
 
754
    }
 
755
 
 
756
    /* FEEDBACK */
 
757
    /*        I think the only way to check a given format SR combo is     */
 
758
    /*        to try opening it. This could be disruptive, is that Okay?   */
 
759
    /*        The alternative is to just read off available sample rates,  */
 
760
    /*        but this will not work %100 of the time (eg, a device that   */
 
761
    /*        supports N output at one rate but only N/2 at a higher rate.)*/
 
762
 
 
763
    /* The following code opens the device with the requested parameters to
 
764
       see if it works. */
 
765
    {
 
766
       PaError err;
 
767
       PaStream *s;
 
768
       err = OpenStream( hostApi, &s, inputParameters, outputParameters,
 
769
                           sampleRate, 1024, 0, (PaStreamCallback *)1, NULL );
 
770
       if( err != paNoError && err != paInvalidSampleRate )
 
771
          DBUG( ( "OpenStream @ %g returned: %d: %s\n",
 
772
                  (float) sampleRate, err, Pa_GetErrorText( err ) ) );
 
773
       if( err ) 
 
774
          return err;
 
775
       err = CloseStream( s );
 
776
       if( err ) {
 
777
          /* FEEDBACK: is this more serious? should we assert? */
 
778
          DBUG( ( "WARNING: could not close Stream. %d: %s\n",
 
779
                  err, Pa_GetErrorText( err ) ) );
 
780
       }
 
781
    }
 
782
 
 
783
    return paFormatIsSupported;
 
784
}
 
785
 
 
786
static PaError OpenAndSetupOneAudioUnit(
 
787
                                   const PaMacCoreStream *stream,
 
788
                                   const PaStreamParameters *inStreamParams,
 
789
                                   const PaStreamParameters *outStreamParams,
 
790
                                   const UInt32 requestedFramesPerBuffer,
 
791
                                   UInt32 *actualInputFramesPerBuffer,
 
792
                                   UInt32 *actualOutputFramesPerBuffer,
 
793
                                   const PaMacAUHAL *auhalHostApi,
 
794
                                   AudioUnit *audioUnit,
 
795
                                   AudioConverterRef *srConverter,
 
796
                                   AudioDeviceID *audioDevice,
 
797
                                   const double sampleRate,
 
798
                                   void *refCon )
 
799
{
 
800
    ComponentDescription desc;
 
801
    Component comp;
 
802
    /*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
 
803
    AudioStreamBasicDescription desiredFormat;
 
804
    OSStatus result = noErr;
 
805
    PaError paResult = paNoError;
 
806
    int line = 0;
 
807
    UInt32 callbackKey;
 
808
    AURenderCallbackStruct rcbs;
 
809
    unsigned long macInputStreamFlags  = paMacCorePlayNice;
 
810
    unsigned long macOutputStreamFlags = paMacCorePlayNice;
 
811
    SInt32 const *inChannelMap = NULL;
 
812
    SInt32 const *outChannelMap = NULL;
 
813
    unsigned long inChannelMapSize = 0;
 
814
    unsigned long outChannelMapSize = 0;
 
815
 
 
816
    VVDBUG(("OpenAndSetupOneAudioUnit(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld, requestedFramesPerBuffer=%ld\n",
 
817
                inStreamParams  ? inStreamParams->channelCount  : -1,
 
818
                inStreamParams  ? inStreamParams->sampleFormat  : -1,
 
819
                outStreamParams ? outStreamParams->channelCount : -1,
 
820
                outStreamParams ? outStreamParams->sampleFormat : -1,
 
821
                requestedFramesPerBuffer ));
 
822
 
 
823
    /* -- handle the degenerate case  -- */
 
824
    if( !inStreamParams && !outStreamParams ) {
 
825
       *audioUnit = NULL;
 
826
       *audioDevice = kAudioDeviceUnknown;
 
827
       return paNoError;
 
828
    }
 
829
 
 
830
    /* -- get the user's api specific info, if they set any -- */
 
831
    if( inStreamParams && inStreamParams->hostApiSpecificStreamInfo )
 
832
    {
 
833
       macInputStreamFlags=
 
834
            ((PaMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo)
 
835
                  ->flags;
 
836
       inChannelMap = ((PaMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo)
 
837
                  ->channelMap;
 
838
       inChannelMapSize = ((PaMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo)
 
839
                  ->channelMapSize;
 
840
    }
 
841
    if( outStreamParams && outStreamParams->hostApiSpecificStreamInfo )
 
842
    {
 
843
       macOutputStreamFlags=
 
844
            ((PaMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
 
845
                  ->flags;
 
846
       outChannelMap = ((PaMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
 
847
                  ->channelMap;
 
848
       outChannelMapSize = ((PaMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
 
849
                  ->channelMapSize; 
 
850
    }
 
851
    /* Override user's flags here, if desired for testing. */
 
852
 
 
853
    /*
 
854
     * The HAL AU is a Mac OS style "component".
 
855
     * the first few steps deal with that.
 
856
     * Later steps work on a combination of Mac OS
 
857
     * components and the slightly lower level
 
858
     * HAL.
 
859
     */
 
860
 
 
861
    /* -- describe the output type AudioUnit -- */
 
862
    /*  Note: for the default AudioUnit, we could use the
 
863
     *  componentSubType value kAudioUnitSubType_DefaultOutput;
 
864
     *  but I don't think that's relevant here.
 
865
     */
 
866
    desc.componentType         = kAudioUnitType_Output;
 
867
    desc.componentSubType      = kAudioUnitSubType_HALOutput;
 
868
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
 
869
    desc.componentFlags        = 0;
 
870
    desc.componentFlagsMask    = 0;
 
871
    /* -- find the component -- */
 
872
    comp = FindNextComponent( NULL, &desc );
 
873
    if( !comp )
 
874
    {
 
875
       DBUG( ( "AUHAL component not found." ) );
 
876
       *audioUnit = NULL;
 
877
       *audioDevice = kAudioDeviceUnknown;
 
878
       return paUnanticipatedHostError;
 
879
    }
 
880
    /* -- open it -- */
 
881
    result = OpenAComponent( comp, audioUnit );
 
882
    if( result )
 
883
    {
 
884
       DBUG( ( "Failed to open AUHAL component." ) );
 
885
       *audioUnit = NULL;
 
886
       *audioDevice = kAudioDeviceUnknown;
 
887
       return ERR( result );
 
888
    }
 
889
    /* -- prepare a little error handling logic / hackery -- */
 
890
#define ERR_WRAP(mac_err) do { result = mac_err ; line = __LINE__ ; if ( result != noErr ) goto error ; } while(0)
 
891
 
 
892
    /* -- if there is input, we have to explicitly enable input -- */
 
893
    if( inStreamParams )
 
894
    {
 
895
       UInt32 enableIO = 1;
 
896
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
897
                 kAudioOutputUnitProperty_EnableIO,
 
898
                 kAudioUnitScope_Input,
 
899
                 INPUT_ELEMENT,
 
900
                 &enableIO,
 
901
                 sizeof(enableIO) ) );
 
902
    }
 
903
    /* -- if there is no output, we must explicitly disable output -- */
 
904
    if( !outStreamParams )
 
905
    {
 
906
       UInt32 enableIO = 0;
 
907
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
908
                 kAudioOutputUnitProperty_EnableIO,
 
909
                 kAudioUnitScope_Output,
 
910
                 OUTPUT_ELEMENT,
 
911
                 &enableIO,
 
912
                 sizeof(enableIO) ) );
 
913
    }
 
914
 
 
915
    /* -- set the devices -- */
 
916
    /* make sure input and output are the same device if we are doing input and
 
917
       output. */
 
918
    if( inStreamParams && outStreamParams )
 
919
    {
 
920
       assert( outStreamParams->device == inStreamParams->device );
 
921
    }
 
922
    if( inStreamParams )
 
923
    {
 
924
       *audioDevice = auhalHostApi->devIds[inStreamParams->device] ;
 
925
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
926
                    kAudioOutputUnitProperty_CurrentDevice,
 
927
                    kAudioUnitScope_Global,
 
928
                    INPUT_ELEMENT,
 
929
                    audioDevice,
 
930
                    sizeof(AudioDeviceID) ) );
 
931
    }
 
932
    if( outStreamParams && outStreamParams != inStreamParams )
 
933
    {
 
934
       *audioDevice = auhalHostApi->devIds[outStreamParams->device] ;
 
935
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
936
                    kAudioOutputUnitProperty_CurrentDevice,
 
937
                    kAudioUnitScope_Global,
 
938
                    OUTPUT_ELEMENT,
 
939
                    audioDevice,
 
940
                    sizeof(AudioDeviceID) ) );
 
941
    }
 
942
    /* -- add listener for dropouts -- */
 
943
    result = AudioDeviceAddPropertyListener( *audioDevice,
 
944
                                             0,
 
945
                                             outStreamParams ? false : true,
 
946
                                             kAudioDeviceProcessorOverload,
 
947
                                             xrunCallback,
 
948
                                             addToXRunListenerList( (void *)stream ) ) ;
 
949
    if( result == kAudioHardwareIllegalOperationError ) {
 
950
       // -- already registered, we're good
 
951
    } else {
 
952
       // -- not already registered, just check for errors
 
953
       ERR_WRAP( result );
 
954
    }
 
955
    /* -- listen for stream start and stop -- */
 
956
    ERR_WRAP( AudioUnitAddPropertyListener( *audioUnit,
 
957
                                            kAudioOutputUnitProperty_IsRunning,
 
958
                                            startStopCallback,
 
959
                                            (void *)stream ) );
 
960
 
 
961
    /* -- set format -- */
 
962
    bzero( &desiredFormat, sizeof(desiredFormat) );
 
963
    desiredFormat.mFormatID         = kAudioFormatLinearPCM ;
 
964
    desiredFormat.mFormatFlags      = kAudioFormatFlagsNativeFloatPacked;
 
965
    desiredFormat.mFramesPerPacket  = 1;
 
966
    desiredFormat.mBitsPerChannel   = sizeof( float ) * 8;
 
967
 
 
968
    result = 0;
 
969
    /*  set device format first, but only touch the device if the user asked */
 
970
    if( inStreamParams ) {
 
971
       /*The callback never calls back if we don't set the FPB */
 
972
       /*This seems wierd, because I would think setting anything on the device
 
973
         would be disruptive.*/
 
974
       paResult = setBestFramesPerBuffer( *audioDevice, FALSE,
 
975
                                          requestedFramesPerBuffer,
 
976
                                          actualInputFramesPerBuffer );
 
977
       if( paResult ) goto error;
 
978
       if( macInputStreamFlags & paMacCoreChangeDeviceParameters ) {
 
979
          bool requireExact;
 
980
          requireExact=macInputStreamFlags & paMacCoreFailIfConversionRequired;
 
981
          paResult = setBestSampleRateForDevice( *audioDevice, FALSE,
 
982
                                                 requireExact, sampleRate );
 
983
          if( paResult ) goto error;
 
984
       }
 
985
       if( actualInputFramesPerBuffer && actualOutputFramesPerBuffer )
 
986
          *actualOutputFramesPerBuffer = *actualInputFramesPerBuffer ;
 
987
    }
 
988
    if( outStreamParams && !inStreamParams ) {
 
989
       /*The callback never calls back if we don't set the FPB */
 
990
       /*This seems wierd, because I would think setting anything on the device
 
991
         would be disruptive.*/
 
992
       paResult = setBestFramesPerBuffer( *audioDevice, TRUE,
 
993
                                          requestedFramesPerBuffer,
 
994
                                          actualOutputFramesPerBuffer );
 
995
       if( paResult ) goto error;
 
996
       if( macOutputStreamFlags & paMacCoreChangeDeviceParameters ) {
 
997
          bool requireExact;
 
998
          requireExact=macOutputStreamFlags & paMacCoreFailIfConversionRequired;
 
999
          paResult = setBestSampleRateForDevice( *audioDevice, TRUE,
 
1000
                                                 requireExact, sampleRate );
 
1001
          if( paResult ) goto error;
 
1002
       }
 
1003
    }
 
1004
 
 
1005
    /* -- set the quality of the output converter -- */
 
1006
    if( outStreamParams ) {
 
1007
       UInt32 value = kAudioConverterQuality_Max;
 
1008
       switch( macOutputStreamFlags & 0x0700 ) {
 
1009
       case 0x0100: /*paMacCore_ConversionQualityMin:*/
 
1010
          value=kRenderQuality_Min;
 
1011
          break;
 
1012
       case 0x0200: /*paMacCore_ConversionQualityLow:*/
 
1013
          value=kRenderQuality_Low;
 
1014
          break;
 
1015
       case 0x0300: /*paMacCore_ConversionQualityMedium:*/
 
1016
          value=kRenderQuality_Medium;
 
1017
          break;
 
1018
       case 0x0400: /*paMacCore_ConversionQualityHigh:*/
 
1019
          value=kRenderQuality_High;
 
1020
          break;
 
1021
       }
 
1022
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
1023
                    kAudioUnitProperty_RenderQuality,
 
1024
                    kAudioUnitScope_Global,
 
1025
                    OUTPUT_ELEMENT,
 
1026
                    &value,
 
1027
                    sizeof(value) ) );
 
1028
    }
 
1029
    /* now set the format on the Audio Units. */
 
1030
    if( outStreamParams )
 
1031
    {
 
1032
       desiredFormat.mSampleRate    =sampleRate;
 
1033
       desiredFormat.mBytesPerPacket=sizeof(float)*outStreamParams->channelCount;
 
1034
       desiredFormat.mBytesPerFrame =sizeof(float)*outStreamParams->channelCount;
 
1035
       desiredFormat.mChannelsPerFrame = outStreamParams->channelCount;
 
1036
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
1037
                            kAudioUnitProperty_StreamFormat,
 
1038
                            kAudioUnitScope_Input,
 
1039
                            OUTPUT_ELEMENT,
 
1040
                            &desiredFormat,
 
1041
                            sizeof(AudioStreamBasicDescription) ) );
 
1042
    }
 
1043
    if( inStreamParams )
 
1044
    {
 
1045
       AudioStreamBasicDescription sourceFormat;
 
1046
       UInt32 size = sizeof( AudioStreamBasicDescription );
 
1047
 
 
1048
       /* keep the sample rate of the device, or we confuse AUHAL */
 
1049
       ERR_WRAP( AudioUnitGetProperty( *audioUnit,
 
1050
                            kAudioUnitProperty_StreamFormat,
 
1051
                            kAudioUnitScope_Input,
 
1052
                            INPUT_ELEMENT,
 
1053
                            &sourceFormat,
 
1054
                            &size ) );
 
1055
       desiredFormat.mSampleRate = sourceFormat.mSampleRate;
 
1056
       desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount;
 
1057
       desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount;
 
1058
       desiredFormat.mChannelsPerFrame = inStreamParams->channelCount;
 
1059
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
1060
                            kAudioUnitProperty_StreamFormat,
 
1061
                            kAudioUnitScope_Output,
 
1062
                            INPUT_ELEMENT,
 
1063
                            &desiredFormat,
 
1064
                            sizeof(AudioStreamBasicDescription) ) );
 
1065
    }
 
1066
    /* set the maximumFramesPerSlice */
 
1067
    /* not doing this causes real problems
 
1068
       (eg. the callback might not be called). The idea of setting both this
 
1069
       and the frames per buffer on the device is that we'll be most likely
 
1070
       to actually get the frame size we requested in the callback with the
 
1071
       minimum latency. */
 
1072
    if( outStreamParams ) {
 
1073
       UInt32 size = sizeof( *actualOutputFramesPerBuffer );
 
1074
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
1075
                            kAudioUnitProperty_MaximumFramesPerSlice,
 
1076
                            kAudioUnitScope_Input,
 
1077
                            OUTPUT_ELEMENT,
 
1078
                            actualOutputFramesPerBuffer,
 
1079
                            sizeof(*actualOutputFramesPerBuffer) ) );
 
1080
       ERR_WRAP( AudioUnitGetProperty( *audioUnit,
 
1081
                            kAudioUnitProperty_MaximumFramesPerSlice,
 
1082
                            kAudioUnitScope_Global,
 
1083
                            OUTPUT_ELEMENT,
 
1084
                            actualOutputFramesPerBuffer,
 
1085
                            &size ) );
 
1086
    }
 
1087
    if( inStreamParams ) {
 
1088
       /*UInt32 size = sizeof( *actualInputFramesPerBuffer );*/
 
1089
       ERR_WRAP( AudioUnitSetProperty( *audioUnit,
 
1090
                            kAudioUnitProperty_MaximumFramesPerSlice,
 
1091
                            kAudioUnitScope_Output,
 
1092
                            INPUT_ELEMENT,
 
1093
                            actualInputFramesPerBuffer,
 
1094
                            sizeof(*actualInputFramesPerBuffer) ) );
 
1095
/* Don't know why this causes problems
 
1096
       ERR_WRAP( AudioUnitGetProperty( *audioUnit,
 
1097
                            kAudioUnitProperty_MaximumFramesPerSlice,
 
1098
                            kAudioUnitScope_Global, //Output,
 
1099
                            INPUT_ELEMENT,
 
1100
                            actualInputFramesPerBuffer,
 
1101
                            &size ) );
 
1102
*/
 
1103
    }
 
1104
 
 
1105
    /* -- if we have input, we may need to setup an SR converter -- */
 
1106
    /* even if we got the sample rate we asked for, we need to do
 
1107
       the conversion in case another program changes the underlying SR. */
 
1108
    /* FIXME: I think we need to monitor stream and change the converter if the incoming format changes. */
 
1109
    if( inStreamParams ) {
 
1110
       AudioStreamBasicDescription desiredFormat;
 
1111
       AudioStreamBasicDescription sourceFormat;
 
1112
       UInt32 sourceSize = sizeof( sourceFormat );
 
1113
       bzero( &desiredFormat, sizeof(desiredFormat) );
 
1114
       desiredFormat.mSampleRate       = sampleRate;
 
1115
       desiredFormat.mFormatID         = kAudioFormatLinearPCM ;
 
1116
       desiredFormat.mFormatFlags      = kAudioFormatFlagsNativeFloatPacked;
 
1117
       desiredFormat.mFramesPerPacket  = 1;
 
1118
       desiredFormat.mBitsPerChannel   = sizeof( float ) * 8;
 
1119
       desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount;
 
1120
       desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount;
 
1121
       desiredFormat.mChannelsPerFrame = inStreamParams->channelCount;
 
1122
 
 
1123
       /* get the source format */
 
1124
       ERR_WRAP( AudioUnitGetProperty(
 
1125
                         *audioUnit,
 
1126
                         kAudioUnitProperty_StreamFormat,
 
1127
                         kAudioUnitScope_Output,
 
1128
                         INPUT_ELEMENT,
 
1129
                         &sourceFormat,
 
1130
                         &sourceSize ) );
 
1131
 
 
1132
       if( desiredFormat.mSampleRate != sourceFormat.mSampleRate )
 
1133
       {
 
1134
          UInt32 value = kAudioConverterQuality_Max;
 
1135
          switch( macInputStreamFlags & 0x0700 ) {
 
1136
          case 0x0100: /*paMacCore_ConversionQualityMin:*/
 
1137
             value=kAudioConverterQuality_Min;
 
1138
             break;
 
1139
          case 0x0200: /*paMacCore_ConversionQualityLow:*/
 
1140
             value=kAudioConverterQuality_Low;
 
1141
             break;
 
1142
          case 0x0300: /*paMacCore_ConversionQualityMedium:*/
 
1143
             value=kAudioConverterQuality_Medium;
 
1144
             break;
 
1145
          case 0x0400: /*paMacCore_ConversionQualityHigh:*/
 
1146
             value=kAudioConverterQuality_High;
 
1147
             break;
 
1148
          }
 
1149
          VDBUG(( "Creating sample rate converter for input"
 
1150
                  " to convert from %g to %g\n",
 
1151
                  (float)sourceFormat.mSampleRate,
 
1152
                  (float)desiredFormat.mSampleRate ) );
 
1153
          /* create our converter */
 
1154
          ERR_WRAP( AudioConverterNew( 
 
1155
                             &sourceFormat,
 
1156
                             &desiredFormat,
 
1157
                             srConverter ) );
 
1158
          /* Set quality */
 
1159
          ERR_WRAP( AudioConverterSetProperty(
 
1160
                             *srConverter,
 
1161
                             kAudioConverterSampleRateConverterQuality,
 
1162
                             sizeof( value ),
 
1163
                             &value ) );
 
1164
       }
 
1165
    }
 
1166
    /* -- set IOProc (callback) -- */
 
1167
    callbackKey = outStreamParams ? kAudioUnitProperty_SetRenderCallback
 
1168
                                  : kAudioOutputUnitProperty_SetInputCallback ;
 
1169
    rcbs.inputProc = AudioIOProc;
 
1170
    rcbs.inputProcRefCon = refCon;
 
1171
    ERR_WRAP( AudioUnitSetProperty(
 
1172
                               *audioUnit,
 
1173
                               callbackKey,
 
1174
                               kAudioUnitScope_Output,
 
1175
                               outStreamParams ? OUTPUT_ELEMENT : INPUT_ELEMENT,
 
1176
                               &rcbs,
 
1177
                               sizeof(rcbs)) );
 
1178
 
 
1179
    if( inStreamParams && outStreamParams && *srConverter )
 
1180
           ERR_WRAP( AudioUnitSetProperty(
 
1181
                               *audioUnit,
 
1182
                               kAudioOutputUnitProperty_SetInputCallback,
 
1183
                               kAudioUnitScope_Output,
 
1184
                               INPUT_ELEMENT,
 
1185
                               &rcbs,
 
1186
                               sizeof(rcbs)) );
 
1187
 
 
1188
    /* channel mapping. */
 
1189
    if(inChannelMap)
 
1190
    {
 
1191
        UInt32 mapSize = inChannelMapSize *sizeof(SInt32);
 
1192
 
 
1193
        //for each channel of desired input, map the channel from
 
1194
        //the device's output channel.
 
1195
        ERR_WRAP( AudioUnitSetProperty(*audioUnit,
 
1196
                                kAudioOutputUnitProperty_ChannelMap,
 
1197
                                kAudioUnitScope_Output,
 
1198
                                INPUT_ELEMENT,
 
1199
                                inChannelMap,
 
1200
                                mapSize));
 
1201
    }
 
1202
    if(outChannelMap)
 
1203
    {
 
1204
        UInt32 mapSize = outChannelMapSize *sizeof(SInt32);
 
1205
 
 
1206
        //for each channel of desired output, map the channel from
 
1207
        //the device's output channel.
 
1208
        ERR_WRAP(AudioUnitSetProperty(*audioUnit,
 
1209
                                kAudioOutputUnitProperty_ChannelMap,
 
1210
                                kAudioUnitScope_Output,
 
1211
                                OUTPUT_ELEMENT,
 
1212
                                outChannelMap,
 
1213
                                mapSize));
 
1214
    }
 
1215
    /* initialize the audio unit */
 
1216
    ERR_WRAP( AudioUnitInitialize(*audioUnit) );
 
1217
 
 
1218
    if( inStreamParams && outStreamParams )
 
1219
       VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) );
 
1220
    else if( inStreamParams )
 
1221
       VDBUG( ("Opened device %ld for input.\n", *audioDevice ) );
 
1222
    else if( outStreamParams )
 
1223
       VDBUG( ("Opened device %ld for output.\n", *audioDevice ) );
 
1224
    return paNoError;
 
1225
#undef ERR_WRAP
 
1226
 
 
1227
    error:
 
1228
       CloseComponent( *audioUnit );
 
1229
       *audioUnit = NULL;
 
1230
       if( result )
 
1231
          return PaMacCore_SetError( result, line, 1 );
 
1232
       return paResult;
 
1233
}
 
1234
 
 
1235
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
 
1236
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
1237
                           PaStream** s,
 
1238
                           const PaStreamParameters *inputParameters,
 
1239
                           const PaStreamParameters *outputParameters,
 
1240
                           double sampleRate,
 
1241
                           unsigned long framesPerBuffer,
 
1242
                           PaStreamFlags streamFlags,
 
1243
                           PaStreamCallback *streamCallback,
 
1244
                           void *userData )
 
1245
{
 
1246
    PaError result = paNoError;
 
1247
    PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
 
1248
    PaMacCoreStream *stream = 0;
 
1249
    int inputChannelCount, outputChannelCount;
 
1250
    PaSampleFormat inputSampleFormat, outputSampleFormat;
 
1251
    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
 
1252
    VVDBUG(("OpenStream(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld SR=%g, FPB=%ld\n",
 
1253
                inputParameters  ? inputParameters->channelCount  : -1,
 
1254
                inputParameters  ? inputParameters->sampleFormat  : -1,
 
1255
                outputParameters ? outputParameters->channelCount : -1,
 
1256
                outputParameters ? outputParameters->sampleFormat : -1,
 
1257
                (float) sampleRate,
 
1258
                framesPerBuffer ));
 
1259
    VDBUG( ("Opening Stream.\n") );
 
1260
 
 
1261
    /*These first few bits of code are from paSkeleton with few modifications.*/
 
1262
    if( inputParameters )
 
1263
    {
 
1264
        inputChannelCount = inputParameters->channelCount;
 
1265
        inputSampleFormat = inputParameters->sampleFormat;
 
1266
 
 
1267
        /* unless alternate device specification is supported, reject the use of
 
1268
            paUseHostApiSpecificDeviceSpecification */
 
1269
 
 
1270
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
1271
            return paInvalidDevice;
 
1272
 
 
1273
        /* check that input device can support inputChannelCount */
 
1274
        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
 
1275
            return paInvalidChannelCount;
 
1276
 
 
1277
        /* Host supports interleaved float32 */
 
1278
        hostInputSampleFormat = paFloat32;
 
1279
    }
 
1280
    else
 
1281
    {
 
1282
        inputChannelCount = 0;
 
1283
        inputSampleFormat = hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */
 
1284
    }
 
1285
 
 
1286
    if( outputParameters )
 
1287
    {
 
1288
        outputChannelCount = outputParameters->channelCount;
 
1289
        outputSampleFormat = outputParameters->sampleFormat;
 
1290
        
 
1291
        /* unless alternate device specification is supported, reject the use of
 
1292
            paUseHostApiSpecificDeviceSpecification */
 
1293
 
 
1294
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
 
1295
            return paInvalidDevice;
 
1296
 
 
1297
        /* check that output device can support inputChannelCount */
 
1298
        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
 
1299
            return paInvalidChannelCount;
 
1300
 
 
1301
        /* Host supports interleaved float32 */
 
1302
        hostOutputSampleFormat = paFloat32;
 
1303
    }
 
1304
    else
 
1305
    {
 
1306
        outputChannelCount = 0;
 
1307
        outputSampleFormat = hostOutputSampleFormat = paFloat32; /* Surpress 'uninitialized var' warnings. */
 
1308
    }
 
1309
 
 
1310
    /* validate platform specific flags */
 
1311
    if( (streamFlags & paPlatformSpecificFlags) != 0 )
 
1312
        return paInvalidFlag; /* unexpected platform specific flag */
 
1313
 
 
1314
    stream = (PaMacCoreStream*)PaUtil_AllocateMemory( sizeof(PaMacCoreStream) );
 
1315
    if( !stream )
 
1316
    {
 
1317
        result = paInsufficientMemory;
 
1318
        goto error;
 
1319
    }
 
1320
 
 
1321
    /* If we fail after this point, we my be left in a bad state, with
 
1322
       some data structures setup and others not. So, first thing we
 
1323
       do is initialize everything so that if we fail, we know what hasn't
 
1324
       been touched.
 
1325
     */
 
1326
 
 
1327
    stream->inputAudioBufferList.mBuffers[0].mData = NULL;
 
1328
    stream->inputRingBuffer.buffer = NULL;
 
1329
    bzero( &stream->blio, sizeof( PaMacBlio ) );
 
1330
/*
 
1331
    stream->blio.inputRingBuffer.buffer = NULL;
 
1332
    stream->blio.outputRingBuffer.buffer = NULL;
 
1333
    stream->blio.inputSampleFormat = inputParameters?inputParameters->sampleFormat:0;
 
1334
    stream->blio.inputSampleSize = computeSampleSizeFromFormat(stream->blio.inputSampleFormat);
 
1335
    stream->blio.outputSampleFormat=outputParameters?outputParameters->sampleFormat:0;
 
1336
    stream->blio.outputSampleSize = computeSampleSizeFromFormat(stream->blio.outputSampleFormat);
 
1337
*/
 
1338
    stream->inputSRConverter = NULL;
 
1339
    stream->inputUnit = NULL;
 
1340
    stream->outputUnit = NULL;
 
1341
    stream->inputFramesPerBuffer = 0;
 
1342
    stream->outputFramesPerBuffer = 0;
 
1343
    stream->bufferProcessorIsInitialized = FALSE;
 
1344
 
 
1345
    /* assert( streamCallback ) ; */ /* only callback mode is implemented */
 
1346
    if( streamCallback )
 
1347
    {
 
1348
        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
 
1349
                                        &auhalHostApi->callbackStreamInterface,
 
1350
                                        streamCallback, userData );
 
1351
    }
 
1352
    else
 
1353
    {
 
1354
        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
 
1355
                                        &auhalHostApi->blockingStreamInterface,
 
1356
                                        BlioCallback, &stream->blio );
 
1357
    }
 
1358
 
 
1359
    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
 
1360
 
 
1361
    /* -- handle paFramesPerBufferUnspecified -- */
 
1362
    if( framesPerBuffer == paFramesPerBufferUnspecified ) {
 
1363
       long requested = 64;
 
1364
       if( inputParameters )
 
1365
          requested = MAX( requested, inputParameters->suggestedLatency * sampleRate / 2 );
 
1366
       if( outputParameters )
 
1367
          requested = MAX( requested, outputParameters->suggestedLatency *sampleRate / 2 );
 
1368
       VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n",
 
1369
              requested ) );
 
1370
       if( requested <= 64 ) {
 
1371
          /*requested a realtively low latency. make sure this is in range of devices */
 
1372
          /*try to get the device's min natural buffer size and use that (but no smaller than 64).*/
 
1373
          AudioValueRange audioRange;
 
1374
          UInt32 size = sizeof( audioRange );
 
1375
          if( inputParameters ) {
 
1376
             WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
 
1377
                                          0,
 
1378
                                          false,
 
1379
                                          kAudioDevicePropertyBufferFrameSizeRange,
 
1380
                                          &size, &audioRange ) );
 
1381
             if( result )
 
1382
                requested = MAX( requested, audioRange.mMinimum );
 
1383
          }
 
1384
          size = sizeof( audioRange );
 
1385
          if( outputParameters ) {
 
1386
             WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
 
1387
                                          0,
 
1388
                                          false,
 
1389
                                          kAudioDevicePropertyBufferFrameSizeRange,
 
1390
                                          &size, &audioRange ) );
 
1391
             if( result )
 
1392
                requested = MAX( requested, audioRange.mMinimum );
 
1393
          }
 
1394
       } else {
 
1395
          /* requested a realtively high latency. make sure this is in range of devices */
 
1396
          /*try to get the device's max natural buffer size and use that (but no larger than 1024).*/
 
1397
          AudioValueRange audioRange;
 
1398
          UInt32 size = sizeof( audioRange );
 
1399
          requested = MIN( requested, 1024 );
 
1400
          if( inputParameters ) {
 
1401
             WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
 
1402
                                          0,
 
1403
                                          false,
 
1404
                                          kAudioDevicePropertyBufferFrameSizeRange,
 
1405
                                          &size, &audioRange ) );
 
1406
             if( result )
 
1407
                requested = MIN( requested, audioRange.mMaximum );
 
1408
          }
 
1409
          size = sizeof( audioRange );
 
1410
          if( outputParameters ) {
 
1411
             WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
 
1412
                                          0,
 
1413
                                          false,
 
1414
                                          kAudioDevicePropertyBufferFrameSizeRange,
 
1415
                                          &size, &audioRange ) );
 
1416
             if( result )
 
1417
                requested = MIN( requested, audioRange.mMaximum );
 
1418
          }
 
1419
       }
 
1420
       /* -- double check ranges -- */
 
1421
       if( requested > 1024 ) requested = 1024;
 
1422
       if( requested < 64 ) requested = 64;
 
1423
       VDBUG(("After querying hardware, setting block size to %ld.\n", requested));
 
1424
       framesPerBuffer = requested;
 
1425
    }
 
1426
 
 
1427
    /* -- Now we actually open and setup streams. -- */
 
1428
    if( inputParameters && outputParameters && outputParameters->device == inputParameters->device )
 
1429
    { /* full duplex. One device. */
 
1430
       UInt32 inputFramesPerBuffer  = (UInt32) stream->inputFramesPerBuffer;
 
1431
       UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
 
1432
       result = OpenAndSetupOneAudioUnit( stream,
 
1433
                                          inputParameters,
 
1434
                                          outputParameters,
 
1435
                                          framesPerBuffer,
 
1436
                                          &inputFramesPerBuffer,
 
1437
                                          &outputFramesPerBuffer,
 
1438
                                          auhalHostApi,
 
1439
                                          &(stream->inputUnit),
 
1440
                                          &(stream->inputSRConverter),
 
1441
                                          &(stream->inputDevice),
 
1442
                                          sampleRate,
 
1443
                                          stream );
 
1444
       stream->inputFramesPerBuffer = inputFramesPerBuffer;
 
1445
       stream->outputFramesPerBuffer = outputFramesPerBuffer;
 
1446
       stream->outputUnit = stream->inputUnit;
 
1447
       stream->outputDevice = stream->inputDevice;
 
1448
       if( result != paNoError )
 
1449
           goto error;
 
1450
    }
 
1451
    else
 
1452
    { /* full duplex, different devices OR simplex */
 
1453
       UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
 
1454
       UInt32 inputFramesPerBuffer  = (UInt32) stream->inputFramesPerBuffer;
 
1455
       result = OpenAndSetupOneAudioUnit( stream,
 
1456
                                          NULL,
 
1457
                                          outputParameters,
 
1458
                                          framesPerBuffer,
 
1459
                                          NULL,
 
1460
                                          &outputFramesPerBuffer,
 
1461
                                          auhalHostApi,
 
1462
                                          &(stream->outputUnit),
 
1463
                                          NULL,
 
1464
                                          &(stream->outputDevice),
 
1465
                                          sampleRate,
 
1466
                                          stream );
 
1467
       if( result != paNoError )
 
1468
           goto error;
 
1469
       result = OpenAndSetupOneAudioUnit( stream,
 
1470
                                          inputParameters,
 
1471
                                          NULL,
 
1472
                                          framesPerBuffer,
 
1473
                                          &inputFramesPerBuffer,
 
1474
                                          NULL,
 
1475
                                          auhalHostApi,
 
1476
                                          &(stream->inputUnit),
 
1477
                                          &(stream->inputSRConverter),
 
1478
                                          &(stream->inputDevice),
 
1479
                                          sampleRate,
 
1480
                                          stream );
 
1481
       if( result != paNoError )
 
1482
           goto error;
 
1483
       stream->inputFramesPerBuffer = inputFramesPerBuffer;
 
1484
       stream->outputFramesPerBuffer = outputFramesPerBuffer;
 
1485
    }
 
1486
 
 
1487
    if( stream->inputUnit ) {
 
1488
       const size_t szfl = sizeof(float);
 
1489
       /* setup the AudioBufferList used for input */
 
1490
       bzero( &stream->inputAudioBufferList, sizeof( AudioBufferList ) );
 
1491
       stream->inputAudioBufferList.mNumberBuffers = 1;
 
1492
       stream->inputAudioBufferList.mBuffers[0].mNumberChannels
 
1493
                 = inputChannelCount;
 
1494
       stream->inputAudioBufferList.mBuffers[0].mDataByteSize
 
1495
                 = stream->inputFramesPerBuffer*inputChannelCount*szfl;
 
1496
       stream->inputAudioBufferList.mBuffers[0].mData
 
1497
                 = (float *) calloc(
 
1498
                               stream->inputFramesPerBuffer*inputChannelCount,
 
1499
                               szfl );
 
1500
       if( !stream->inputAudioBufferList.mBuffers[0].mData )
 
1501
       {
 
1502
          result = paInsufficientMemory;
 
1503
          goto error;
 
1504
       }
 
1505
        
 
1506
       /*
 
1507
        * If input and output devs are different or we are doing SR conversion,
 
1508
        * we also need a
 
1509
        * ring buffer to store inpt data while waiting for output
 
1510
        * data.
 
1511
        */
 
1512
       if( (stream->outputUnit && stream->inputUnit != stream->outputUnit)
 
1513
           || stream->inputSRConverter )
 
1514
       {
 
1515
          /* May want the ringSize ot initial position in
 
1516
             ring buffer to depend somewhat on sample rate change */
 
1517
 
 
1518
          void *data;
 
1519
          long ringSize;
 
1520
 
 
1521
          ringSize = computeRingBufferSize( inputParameters,
 
1522
                                            outputParameters,
 
1523
                                            stream->inputFramesPerBuffer,
 
1524
                                            stream->outputFramesPerBuffer,
 
1525
                                            sampleRate );
 
1526
          /*ringSize <<= 4; *//*16x bigger, for testing */
 
1527
 
 
1528
 
 
1529
          /*now, we need to allocate memory for the ring buffer*/
 
1530
          data = calloc( ringSize, szfl );
 
1531
          if( !data )
 
1532
          {
 
1533
             result = paInsufficientMemory;
 
1534
             goto error;
 
1535
          }
 
1536
 
 
1537
          /* now we can initialize the ring buffer */
 
1538
          PaUtil_InitializeRingBuffer( &stream->inputRingBuffer,
 
1539
                                   1, ringSize*szfl, data ) ;
 
1540
          /* advance the read point a little, so we are reading from the
 
1541
             middle of the buffer */
 
1542
          if( stream->outputUnit )
 
1543
             PaUtil_AdvanceRingBufferWriteIndex( &stream->inputRingBuffer, ringSize*szfl / RING_BUFFER_ADVANCE_DENOMINATOR );
 
1544
       }
 
1545
    }
 
1546
 
 
1547
    /* -- initialize Blio Buffer Processors -- */
 
1548
    if( !streamCallback )
 
1549
    {
 
1550
       long ringSize;
 
1551
 
 
1552
       ringSize = computeRingBufferSize( inputParameters,
 
1553
                                         outputParameters,
 
1554
                                         stream->inputFramesPerBuffer,
 
1555
                                         stream->outputFramesPerBuffer,
 
1556
                                         sampleRate );
 
1557
       result = initializeBlioRingBuffers( &stream->blio,
 
1558
              inputParameters?inputParameters->sampleFormat:0 ,
 
1559
              outputParameters?outputParameters->sampleFormat:0 ,
 
1560
              MAX(stream->inputFramesPerBuffer,stream->outputFramesPerBuffer),
 
1561
              ringSize,
 
1562
              inputParameters?inputChannelCount:0 ,
 
1563
              outputParameters?outputChannelCount:0 ) ;
 
1564
       if( result != paNoError )
 
1565
          goto error;
 
1566
    }
 
1567
 
 
1568
    /* -- initialize Buffer Processor -- */
 
1569
    {
 
1570
       unsigned long maxHostFrames = stream->inputFramesPerBuffer;
 
1571
       if( stream->outputFramesPerBuffer > maxHostFrames )
 
1572
          maxHostFrames = stream->outputFramesPerBuffer;
 
1573
       result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
 
1574
                 inputChannelCount, inputSampleFormat,
 
1575
                 hostInputSampleFormat,
 
1576
                 outputChannelCount, outputSampleFormat,
 
1577
                 hostOutputSampleFormat,
 
1578
                 sampleRate,
 
1579
                 streamFlags,
 
1580
                 framesPerBuffer,
 
1581
                 /* If sample rate conversion takes place, the buffer size
 
1582
                    will not be known. */
 
1583
                 maxHostFrames,
 
1584
                 stream->inputSRConverter
 
1585
                              ? paUtilUnknownHostBufferSize
 
1586
                              : paUtilBoundedHostBufferSize,
 
1587
                 streamCallback ? streamCallback : BlioCallback,
 
1588
                 streamCallback ? userData : &stream->blio );
 
1589
       if( result != paNoError )
 
1590
           goto error;
 
1591
    }
 
1592
    stream->bufferProcessorIsInitialized = TRUE;
 
1593
 
 
1594
    /*
 
1595
        IMPLEMENT ME: initialise the following fields with estimated or actual
 
1596
        values.
 
1597
        I think this is okay the way it is br 12/1/05
 
1598
        maybe need to change input latency estimate if IO devs differ
 
1599
    */
 
1600
    stream->streamRepresentation.streamInfo.inputLatency =
 
1601
            PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)/sampleRate;
 
1602
    stream->streamRepresentation.streamInfo.outputLatency =
 
1603
            PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)/sampleRate;
 
1604
    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
 
1605
 
 
1606
    stream->sampleRate  = sampleRate;
 
1607
    stream->outDeviceSampleRate = 0;
 
1608
    if( stream->outputUnit ) {
 
1609
       Float64 rate;
 
1610
       UInt32 size = sizeof( rate );
 
1611
       result = ERR( AudioDeviceGetProperty( stream->outputDevice,
 
1612
                                    0,
 
1613
                                    FALSE,
 
1614
                                    kAudioDevicePropertyNominalSampleRate,
 
1615
                                    &size, &rate ) );
 
1616
       if( result )
 
1617
          goto error;
 
1618
       stream->outDeviceSampleRate = rate;
 
1619
    }
 
1620
    stream->inDeviceSampleRate = 0;
 
1621
    if( stream->inputUnit ) {
 
1622
       Float64 rate;
 
1623
       UInt32 size = sizeof( rate );
 
1624
       result = ERR( AudioDeviceGetProperty( stream->inputDevice,
 
1625
                                    0,
 
1626
                                    TRUE,
 
1627
                                    kAudioDevicePropertyNominalSampleRate,
 
1628
                                    &size, &rate ) );
 
1629
       if( result )
 
1630
          goto error;
 
1631
       stream->inDeviceSampleRate = rate;
 
1632
    }
 
1633
    stream->userInChan  = inputChannelCount;
 
1634
    stream->userOutChan = outputChannelCount;
 
1635
 
 
1636
    stream->isTimeSet   = FALSE;
 
1637
    stream->state = STOPPED;
 
1638
    stream->xrunFlags = 0;
 
1639
 
 
1640
    *s = (PaStream*)stream;
 
1641
 
 
1642
    return result;
 
1643
 
 
1644
error:
 
1645
    CloseStream( stream );
 
1646
    return result;
 
1647
}
 
1648
 
 
1649
PaTime GetStreamTime( PaStream *s )
 
1650
{
 
1651
   /* FIXME: I am not at all sure this timing info stuff is right.
 
1652
             patest_sine_time reports negative latencies, which is wierd.*/
 
1653
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
1654
    AudioTimeStamp timeStamp;
 
1655
 
 
1656
    VVDBUG(("GetStreamTime()\n"));
 
1657
 
 
1658
    if ( !stream->isTimeSet )
 
1659
        return (PaTime)0;
 
1660
 
 
1661
    if ( stream->outputDevice ) {
 
1662
        AudioDeviceGetCurrentTime( stream->outputDevice, &timeStamp);
 
1663
        return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->outDeviceSampleRate;
 
1664
    } else if ( stream->inputDevice ) {
 
1665
        AudioDeviceGetCurrentTime( stream->inputDevice, &timeStamp);
 
1666
    return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->inDeviceSampleRate;
 
1667
    } else {
 
1668
        return (PaTime)0;
 
1669
    }
 
1670
}
 
1671
 
 
1672
static void setStreamStartTime( PaStream *stream )
 
1673
{
 
1674
   /* FIXME: I am not at all sure this timing info stuff is right.
 
1675
             patest_sine_time reports negative latencies, which is wierd.*/
 
1676
   PaMacCoreStream *s = (PaMacCoreStream *) stream;
 
1677
   VVDBUG(("setStreamStartTime()\n"));
 
1678
   if( s->outputDevice )
 
1679
      AudioDeviceGetCurrentTime( s->outputDevice, &s->startTime);
 
1680
   else if( s->inputDevice )
 
1681
      AudioDeviceGetCurrentTime( s->inputDevice, &s->startTime);
 
1682
   else
 
1683
      bzero( &s->startTime, sizeof( s->startTime ) );
 
1684
 
 
1685
   //FIXME: we need a memory barier here
 
1686
 
 
1687
   s->isTimeSet = TRUE;
 
1688
}
 
1689
 
 
1690
 
 
1691
static PaTime TimeStampToSecs(PaMacCoreStream *stream, const AudioTimeStamp* timeStamp)
 
1692
{
 
1693
    VVDBUG(("TimeStampToSecs()\n"));
 
1694
    //printf( "ATS: %lu, %g, %g\n", timeStamp->mFlags, timeStamp->mSampleTime, timeStamp->mRateScalar );
 
1695
    if (timeStamp->mFlags & kAudioTimeStampSampleTimeValid)
 
1696
        return (timeStamp->mSampleTime / stream->sampleRate);
 
1697
    else
 
1698
        return 0;
 
1699
}
 
1700
 
 
1701
#define RING_BUFFER_EMPTY (1000)
 
1702
 
 
1703
static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter, 
 
1704
                             UInt32*ioDataSize, 
 
1705
                             void** outData, 
 
1706
                             void*inUserData )
 
1707
{
 
1708
   void *dummyData;
 
1709
   long dummySize;
 
1710
   PaUtilRingBuffer *rb = (PaUtilRingBuffer *) inUserData;
 
1711
 
 
1712
   VVDBUG(("ringBufferIOProc()\n"));
 
1713
 
 
1714
   assert( sizeof( UInt32 ) == sizeof( long ) );
 
1715
   if( PaUtil_GetRingBufferReadAvailable( rb ) == 0 ) {
 
1716
      *outData = NULL;
 
1717
      *ioDataSize = 0;
 
1718
      return RING_BUFFER_EMPTY;
 
1719
   }
 
1720
   PaUtil_GetRingBufferReadRegions( rb, *ioDataSize,
 
1721
                                    outData, (long *)ioDataSize, 
 
1722
                                    &dummyData, &dummySize );
 
1723
      
 
1724
   assert( *ioDataSize );
 
1725
   PaUtil_AdvanceRingBufferReadIndex( rb, *ioDataSize );
 
1726
 
 
1727
   return noErr;
 
1728
}
 
1729
 
 
1730
/*
 
1731
 * Called by the AudioUnit API to process audio from the sound card.
 
1732
 * This is where the magic happens.
 
1733
 */
 
1734
/* FEEDBACK: there is a lot of redundant code here because of how all the cases differ. This makes it hard to maintain, so if there are suggestinos for cleaning it up, I'm all ears. */
 
1735
static OSStatus AudioIOProc( void *inRefCon,
 
1736
                               AudioUnitRenderActionFlags *ioActionFlags,
 
1737
                               const AudioTimeStamp *inTimeStamp,
 
1738
                               UInt32 inBusNumber,
 
1739
                               UInt32 inNumberFrames,
 
1740
                               AudioBufferList *ioData )
 
1741
{
 
1742
   unsigned long framesProcessed     = 0;
 
1743
   PaStreamCallbackTimeInfo timeInfo = {0,0,0};
 
1744
   PaMacCoreStream *stream           = (PaMacCoreStream*)inRefCon;
 
1745
   const bool isRender               = inBusNumber == OUTPUT_ELEMENT;
 
1746
   int callbackResult                = paContinue ;
 
1747
 
 
1748
   VVDBUG(("AudioIOProc()\n"));
 
1749
 
 
1750
   PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
 
1751
 
 
1752
   /* -----------------------------------------------------------------*\
 
1753
      This output may be useful for debugging,
 
1754
      But printing durring the callback is a bad enough idea that
 
1755
      this is not enabled by enableing the usual debugging calls.
 
1756
   \* -----------------------------------------------------------------*/
 
1757
   /*
 
1758
   static int renderCount = 0;
 
1759
   static int inputCount = 0;
 
1760
   printf( "-------------------  starting reder/input\n" );
 
1761
   if( isRender )
 
1762
      printf("Render callback (%d):\t", ++renderCount);
 
1763
   else
 
1764
      printf("Input callback  (%d):\t", ++inputCount);
 
1765
   printf( "Call totals: %d (input), %d (render)\n", inputCount, renderCount );
 
1766
 
 
1767
   printf( "--- inBusNumber: %lu\n", inBusNumber );
 
1768
   printf( "--- inNumberFrames: %lu\n", inNumberFrames );
 
1769
   printf( "--- %x ioData\n", (unsigned) ioData );
 
1770
   if( ioData )
 
1771
   {
 
1772
      int i=0;
 
1773
      printf( "--- ioData.mNumBuffers %lu: \n", ioData->mNumberBuffers );
 
1774
      for( i=0; i<ioData->mNumberBuffers; ++i )
 
1775
         printf( "--- ioData buffer %d size: %lu.\n", i, ioData->mBuffers[i].mDataByteSize );
 
1776
   }
 
1777
      ----------------------------------------------------------------- */
 
1778
 
 
1779
   if( !stream->isTimeSet )
 
1780
      setStreamStartTime( stream );
 
1781
 
 
1782
   if( isRender ) {
 
1783
      AudioTimeStamp currentTime;
 
1784
      timeInfo.outputBufferDacTime = TimeStampToSecs(stream, inTimeStamp);
 
1785
      AudioDeviceGetCurrentTime(stream->outputDevice, &currentTime);
 
1786
      timeInfo.currentTime = TimeStampToSecs(stream, &currentTime);
 
1787
   }
 
1788
   if( isRender && stream->inputUnit == stream->outputUnit )
 
1789
      timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp);
 
1790
   if( !isRender ) {
 
1791
      AudioTimeStamp currentTime;
 
1792
      timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp);
 
1793
      AudioDeviceGetCurrentTime(stream->inputDevice, &currentTime);
 
1794
      timeInfo.currentTime = TimeStampToSecs(stream, &currentTime);
 
1795
   }
 
1796
 
 
1797
   //printf( "---%g, %g, %g\n", timeInfo.inputBufferAdcTime, timeInfo.currentTime, timeInfo.outputBufferDacTime );
 
1798
 
 
1799
   if( isRender && stream->inputUnit == stream->outputUnit
 
1800
                && !stream->inputSRConverter )
 
1801
   {
 
1802
      /* --------- Full Duplex, One Device, no SR Conversion -------
 
1803
       *
 
1804
       * This is the lowest latency case, and also the simplest.
 
1805
       * Input data and output data are available at the same time.
 
1806
       * we do not use the input SR converter or the input ring buffer.
 
1807
       *
 
1808
       */
 
1809
      OSStatus err = 0;
 
1810
      unsigned long frames;
 
1811
 
 
1812
      /* -- start processing -- */
 
1813
      PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
 
1814
                                    &timeInfo,
 
1815
                                    stream->xrunFlags );
 
1816
      stream->xrunFlags = 0; //FIXME: this flag also gets set outside by a callback, which calls the xrunCallback function. It should be in the same thread as the main audio callback, but the apple docs just use the word "usually" so it may be possible to loose an xrun notification, if that callback happens here.
 
1817
 
 
1818
      /* -- compute frames. do some checks -- */
 
1819
      assert( ioData->mNumberBuffers == 1 );
 
1820
      assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan );
 
1821
      frames = ioData->mBuffers[0].mDataByteSize;
 
1822
      frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels;
 
1823
      /* -- copy and process input data -- */
 
1824
      err= AudioUnitRender(stream->inputUnit,
 
1825
                    ioActionFlags,
 
1826
                    inTimeStamp,
 
1827
                    INPUT_ELEMENT,
 
1828
                    inNumberFrames,
 
1829
                    &stream->inputAudioBufferList );
 
1830
      /* FEEDBACK: I'm not sure what to do when this call fails. There's nothing in the PA API to
 
1831
       * do about failures in the callback system. */
 
1832
      assert( !err );
 
1833
 
 
1834
      PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
 
1835
      PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
 
1836
                          0,
 
1837
                          stream->inputAudioBufferList.mBuffers[0].mData,
 
1838
                          stream->inputAudioBufferList.mBuffers[0].mNumberChannels);
 
1839
      /* -- Copy and process output data -- */
 
1840
      PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames );
 
1841
      PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor),
 
1842
                                        0,
 
1843
                                        ioData->mBuffers[0].mData,
 
1844
                                        ioData->mBuffers[0].mNumberChannels);
 
1845
      /* -- complete processing -- */
 
1846
      framesProcessed =
 
1847
                 PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
 
1848
                                             &callbackResult );
 
1849
   }
 
1850
   else if( isRender )
 
1851
   {
 
1852
      /* -------- Output Side of Full Duplex (Separate Devices or SR Conversion)
 
1853
       *       -- OR Simplex Output
 
1854
       *
 
1855
       * This case handles output data as in the full duplex case,
 
1856
       * and, if there is input data, reads it off the ring buffer 
 
1857
       * and into the PA buffer processor. If sample rate conversion
 
1858
       * is required on input, that is done here as well.
 
1859
       */
 
1860
      unsigned long frames;
 
1861
 
 
1862
      /* Sometimes, when stopping a duplex stream we get erroneous
 
1863
         xrun flags, so if this is our last run, clear the flags. */
 
1864
      int xrunFlags = stream->xrunFlags;
 
1865
/*
 
1866
      if( xrunFlags & paInputUnderflow )
 
1867
         printf( "input underflow.\n" );
 
1868
      if( xrunFlags & paInputOverflow )
 
1869
         printf( "input overflow.\n" );
 
1870
*/
 
1871
      if( stream->state == STOPPING || stream->state == CALLBACK_STOPPED )
 
1872
         xrunFlags = 0;
 
1873
 
 
1874
      /* -- start processing -- */
 
1875
      PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
 
1876
                                    &timeInfo,
 
1877
                                    xrunFlags );
 
1878
      stream->xrunFlags = 0; /* FEEDBACK: we only send flags to Buf Proc once */
 
1879
 
 
1880
      /* -- Copy and process output data -- */
 
1881
      assert( ioData->mNumberBuffers == 1 );
 
1882
      frames = ioData->mBuffers[0].mDataByteSize;
 
1883
      frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels;
 
1884
      assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan );
 
1885
      PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames );
 
1886
      PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor),
 
1887
                                     0,
 
1888
                                     ioData->mBuffers[0].mData,
 
1889
                                     ioData->mBuffers[0].mNumberChannels);
 
1890
 
 
1891
      /* -- copy and process input data, and complete processing -- */
 
1892
      if( stream->inputUnit ) {
 
1893
         const int flsz = sizeof( float );
 
1894
         /* Here, we read the data out of the ring buffer, through the
 
1895
            audio converter. */
 
1896
         int inChan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels;
 
1897
         if( stream->inputSRConverter )
 
1898
         {
 
1899
               OSStatus err;
 
1900
               UInt32 size;
 
1901
               float data[ inChan * frames ];
 
1902
               size = sizeof( data );
 
1903
               err = AudioConverterFillBuffer( 
 
1904
                             stream->inputSRConverter,
 
1905
                             ringBufferIOProc,
 
1906
                             &stream->inputRingBuffer,
 
1907
                             &size,
 
1908
                             (void *)&data );
 
1909
               if( err == RING_BUFFER_EMPTY )
 
1910
               { /*the ring buffer callback underflowed */
 
1911
                  err = 0;
 
1912
                  bzero( ((char *)data) + size, sizeof(data)-size );
 
1913
                  stream->xrunFlags |= paInputUnderflow;
 
1914
               }
 
1915
               ERR( err );
 
1916
               assert( !err );
 
1917
               
 
1918
               PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
 
1919
               PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
 
1920
                                   0,
 
1921
                                   data,
 
1922
                                   inChan );
 
1923
               framesProcessed =
 
1924
                    PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
 
1925
                                                &callbackResult );
 
1926
         }
 
1927
         else
 
1928
         {
 
1929
            /* Without the AudioConverter is actually a bit more complex
 
1930
               because we have to do a little buffer processing that the
 
1931
               AudioConverter would otherwise handle for us. */
 
1932
            void *data1, *data2;
 
1933
            long size1, size2;
 
1934
            PaUtil_GetRingBufferReadRegions( &stream->inputRingBuffer,
 
1935
                                             inChan*frames*flsz,
 
1936
                                             &data1, &size1,
 
1937
                                             &data2, &size2 );
 
1938
            if( size1 / ( flsz * inChan ) == frames ) {
 
1939
               /* simplest case: all in first buffer */
 
1940
               PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
 
1941
               PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
 
1942
                                   0,
 
1943
                                   data1,
 
1944
                                   inChan );
 
1945
               framesProcessed =
 
1946
                    PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
 
1947
                                                &callbackResult );
 
1948
               PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, size1 );
 
1949
            } else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) {
 
1950
               /*we underflowed. take what data we can, zero the rest.*/
 
1951
               unsigned char data[frames*inChan*flsz];
 
1952
               if( size1 )
 
1953
                  memcpy( data, data1, size1 );
 
1954
               if( size2 )
 
1955
                  memcpy( data+size1, data2, size2 );
 
1956
               bzero( data+size1+size2, frames*flsz*inChan - size1 - size2 );
 
1957
 
 
1958
               PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
 
1959
               PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
 
1960
                                   0,
 
1961
                                   data,
 
1962
                                   inChan );
 
1963
               framesProcessed =
 
1964
                    PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
 
1965
                                                &callbackResult );
 
1966
               PaUtil_AdvanceRingBufferReadIndex( &stream->inputRingBuffer,
 
1967
                                                  size1+size2 );
 
1968
               /* flag underflow */
 
1969
               stream->xrunFlags |= paInputUnderflow;
 
1970
            } else {
 
1971
               /*we got all the data, but split between buffers*/
 
1972
               PaUtil_SetInputFrameCount( &(stream->bufferProcessor),
 
1973
                                          size1 / ( flsz * inChan ) );
 
1974
               PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
 
1975
                                   0,
 
1976
                                   data1,
 
1977
                                   inChan );
 
1978
               PaUtil_Set2ndInputFrameCount( &(stream->bufferProcessor),
 
1979
                                             size2 / ( flsz * inChan ) );
 
1980
               PaUtil_Set2ndInterleavedInputChannels( &(stream->bufferProcessor),
 
1981
                                   0,
 
1982
                                   data2,
 
1983
                                   inChan );
 
1984
               framesProcessed =
 
1985
                    PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
 
1986
                                                &callbackResult );
 
1987
               PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, size1+size2 );
 
1988
            }
 
1989
         }
 
1990
      } else {
 
1991
         framesProcessed =
 
1992
                 PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
 
1993
                                             &callbackResult );
 
1994
      }
 
1995
 
 
1996
   }
 
1997
   else
 
1998
   {
 
1999
      /* ------------------ Input
 
2000
       *
 
2001
       * First, we read off the audio data and put it in the ring buffer.
 
2002
       * if this is an input-only stream, we need to process it more,
 
2003
       * otherwise, we let the output case deal with it.
 
2004
       */
 
2005
      OSStatus err = 0;
 
2006
      int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ;
 
2007
      /* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */
 
2008
      do {
 
2009
         err= AudioUnitRender(stream->inputUnit,
 
2010
                 ioActionFlags,
 
2011
                 inTimeStamp,
 
2012
                 INPUT_ELEMENT,
 
2013
                 inNumberFrames,
 
2014
                 &stream->inputAudioBufferList );
 
2015
         if( err == -10874 )
 
2016
            inNumberFrames /= 2;
 
2017
      } while( err == -10874 && inNumberFrames > 1 );
 
2018
      /* FEEDBACK: I'm not sure what to do when this call fails */
 
2019
      ERR( err );
 
2020
      assert( !err );
 
2021
      if( stream->inputSRConverter || stream->outputUnit )
 
2022
      {
 
2023
         /* If this is duplex or we use a converter, put the data
 
2024
            into the ring buffer. */
 
2025
         long bytesIn, bytesOut;
 
2026
         bytesIn = sizeof( float ) * inNumberFrames * chan;
 
2027
         bytesOut = PaUtil_WriteRingBuffer( &stream->inputRingBuffer,
 
2028
                                            stream->inputAudioBufferList.mBuffers[0].mData,
 
2029
                                            bytesIn );
 
2030
         if( bytesIn != bytesOut )
 
2031
            stream->xrunFlags |= paInputOverflow ;
 
2032
      }
 
2033
      else
 
2034
      {
 
2035
         /* for simplex input w/o SR conversion,
 
2036
            just pop the data into the buffer processor.*/
 
2037
         PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
 
2038
                              &timeInfo,
 
2039
                              stream->xrunFlags );
 
2040
         stream->xrunFlags = 0;
 
2041
 
 
2042
         PaUtil_SetInputFrameCount( &(stream->bufferProcessor), inNumberFrames);
 
2043
         PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
 
2044
                             0,
 
2045
                             stream->inputAudioBufferList.mBuffers[0].mData,
 
2046
                             chan );
 
2047
         framesProcessed =
 
2048
              PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
 
2049
                                          &callbackResult );
 
2050
      }
 
2051
      if( !stream->outputUnit && stream->inputSRConverter )
 
2052
      {
 
2053
         /* ------------------ Simplex Input w/ SR Conversion
 
2054
          *
 
2055
          * if this is a simplex input stream, we need to read off the buffer,
 
2056
          * do our sample rate conversion and pass the results to the buffer
 
2057
          * processor.
 
2058
          * The logic here is complicated somewhat by the fact that we don't
 
2059
          * know how much data is available, so we loop on reasonably sized
 
2060
          * chunks, and let the BufferProcessor deal with the rest.
 
2061
          *
 
2062
          */
 
2063
         /*This might be too big or small depending on SR conversion*/
 
2064
         float data[ chan * inNumberFrames ];
 
2065
         OSStatus err;
 
2066
         do
 
2067
         { /*Run the buffer processor until we are out of data*/
 
2068
            UInt32 size;
 
2069
            long f;
 
2070
 
 
2071
            size = sizeof( data );
 
2072
            err = AudioConverterFillBuffer( 
 
2073
                          stream->inputSRConverter,
 
2074
                          ringBufferIOProc,
 
2075
                          &stream->inputRingBuffer,
 
2076
                          &size,
 
2077
                          (void *)data );
 
2078
            if( err != RING_BUFFER_EMPTY )
 
2079
               ERR( err );
 
2080
            assert( err == 0 || err == RING_BUFFER_EMPTY );
 
2081
 
 
2082
            f = size / ( chan * sizeof(float) );
 
2083
            PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f );
 
2084
            if( f )
 
2085
            {
 
2086
               PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
 
2087
                                             &timeInfo,
 
2088
                                             stream->xrunFlags );
 
2089
               stream->xrunFlags = 0;
 
2090
 
 
2091
               PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
 
2092
                                0,
 
2093
                                data,
 
2094
                                chan );
 
2095
               framesProcessed =
 
2096
                    PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
 
2097
                                                &callbackResult );
 
2098
            }
 
2099
         } while( callbackResult == paContinue && !err );
 
2100
      }
 
2101
   }
 
2102
 
 
2103
   switch( callbackResult )
 
2104
   {
 
2105
   case paContinue: break;
 
2106
   case paComplete:
 
2107
   case paAbort:
 
2108
      stream->isTimeSet = FALSE;
 
2109
      stream->state = CALLBACK_STOPPED ;
 
2110
      if( stream->outputUnit )
 
2111
         AudioOutputUnitStop(stream->outputUnit);
 
2112
      if( stream->inputUnit )
 
2113
         AudioOutputUnitStop(stream->inputUnit);
 
2114
      break;
 
2115
   }
 
2116
 
 
2117
   PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
 
2118
   return noErr;
 
2119
}
 
2120
 
 
2121
 
 
2122
/*
 
2123
    When CloseStream() is called, the multi-api layer ensures that
 
2124
    the stream has already been stopped or aborted.
 
2125
*/
 
2126
static PaError CloseStream( PaStream* s )
 
2127
{
 
2128
    /* This may be called from a failed OpenStream.
 
2129
       Therefore, each piece of info is treated seperately. */
 
2130
    PaError result = paNoError;
 
2131
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
2132
 
 
2133
    VVDBUG(("CloseStream()\n"));
 
2134
    VDBUG( ( "Closing stream.\n" ) );
 
2135
 
 
2136
    if( stream ) {
 
2137
       if( stream->outputUnit ) {
 
2138
          int count = removeFromXRunListenerList( stream );
 
2139
          if( count == 0 )
 
2140
             AudioDeviceRemovePropertyListener( stream->outputDevice,
 
2141
                                                0,
 
2142
                                                false,
 
2143
                                                kAudioDeviceProcessorOverload,
 
2144
                                                xrunCallback );
 
2145
       }
 
2146
       if( stream->inputUnit && stream->outputUnit != stream->inputUnit ) {
 
2147
          int count = removeFromXRunListenerList( stream );
 
2148
          if( count == 0 )
 
2149
             AudioDeviceRemovePropertyListener( stream->inputDevice,
 
2150
                                                0,
 
2151
                                                true,
 
2152
                                                kAudioDeviceProcessorOverload,
 
2153
                                                xrunCallback );
 
2154
       }
 
2155
       if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
 
2156
          AudioUnitUninitialize( stream->outputUnit );
 
2157
          CloseComponent( stream->outputUnit );
 
2158
       }
 
2159
       stream->outputUnit = NULL;
 
2160
       if( stream->inputUnit )
 
2161
       {
 
2162
          AudioUnitUninitialize( stream->inputUnit );
 
2163
          CloseComponent( stream->inputUnit );
 
2164
          stream->inputUnit = NULL;
 
2165
       }
 
2166
       if( stream->inputRingBuffer.buffer )
 
2167
          free( (void *) stream->inputRingBuffer.buffer );
 
2168
       stream->inputRingBuffer.buffer = NULL;
 
2169
       /*TODO: is there more that needs to be done on error
 
2170
               from AudioConverterDispose?*/
 
2171
       if( stream->inputSRConverter )
 
2172
          ERR( AudioConverterDispose( stream->inputSRConverter ) );
 
2173
       stream->inputSRConverter = NULL;
 
2174
       if( stream->inputAudioBufferList.mBuffers[0].mData )
 
2175
          free( stream->inputAudioBufferList.mBuffers[0].mData );
 
2176
       stream->inputAudioBufferList.mBuffers[0].mData = NULL;
 
2177
 
 
2178
       result = destroyBlioRingBuffers( &stream->blio );
 
2179
       if( result )
 
2180
          return result;
 
2181
       if( stream->bufferProcessorIsInitialized )
 
2182
          PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
 
2183
       PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
 
2184
       PaUtil_FreeMemory( stream );
 
2185
    }
 
2186
 
 
2187
    return result;
 
2188
}
 
2189
 
 
2190
static PaError StartStream( PaStream *s )
 
2191
{
 
2192
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
2193
    OSStatus result = noErr;
 
2194
    VVDBUG(("StartStream()\n"));
 
2195
    VDBUG( ( "Starting stream.\n" ) );
 
2196
 
 
2197
#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
 
2198
 
 
2199
    /*FIXME: maybe want to do this on close/abort for faster start? */
 
2200
    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
 
2201
    if(  stream->inputSRConverter )
 
2202
       ERR_WRAP( AudioConverterReset( stream->inputSRConverter ) );
 
2203
 
 
2204
    /* -- start -- */
 
2205
    stream->state = ACTIVE;
 
2206
    if( stream->inputUnit ) {
 
2207
       ERR_WRAP( AudioOutputUnitStart(stream->inputUnit) );
 
2208
    }
 
2209
    if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
 
2210
       ERR_WRAP( AudioOutputUnitStart(stream->outputUnit) );
 
2211
    }
 
2212
 
 
2213
    //setStreamStartTime( stream );
 
2214
    //stream->isTimeSet = TRUE;
 
2215
 
 
2216
    return paNoError;
 
2217
#undef ERR_WRAP
 
2218
}
 
2219
 
 
2220
// it's not clear from appl's docs that this really waits
 
2221
// until all data is flushed.
 
2222
static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element )
 
2223
{
 
2224
    Boolean isRunning = 1;
 
2225
    while( isRunning ) {
 
2226
       UInt32 s = sizeof( isRunning );
 
2227
       ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element,  &isRunning, &s );
 
2228
       if( err )
 
2229
          return err;
 
2230
       Pa_Sleep( 100 );
 
2231
    }
 
2232
    return noErr;
 
2233
}
 
2234
 
 
2235
static PaError StopStream( PaStream *s )
 
2236
{
 
2237
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
2238
    OSStatus result = noErr;
 
2239
    PaError paErr;
 
2240
    VVDBUG(("StopStream()\n"));
 
2241
 
 
2242
    VDBUG( ("Waiting for BLIO.\n") );
 
2243
    waitUntilBlioWriteBufferIsFlushed( &stream->blio );
 
2244
    VDBUG( ( "Stopping stream.\n" ) );
 
2245
 
 
2246
    stream->isTimeSet = FALSE;
 
2247
    stream->state = STOPPING;
 
2248
 
 
2249
#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
 
2250
    /* -- stop and reset -- */
 
2251
    if( stream->inputUnit == stream->outputUnit && stream->inputUnit )
 
2252
    {
 
2253
       ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) );
 
2254
       ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,0) );
 
2255
       ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,1) );
 
2256
       ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) );
 
2257
       ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) );
 
2258
    }
 
2259
    else
 
2260
    {
 
2261
       if( stream->inputUnit )
 
2262
       {
 
2263
          ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) );
 
2264
          ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,1) );
 
2265
          ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1));
 
2266
       }
 
2267
       if( stream->outputUnit )
 
2268
       {
 
2269
          ERR_WRAP(AudioOutputUnitStop(stream->outputUnit));
 
2270
          ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->outputUnit,0) );
 
2271
          ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0));
 
2272
       }
 
2273
    }
 
2274
    if( stream->inputRingBuffer.buffer ) {
 
2275
       PaUtil_FlushRingBuffer( &stream->inputRingBuffer );
 
2276
       bzero( (void *)stream->inputRingBuffer.buffer,
 
2277
              stream->inputRingBuffer.bufferSize );
 
2278
       /* advance the write point a little, so we are reading from the
 
2279
          middle of the buffer. We'll need extra at the end because
 
2280
          testing has shown that this helps. */
 
2281
       if( stream->outputUnit )
 
2282
          PaUtil_AdvanceRingBufferWriteIndex( &stream->inputRingBuffer,
 
2283
                                              stream->inputRingBuffer.bufferSize
 
2284
                                              / RING_BUFFER_ADVANCE_DENOMINATOR );
 
2285
    }
 
2286
 
 
2287
    stream->xrunFlags = 0;
 
2288
    stream->state = STOPPED;
 
2289
 
 
2290
    paErr = resetBlioRingBuffers( &stream->blio );
 
2291
    if( paErr )
 
2292
       return paErr;
 
2293
 
 
2294
/*
 
2295
    //stream->isTimeSet = FALSE;
 
2296
*/
 
2297
 
 
2298
    VDBUG( ( "Stream Stopped.\n" ) );
 
2299
    return paNoError;
 
2300
#undef ERR_WRAP
 
2301
}
 
2302
 
 
2303
static PaError AbortStream( PaStream *s )
 
2304
{
 
2305
    VVDBUG(("AbortStream()->StopStream()\n"));
 
2306
    VDBUG( ( "Aborting stream.\n" ) );
 
2307
    /* We have nothing faster than StopStream. */
 
2308
    return StopStream(s);
 
2309
}
 
2310
 
 
2311
 
 
2312
static PaError IsStreamStopped( PaStream *s )
 
2313
{
 
2314
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
2315
    VVDBUG(("IsStreamStopped()\n"));
 
2316
 
 
2317
    return stream->state == STOPPED ? 1 : 0;
 
2318
}
 
2319
 
 
2320
 
 
2321
static PaError IsStreamActive( PaStream *s )
 
2322
{
 
2323
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
2324
    VVDBUG(("IsStreamActive()\n"));
 
2325
    return ( stream->state == ACTIVE || stream->state == STOPPING );
 
2326
}
 
2327
 
 
2328
 
 
2329
static double GetStreamCpuLoad( PaStream* s )
 
2330
{
 
2331
    PaMacCoreStream *stream = (PaMacCoreStream*)s;
 
2332
    VVDBUG(("GetStreamCpuLoad()\n"));
 
2333
 
 
2334
    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
 
2335
}