#include #include #include "InputPlayer.h" lineInDef::lineInDef(void) { channelMap = NULL; } lineInDef::~lineInDef() { if(channelMap) delete channelMap; } void lineInDef::set(string UID, unsigned long busdef, long map[], unsigned long startCode, unsigned long stopCode) { int i; deviceUID = UID; bus = busdef; channelMap = new long[inChNum]; for(i=0; imNumberBuffers; i++){ free(mInputBuffer->mBuffers[i].mData); } free(mInputBuffer); mInputBuffer = 0; } if(chanMap) delete chanMap; deleteMetaRecord(UID); DeleteFromLists(this); // send out notifications struct notifyData data; data.pNum = pNum; data.iVal = 0; data.fVal = 0.0; data.type = nType_vol; Notifier->MakeEntry(data); data.type = nType_bal; Notifier->MakeEntry(data); data.type = nType_bus; Notifier->MakeEntry(data); data.type = nType_stat; Notifier->MakeEntry(data); } unsigned char InputPlayer::load(string UID, long *chMap) { //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 if(!SetupAudio(GetDeviceID(UID.c_str()), chMap)) goto errorReturn; status = status_standby; // send out notifications struct notifyData data; data.pNum = pNum; data.type = nType_vol; data.iVal = 0; data.fVal = getMixerInputVolume(pNum); Notifier->MakeEntry(data); data.type = nType_bal; data.iVal = 0; data.fVal = balance; Notifier->MakeEntry(data); data.type = nType_bus; data.iVal = bus_assignment; data.fVal = 0.0; Notifier->MakeEntry(data); data.type = nType_stat; data.iVal = status; data.fVal = 0.0; Notifier->MakeEntry(data); return true; errorReturn: status = status_empty; return false; } void InputPlayer::start() { OSStatus err; TaskItem *task; unsigned char mData[4]; unsigned char i; //Start the input unit err = AudioOutputUnitStart(mInputUnit); if(!err){ //reset sample times FirstInputTime = -1; FirstOutputTime = -1; EndInputTime = -1; status = status & ~status_finished; status = status | status_playing; task = new TaskItem("Update Mute Groups", RefreshMutesGroups, NULL, 0L, 0L, false); if(!(bus_assignment & 2L)){ // not in cue status = status | status_hasPlayed; // create log entry ProgramLoger->UIDEntry(UID, false, true); } // send out notifications struct notifyData data; data.pNum = pNum; data.type = nType_stat; data.iVal = status; data.fVal = 0.0; Notifier->MakeEntry(data); // send specified midi start code - conver long integer to byte array mData[3] = startMIDI & 0xff; startMIDI = startMIDI >> 8; mData[2] = startMIDI & 0xff; startMIDI = startMIDI >> 8; mData[1] = startMIDI & 0xff; startMIDI = startMIDI >> 8; mData[0] = startMIDI & 0xff; for(i = 0; i < 4; i++) if(mData[i]) break; if(i < 4) MidiObj->MidiSendtoAll(&mData[i], 4-i); } } void InputPlayer::stop() { OSStatus err; ARPlayer *nextp = NULL; unsigned char mData[4]; unsigned char i; //Stop the input unit err = AudioOutputUnitStop(mInputUnit); if(!err){ //reset sample times FirstInputTime = -1; FirstOutputTime = -1; EndInputTime = -1; status = status & ~status_playing; status = status & ~status_logged; TaskItem *task; task = new TaskItem("Update Mute Groups", RefreshMutesGroups, NULL, 0L, 0L, false); } // force a segue if next player is set if(next >= 0){ pMutex->readLock(true); if(nextp = pList[next]){ nextp->start(); } next = -1; pMutex->readUnlock(); } // send out notifications struct notifyData data; data.pNum = pNum; data.type = nType_stat; data.iVal = status; data.fVal = 0.0; Notifier->MakeEntry(data); // send specified midi stop code - conver long integer to byte array mData[3] = stopMIDI & 0xff; stopMIDI = stopMIDI >> 8; mData[2] = stopMIDI & 0xff; stopMIDI = stopMIDI >> 8; mData[1] = stopMIDI & 0xff; stopMIDI = stopMIDI >> 8; mData[0] = stopMIDI & 0xff; for(i = 0; i < 4; i++) if(mData[i]) break; if(i < 4) MidiObj->MidiSendtoAll(&mData[i], 4-i); } void InputPlayer::volChange(float vol) { int i; // update playthought volume for direct monitoring vol = vol * 0.707; for(i=0; iMakeEntry(data); } OSStatus InputPlayer::SetupAudio(AudioDeviceID in, long *OutputMap) { OSStatus err; AURenderCallbackStruct rcbs; unsigned long size; unsigned long enableIO; ComponentDescription desc; Component comp; devID = in; chanMap = new long[inChNum]; for(int i=0; imNumberBuffers = inChNum; //pre-malloc buffers for AudioBufferLists for(unsigned long 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(outFormat.mChannelsPerFrame, outFormat.mBytesPerFrame, (inbufferSizeFrames + safteyFrames) * 16); return true; } OSStatus InputPlayer::FillBuffer(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, unsigned long inBusNumber, unsigned long inNumberFrames, AudioBufferList * ioData) { OSStatus err = noErr; InputPlayer *parent = (InputPlayer *)inRefCon; //Get the new audio data err= AudioUnitRender(parent->mInputUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, //# of frames requested parent->mInputBuffer);// Audio Buffer List to hold data parent->mBuffer->Store(parent->mInputBuffer, inNumberFrames, (long long int)inTimeStamp->mSampleTime); // record the time we started running if (parent->FirstInputTime < 0.) parent->FirstInputTime = inTimeStamp->mSampleTime; parent->EndInputTime = inTimeStamp->mSampleTime + inNumberFrames; return err; } OSStatus InputPlayer::ReadBuffer(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *TimeStamp, unsigned long inBusNumber, unsigned long inNumberFrames, AudioBufferList * ioData) { unsigned int i; double actualLatency, error, adjust, dither; InputPlayer *parent = (InputPlayer *)inRefCon; ARPlayer *next = NULL; struct notifyData data; if (parent->FirstInputTime < 0.) { // input hasn't run yet -> silence goto silence; } //get Delta between the devices and add it to the offset if this is the first time through if (parent->FirstOutputTime < 0.) { unsigned int larger = parent->inbufferSizeFrames; if(larger < inNumberFrames) larger = inNumberFrames; parent->TargetLatency = larger + parent->safteyFrames + 4; if ((parent->EndInputTime - parent->FirstInputTime) > (parent->TargetLatency)){ parent->FirstOutputTime = TimeStamp->mSampleTime; parent->IOOffset = parent->FirstOutputTime - parent->FirstInputTime; parent->errorFactor = 0; }else goto silence; } // device sync control algorithm actualLatency = parent->EndInputTime - (TimeStamp->mSampleTime + inNumberFrames - parent->IOOffset); parent->bufftime = actualLatency * 1000 / parent->inFormat.mSampleRate; // latency in mS error = actualLatency - parent->TargetLatency; if(fabs(error) > parent->TargetLatency){ // if we get VERY far off, skip by what ever the error is... this will be audible as a skip. adjust = error; dither = 0; parent->errorFactor = 0; }else{ // avarage the error... to avarage out the buffer size quantization float avrFactor = 1.0 / inNumberFrames; parent->errorFactor = (parent->errorFactor * (1.0 - avrFactor)) + (error * avrFactor); // create a dither pointer between 0 and inNumberFrames to randomly pick were in the buffer a sample (if any) is added or removed dither = (float)rand() / RAND_MAX; dither = floor((inNumberFrames - 2) * dither); // limit/quantize to +1, 0 or -1 sample change adjust = 0; dither = 0; if(parent->errorFactor > 1){ adjust = 1; } if(parent->errorFactor < -1){ adjust = -1; } parent->errorFactor = parent->errorFactor - adjust; if(dither > 0){ //copy the data from the buffer starting at this timestamp less the offset time if(BusBuffer->Fetch(ioData, (long unsigned int)dither, (long long int)(TimeStamp->mSampleTime - parent->IOOffset), 0, NULL) != kAudioRingBufferError_OK) goto silence; } } parent->errorFactor = parent->errorFactor - adjust; if(dither > 0){ //copy the data from the buffer starting at this timestamp less the offset time if(parent->mBuffer->Fetch(ioData, (long unsigned int)dither, (long long int)(TimeStamp->mSampleTime - parent->IOOffset), 0, NULL) != kAudioRingBufferError_OK) goto silence; } // adjust offset accordingly... parent->IOOffset = parent->IOOffset - adjust; //copy the data from the buffer starting at this timestamp less the offset time if(parent->mBuffer->Fetch(ioData, (long unsigned int)(inNumberFrames - dither), (long long int)(TimeStamp->mSampleTime + dither - parent->IOOffset), (long unsigned int)dither, NULL) != kAudioRingBufferError_OK) goto silence; // update time position counter parent->position = (TimeStamp->mSampleTime - parent->FirstOutputTime) / parent->inFormat.mSampleRate; // this is a good, reliable, periodicly accessed place to fade out and segue if( (parent->position > parent->fade) && (parent->fade > 0.0) ) // fadeout check { parent->segout = parent->fade; // segout on fade too float volume = 1.0; // default AudioUnitGetParameter(mixer, kMatrixMixerParam_Volume, kAudioUnitScope_Global, ((parent->pNum*inChNum)<<16)+0xffff, &volume); volume = volume - 0.002; if (volume < 0.002){ volume = 0.0; parent->fade = 0.0; setMixerInputVolume(parent->pNum, volume); parent->stop(); parent->status = parent->status | status_finished; data.pNum = parent->pNum; data.type = nType_stat; data.iVal = parent->status; data.fVal = 0.0; Notifier->MakeEntry(data); }else setMixerInputVolume(parent->pNum, volume); data.pNum = parent->pNum; data.type = nType_vol; data.iVal = 0; data.fVal = volume; Notifier->MakeEntry(data); } if( (parent->position > parent->segout) && (parent->next >= 0) ) // segout check { if(pMutex->readLock(false)){ // the playerlist mutex is not locked... we locked it and can proceed. Other wise skip until next time so there are no audio drop outs. if(next = pList[parent->next]){ next->start(); } parent->next = -1; pMutex->readUnlock(); } } return noErr; silence: // clear the actual buffers for(i = 0 ; i < ioData->mNumberBuffers ; i++){ bzero(ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize); } *ioActionFlags = kAudioUnitRenderAction_OutputIsSilence; return noErr; }