#include "cmGlobal.h" #include "cmFloatTypes.h" #include "cmRpt.h" #include "cmErr.h" #include "cmCtx.h" #include "cmMem.h" #include "cmMallocDebug.h" #include "cmTime.h" #include "cmAudioPort.h" #include "cmAudioNrtDev.h" #include "cmThread.h" enum { kStartedApNrtFl = 0x01, kInStateApNrtFl = 0x02 }; typedef struct { unsigned phase; bool implFl; double gain; } cmApNrtCh_t; typedef struct cmApNrtDev_str { unsigned flags; unsigned devIdx; // nrt device index unsigned baseApDevIdx; // global audio device index for first nrt device cmChar_t* label; unsigned iChCnt; unsigned oChCnt; double srate; unsigned fpc; cmThreadH_t thH; cmApCallbackPtr_t cbPtr; void* cbArg; unsigned cbPeriodMs; struct cmApNrtDev_str* link; unsigned curTimeSmp; cmApNrtCh_t* iChArray; cmApNrtCh_t* oChArray; } cmApNrtDev_t; typedef struct { cmErr_t err; unsigned devCnt; cmApNrtDev_t* devs; unsigned baseApDevIdx; } cmApNrt_t; cmApNrt_t* _cmNrt = NULL; cmApNrtDev_t* _cmApNrtIndexToDev( unsigned idx ) { cmApNrtDev_t* dp = _cmNrt->devs; unsigned i; for(i=0; dp!=NULL && ilink; assert( dp != NULL ); return dp; } void cmApNrtGenImpulseCh( cmApNrtDev_t* dp, unsigned chIdx, cmSample_t* buf, unsigned chCnt, unsigned frmCnt ) { double periodMs = 500; // impulse period double durMs = 50; // impulse length double loDb = -90.0; double hiDb = -20.0; double loLin = pow(10.0,loDb/20.0); double hiLin = pow(10.0,hiDb/20.0); unsigned periodSmp = (unsigned)(periodMs * dp->srate / 1000.0); unsigned durSmp = (unsigned)( durMs * dp->srate / 1000.0); assert( chIdx < chCnt ); cmApNrtCh_t* cp = dp->iChArray + chIdx; cmSample_t* sp = buf + chIdx; unsigned i; for(i=0; iphase += 1; if( cp->implFl ) { gain = cp->gain; limit = durSmp; } *sp = (gain * 2.0 * rand()/RAND_MAX) - 1.0; if( cp->phase > limit ) { cp->phase = 0; cp->implFl = !cp->implFl; if( cp->implFl ) cp->gain = loLin + (hiLin - loLin) * rand()/RAND_MAX ; } } } void cmApNrtGenImpulse( cmApNrtDev_t* dp, cmSample_t* buf, unsigned chCnt, unsigned frmCnt ) { unsigned i=0; for(; icurTimeSmp += frmCnt; } // return 'false' to terminate otherwise return 'true'. bool cmApNrtThreadFunc(void* param) { cmApNrtDev_t* dp = (cmApNrtDev_t*)param; cmSleepUs( dp->cbPeriodMs * 1000 ); cmApAudioPacket_t pkt; bool inFl = cmIsFlag(dp->flags,kInStateApNrtFl); pkt.devIdx = dp->devIdx + dp->baseApDevIdx; pkt.begChIdx = 0; pkt.audioFramesCnt = dp->fpc; pkt.bitsPerSample = 32; pkt.flags = kInterleavedApFl | kFloatApFl; pkt.userCbPtr = dp->cbArg; if( inFl ) { unsigned bn = dp->iChCnt * dp->fpc; cmSample_t b[ bn ]; pkt.chCnt = dp->iChCnt; pkt.audioBytesPtr = b; cmApNrtGenSamples(dp,b,dp->iChCnt,dp->fpc); dp->cbPtr(&pkt,1,NULL,0 ); // send the samples to the application } else { unsigned bn = dp->oChCnt * dp->fpc; cmSample_t b[ bn ]; pkt.chCnt = dp->oChCnt; pkt.audioBytesPtr = b; dp->cbPtr(NULL,0,&pkt,1 ); // recv the samples from the application } dp->flags = cmTogFlag(dp->flags,kInStateApNrtFl); return true; } cmApRC_t cmApNrtAllocate( cmRpt_t* rpt ) { if( _cmNrt != NULL ) cmApNrtFree(); _cmNrt = cmMemAllocZ(cmApNrt_t,1); cmErrSetup(&_cmNrt->err,rpt,"cmAudioNrtDev"); _cmNrt->devCnt = 0; _cmNrt->devs = NULL; _cmNrt->baseApDevIdx = 0; return kOkApRC; } cmApRC_t cmApNrtFree() { cmApRC_t rc = kOkApRC; cmApNrtDev_t* dp = _cmNrt->devs; while( dp != NULL ) { cmApNrtDev_t* np = dp->link; if( cmThreadIsValid(dp->thH) ) if( cmThreadDestroy(&dp->thH) != kOkThRC ) rc = cmErrMsg(&_cmNrt->err,kThreadFailApRC,"Thread destroy failed."); cmMemFree(dp->label); cmMemFree(dp->iChArray); cmMemFree(dp->oChArray); cmMemFree(dp); dp = np; } if( rc == kOkApRC ) { cmMemPtrFree(&_cmNrt); } return rc; } cmApRC_t cmApNrtCreateDevice( const cmChar_t* label, double srate, unsigned iChCnt, unsigned oChCnt, unsigned cbPeriodMs ) { cmApRC_t rc = kOkApRC; cmApNrtDev_t* dp = cmMemAllocZ(cmApNrtDev_t,1); dp->devIdx = _cmNrt->devCnt; dp->baseApDevIdx = _cmNrt->baseApDevIdx; dp->label = cmMemAllocStr(label); dp->iChCnt = iChCnt; dp->oChCnt = oChCnt; dp->srate = srate; dp->fpc = 0; dp->cbPeriodMs = cbPeriodMs; dp->cbPtr = NULL; dp->cbArg = NULL; dp->link = NULL; dp->iChArray = iChCnt==0 ? NULL : cmMemAllocZ(cmApNrtCh_t,iChCnt); dp->oChArray = oChCnt==0 ? NULL : cmMemAllocZ(cmApNrtCh_t,oChCnt); // attach the new recd to the end of the list cmApNrtDev_t* np = _cmNrt->devs; while( np != NULL && np->link != NULL ) np = np->link; if( np == NULL ) _cmNrt->devs = dp; else np->link = dp; if( cmThreadCreate( &dp->thH, cmApNrtThreadFunc, dp, _cmNrt->err.rpt ) != kOkThRC ) rc = cmErrMsg(&_cmNrt->err,kThreadFailApRC,"Thread create failed."); ++_cmNrt->devCnt; return rc; } cmApRC_t cmApNrtInitialize( cmRpt_t* rpt, unsigned baseApDevIdx ) { // set the baseApDevIdx for each device cmApNrtDev_t* dp = _cmNrt->devs; for(; dp!=NULL; dp=dp->link) dp->baseApDevIdx = baseApDevIdx; // store the baseApDevIdx for any devices that may be created after initialization _cmNrt->baseApDevIdx = baseApDevIdx; return kOkApRC; } cmApRC_t cmApNrtFinalize() { return kOkApRC; } unsigned cmApNrtDeviceCount() { return _cmNrt==NULL ? 0 : _cmNrt->devCnt; } const char* cmApNrtDeviceLabel( unsigned devIdx ) { assert( devIdx < _cmNrt->devCnt ); const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx); return dp->label; } unsigned cmApNrtDeviceChannelCount( unsigned devIdx, bool inputFl ) { assert( devIdx < _cmNrt->devCnt ); const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx); return inputFl ? dp->iChCnt : dp->oChCnt; } double cmApNrtDeviceSampleRate( unsigned devIdx ) { assert( devIdx < _cmNrt->devCnt ); const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx); return dp->srate; } unsigned cmApNrtDeviceFramesPerCycle( unsigned devIdx, bool inputFl ) { assert( devIdx < _cmNrt->devCnt ); const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx); return dp->fpc; } cmApRC_t cmApNrtDeviceSetup( unsigned devIdx, double srate, unsigned framesPerCycle, cmApCallbackPtr_t callbackPtr, void* userCbPtr ) { assert( devIdx < _cmNrt->devCnt ); cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx); dp->srate = srate; dp->fpc = framesPerCycle; dp->cbPtr = callbackPtr; dp->cbArg = userCbPtr; return kOkApRC; } cmApRC_t cmApNrtDeviceStart( unsigned devIdx ) { assert( devIdx < _cmNrt->devCnt ); cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx); dp->curTimeSmp = 0; if( cmThreadPause( dp->thH, 0 ) != kOkThRC ) return cmErrMsg(&_cmNrt->err,kThreadFailApRC,"Thread start failed."); dp->flags = cmSetFlag(dp->flags,kStartedApNrtFl); return kOkApRC; } cmApRC_t cmApNrtDeviceStop( unsigned devIdx ) { assert( devIdx < _cmNrt->devCnt ); cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx); if( cmThreadPause( dp->thH, kPauseThFl ) != kOkThRC ) return cmErrMsg(&_cmNrt->err,kThreadFailApRC,"Thread pause failed."); dp->flags = cmClrFlag(dp->flags,kStartedApNrtFl); return kOkApRC; } bool cmApNrtDeviceIsStarted( unsigned devIdx ) { assert( devIdx < _cmNrt->devCnt ); const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx); return cmIsFlag(dp->flags,kStartedApNrtFl); }