libcm/cmAudioPort.c

829 строки
24 KiB
C

#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));
}
}
//cmApAlsaDeviceReport(rpt);
}
/// [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.srate = _cmApGetOpt(argc,argv,"-r",44100,false);
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.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]