/* * * This is based on Apple example software AudioBackEnd.cpp * * Copyright © 2004 Apple Computer, Inc., All Rights Reserved * Original Apple code modified by Daniel Sumorok * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "AudioBackEnd.h" #pragma mark ---Public Methods--- AudioBackEnd::AudioBackEnd(int bitsPerSample, int numChannels, int sampleRate): mBitsPerSample(bitsPerSample), mSampleRate(sampleRate), mNumChannels(numChannels), mCallback(NULL), mCallbackArg(NULL), mBufferSizeFrames(0), mFramesProcessed(0), mAudioBuffer(NULL), mAudioBufferWriteIndex(0), mAudioBufferReadIndex(0), mBytesPerFrame(0), mAudioBufferSize(0) { OSStatus err = noErr; err = Init(); if(err) { fprintf(stderr,"AudioBackEnd ERROR: Cannot Init AudioBackEnd"); exit(1); } } AudioBackEnd::~AudioBackEnd() { //clean up Stop(); AUGraphClose(mGraph); DisposeAUGraph(mGraph); if(mAudioBuffer != NULL) { delete mAudioBuffer; mAudioBuffer = NULL; mAudioBufferSize = 0; } } OSStatus AudioBackEnd::Init() { OSStatus err = noErr; err = SetupGraph(); checkErr(err); err = SetupBuffers(); checkErr(err); err = AUGraphInitialize(mGraph); checkErr(err); return err; } #pragma mark --- Operation--- OSStatus AudioBackEnd::Start() { OSStatus err = noErr; if(!IsRunning()) { mFramesProcessed = 0; mAudioBufferWriteIndex = 0; mAudioBufferReadIndex = 0; err = AUGraphStart(mGraph); } return err; } OSStatus AudioBackEnd::Stop() { OSStatus err = noErr; if(IsRunning()) { err = AUGraphStop(mGraph); } return err; } Boolean AudioBackEnd::IsRunning() { OSStatus err = noErr; Boolean graphRunning; err = AUGraphIsRunning(mGraph,&graphRunning); return (graphRunning); } #pragma mark - #pragma mark --Private methods--- OSStatus AudioBackEnd::SetupGraph() { OSStatus err = noErr; AURenderCallbackStruct output; UInt32 size; ComponentDescription outDesc; AudioDeviceID out; //Make a New Graph err = NewAUGraph(&mGraph); checkErr(err); //Open the Graph, AudioUnits are opened but not initialized err = AUGraphOpen(mGraph); checkErr(err); 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 err = AUGraphNewNode(mGraph, &outDesc, 0, NULL, &mOutputNode); checkErr(err); //Get Audio Units from AUGraph node err = AUGraphGetNodeInfo(mGraph, mOutputNode, NULL, NULL, NULL, &mOutputUnit); checkErr(err); err = AUGraphUpdate(mGraph, NULL); checkErr(err); size = sizeof(AudioDeviceID); err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &out); checkErr(err); mOutputDevice.Init(out, false); //Set the Current Device to the Default Output Unit. err = AudioUnitSetProperty(mOutputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &out, sizeof(out)); checkErr(err); output.inputProc = OutputProc; output.inputProcRefCon = this; err = AudioUnitSetProperty(mOutputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); checkErr(err); return err; } //Allocate Audio Buffer List(s) to hold the data from input. OSStatus AudioBackEnd::SetupBuffers() { OSStatus err = noErr; UInt32 safetyOffset; AudioStreamBasicDescription asbd; UInt32 propertySize; propertySize = sizeof(mBufferSizeFrames); err = AudioUnitGetProperty(mOutputUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, &mBufferSizeFrames, &propertySize); propertySize = sizeof(safetyOffset); safetyOffset = 0; err = AudioUnitGetProperty(mOutputUnit, kAudioDevicePropertySafetyOffset, kAudioUnitScope_Global, 0, &safetyOffset, &propertySize); asbd.mFormatID = 0x6c70636d; // 'lpcm' asbd.mFormatFlags = (kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsPacked); asbd.mChannelsPerFrame = mNumChannels; asbd.mSampleRate = mSampleRate; if(asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger) { asbd.mBitsPerChannel = mBitsPerSample; } else if(asbd.mFormatFlags & kAudioFormatFlagIsFloat) { asbd.mBitsPerChannel = 32; } else { asbd.mBitsPerChannel = 0; } asbd.mFramesPerPacket = 1; asbd.mBytesPerFrame = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame; asbd.mBytesPerPacket = asbd.mBytesPerFrame * asbd.mFramesPerPacket; asbd.mReserved = 0; mBytesPerFrame = asbd.mBytesPerFrame; if((mBytesPerFrame & (mBytesPerFrame - 1)) != 0) { printf("Audio buffer size must be a power of two!\n"); return -1; } propertySize = sizeof(asbd); err = AudioUnitSetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, propertySize); checkErr(err); if(mAudioBuffer != NULL) { delete mAudioBuffer; mAudioBufferSize = 0; } mAudioBufferSize = mBytesPerFrame * mBufferSizeFrames * 2; mAudioBuffer = new UInt8[mAudioBufferSize]; bzero(mAudioBuffer, mAudioBufferSize); return err; } #pragma mark - #pragma mark -- IO Procs -- OSStatus AudioBackEnd::OutputProc(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *TimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData) { OSStatus err = noErr; AudioBackEnd *This = (AudioBackEnd *)inRefCon; UInt8 *dstPtr; UInt32 bytesToCopy; UInt32 bytesUntilEnd; This->mFramesProcessed += inNumberFrames; dstPtr = (UInt8 *)ioData->mBuffers[0].mData; if(This->mAudioBuffer == NULL) { bzero(dstPtr, inNumberFrames * This->mBytesPerFrame); return noErr; } bytesToCopy = inNumberFrames * This->mBytesPerFrame; bytesUntilEnd = This->mAudioBufferSize - This->mAudioBufferReadIndex; if(bytesUntilEnd < bytesToCopy) { memcpy(dstPtr, &This->mAudioBuffer[This->mAudioBufferReadIndex], bytesUntilEnd); memcpy(dstPtr, This->mAudioBuffer, bytesToCopy - bytesUntilEnd); This->mAudioBufferReadIndex = bytesToCopy - bytesUntilEnd; } else { memcpy(dstPtr, &This->mAudioBuffer[This->mAudioBufferReadIndex], bytesToCopy); This->mAudioBufferReadIndex += bytesToCopy; } while(This->mFramesProcessed >= This->mBufferSizeFrames) { This->mFramesProcessed -= This->mBufferSizeFrames; if(This->mCallback != NULL) { This->mCallback(This->mCallbackArg); } } return err; } void AudioBackEnd::setCallback(playthruCallback func, void *arg) { mCallback = func; mCallbackArg = arg; } UInt32 AudioBackEnd::BufferSizeFrames() { return mBufferSizeFrames; } int AudioBackEnd::sendAudioBuffer(void *buffer, int numFrames) { UInt8 *dstBuffer; int totalBytes; mAudioBufferWriteIndex += (mAudioBufferSize / 2); mAudioBufferWriteIndex &= (mAudioBufferSize - 1); dstBuffer = &mAudioBuffer[mAudioBufferWriteIndex]; totalBytes = mBytesPerFrame * numFrames; memcpy(dstBuffer, buffer, totalBytes); dstBuffer += totalBytes; bzero(dstBuffer, (mBufferSizeFrames * mBytesPerFrame) - totalBytes); return numFrames; }