#include "cmPrefix.h" #include "cmGlobal.h" #include "cmRpt.h" #include "cmErr.h" #include "cmCtx.h" #include "cmMem.h" #include "cmMallocDebug.h" #include "cmTime.h" #include "cmAudioPort.h" #include "cmApBuf.h" // only needed for cmApBufTest(). #include "cmAudioPortFile.h" #include "cmAudioAggDev.h" #include "cmAudioNrtDev.h" #ifdef OS_LINUX #include "linux/cmAudioPortAlsa.h" #endif #ifdef OS_OSX #include "osx/cmAudioPortOsx.h" #endif typedef struct { unsigned begDevIdx; unsigned endDevIdx; cmApRC_t (*initialize)( cmRpt_t* rpt, unsigned baseApDevIdx ); cmApRC_t (*finalize)(); cmApRC_t (*deviceCount)(); const char* (*deviceLabel)( unsigned devIdx ); unsigned (*deviceChannelCount)( unsigned devIdx, bool inputFl ); double (*deviceSampleRate)( unsigned devIdx ); unsigned (*deviceFramesPerCycle)( unsigned devIdx, bool inputFl ); cmApRC_t (*deviceSetup)( unsigned devIdx, double sr, unsigned frmPerCycle, cmApCallbackPtr_t cb, void* cbData ); cmApRC_t (*deviceStart)( unsigned devIdx ); cmApRC_t (*deviceStop)( unsigned devIdx ); bool (*deviceIsStarted)( unsigned devIdx ); } cmApDriver_t; typedef struct { cmErr_t err; cmApDriver_t* drvArray; unsigned drvCnt; unsigned devCnt; } cmAp_t; cmAp_t* _ap = NULL; cmApRC_t _cmApIndexToDev( unsigned devIdx, cmApDriver_t** drvPtrPtr, unsigned* devIdxPtr ) { assert( drvPtrPtr != NULL && devIdxPtr != NULL ); *drvPtrPtr = NULL; *devIdxPtr = cmInvalidIdx; unsigned i; for(i=0; i<_ap->drvCnt; ++i) if( _ap->drvArray[i].begDevIdx != cmInvalidIdx ) if( (_ap->drvArray[i].begDevIdx <= devIdx) && (devIdx <= _ap->drvArray[i].endDevIdx) ) { *drvPtrPtr = _ap->drvArray + i; *devIdxPtr = devIdx - _ap->drvArray[i].begDevIdx; return kOkApRC; } return cmErrMsg(&_ap->err,kInvalidDevIdApRC,"The audio port device index %i is not valid.",devIdx); } cmApRC_t cmApInitialize( cmRpt_t* rpt ) { cmApRC_t rc = kOkApRC; if((rc = cmApFinalize()) != kOkApRC ) return rc; _ap = cmMemAllocZ(cmAp_t,1); cmErrSetup(&_ap->err,rpt,"Audio Port Driver"); _ap->drvCnt = 4; _ap->drvArray = cmMemAllocZ(cmApDriver_t,_ap->drvCnt); cmApDriver_t* dp = _ap->drvArray; #ifdef OS_OSX dp->initialize = cmApOsxInitialize; dp->finalize = cmApOsxFinalize; dp->deviceCount = cmApOsxDeviceCount; dp->deviceLabel = cmApOsxDeviceLabel; dp->deviceChannelCount = cmApOsxDeviceChannelCount; dp->deviceSampleRate = cmApOsxDeviceSampleRate; dp->deviceFramesPerCycle = cmApOsxDeviceFramesPerCycle; dp->deviceSetup = cmApOsxDeviceSetup; dp->deviceStart = cmApOsxDeviceStart; dp->deviceStop = cmApOsxDeviceStop; dp->deviceIsStarted = cmApOsxDeviceIsStarted; #endif #ifdef OS_LINUX dp->initialize = cmApAlsaInitialize; dp->finalize = cmApAlsaFinalize; dp->deviceCount = cmApAlsaDeviceCount; dp->deviceLabel = cmApAlsaDeviceLabel; dp->deviceChannelCount = cmApAlsaDeviceChannelCount; dp->deviceSampleRate = cmApAlsaDeviceSampleRate; dp->deviceFramesPerCycle = cmApAlsaDeviceFramesPerCycle; dp->deviceSetup = cmApAlsaDeviceSetup; dp->deviceStart = cmApAlsaDeviceStart; dp->deviceStop = cmApAlsaDeviceStop; dp->deviceIsStarted = cmApAlsaDeviceIsStarted; #endif dp = _ap->drvArray + 1; dp->initialize = cmApFileInitialize; dp->finalize = cmApFileFinalize; dp->deviceCount = cmApFileDeviceCount; dp->deviceLabel = cmApFileDeviceLabel; dp->deviceChannelCount = cmApFileDeviceChannelCount; dp->deviceSampleRate = cmApFileDeviceSampleRate; dp->deviceFramesPerCycle = cmApFileDeviceFramesPerCycle; dp->deviceSetup = cmApFileDeviceSetup; dp->deviceStart = cmApFileDeviceStart; dp->deviceStop = cmApFileDeviceStop; dp->deviceIsStarted = cmApFileDeviceIsStarted; dp = _ap->drvArray + 2; dp->initialize = cmApAggInitialize; dp->finalize = cmApAggFinalize; dp->deviceCount = cmApAggDeviceCount; dp->deviceLabel = cmApAggDeviceLabel; dp->deviceChannelCount = cmApAggDeviceChannelCount; dp->deviceSampleRate = cmApAggDeviceSampleRate; dp->deviceFramesPerCycle = cmApAggDeviceFramesPerCycle; dp->deviceSetup = cmApAggDeviceSetup; dp->deviceStart = cmApAggDeviceStart; dp->deviceStop = cmApAggDeviceStop; dp->deviceIsStarted = cmApAggDeviceIsStarted; dp = _ap->drvArray + 3; dp->initialize = cmApNrtInitialize; dp->finalize = cmApNrtFinalize; dp->deviceCount = cmApNrtDeviceCount; dp->deviceLabel = cmApNrtDeviceLabel; dp->deviceChannelCount = cmApNrtDeviceChannelCount; dp->deviceSampleRate = cmApNrtDeviceSampleRate; dp->deviceFramesPerCycle = cmApNrtDeviceFramesPerCycle; dp->deviceSetup = cmApNrtDeviceSetup; dp->deviceStart = cmApNrtDeviceStart; dp->deviceStop = cmApNrtDeviceStop; dp->deviceIsStarted = cmApNrtDeviceIsStarted; _ap->devCnt = 0; unsigned i; for(i=0; i<_ap->drvCnt; ++i) { unsigned dn; cmApRC_t rc0; _ap->drvArray[i].begDevIdx = cmInvalidIdx; _ap->drvArray[i].endDevIdx = cmInvalidIdx; if((rc0 = _ap->drvArray[i].initialize(rpt,_ap->devCnt)) != kOkApRC ) { rc = rc0; continue; } if((dn = _ap->drvArray[i].deviceCount()) > 0) { _ap->drvArray[i].begDevIdx = _ap->devCnt; _ap->drvArray[i].endDevIdx = _ap->devCnt + dn - 1; _ap->devCnt += dn; } } if( rc != kOkApRC ) cmApFinalize(); return rc; } cmApRC_t cmApFinalize() { cmApRC_t rc=kOkApRC; cmApRC_t rc0 = kOkApRC; unsigned i; if( _ap == NULL ) return kOkApRC; for(i=0; i<_ap->drvCnt; ++i) { if((rc0 = _ap->drvArray[i].finalize()) != kOkApRC ) rc = rc0; } cmMemPtrFree(&_ap->drvArray); cmMemPtrFree(&_ap); return rc; } unsigned cmApDeviceCount() { return _ap->devCnt; } const char* cmApDeviceLabel( unsigned devIdx ) { cmApDriver_t* dp = NULL; unsigned di = cmInvalidIdx; cmApRC_t rc; if( devIdx == cmInvalidIdx ) return NULL; if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC ) return cmStringNullGuard(NULL); return dp->deviceLabel(di); } unsigned cmApDeviceLabelToIndex( const cmChar_t* label ) { unsigned n = cmApDeviceCount(); unsigned i; for(i=0; i<n; ++i) { const cmChar_t* s = cmApDeviceLabel(i); if( s!=NULL && strcmp(s,label)==0) return i; } return cmInvalidIdx; } unsigned cmApDeviceChannelCount( unsigned devIdx, bool inputFl ) { cmApDriver_t* dp = NULL; unsigned di = cmInvalidIdx; cmApRC_t rc; if( devIdx == cmInvalidIdx ) return 0; if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC ) return rc; return dp->deviceChannelCount(di,inputFl); } double cmApDeviceSampleRate( unsigned devIdx ) { cmApDriver_t* dp = NULL; unsigned di = cmInvalidIdx; cmApRC_t rc; if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC ) return rc; return dp->deviceSampleRate(di); } unsigned cmApDeviceFramesPerCycle( unsigned devIdx, bool inputFl ) { cmApDriver_t* dp = NULL; unsigned di = cmInvalidIdx; cmApRC_t rc; if( devIdx == cmInvalidIdx ) return 0; if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC ) return rc; return dp->deviceFramesPerCycle(di,inputFl); } cmApRC_t cmApDeviceSetup( unsigned devIdx, double srate, unsigned framesPerCycle, cmApCallbackPtr_t callbackPtr, void* userCbPtr ) { cmApDriver_t* dp; unsigned di; cmApRC_t rc; if( devIdx == cmInvalidIdx ) return kOkApRC; if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC ) return rc; return dp->deviceSetup(di,srate,framesPerCycle,callbackPtr,userCbPtr); } cmApRC_t cmApDeviceStart( unsigned devIdx ) { cmApDriver_t* dp; unsigned di; cmApRC_t rc; if( devIdx == cmInvalidIdx ) return kOkApRC; if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC ) return rc; return dp->deviceStart(di); } cmApRC_t cmApDeviceStop( unsigned devIdx ) { cmApDriver_t* dp; unsigned di; cmApRC_t rc; if( devIdx == cmInvalidIdx ) return kOkApRC; if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC ) return rc; return dp->deviceStop(di); } bool cmApDeviceIsStarted( unsigned devIdx ) { cmApDriver_t* dp; unsigned di; cmApRC_t rc; if( devIdx == cmInvalidIdx ) return false; if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC ) return rc; return dp->deviceIsStarted(di); } void cmApReport( cmRpt_t* rpt ) { unsigned i,j,k; for(j=0,k=0; j<_ap->drvCnt; ++j) { cmApDriver_t* drvPtr = _ap->drvArray + j; unsigned n = drvPtr->deviceCount(); for(i=0; i<n; ++i,++k) { cmRptPrintf(rpt, "%i %f in:%i (%i) out:%i (%i) %s\n", k, drvPtr->deviceSampleRate(i), drvPtr->deviceChannelCount(i,true), drvPtr->deviceFramesPerCycle(i,true), drvPtr->deviceChannelCount(i,false), drvPtr->deviceFramesPerCycle(i,false), drvPtr->deviceLabel(i)); } } } /// [cmAudioPortExample] // See cmApPortTest() below for the main point of entry. // Data structure used to hold the parameters for cpApPortTest() // and the user defined data record passed to the host from the // audio port callback functions. typedef struct { unsigned bufCnt; // 2=double buffering 3=triple buffering unsigned chIdx; // first test channel unsigned chCnt; // count of channels to test unsigned framesPerCycle; // DSP frames per cycle unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle) unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt) unsigned inDevIdx; // input device index unsigned outDevIdx; // output device index double srate; // audio sample rate unsigned meterMs; // audio meter buffer length // param's and state for cmApSynthSine() unsigned phase; // sine synth phase double frqHz; // sine synth frequency in Hz // buffer and state for cmApCopyIn/Out() cmApSample_t* buf; // buf[bufSmpCnt] - circular interleaved audio buffer unsigned bufInIdx; // next input buffer index unsigned bufOutIdx; // next output buffer index unsigned bufFullCnt; // count of full samples // debugging log data arrays unsigned logCnt; // count of elements in log[] and ilong[] char* log; // log[logCnt] unsigned* ilog; // ilog[logCnt] unsigned logIdx; // current log index unsigned cbCnt; // count the callback } cmApPortTestRecd; #ifdef NOT_DEF // The application can request any block of channels from the device. The packets are provided with the starting // device channel and channel count. This function converts device channels and channel counts to buffer // channel indexes and counts. // // Example: // input output // i,n i n // App: 0,4 0 1 2 3 -> 2 2 // Pkt 2,8 2 3 4 5 6 7 8 -> 0 2 // // The return value is the count of application requested channels located in this packet. // // input: *appChIdxPtr and appChCnt describe a block of device channels requested by the application. // *pktChIdxPtr and pktChCnt describe a block of device channels provided to the application // // output:*appChIdxPtr and <return value> describe a block of app buffer channels which will send/recv samples. // *pktChIdxPtr and <return value> describe a block of pkt buffer channels which will send/recv samples // unsigned _cmApDeviceToBuffer( unsigned* appChIdxPtr, unsigned appChCnt, unsigned* pktChIdxPtr, unsigned pktChCnt ) { unsigned abi = *appChIdxPtr; unsigned aei = abi+appChCnt-1; unsigned pbi = *pktChIdxPtr; unsigned pei = pbi+pktChCnt-1; // if the ch's rqstd by the app do not overlap with this packet - return false. if( aei < pbi || abi > pei ) return 0; // if the ch's rqstd by the app overlap with the beginning of the pkt channel block if( abi < pbi ) { appChCnt -= pbi - abi; *appChIdxPtr = pbi - abi; *pktChIdxPtr = 0; } else { // the rqstd ch's begin inside the pkt channel block pktChCnt -= abi - pbi; *pktChIdxPtr = abi - pbi; *appChIdxPtr = 0; } // if the pkt channels extend beyond the rqstd ch block if( aei < pei ) pktChCnt -= pei - aei; else appChCnt -= aei - pei; // the rqstd ch's extend beyond or coincide with the pkt block // the returned channel count must always be the same for both the rqstd and pkt return cmMin(appChCnt,pktChCnt); } // synthesize a sine signal into an interleaved audio buffer unsigned _cmApSynthSine( cmApPortTestRecd* r, float* p, unsigned chIdx, unsigned chCnt, unsigned frmCnt, unsigned phs, double hz ) { long ph = 0; unsigned i; unsigned bufIdx = r->chIdx; unsigned bufChCnt; if( (bufChCnt = _cmApDeviceToBuffer( &bufIdx, r->chCnt, &chIdx, chCnt )) == 0) return phs; //if( r->cbCnt < 50 ) // printf("ch:%i cnt:%i ch:%i cnt:%i bi:%i bcn:%i\n",r->chIdx,r->chCnt,chIdx,chCnt,bufIdx,bufChCnt); for(i=bufIdx; i<bufIdx+bufChCnt; ++i) { unsigned j; float* op = p + i; ph = phs; for(j=0; j<frmCnt; j++, op+=chCnt, ph++) { *op = (float)(0.9 * sin( 2.0 * M_PI * hz * ph / r->srate )); } } return ph; } // Copy the audio samples in the interleaved audio buffer sp[srcChCnt*srcFrameCnt] // to the internal record buffer. void _cmApCopyIn( cmApPortTestRecd* r, const cmApSample_t* sp, unsigned srcChIdx, unsigned srcChCnt, unsigned srcFrameCnt ) { unsigned i,j; unsigned chCnt = cmMin(r->chCnt,srcChCnt); for(i=0; i<srcFrameCnt; ++i) { for(j=0; j<chCnt; ++j) r->buf[ r->bufInIdx + j ] = sp[ (i*srcChCnt) + j ]; for(; j<r->chCnt; ++j) r->buf[ r->bufInIdx + j ] = 0; r->bufInIdx = (r->bufInIdx+r->chCnt) % r->bufFrmCnt; } //r->bufFullCnt = (r->bufFullCnt + srcFrameCnt) % r->bufFrmCnt; r->bufFullCnt += srcFrameCnt; } // Copy audio samples out of the internal record buffer into dp[dstChCnt*dstFrameCnt]. void _cmApCopyOut( cmApPortTestRecd* r, cmApSample_t* dp, unsigned dstChIdx, unsigned dstChCnt, unsigned dstFrameCnt ) { // if there are not enough samples available to fill the destination buffer then zero the dst buf. if( r->bufFullCnt < dstFrameCnt ) { printf("Empty Output Buffer\n"); memset( dp, 0, dstFrameCnt*dstChCnt*sizeof(cmApSample_t) ); } else { unsigned i,j; unsigned chCnt = cmMin(dstChCnt, r->chCnt); // for each output frame for(i=0; i<dstFrameCnt; ++i) { // copy the first chCnt samples from the internal buf to the output buf for(j=0; j<chCnt; ++j) dp[ (i*dstChCnt) + j ] = r->buf[ r->bufOutIdx + j ]; // zero any output ch's for which there is no internal buf channel for(; j<dstChCnt; ++j) dp[ (i*dstChCnt) + j ] = 0; // advance the internal buffer r->bufOutIdx = (r->bufOutIdx + r->chCnt) % r->bufFrmCnt; } r->bufFullCnt -= dstFrameCnt; } } // Audio port callback function called from the audio device thread. void _cmApPortCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt ) { unsigned i; // for each incoming audio packet for(i=0; i<inPktCnt; ++i) { cmApPortTestRecd* r = (cmApPortTestRecd*)inPktArray[i].userCbPtr; if( inPktArray[i].devIdx == r->inDevIdx ) { // copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut(). _cmApCopyIn( r, (cmApSample_t*)inPktArray[i].audioBytesPtr, inPktArray[i].begChIdx, inPktArray[i].chCnt, inPktArray[i].audioFramesCnt ); } ++r->cbCnt; //printf("i %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx); } unsigned hold_phase = 0; // for each outgoing audio packet for(i=0; i<outPktCnt; ++i) { cmApPortTestRecd* r = (cmApPortTestRecd*)outPktArray[i].userCbPtr; if( outPktArray[i].devIdx == r->outDevIdx ) { // zero the output buffer memset(outPktArray[i].audioBytesPtr,0,outPktArray[i].chCnt * outPktArray[i].audioFramesCnt * sizeof(cmApSample_t) ); // if the synth is enabled if( r->synthFl ) { unsigned tmp_phase = _cmApSynthSine( r, outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt, r->phase, r->frqHz ); // the phase will only change on packets that are actually used if( tmp_phase != r->phase ) hold_phase = tmp_phase; } else { // copy the any audio in the internal record buffer to the playback device _cmApCopyOut( r, (cmApSample_t*)outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt ); } } r->phase = hold_phase; //printf("o %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx); // count callbacks ++r->cbCnt; } } #endif // print the usage message for cmAudioPortTest.c void _cmApPrintUsage( cmRpt_t* rpt ) { char msg[] = "cmApPortTest() command switches\n" "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -t -p -h \n" "\n" "-r <srate> = sample rate\n" "-a <chidx> = first channel\n" "-c <chcnt> = audio channels\n" "-b <bufcnt> = count of buffers\n" "-f <frmcnt> = count of samples per buffer\n" "-i <idevidx> = input device index\n" "-o <odevidx> = output device index\n" "-p = print report but do not start audio devices\n" "-h = print this usage message\n"; cmRptPrintf(rpt,msg); } // Get a command line option. int _cmApGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl ) { int i = 0; for(; i<argc; ++i) if( strcmp(label,argv[i]) == 0 ) { if(boolFl) return 1; if( i == (argc-1) ) return defaultVal; return atoi(argv[i+1]); } return defaultVal; } unsigned _cmGlobalInDevIdx = 0; unsigned _cmGlobalOutDevIdx = 0; void _cmApPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt ) { cmApBufInputToOutput( _cmGlobalInDevIdx, _cmGlobalOutDevIdx ); cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt ); } // Audio Port testing function int cmApPortTest( bool runFl, cmRpt_t* rpt, int argc, const char* argv[] ) { cmApPortTestRecd r; unsigned i; int result = 0; if( _cmApGetOpt(argc,argv,"-h",0,true) ) _cmApPrintUsage(rpt); runFl = _cmApGetOpt(argc,argv,"-p",!runFl,true)?false:true; r.chIdx = _cmApGetOpt(argc,argv,"-a",0,false); r.chCnt = _cmApGetOpt(argc,argv,"-c",2,false); r.bufCnt = _cmApGetOpt(argc,argv,"-b",3,false); r.framesPerCycle = _cmApGetOpt(argc,argv,"-f",512,false); r.bufFrmCnt = (r.bufCnt*r.framesPerCycle); r.bufSmpCnt = (r.chCnt * r.bufFrmCnt); r.logCnt = 100; r.meterMs = 50; cmApSample_t buf[r.bufSmpCnt]; char log[r.logCnt]; unsigned ilog[r.logCnt]; r.inDevIdx = _cmGlobalInDevIdx = _cmApGetOpt(argc,argv,"-i",0,false); r.outDevIdx = _cmGlobalOutDevIdx = _cmApGetOpt(argc,argv,"-o",2,false); r.phase = 0; r.frqHz = 2000; r.srate = 44100; r.bufInIdx = 0; r.bufOutIdx = 0; r.bufFullCnt = 0; r.logIdx = 0; r.buf = buf; r.log = log; r.ilog = ilog; r.cbCnt = 0; cmRptPrintf(rpt,"%s in:%i out:%i chidx:%i chs:%i bufs=%i frm=%i rate=%f\n",runFl?"exec":"rpt",r.inDevIdx,r.outDevIdx,r.chIdx,r.chCnt,r.bufCnt,r.framesPerCycle,r.srate); if( cmApFileAllocate(rpt) != kOkApRC ) { cmRptPrintf(rpt,"Audio port file allocation failed."); result = -1; goto errLabel; } // allocate the non-real-time port if( cmApNrtAllocate(rpt) != kOkApRC ) { cmRptPrintf(rpt,"Non-real-time system allocation failed."); result = 1; goto errLabel; } // initialize the audio device interface if( cmApInitialize(rpt) != kOkApRC ) { cmRptPrintf(rpt,"Initialize failed.\n"); result = 1; goto errLabel; } // report the current audio device configuration for(i=0; i<cmApDeviceCount(); ++i) { cmRptPrintf(rpt,"%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%f %s\n",i,cmApDeviceChannelCount(i,true),cmApDeviceFramesPerCycle(i,true),cmApDeviceChannelCount(i,false),cmApDeviceFramesPerCycle(i,false),cmApDeviceSampleRate(i),cmApDeviceLabel(i)); } // report the current audio devices using the audio port interface function cmApReport(rpt); if( runFl ) { // initialize the audio bufer cmApBufInitialize( cmApDeviceCount(), r.meterMs ); // setup the buffer for the output device cmApBufSetup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.outDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.outDevIdx,false), r.framesPerCycle ); // setup the buffer for the input device if( r.inDevIdx != r.outDevIdx ) cmApBufSetup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.inDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.inDevIdx,false), r.framesPerCycle ); // setup an output device if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC ) cmRptPrintf(rpt,"Out device setup failed.\n"); else // setup an input device if( cmApDeviceSetup(r.inDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC ) cmRptPrintf(rpt,"In device setup failed.\n"); else // start the input device if( cmApDeviceStart(r.inDevIdx) != kOkApRC ) cmRptPrintf(rpt,"In device start failed.\n"); else // start the output device if( cmApDeviceStart(r.outDevIdx) != kOkApRC ) cmRptPrintf(rpt,"Out Device start failed.\n"); cmRptPrintf(rpt,"q=quit O/o output tone, I/i input tone P/p pass\n"); char c; while((c=getchar()) != 'q') { //cmApAlsaDeviceRtReport(rpt,r.outDevIdx); switch(c) { case 'i': case 'I': cmApBufEnableTone(r.inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0)); break; case 'o': case 'O': cmApBufEnableTone(r.outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0)); break; case 'p': case 'P': cmApBufEnablePass(r.outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0)); break; case 's': cmApBufReport(rpt); break; } } // stop the input device if( cmApDeviceIsStarted(r.inDevIdx) ) if( cmApDeviceStop(r.inDevIdx) != kOkApRC ) cmRptPrintf(rpt,"In device stop failed.\n"); // stop the output device if( cmApDeviceIsStarted(r.outDevIdx) ) if( cmApDeviceStop(r.outDevIdx) != kOkApRC ) cmRptPrintf(rpt,"Out device stop failed.\n"); } errLabel: // release any resources held by the audio port interface if( cmApFinalize() != kOkApRC ) cmRptPrintf(rpt,"Finalize failed.\n"); cmApBufFinalize(); cmApNrtFree(); cmApFileFree(); // report the count of audio buffer callbacks cmRptPrintf(rpt,"cb count:%i\n", r.cbCnt ); //for(i=0; i<_logCnt; ++i) // cmRptPrintf(rpt,"%c(%i)",_log[i],_ilog[i]); //cmRptPrintf(rpt,"\n"); return result; } /// [cmAudioPortExample]