Logo Search packages:      
Sourcecode: csound version File versions  Download package

ComplexPlayThru.cpp

/*

 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
 Computer, Inc. ("Apple") in consideration of your agreement to the
 following terms, and your use, installation, modification or
 redistribution of this Apple software constitutes acceptance of these
 terms.  If you do not agree with these terms, please do not use,
 install, modify or redistribute this Apple software.

 In consideration of your agreement to abide by the following terms, and
 subject to these terms, Apple grants you a personal, non-exclusive
 license, under Apple's copyrights in this original Apple software (the
 "Apple Software"), to use, reproduce, modify and redistribute the Apple
 Software, with or without modifications, in source and/or binary forms;
 provided that if you redistribute the Apple Software in its entirety and
 without modifications, you must retain this notice and the following
 text and disclaimers in all such redistributions of the Apple Software.
 Neither the name, trademarks, service marks or logos of Apple Computer,
 Inc. may be used to endorse or promote products derived from the Apple
 Software without specific prior written permission from Apple.  Except
 as expressly stated in this notice, no other rights or licenses, express
 or implied, are granted by Apple herein, including but not limited to
 any patent rights that may be infringed by your derivative works or by
 other works in which the Apple Software may be incorporated.

 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.

 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.

 Copyright  2004 Apple Computer, Inc., All Rights Reserved

 */

#include "ComplexPlayThru.h"
void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
{
  if (!inDesc) {
    printf ("Can't print a NULL desc!\n");
    return;
  }

  printf ("- - - - - - - - - - - - - - - - - - - -\n");
  printf ("  Sample Rate:%f\n", inDesc->mSampleRate);
  printf ("  Format ID:%s\n", (char*)&inDesc->mFormatID);
  printf ("  Format Flags:%lX\n", inDesc->mFormatFlags);
  printf ("  Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
  printf ("  Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
  printf ("  Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
  printf ("  Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
  printf ("  Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
  printf ("- - - - - - - - - - - - - - - - - - - -\n");
}
#pragma mark ---Public Methods---

ComplexPlayThru::ComplexPlayThru():
mBuffer(NULL),
mFirstInputTime(-1),
mFirstOutputTime(-1),
mInToOutSampleOffset(0)
{
        OSStatus err = noErr;
        UInt32 propsize=0;
        AudioDeviceID input, output;

        propsize = sizeof(AudioDeviceID);
        checkErr (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &input));

        propsize = sizeof(AudioDeviceID);
        checkErr (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &output));

        AudioDevice *inDev = new AudioDevice(input, YES);
        inDev->SetBufferSize(RT_BUFFERSIZE); //##QUICK TEST ONLY -- USER SHOULD BE ABLE TO SET THIS
        AudioDevice *outDev = new AudioDevice(output, NO);
        outDev->SetBufferSize(RT_BUFFERSIZE);//##QUICK TEST ONLY -- USER SHOULD BE ABLE TO SET THIS

        err =Init(input,output);
    if(err) {
                fprintf(stderr,"ComplexPlayThru ERROR: Cannot Init ComplexPlayThru");
                exit(1);
        }
}

ComplexPlayThru::ComplexPlayThru(AudioDeviceID input, AudioDeviceID output):
mBuffer(NULL),
mFirstInputTime(-1),
mFirstOutputTime(-1),
mInToOutSampleOffset(0)
{
        OSStatus err = noErr;
        err =Init(input,output);
    if(err) {
                fprintf(stderr,"ComplexPlayThru ERROR: Cannot Init ComplexPlayThru");
                exit(1);
        }
}

ComplexPlayThru::~ComplexPlayThru()
{   //clean up
        Stop();
        AudioDeviceRemovePropertyListener(mInputDevice.mID,
                                                                kAudioPropertyWildcardChannel,
                                                                1,
                                                                kAudioDevicePropertyNominalSampleRate,
                                                                DeviceSampleRateListener);

        delete mBuffer;
        mBuffer = 0;
        if(mInputBuffer){
                for(UInt32 i = 0; i<mInputBuffer->mNumberBuffers; i++)
                        free(mInputBuffer->mBuffers[i].mData);
                free(mInputBuffer);
                mInputBuffer = 0;
        }
        AudioUnitUninitialize(mInputUnit);
        AUGraphClose(mGraph);
        DisposeAUGraph(mGraph);

}

OSStatus ComplexPlayThru::Init(AudioDeviceID input, AudioDeviceID output)
{
    OSStatus err = noErr;

        //Note: You can interface to input and output devices with "output" audio units.
        //Please keep in mind that you are only allowed to have one output audio unit per graph (AUGraph).
        //As you will see, this sample code splits up the two output units.  The "output" unit that will
        //be used for device input will not be contained in a AUGraph, while the "output" unit that will
        //interface the default output device will be in a graph.

        //Setup AUHAL for an input device
        err = SetupAUHAL(input);
        checkErr(err);

        //Setup Graph containing Varispeed Unit & Default Output Unit
        err = SetupGraph(output);
        checkErr(err);

        err = SetupBuffers();
        checkErr(err);

        err =AUGraphInitialize(mGraph);
        checkErr(err);

        //If the sample rates of the devices do not match, we will need to change the varispeed audio unit
        //from the default rate of 1.  The new rate should be  (sample rate of the input device)/ (sample rate
        //of output device).  This will correct devices running at diffent sample rates
        if (mInputDevice.mFormat.mSampleRate != mOutputDevice.mFormat.mSampleRate) {
                Float32 rate = mInputDevice.mFormat.mSampleRate/mOutputDevice.mFormat.mSampleRate ;
                AudioUnitSetParameter(mVarispeedUnit,kVarispeedParam_PlaybackRate,kAudioUnitScope_Global,0, rate,0);
                printf( "Info::Sample rate mismatched: (in=%f / out=%f).\n\t\t Setting Varispeed rate to %f.\n", mInputDevice.mFormat.mSampleRate, mOutputDevice.mFormat.mSampleRate, rate);
        }

        //Channel mapping will specify what channels of the device your audio unit will interact with.
        //Dont actually do Channel Mapping unless it is really needed, the default should work in most cases.
        //The defualt mapping, maps channel 1->1 ,...
        //For example, you can use channel mapping to map channel 4->3, etc.
        //err = MapChannels();
        //checkErr(err);

        //Add a listener for the input device's sample rate
        //the method DeviceSample rate listener will be called if the user
        //decides to change the sample rate of the device
        err = AudioDeviceAddPropertyListener(mInputDevice.mID,
                                                                kAudioPropertyWildcardChannel,
                                                                1,
                                                                kAudioDevicePropertyNominalSampleRate,
                                                                DeviceSampleRateListener,
                                                                this);
        checkErr(err);
        //Add latency between the two devices
        ComputeThruOffset();

        return err;
}

#pragma mark --- Operation---

OSStatus ComplexPlayThru::Start()
{
        OSStatus err = noErr;
        if(!IsRunning()){
                //Start pulling for audio data
                err = AudioOutputUnitStart(mInputUnit);
                err = AUGraphStart(mGraph);

                //reset sample times
                mFirstInputTime = -1;
                mFirstOutputTime = -1;
        }
        return err;
}

OSStatus ComplexPlayThru::Stop()
{
        OSStatus err = noErr;
        if(IsRunning()){
                //Stop the AUHAL
                err = AudioOutputUnitStop(mInputUnit);
                err = AUGraphStop(mGraph);
                mFirstInputTime = -1;
                mFirstOutputTime = -1;
        }
        return err;
}

Boolean ComplexPlayThru::IsRunning()
{
        OSStatus err = noErr;
        UInt32 auhalRunning = 0, size = 0;
        Boolean graphRunning;
        size = sizeof(auhalRunning);

        err = AudioUnitGetProperty(mInputUnit,
                                                          kAudioOutputUnitProperty_IsRunning,
                                                          kAudioUnitScope_Global,
                                                          0, // input element
                                                          &auhalRunning,
                                                          &size);
        err = AUGraphIsRunning(mGraph,&graphRunning);

        return (auhalRunning || graphRunning);
}

OSStatus ComplexPlayThru::SetOutputDeviceAsCurrent(AudioDeviceID out)
{
    UInt32 size = sizeof(AudioDeviceID);;
    OSStatus err = noErr;

        if(out == kAudioDeviceUnknown) //Retrieve the default output device
        {
                err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
                                                                           &size,
                                                                           &out);
        }
        mOutputDevice.Init(out, false);
        checkErr(err);

        //Set the Current Device to the Default Output Unit.
    err = AudioUnitSetProperty(mOutputUnit,
                                                          kAudioOutputUnitProperty_CurrentDevice,
                                                          kAudioUnitScope_Global,
                                                          0,
                                                          &mOutputDevice.mID,
                                                          sizeof(mOutputDevice.mID));
        return err;
}

OSStatus ComplexPlayThru::SetInputDeviceAsCurrent(AudioDeviceID in)
{
    UInt32 size = sizeof(AudioDeviceID);
    OSStatus err = noErr;

        if(in == kAudioDeviceUnknown) //get the default input device if device is unknown
        {
                err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
                                                                           &size,
                                                                           &in);
                checkErr(err);
        }

        mInputDevice.Init(in, true);

        //Set the Current Device to the AUHAL.
        //this should be done only after IO has been enabled on the AUHAL.
    err = AudioUnitSetProperty(mInputUnit,
                                                          kAudioOutputUnitProperty_CurrentDevice,
                                                          kAudioUnitScope_Global,
                                                          0,
                                                          &mInputDevice.mID,
                                                          sizeof(mInputDevice.mID));
        checkErr(err);
        return err;
}

#pragma mark -
#pragma mark --Private methods---
OSStatus ComplexPlayThru::SetupGraph(AudioDeviceID out)
{
        OSStatus err = noErr;
        AURenderCallbackStruct output;

        //Make a New Graph
    NewAUGraph(&mGraph);
        //Open the Graph, AudioUnits are opened but not initialized
    AUGraphOpen(mGraph);
        //AUGraphInitialize(mGraph); Don't initialze graph yet

        err = MakeGraph();
        checkErr(err);

        err = SetOutputDeviceAsCurrent(out);
        checkErr(err);

        output.inputProc = OutputProc;
        output.inputProcRefCon = this;

        err = AudioUnitSetProperty(mVarispeedUnit,
                                                          kAudioUnitProperty_SetRenderCallback,
                                                          kAudioUnitScope_Input,
                                                          0,
                                                          &output,
                                                          sizeof(output));
        checkErr(err);
        return err;
}

OSStatus ComplexPlayThru::MakeGraph()
{
        OSStatus err = noErr;
        ComponentDescription varispeedDesc,outDesc;

        //Q:Why do we need a varispeed unit?
        //A:If the input device and the output device are running at different sample rates
        //we will need to move the data coming to the graph slower/faster to avoid a pitch change.
        varispeedDesc.componentType = kAudioUnitType_FormatConverter;
        varispeedDesc.componentSubType = kAudioUnitSubType_Varispeed;
        varispeedDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
        varispeedDesc.componentFlags = 0;
        varispeedDesc.componentFlagsMask = 0;

        outDesc.componentType = kAudioUnitType_Output;
        outDesc.componentSubType = kAudioUnitSubType_DefaultOutput;
        outDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
        outDesc.componentFlags = 0;
        outDesc.componentFlagsMask = 0;

        //////////////////////////
        ///MAKE NODES
        //This creates a node in the graph that is an AudioUnit, using
        //the supplied ComponentDescription to find and open that unit
        AUGraphNewNode(mGraph, &varispeedDesc, 0, NULL, &mVarispeedNode);
        AUGraphNewNode(mGraph, &outDesc, 0, NULL, &mOutputNode);

        //Get Audio Units from AUGraph node
        AUGraphGetNodeInfo(mGraph, mVarispeedNode, NULL, NULL, NULL, &mVarispeedUnit);
        AUGraphGetNodeInfo(mGraph, mOutputNode, NULL, NULL, NULL, &mOutputUnit);

        //connect nodes
        AUGraphConnectNodeInput(mGraph, mVarispeedNode, 0, mOutputNode, 0);

        AUGraphUpdate(mGraph, NULL);

        return err;
}

OSStatus ComplexPlayThru::SetupAUHAL(AudioDeviceID in)
{
        OSStatus err = noErr;

        Component comp;
        ComponentDescription desc;

        //There are several different types of Audio Units.
        //Some audio units serve as Outputs, Mixers, or DSP
        //units. See AUComponent.h for listing
        desc.componentType = kAudioUnitType_Output;

        //Every Component has a subType, which will give a clearer picture
        //of what this components function will be.
        desc.componentSubType = kAudioUnitSubType_HALOutput;

        //all Audio Units in AUComponent.h must use
        //"kAudioUnitManufacturer_Apple" as the Manufacturer
        desc.componentManufacturer = kAudioUnitManufacturer_Apple;
        desc.componentFlags = 0;
        desc.componentFlagsMask = 0;

        //Finds a component that meets the desc spec's
        comp = FindNextComponent(NULL, &desc);
        if (comp == NULL) exit (-1);

        //gains access to the services provided by the component
        OpenAComponent(comp, &mInputUnit);

        err = EnableIO();
        checkErr(err);

        err= SetInputDeviceAsCurrent(in);
        checkErr(err);

        err = CallbackSetup();
        checkErr(err);

        //Don't setup buffers until you know what the
        //input and output device audio streams look like.
        //err = SetupBuffers();

        err = AudioUnitInitialize(mInputUnit);
        return err;
}

OSStatus ComplexPlayThru::EnableIO()
{
        OSStatus err = noErr;
        UInt32 enableIO;

        ///////////////
        //ENABLE IO (INPUT)
        //You must enable the Audio Unit (AUHAL) for input and disable output
        //BEFORE setting the AUHAL's current device.

        //Enable input on the AUHAL
        enableIO = 1;
        err =  AudioUnitSetProperty(mInputUnit,
                                                                kAudioOutputUnitProperty_EnableIO,
                                                                kAudioUnitScope_Input,
                                                                1, // input element
                                                                &enableIO,
                                                                sizeof(enableIO));
        checkErr(err);

        //disable Output on the AUHAL
        enableIO = 0;
        err = AudioUnitSetProperty(mInputUnit,
                                                          kAudioOutputUnitProperty_EnableIO,
                                                          kAudioUnitScope_Output,
                                                          0,   //output element
                                                          &enableIO,
                                                          sizeof(enableIO));
        return err;
}

OSStatus ComplexPlayThru::CallbackSetup()
{
        OSStatus err = noErr;
    AURenderCallbackStruct input;

    input.inputProc = InputProc;
    input.inputProcRefCon = this;

        //Setup the input callback.
        err = AudioUnitSetProperty(mInputUnit,
                                                          kAudioOutputUnitProperty_SetInputCallback,
                                                          kAudioUnitScope_Global,
                                                          0,
                                                          &input,
                                                          sizeof(input));
        checkErr(err);
        return err;
}

//Allocate Audio Buffer List(s) to hold the data from input.
OSStatus ComplexPlayThru::SetupBuffers()
{
        OSStatus err = noErr;
        UInt32 bufferSizeFrames,bufferSizeBytes,propsize;

        AudioStreamBasicDescription asbd,asbd_dev1_in,asbd_dev2_out;
        Float64 rate=0;

        //Get the size of the IO buffer(s)
        UInt32 propertySize = sizeof(bufferSizeFrames);
        err = AudioUnitGetProperty(mInputUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, &bufferSizeFrames, &propertySize);
        bufferSizeBytes = bufferSizeFrames * sizeof(Float32);

        //Get the Stream Format (Output client side)
        propertySize = sizeof(asbd_dev1_in);
        err = AudioUnitGetProperty(mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &asbd_dev1_in, &propertySize);
        //printf("=====Input DEVICE stream format\n" );
        //PrintStreamDesc(&asbd_dev1_in);

        //Get the Stream Format (client side)
        propertySize = sizeof(asbd);
        err = AudioUnitGetProperty(mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &asbd, &propertySize);
        //printf("=====current Input (Client) stream format\n");
        //PrintStreamDesc(&asbd);

        //Get the Stream Format (Output client side)
        propertySize = sizeof(asbd_dev2_out);
        err = AudioUnitGetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &asbd_dev2_out, &propertySize);
        //printf("=====Output (Device) stream format\n");
        //PrintStreamDesc(&asbd_dev2_out);

        //////////////////////////////////////
        //Set the format of all the AUs to the input/output devices channel count
        //For a simple case, you want to set this to the lower of count of the channels
        //in the input device vs output device
        //////////////////////////////////////
        asbd.mChannelsPerFrame =((asbd_dev1_in.mChannelsPerFrame < asbd_dev2_out.mChannelsPerFrame) ?asbd_dev1_in.mChannelsPerFrame :asbd_dev2_out.mChannelsPerFrame) ;
        printf("Info: Input Device channel count=%ld\t Input Device channel count=%ld\n",asbd_dev1_in.mChannelsPerFrame,asbd_dev2_out.mChannelsPerFrame);
        printf("Info: ComplexPlayThru will use %ld channels\n",asbd.mChannelsPerFrame);

        // We must get the sample rate of the input device and set it to the stream format of AUHAL
        propertySize = sizeof(Float64);
        AudioDeviceGetProperty(mInputDevice.mID, 0, 1, kAudioDevicePropertyNominalSampleRate, &propertySize, &rate);
        asbd.mSampleRate =rate;
        propertySize = sizeof(asbd);

        //Set the new formats to the AUs...
        err = AudioUnitSetProperty(mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &asbd, propertySize);
        checkErr(err);
        err = AudioUnitSetProperty(mVarispeedUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, propertySize);
        checkErr(err);

        //Set the correct sample rate for the output device, but keep the channe count the same
        propertySize = sizeof(Float64);
        AudioDeviceGetProperty(mOutputDevice.mID, 0, 0, kAudioDevicePropertyNominalSampleRate, &propertySize, &rate);
        asbd.mSampleRate =rate;
        propertySize = sizeof(asbd);
        //Set the new audio stream formats for the rest of the AUs...
        err = AudioUnitSetProperty(mVarispeedUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &asbd, propertySize);
        checkErr(err);
        err = AudioUnitSetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, propertySize);
        checkErr(err);

        //calculate number of buffers from channels
        propsize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) *asbd.mChannelsPerFrame);

        //malloc buffer lists
        mInputBuffer = (AudioBufferList *)malloc(propsize);
        mInputBuffer->mNumberBuffers = asbd.mChannelsPerFrame;

        //pre-malloc buffers for AudioBufferLists
        for(UInt32 i =0; i< mInputBuffer->mNumberBuffers ; i++) {
                mInputBuffer->mBuffers[i].mNumberChannels = 1;
                mInputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
                mInputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
        }

        //Alloc ring buffer that will hold data between the two audio devices
        mBuffer = new AudioRingBuffer();
        mBuffer->Allocate(asbd.mChannelsPerFrame, asbd.mBytesPerFrame, bufferSizeFrames * 20);

    return err;
}

OSStatus ComplexPlayThru::MapChannels()
{
        OSStatus err = noErr;
        AudioStreamBasicDescription asbdInput, asbdOutput;
        SInt32 *channelMap = NULL;
        UInt32 numOfChannels;

        //Get the Stream Formats from both Audio Units (client side)
        UInt32 propertySize = sizeof(AudioStreamBasicDescription);
        AudioUnitGetProperty(mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &asbdInput, &propertySize);
        AudioUnitGetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &asbdOutput, &propertySize);

        if(asbdOutput.mChannelsPerFrame == asbdInput.mChannelsPerFrame)
        {
                //If the data format between the audio device's channels and the desired format's channels
                //correspond to a 1:1 ratio, channel mapping is not needed.
                return noErr;
        }

        numOfChannels = asbdInput.mChannelsPerFrame;
    channelMap = (SInt32 *)malloc(numOfChannels*sizeof(SInt32));
        propertySize = numOfChannels*sizeof(SInt32);

        for(UInt32 i=0;i<numOfChannels;i++)
    {
                //channelMap[desiredInputChannel] = deviceOutputChannel;
                channelMap[i]=-1;  //-1 value denotes unused channel, init all channels to -1
    }

    //for each channel of desired input, map the channel from
    //the device's output channel. (this is the Default Channel Mapping...)

    for(UInt32 i=0;i<numOfChannels;i++)
    {
                //channelMap[desiredInputChannel] = deviceOutputChannel;
                channelMap[i]=i;
    }

        printf("---Channel Mapping---");
        for(UInt32 i=0;i<numOfChannels;i++)
                printf("Info:Input Channel (%ld): maps to output channel %ld  \n", i,channelMap[i]);

        err = AudioUnitSetProperty(mInputUnit,
                                                          kAudioOutputUnitProperty_ChannelMap,
                                                          kAudioUnitScope_Output,
                                                          1,
                                                          channelMap,
                                                          propertySize);
        free(channelMap);

        return err;
}

void    ComplexPlayThru::ComputeThruOffset()
{
        //The initial latency will at least be the saftey offset's of the devices + the buffer sizes
        mInToOutSampleOffset = SInt32(mInputDevice.mSafetyOffset +  mInputDevice.mBufferSizeFrames +
                                                mOutputDevice.mSafetyOffset + mOutputDevice.mBufferSizeFrames);
}

#pragma mark -
#pragma mark -- IO Procs --
OSStatus ComplexPlayThru::InputProc(void *inRefCon,
                                                                        AudioUnitRenderActionFlags *ioActionFlags,
                                                                        const AudioTimeStamp *inTimeStamp,
                                                                        UInt32 inBusNumber,
                                                                        UInt32 inNumberFrames,
                                                                        AudioBufferList * ioData)
{
    OSStatus err = noErr;

        ComplexPlayThru *This = (ComplexPlayThru *)inRefCon;
        if (This->mFirstInputTime < 0.)
                This->mFirstInputTime = inTimeStamp->mSampleTime;

        //Get the new audio data
        err= AudioUnitRender(This->mInputUnit,
                                                 ioActionFlags,
                                                 inTimeStamp,
                                                 inBusNumber,
                                                 inNumberFrames, //# of frames requested
                                                 This->mInputBuffer);// Audio Buffer List to hold data
        checkErr( err);

        This->mBuffer->Store(This->mInputBuffer, inNumberFrames, SInt64(inTimeStamp->mSampleTime));
        return err;
}

OSStatus ComplexPlayThru::OutputProc(void *inRefCon,
                                                                         AudioUnitRenderActionFlags *ioActionFlags,
                                                                         const AudioTimeStamp *TimeStamp,
                                                                         UInt32 inBusNumber,
                                                                         UInt32 inNumberFrames,
                                                                         AudioBufferList * ioData)
{
    OSStatus err = noErr;
        ComplexPlayThru *This = (ComplexPlayThru *)inRefCon;

        if (This->mFirstInputTime < 0.) {
                // input hasn't run yet -> silence
                return -1;
        }

        //get Delta between the devices and add it to the offset
        if (This->mFirstOutputTime < 0.) {
                This->mFirstOutputTime = TimeStamp->mSampleTime;
                Float64 delta = (This->mFirstInputTime - This->mFirstOutputTime);
                This->ComputeThruOffset();
                //changed: 3865519 11/10/04
                if (delta < 0.0)
                        This->mInToOutSampleOffset -= delta;
                else
                        This->mInToOutSampleOffset = -delta + This->mInToOutSampleOffset;

                return -1;
        }

        //copy the data from the buffer
        This->mBuffer->Fetch(ioData,inNumberFrames,SInt64(TimeStamp->mSampleTime - This->mInToOutSampleOffset));

        extern void Process(AudioBufferList * ioData);
        Process(ioData);

        return err;
}

#pragma mark -- Listeners --
OSStatus ComplexPlayThru::DeviceSampleRateListener(AudioDeviceID                        inDevice,
                                                                                                                UInt32                                  inChannel,
                                                                                                                Boolean                                 isInput,
                                                                                                                AudioDevicePropertyID   inPropertyID,
                                                                                                                void*                                   inClientData)
{
        ComplexPlayThru *This = (ComplexPlayThru *)inClientData;
        UInt32 propertySize = 0;
        Float64 sampleRate = 0;
        Float32 rate = 0;
        OSStatus err = noErr;
        AudioStreamBasicDescription asbd;

        if(isInput){
                propertySize = sizeof(asbd);
                AudioUnitGetProperty(This->mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &asbd, &propertySize);
                // here we'll get the unit's format

                // We must get the sample rate of the input device and set it to the stream format of AUHAL
                propertySize = sizeof(Float64);
                AudioDeviceGetProperty(This->mInputDevice.mID, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propertySize, &sampleRate);
                asbd.mSampleRate = sampleRate;

                propertySize = sizeof(asbd);
                err = AudioUnitSetProperty(This->mInputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &asbd, propertySize);
                checkErr(err);

                //We must also determine the new playback rate of the varispeed unit
                rate = sampleRate/This->mOutputDevice.mFormat.mSampleRate;
                err = AudioUnitSetParameter(This->mVarispeedUnit,kVarispeedParam_PlaybackRate,kAudioUnitScope_Global,0, rate,0);
                This->mInputDevice.mFormat.mSampleRate =sampleRate; //update
                checkErr(err);
                //Reset time
                This->mFirstInputTime = -1;
                This->mFirstOutputTime = -1;
        }

        return err;
}

Generated by  Doxygen 1.6.0   Back to index