Initial commit
This commit is contained in:
commit
b108da1911
75
Makefile.am
Normal file
75
Makefile.am
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
|
||||||
|
cmHDR =
|
||||||
|
cmSRC =
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmErr.h src/libcm/cmCtx.h src/libcm/cmRpt.h src/libcm/cmGlobal.h src/libcm/cmComplexTypes.h src/libcm/cmFloatTypes.h src/libcm/cmPrefix.h
|
||||||
|
cmSRC += src/libcm/cmErr.c src/libcm/cmCtx.c src/libcm/cmRpt.c src/libcm/cmGlobal.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmSerialize.h src/libcm/cmSymTbl.h src/libcm/cmFileSys.h src/libcm/cmFile.h src/libcm/cmMem.h src/libcm/cmTime.h src/libcm/cmPgmOpts.h
|
||||||
|
cmSRC += src/libcm/cmSerialize.c src/libcm/cmSymTbl.c src/libcm/cmFileSys.c src/libcm/cmFile.c src/libcm/cmMem.c src/libcm/cmTime.c src/libcm/cmPgmOpts.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmLib.h src/libcm/cmText.h src/libcm/cmMath.h src/libcm/cmOp.h src/libcm/cmGnuPlot.h src/libcm/cmKeyboard.h
|
||||||
|
cmSRC += src/libcm/cmLib.c src/libcm/cmText.c src/libcm/cmMath.c src/libcm/cmOp.c src/libcm/cmGnuPlot.c src/libcm/cmKeyboard.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmLinkedHeap.h src/libcm/cmMallocDebug.h src/libcm/cmLex.h src/libcm/cmJson.h src/libcm/cmPrefs.h src/libcm/cmStack.h
|
||||||
|
cmSRC += src/libcm/cmLinkedHeap.c src/libcm/cmMallocDebug.c src/libcm/cmLex.c src/libcm/cmJson.c src/libcm/cmPrefs.c src/libcm/cmStack.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmUdpPort.h src/libcm/cmUdpNet.h src/libcm/cmVirtNet.h
|
||||||
|
cmSRC += src/libcm/cmUdpPort.c src/libcm/cmUdpNet.c src/libcm/cmVirtNet.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmAudioPort.h src/libcm/cmApBuf.h src/libcm/cmAudioAggDev.h src/libcm/cmAudioNrtDev.h src/libcm/cmThread.h
|
||||||
|
cmSRC += src/libcm/cmAudioPort.c src/libcm/cmApBuf.c src/libcm/cmAudioAggDev.c src/libcm/cmAudioNrtDev.c src/libcm/cmThread.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmMidiFilePlay.h src/libcm/cmMidiPort.h src/libcm/cmMidiFile.h src/libcm/cmMidi.h
|
||||||
|
cmSRC += src/libcm/cmMidiFilePlay.c src/libcm/cmMidiPort.c src/libcm/cmMidiFile.c src/libcm/cmMidi.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmAudioFile.h src/libcm/cmAudioFileMgr.h src/libcm/cmMsgProtocol.h src/libcm/cmAudioSys.h src/libcm/cmAudioPortFile.h src/libcm/cmAudioFileDev.h
|
||||||
|
cmSRC += src/libcm/cmAudioFile.c src/libcm/cmAudioFileMgr.c src/libcm/cmMsgProtocol.c src/libcm/cmAudioSys.c src/libcm/cmAudioPortFile.c src/libcm/cmAudioFileDev.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmFrameFile.h src/libcm/cmFeatFile.h src/libcm/cmCsv.h src/libcm/cmAudLabelFile.h src/libcm/cmTagFile.h
|
||||||
|
cmSRC += src/libcm/cmFrameFile.c src/libcm/cmFeatFile.c src/libcm/cmCsv.c src/libcm/cmAudLabelFile.c src/libcm/cmTagFile.c
|
||||||
|
|
||||||
|
cmSRC += src/libcm/cmGr.c src/libcm/cmGrDevCtx.c src/libcm/cmGrPage.c src/libcm/cmGrPlot.c src/libcm/cmGrPlotAudio.c
|
||||||
|
cmHDR += src/libcm/cmGr.h src/libcm/cmGrDevCtx.h src/libcm/cmGrPage.h src/libcm/cmGrPlot.h src/libcm/cmGrPlotAudio.h
|
||||||
|
|
||||||
|
cmHDR += src/libcm/dsp/cmDspSys.h src/libcm/dsp/cmDspClass.h src/libcm/dsp/cmDspValue.h src/libcm/dsp/cmDspUi.h src/libcm/dsp/cmDspPreset.h src/libcm/dsp/cmDspNet.h
|
||||||
|
cmSRC += src/libcm/dsp/cmDspSys.c src/libcm/dsp/cmDspClass.c src/libcm/dsp/cmDspValue.c src/libcm/dsp/cmDspUi.c src/libcm/dsp/cmDspPreset.c src/libcm/dsp/cmDspNet.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/dsp/cmDspBuiltIn.h src/libcm/dsp/cmDspFx.h
|
||||||
|
cmSRC += src/libcm/dsp/cmDspBuiltIn.c src/libcm/dsp/cmDspFx.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/dsp/cmDspPgm.h src/libcm/dsp/cmDspKr.h src/libcm/dsp/cmDspPgmPP.h src/libcm/dsp/cmDspPgmPPMain.h
|
||||||
|
cmSRC += src/libcm/dsp/cmDspPgm.c src/libcm/dsp/cmDspKr.c src/libcm/dsp/cmDspPgmPP.c src/libcm/dsp/cmDspPgmPPMain.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmAudDsp.h src/libcm/cmAudDspIF.h src/libcm/cmAudDspLocal.h
|
||||||
|
cmSRC += src/libcm/cmAudDsp.c src/libcm/cmAudDspIF.c src/libcm/cmAudDspLocal.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/vop/cmVectOpsTemplateUndef.h src/libcm/vop/cmVectOpsTemplateHdr.h src/libcm/vop/cmVectOpsTemplateCode.h src/libcm/vop/cmVectOpsTemplateMain.h
|
||||||
|
cmHDR += src/libcm/vop/cmVectOpsRIHdr.h src/libcm/vop/cmVectOpsRICode.h
|
||||||
|
cmHDR += src/libcm/vop/cmProcTemplateUndef.h src/libcm/vop/cmProcTemplateHdr.h src/libcm/vop/cmProcTemplateCode.h src/libcm/vop/cmProcTemplateMain.h
|
||||||
|
cmHDR += src/libcm/vop/cmVectOps.h src/libcm/vop/cmProcTemplate.h
|
||||||
|
|
||||||
|
cmSRC += src/libcm/vop/cmVectOps.c src/libcm/vop/cmProcTemplate.c
|
||||||
|
|
||||||
|
cmHDR += src/libcm/cmProcObj.h src/libcm/cmProc.h src/libcm/cmProc2.h src/libcm/cmProc3.h src/libcm/cmProcTest.h
|
||||||
|
cmSRC += src/libcm/cmProcObj.c src/libcm/cmProc.c src/libcm/cmProc2.c src/libcm/cmProc3.c src/libcm/cmProcTest.c
|
||||||
|
|
||||||
|
|
||||||
|
cmHDR += src/libcm/app/cmOnset.h src/libcm/app/cmTimeLine.h src/libcm/app/cmScore.h src/libcm/app/cmPickup.h src/libcm/cmRbm.h
|
||||||
|
cmSRC += src/libcm/app/cmOnset.c src/libcm/app/cmTimeLine.c src/libcm/app/cmScore.c src/libcm/app/cmPickup.c src/libcm/cmRbm.c
|
||||||
|
|
||||||
|
|
||||||
|
if OS_LINUX
|
||||||
|
cmSRC += src/libcm/linux/cmFileSysLinux.c src/libcm/linux/cmAudioPortAlsa.c src/libcm/linux/cmMidiAlsa.c
|
||||||
|
cmHDR += src/libcm/linux/cmFileSysLinux.h src/libcm/linux/cmAudioPortAlsa.h
|
||||||
|
endif
|
||||||
|
|
||||||
|
if OS_OSX
|
||||||
|
cmSRC += src/libcm/osx/clock_gettime_stub.c src/libcm/osx/cmMidiOsx.c src/libcm/osx/cmAudioPortOsx.c src/libcm/osx/cmFileSysOsx.c
|
||||||
|
cmHDR += src/libcm/osx/clock_gettime_stub.h
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
377
app/cmOnset.c
Normal file
377
app/cmOnset.c
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmComplexTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmLinkedHeap.h"
|
||||||
|
#include "cmSymTbl.h"
|
||||||
|
#include "cmAudioFile.h"
|
||||||
|
#include "cmMidi.h"
|
||||||
|
#include "cmFile.h"
|
||||||
|
#include "cmMath.h"
|
||||||
|
|
||||||
|
#include "cmProcObj.h"
|
||||||
|
#include "cmProcTemplateMain.h"
|
||||||
|
#include "cmProc.h"
|
||||||
|
#include "cmProc2.h"
|
||||||
|
#include "cmVectOps.h"
|
||||||
|
|
||||||
|
#include "cmOnset.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmOnsetCfg_t cfg;
|
||||||
|
|
||||||
|
cmCtx* ctxPtr;
|
||||||
|
cmAudioFileRd* afRdPtr;
|
||||||
|
cmPvAnl* pvocPtr;
|
||||||
|
|
||||||
|
cmAudioFileH_t afH; // output audio file
|
||||||
|
cmFileH_t txH; // output text file
|
||||||
|
|
||||||
|
unsigned frmCnt; // spectral frame count
|
||||||
|
cmReal_t* sfV; // sfV[frmCnt] spectral flux vector
|
||||||
|
cmReal_t* dfV; // dfV[frmCnt] onset function vector
|
||||||
|
|
||||||
|
cmAudioFileInfo_t afInfo;
|
||||||
|
unsigned fftSmpCnt;
|
||||||
|
unsigned hopSmpCnt;
|
||||||
|
unsigned binCnt;
|
||||||
|
|
||||||
|
} _cmOn_t;
|
||||||
|
|
||||||
|
cmOnH_t cmOnsetNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
_cmOn_t* _cmOnsetHandleToPtr( cmOnH_t h )
|
||||||
|
{
|
||||||
|
_cmOn_t* p = (_cmOn_t*)h.h;
|
||||||
|
assert(p!=NULL);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmOnRC_t _cmOnsetFinalize( _cmOn_t* p )
|
||||||
|
{
|
||||||
|
cmOnRC_t rc = kOkOnRC;
|
||||||
|
|
||||||
|
if( cmPvAnlFree(&p->pvocPtr) != cmOkRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC,"Phase voocoder free failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmAudioFileRdFree(&p->afRdPtr) != cmOkRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC,"Audio file reader failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmCtxFree(&p->ctxPtr) != cmOkRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC,"Context proc failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemPtrFree(&p->sfV);
|
||||||
|
cmMemPtrFree(&p->dfV);
|
||||||
|
cmMemPtrFree(&p);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmOnRC_t cmOnsetInitialize( cmCtx_t* c, cmOnH_t* hp )
|
||||||
|
{
|
||||||
|
cmOnRC_t rc;
|
||||||
|
if((rc = cmOnsetFinalize(hp)) != kOkOnRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
_cmOn_t* p = cmMemAllocZ(_cmOn_t,1);
|
||||||
|
cmErrSetup(&p->err,&c->rpt,"Onset");
|
||||||
|
|
||||||
|
// create the proc context object
|
||||||
|
if((p->ctxPtr = cmCtxAlloc(NULL,&c->rpt,cmLHeapNullHandle,cmSymTblNullHandle)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC, "The ctx compoenent allocation failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the audio file reader
|
||||||
|
if((p->afRdPtr = cmAudioFileRdAlloc( p->ctxPtr, NULL, 0, NULL, cmInvalidIdx, 0, cmInvalidIdx )) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC, "The audio file reader allocation failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the phase vocoder
|
||||||
|
if((p->pvocPtr = cmPvAnlAlloc( p->ctxPtr, NULL, 0, 0, 0, 0, 0 )) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC,"The phase vocoder allocation failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( rc != kOkOnRC )
|
||||||
|
_cmOnsetFinalize(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmOnRC_t cmOnsetFinalize( cmOnH_t* hp )
|
||||||
|
{
|
||||||
|
cmOnRC_t rc = kOkOnRC;
|
||||||
|
|
||||||
|
if( hp==NULL || cmOnsetIsValid(*hp)==false )
|
||||||
|
return kOkOnRC;
|
||||||
|
|
||||||
|
_cmOn_t* p = _cmOnsetHandleToPtr(*hp);
|
||||||
|
|
||||||
|
rc = _cmOnsetFinalize(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmOnsetIsValid( cmOnH_t h )
|
||||||
|
{ return h.h!=NULL; }
|
||||||
|
|
||||||
|
cmOnRC_t _cmOnsetExec( _cmOn_t* p, unsigned chCnt )
|
||||||
|
{
|
||||||
|
cmOnRC_t rc = kOkOnRC;
|
||||||
|
int fi = 0;
|
||||||
|
unsigned binCnt = p->binCnt; //p->pvocPtr->binCnt;
|
||||||
|
cmReal_t mag0V[ binCnt ];
|
||||||
|
cmSample_t out0V[ p->hopSmpCnt ];
|
||||||
|
cmSample_t out1V[ p->hopSmpCnt ];
|
||||||
|
cmSample_t* aoutV[chCnt];
|
||||||
|
double prog = 0.1;
|
||||||
|
cmReal_t b0 = 1;
|
||||||
|
cmReal_t b[] = {1 };
|
||||||
|
cmReal_t a[] = {p->cfg.filtCoeff};
|
||||||
|
cmReal_t d[] = {0};
|
||||||
|
cmReal_t maxVal = 0;
|
||||||
|
|
||||||
|
if( chCnt > 0 )
|
||||||
|
aoutV[0] = out0V;
|
||||||
|
|
||||||
|
if( chCnt > 1 )
|
||||||
|
aoutV[1] = out1V;
|
||||||
|
|
||||||
|
cmVOR_Zero(mag0V,binCnt);
|
||||||
|
|
||||||
|
// for each frame - read the next block of audio
|
||||||
|
for(; fi<p->frmCnt && cmAudioFileRdRead(p->afRdPtr) != cmEofRC; ++fi )
|
||||||
|
{
|
||||||
|
// calc the spectrum
|
||||||
|
while( cmPvAnlExec(p->pvocPtr, p->afRdPtr->outV, p->afRdPtr->outN ) )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// calc the spectral flux into sfV[fi].
|
||||||
|
cmReal_t sf = 0;
|
||||||
|
for(i=0; i<binCnt; ++i)
|
||||||
|
{
|
||||||
|
cmReal_t m1 = p->pvocPtr->magV[i] * 2.0;
|
||||||
|
|
||||||
|
if( m1 > maxVal )
|
||||||
|
maxVal = m1;
|
||||||
|
|
||||||
|
cmReal_t dif = m1 - mag0V[i]; // calc. spectral flux
|
||||||
|
if( dif > 0 )
|
||||||
|
sf += dif; // accum. flux
|
||||||
|
mag0V[i] = m1; // store magn. for next frame
|
||||||
|
}
|
||||||
|
|
||||||
|
p->sfV[fi] = sf;
|
||||||
|
|
||||||
|
// filter the spectral flux
|
||||||
|
cmVOR_Filter( p->sfV + fi, 1, &sf, 1, b0, b, a, d, 1 );
|
||||||
|
|
||||||
|
if( fi >= prog*p->frmCnt )
|
||||||
|
{
|
||||||
|
cmRptPrintf(p->err.rpt,"%i ",lround(prog*10));
|
||||||
|
prog += 0.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p->frmCnt = fi;
|
||||||
|
|
||||||
|
// normalize the spectral flux vector
|
||||||
|
cmReal_t mean = cmVOR_Mean(p->sfV,p->frmCnt);
|
||||||
|
cmReal_t stdDev = sqrt(cmVOR_Variance(p->sfV, p->frmCnt, &mean ));
|
||||||
|
cmVOR_SubVS(p->sfV,p->frmCnt,mean);
|
||||||
|
cmVOR_DivVS(p->sfV,p->frmCnt,stdDev);
|
||||||
|
cmReal_t maxSf = cmVOR_Max(p->sfV,p->frmCnt,1);
|
||||||
|
prog = 0.1;
|
||||||
|
|
||||||
|
printf("max:%f ",maxVal);
|
||||||
|
printf("mean:%f max:%f sd:%f\n",mean,maxSf,stdDev);
|
||||||
|
|
||||||
|
// Pick peaks from the onset detection function using a subset
|
||||||
|
// of the rules from Dixon, 2006, Onset Detection Revisited.
|
||||||
|
// locate the onsets and store them in dfV[]
|
||||||
|
for(fi=0; fi<p->frmCnt; ++fi)
|
||||||
|
{
|
||||||
|
int bi = cmMax(0, fi - p->cfg.wndFrmCnt); // begin wnd index
|
||||||
|
int ei = cmMin(p->frmCnt, fi + p->cfg.wndFrmCnt); // end wnd index
|
||||||
|
int nn = ei - bi; // wnd frm cnt
|
||||||
|
int wi = fi < p->cfg.wndFrmCnt ? fi : p->cfg.wndFrmCnt; // cur wnd index
|
||||||
|
|
||||||
|
// initialize the out
|
||||||
|
cmVOS_Fill(out1V,p->hopSmpCnt,p->sfV[fi]/maxSf);
|
||||||
|
cmVOS_Zero(out0V,p->hopSmpCnt);
|
||||||
|
|
||||||
|
p->dfV[fi] = 0;
|
||||||
|
|
||||||
|
// if cur index is a peak in the window
|
||||||
|
if( cmVOR_MaxIndex(p->sfV + bi, nn, 1 ) == wi )
|
||||||
|
{
|
||||||
|
// calc an extended window going backwards in time
|
||||||
|
bi = cmMax(0, fi - p->cfg.wndFrmCnt * p->cfg.preWndMult );
|
||||||
|
nn = ei - bi;
|
||||||
|
|
||||||
|
// if the cur value is greater than the mean of the extended window plus a threshold
|
||||||
|
if( p->sfV[fi] > cmVOR_Mean(p->sfV + bi, nn ) + p->cfg.threshold )
|
||||||
|
{
|
||||||
|
p->dfV[fi] = p->sfV[fi];
|
||||||
|
out0V[ p->hopSmpCnt/2 ] = p->sfV[fi]/maxSf;
|
||||||
|
|
||||||
|
unsigned smpIdx = fi * p->hopSmpCnt + p->hopSmpCnt/2;
|
||||||
|
|
||||||
|
// write the output text file
|
||||||
|
if( cmFilePrintf(p->txH, "[ %i, %f ]\n", smpIdx, p->sfV[fi] ) != kOkFileRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspTextFileFailOnRC,"Text output write to '%s' failed.", cmFileName(p->txH));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the output audio file
|
||||||
|
if( cmAudioFileWriteFloat(p->afH, p->hopSmpCnt, chCnt, aoutV ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspAudioFileFailOnRC,"Audio file write to '%s' failed.",cmAudioFileName(p->afH));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( fi >= prog*p->frmCnt )
|
||||||
|
{
|
||||||
|
cmRptPrintf(p->err.rpt,"%i ",lround(prog*10));
|
||||||
|
prog += 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmOnRC_t cmOnsetExec( cmOnH_t h, const cmOnsetCfg_t* cfg, const cmChar_t* inAudioFn, const cmChar_t* outAudioFn, const cmChar_t* outTextFn )
|
||||||
|
{
|
||||||
|
cmOnRC_t rc = kOkOnRC;
|
||||||
|
_cmOn_t* p = _cmOnsetHandleToPtr(h);
|
||||||
|
unsigned audioOutChCnt = 2;
|
||||||
|
p->cfg = *cfg;
|
||||||
|
|
||||||
|
// get the audio file header information
|
||||||
|
if( cmAudioFileGetInfo(inAudioFn, &p->afInfo, p->err.rpt ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC,"The audio file open failed on '%s'.",cmStringNullGuard(inAudioFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->fftSmpCnt = cmNearPowerOfTwo( (unsigned)floor( p->cfg.wndMs * p->afInfo.srate / 1000.0 ) );
|
||||||
|
p->hopSmpCnt = p->fftSmpCnt / p->cfg.hopFact;
|
||||||
|
p->binCnt = cmMin(p->fftSmpCnt/2 + 1, floor(p->cfg.maxFrqHz / (p->afInfo.srate / p->fftSmpCnt)));
|
||||||
|
p->frmCnt = (p->afInfo.frameCnt - p->fftSmpCnt) / p->hopSmpCnt;
|
||||||
|
p->sfV = cmMemResizeZ(cmReal_t,p->sfV,p->frmCnt);
|
||||||
|
p->dfV = cmMemResizeZ(cmReal_t,p->dfV,p->frmCnt);
|
||||||
|
|
||||||
|
// initialize the audio file reader
|
||||||
|
if( cmAudioFileRdOpen( p->afRdPtr, p->hopSmpCnt, inAudioFn, p->cfg.audioChIdx, 0, cmInvalidIdx ) != cmOkRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC, "The audio file reader open failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the phase vocoder
|
||||||
|
if( cmPvAnlInit( p->pvocPtr, p->hopSmpCnt, p->afInfo.srate, p->fftSmpCnt, p->hopSmpCnt, kNoCalcHzPvaFl ) != cmOkRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspProcFailOnRC," The phase vocoder initialization failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initalize the audio output file
|
||||||
|
if( outAudioFn != NULL )
|
||||||
|
if( cmAudioFileIsValid( p->afH = cmAudioFileNewCreate( outAudioFn, p->afInfo.srate, p->afInfo.bits, audioOutChCnt, NULL, p->err.rpt)) == false )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspAudioFileFailOnRC, "The audio output file '%s' could not be opened.", outAudioFn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the text output file
|
||||||
|
if( outTextFn != NULL )
|
||||||
|
{
|
||||||
|
if( cmFileOpen( &p->txH, outTextFn, kWriteFileFl, p->err.rpt ) != kOkFileRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kDspTextFileFailOnRC, "The text output file '%s' could not be opened.",outTextFn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFilePrint(p->txH,"{\n onsetArray : \n[\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = _cmOnsetExec(p,audioOutChCnt);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
// close the output audio file
|
||||||
|
if( cmAudioFileDelete(&p->afH) != kOkAfRC )
|
||||||
|
rc = cmErrMsg(&p->err,kDspAudioFileFailOnRC,"The audio file close failed.");
|
||||||
|
|
||||||
|
// close the text file
|
||||||
|
if( cmFileIsValid(p->txH) )
|
||||||
|
{
|
||||||
|
cmFilePrint(p->txH,"]\n}\n");
|
||||||
|
|
||||||
|
if( cmFileClose(&p->txH) != kOkFileRC )
|
||||||
|
rc = cmErrMsg(&p->err,kDspTextFileFailOnRC,"The text file close failed.");
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmOnRC_t cmOnsetTest( cmCtx_t* c )
|
||||||
|
{
|
||||||
|
cmOnsetCfg_t cfg;
|
||||||
|
cmOnH_t h = cmOnsetNullHandle;
|
||||||
|
cmOnRC_t rc = kOkOnRC;
|
||||||
|
const cmChar_t* inAudioFn = "/home/kevin/temp/onset0.wav";
|
||||||
|
const cmChar_t* outAudioFn = "/home/kevin/temp/mas/mas0.aif";
|
||||||
|
const cmChar_t* outTextFn = "/home/kevin/temp/mas/mas0.txt";
|
||||||
|
|
||||||
|
cfg.wndMs = 42;
|
||||||
|
cfg.hopFact = 4;
|
||||||
|
cfg.audioChIdx = 0;
|
||||||
|
cfg.wndFrmCnt = 3;
|
||||||
|
cfg.preWndMult = 3;
|
||||||
|
cfg.threshold = 0.6;
|
||||||
|
cfg.maxFrqHz = 24000;
|
||||||
|
cfg.filtCoeff = -0.7;
|
||||||
|
|
||||||
|
if((rc = cmOnsetInitialize(c,&h)) != kOkOnRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
rc = cmOnsetExec(h,&cfg,inAudioFn,outAudioFn,outTextFn);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
cmOnsetFinalize(&h);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
55
app/cmOnset.h
Normal file
55
app/cmOnset.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#ifndef cmOnset_h
|
||||||
|
#define cmOnset_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkOnRC = cmOkRC,
|
||||||
|
kDspProcFailOnRC,
|
||||||
|
kDspAudioFileFailOnRC,
|
||||||
|
kDspTextFileFailOnRC,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmOnRC_t;
|
||||||
|
typedef cmHandle_t cmOnH_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
double wndMs;
|
||||||
|
unsigned hopFact;
|
||||||
|
unsigned audioChIdx;
|
||||||
|
|
||||||
|
unsigned wndFrmCnt; //
|
||||||
|
double preWndMult; //
|
||||||
|
double threshold; //
|
||||||
|
double maxFrqHz; //
|
||||||
|
double filtCoeff; //
|
||||||
|
|
||||||
|
} cmOnsetCfg_t;
|
||||||
|
|
||||||
|
extern cmOnH_t cmOnsetNullHandle;
|
||||||
|
|
||||||
|
cmOnRC_t cmOnsetInitialize( cmCtx_t* c, cmOnH_t* hp );
|
||||||
|
|
||||||
|
cmOnRC_t cmOnsetFinalize( cmOnH_t* hp );
|
||||||
|
|
||||||
|
bool cmOnsetIsValid( cmOnH_t h );
|
||||||
|
|
||||||
|
cmOnRC_t cmOnsetExec(
|
||||||
|
cmOnH_t h,
|
||||||
|
const cmOnsetCfg_t* cfg,
|
||||||
|
const cmChar_t* inAudioFn,
|
||||||
|
const cmChar_t* outAudioFn,
|
||||||
|
const cmChar_t* outTextFn );
|
||||||
|
|
||||||
|
cmOnRC_t cmOnsetTest( cmCtx_t* c );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
876
app/cmPickup.c
Normal file
876
app/cmPickup.c
Normal file
@ -0,0 +1,876 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmComplexTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmLinkedHeap.h"
|
||||||
|
#include "cmSymTbl.h"
|
||||||
|
#include "cmJson.h"
|
||||||
|
#include "cmMidi.h"
|
||||||
|
#include "cmAudioFile.h"
|
||||||
|
#include "cmFile.h"
|
||||||
|
#include "cmFileSys.h"
|
||||||
|
#include "cmProcObj.h"
|
||||||
|
#include "cmProcTemplate.h"
|
||||||
|
#include "cmVectOpsTemplateMain.h"
|
||||||
|
#include "cmProc.h"
|
||||||
|
#include "cmProc2.h"
|
||||||
|
#include "cmProc3.h"
|
||||||
|
#include "cmPickup.h"
|
||||||
|
#include "cmAudLabelFile.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kRmsPuId,
|
||||||
|
kMedPuId,
|
||||||
|
kDifPuId,
|
||||||
|
kAvgPuId,
|
||||||
|
kOnsPuId,
|
||||||
|
kFltPuId,
|
||||||
|
kSupPuId,
|
||||||
|
kAtkPuId,
|
||||||
|
kRlsPuId,
|
||||||
|
kSegPuId,
|
||||||
|
kPuCnt
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
unsigned chCnt;
|
||||||
|
cmPuCh_t* chArray;
|
||||||
|
cmCtx_t ctx; // stored ctx used by cmAudLabelFileAllocOpen()
|
||||||
|
|
||||||
|
// cmProc objects
|
||||||
|
cmCtx* ctxp;
|
||||||
|
cmAudioFileRd* afrp;
|
||||||
|
cmShiftBuf* sbp;
|
||||||
|
cmGateDetect2* gdp;
|
||||||
|
cmBinMtxFile_t* mfp;
|
||||||
|
|
||||||
|
const cmChar_t* inAudFn;
|
||||||
|
const cmChar_t* inLabelFn;
|
||||||
|
const cmChar_t* outMtx0Fn;
|
||||||
|
const cmChar_t* outMtx1Fn;
|
||||||
|
const cmChar_t* outAudFn;
|
||||||
|
cmReal_t hopMs;
|
||||||
|
|
||||||
|
cmGateDetectParams gd0Args;
|
||||||
|
cmGateDetectParams gd1Args;
|
||||||
|
} cmPu_t;
|
||||||
|
|
||||||
|
cmPuH_t cmPuNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
cmPu_t* _cmPuHandleToPtr( cmPuH_t h )
|
||||||
|
{
|
||||||
|
cmPu_t* p = (cmPu_t*)h.h;
|
||||||
|
assert(p!=NULL);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t cmPuAlloc( cmCtx_t* ctx, cmPuH_t* hp )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc;
|
||||||
|
if((rc = cmPuFree(hp)) != kOkPuRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmPu_t* p = cmMemAllocZ(cmPu_t,1);
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"Pickup");
|
||||||
|
|
||||||
|
p->ctx = *ctx;
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmPuRC_t cmPuFree( cmPuH_t* hp )
|
||||||
|
{
|
||||||
|
if( hp == NULL || cmPuIsValid(*hp) == false )
|
||||||
|
return kOkPuRC;
|
||||||
|
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(*hp);
|
||||||
|
|
||||||
|
cmMemPtrFree(&p->chArray);
|
||||||
|
cmMemPtrFree(&p);
|
||||||
|
hp->h = NULL;
|
||||||
|
return kOkPuRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmPuIsValid( cmPuH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuReadLabelsAndCreateArray( cmPu_t* p, const cmChar_t* labelFn, cmReal_t srate )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmAlfH_t h = cmAlfNullHandle;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if( cmAudLabelFileAllocOpen(&p->ctx, &h, labelFn) != kOkAlfRC )
|
||||||
|
return cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune audio label file open failed on '%s'",cmStringNullGuard(labelFn));
|
||||||
|
|
||||||
|
if((p->chCnt = cmAudLabelFileCount(h)) == 0 )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune audio label file '%s' does not contain any segment labels.",cmStringNullGuard(labelFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->chArray = cmMemResizeZ(cmPuCh_t,p->chArray,p->chCnt);
|
||||||
|
|
||||||
|
for(i=0; i<p->chCnt; ++i)
|
||||||
|
{
|
||||||
|
const cmAlfLabel_t* lp;
|
||||||
|
if(( lp = cmAudLabelFileLabel(h,i)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune label in '%s' at row %i could not be read.",cmStringNullGuard(labelFn),i+1);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->chArray[i].begSmpIdx = floor(srate * lp->begSecs);
|
||||||
|
p->chArray[i].endSmpIdx = p->chArray[i].begSmpIdx; // default the segment to have 0 length.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( cmAudLabelFileFree(&h) != kOkAlfRC )
|
||||||
|
rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune label file close failed.");
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuWriteMtxFile(cmPu_t* p, bool segFl )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
|
||||||
|
cmReal_t outV[ kPuCnt ];
|
||||||
|
outV[ kRmsPuId ] = p->gdp->rms;
|
||||||
|
outV[ kMedPuId ] = p->gdp->med;
|
||||||
|
outV[ kDifPuId ] = p->gdp->dif;
|
||||||
|
outV[ kAvgPuId ] = p->gdp->avg;
|
||||||
|
outV[ kOnsPuId ] = p->gdp->ons;
|
||||||
|
outV[ kFltPuId ] = p->gdp->flt;
|
||||||
|
outV[ kSupPuId ] = p->gdp->sup;
|
||||||
|
outV[ kAtkPuId ] = p->gdp->onFl;
|
||||||
|
outV[ kRlsPuId ] = p->gdp->offFl;
|
||||||
|
outV[ kSegPuId ] = segFl;
|
||||||
|
|
||||||
|
// write the output file - plot with cmGateDetectPlot.m
|
||||||
|
if( cmBinMtxFileExecR(p->mfp,outV,kPuCnt) != cmOkRC )
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Matrix file write failed.");
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmPuCalcGains( cmPu_t* p )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
cmReal_t avg = 0;
|
||||||
|
|
||||||
|
if( p->chCnt == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(i=0; i<p->chCnt; ++i)
|
||||||
|
avg += p->chArray[i].gateMaxAvg;
|
||||||
|
|
||||||
|
avg /= p->chCnt;
|
||||||
|
|
||||||
|
for(i=0; i<p->chCnt; ++i)
|
||||||
|
{
|
||||||
|
cmReal_t d = p->chArray[i].gateMaxAvg==0 ? 1.0 : p->chArray[i].gateMaxAvg;
|
||||||
|
p->chArray[i].gain = avg / d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuCh_t* _cmPuIncrCh( cmPu_t* p, cmPuCh_t* chp, unsigned* segSmpIdxPtr )
|
||||||
|
{
|
||||||
|
if( *segSmpIdxPtr != p->chArray[0].begSmpIdx )
|
||||||
|
++chp;
|
||||||
|
|
||||||
|
if( chp >= p->chArray + p->chCnt )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if( chp+1 == p->chArray + p->chCnt )
|
||||||
|
*segSmpIdxPtr = p->afrp->info.frameCnt;
|
||||||
|
else
|
||||||
|
*segSmpIdxPtr = (chp+1)->begSmpIdx;
|
||||||
|
|
||||||
|
return chp;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuCalcRerunGateDetectors(
|
||||||
|
cmPu_t* p,
|
||||||
|
const cmChar_t* outMtxFn,
|
||||||
|
const cmChar_t* outAudFn,
|
||||||
|
const cmGateDetectParams* gdArgs,
|
||||||
|
unsigned procSmpCnt,
|
||||||
|
unsigned wndSmpCnt,
|
||||||
|
unsigned hopSmpCnt )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmAudioFileWr* afwp = NULL;
|
||||||
|
unsigned outChCnt = 1;
|
||||||
|
unsigned outChIdx = 0;
|
||||||
|
unsigned bitsPerSmp = 16;
|
||||||
|
unsigned smpIdx = 0;
|
||||||
|
cmSample_t* smpV = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
// rewind the audio file reader
|
||||||
|
if( cmAudioFileRdSeek(p->afrp,0) != cmOkRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kProcFailPuRC,"Audio file seek failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the shift buffer
|
||||||
|
if( cmShiftBufInit( p->sbp, procSmpCnt, wndSmpCnt, hopSmpCnt ) != cmOkRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kProcFailPuRC,"Shift buffer reset failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the gate detector
|
||||||
|
if( cmGateDetectInit2( p->gdp, procSmpCnt, gdArgs ) != cmOkRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kProcFailPuRC,"Gate detector reset failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an new matrix output file
|
||||||
|
if( cmBinMtxFileInit( p->mfp, outMtxFn ) != cmOkRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Output matrix file '%s' initialization failed.",cmStringNullGuard(outMtxFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an audio output file
|
||||||
|
if( (afwp = cmAudioFileWrAlloc(p->ctxp, NULL, procSmpCnt, outAudFn, p->afrp->info.srate, outChCnt, bitsPerSmp )) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Output audio file '%s' initialization failed.",cmStringNullGuard(outAudFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
smpV = cmMemAllocZ(cmSample_t,procSmpCnt);
|
||||||
|
|
||||||
|
cmPuCh_t* chp = p->chArray;
|
||||||
|
unsigned segSmpIdx = chp->begSmpIdx;
|
||||||
|
bool segFl = false;
|
||||||
|
|
||||||
|
// for each procSmpCnt samples
|
||||||
|
for(; cmAudioFileRdRead(p->afrp) != cmEofRC; smpIdx += procSmpCnt )
|
||||||
|
{
|
||||||
|
// apply auto-gain to the audio vector
|
||||||
|
cmVOS_MultVVS(smpV,p->afrp->outN,p->afrp->outV,chp->gain);
|
||||||
|
|
||||||
|
// is this a segment boundary
|
||||||
|
if( smpIdx+procSmpCnt >= p->afrp->info.frameCnt || (smpIdx <= segSmpIdx && segSmpIdx < smpIdx + procSmpCnt) )
|
||||||
|
{
|
||||||
|
segFl = true;
|
||||||
|
|
||||||
|
if((chp = _cmPuIncrCh(p,chp, &segSmpIdx )) == NULL )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shift the new samples into the shift buffer
|
||||||
|
while(cmShiftBufExec(p->sbp,smpV,p->afrp->outN))
|
||||||
|
{
|
||||||
|
|
||||||
|
// update the gate detector
|
||||||
|
cmGateDetectExec2(p->gdp,p->sbp->outV,p->sbp->outN);
|
||||||
|
|
||||||
|
if( _cmPuWriteMtxFile(p,segFl) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
segFl =false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the audio output file
|
||||||
|
if( cmAudioFileWrExec(afwp, outChIdx,smpV,p->afrp->outN ) != cmOkRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kProcFailPuRC,"A write failed to the audio output file '%s'.",outAudFn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
|
||||||
|
cmMemPtrFree(&smpV);
|
||||||
|
|
||||||
|
if( cmAudioFileWrFree(&afwp) != cmOkRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Output audio file '%s' close failed.",cmStringNullGuard(outAudFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t cmPuAutoGainCfg(
|
||||||
|
cmPuH_t h,
|
||||||
|
const cmChar_t* audioFn,
|
||||||
|
const cmChar_t* labelFn,
|
||||||
|
const cmChar_t* outMtx0Fn,
|
||||||
|
const cmChar_t* outMtx1Fn,
|
||||||
|
const cmChar_t* outAudFn,
|
||||||
|
unsigned procSmpCnt,
|
||||||
|
cmReal_t hopMs,
|
||||||
|
const cmGateDetectParams* gd0Args,
|
||||||
|
const cmGateDetectParams* gd1Args )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc;
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(h);
|
||||||
|
int smpIdx = 0;
|
||||||
|
int chIdx = 0;
|
||||||
|
const cmReal_t rmsMax = 1.0;
|
||||||
|
cmReal_t minRms = rmsMax;
|
||||||
|
cmReal_t gateMax = 0;
|
||||||
|
cmReal_t gateSum = 0;
|
||||||
|
unsigned gateCnt = 0;
|
||||||
|
cmPuCh_t* chp = NULL;
|
||||||
|
bool segFl = false;
|
||||||
|
unsigned segSmpIdx = cmInvalidIdx;
|
||||||
|
|
||||||
|
// create a cmProc context
|
||||||
|
if((p->ctxp = cmCtxAlloc(NULL, p->err.rpt, cmLHeapNullHandle, cmSymTblNullHandle )) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Proc context create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a cmProc audio file reader
|
||||||
|
if((p->afrp = cmAudioFileRdAlloc(p->ctxp, NULL, procSmpCnt, audioFn, chIdx, 0, cmInvalidIdx)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Audio file reader creation failed on '%s'.",cmStringNullGuard(audioFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// given the sample rate calculate the hop and window size in samples
|
||||||
|
unsigned hopSmpCnt = floor(p->afrp->info.srate * hopMs / 1000);
|
||||||
|
unsigned wndSmpCnt = hopSmpCnt * gd0Args->medCnt;
|
||||||
|
|
||||||
|
// create a shift buffer to maintain the RMS window for the gate detector
|
||||||
|
if((p->sbp = cmShiftBufAlloc(p->ctxp,NULL,procSmpCnt,wndSmpCnt,hopSmpCnt )) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Shift buffer create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a gate detector
|
||||||
|
if((p->gdp = cmGateDetectAlloc2(p->ctxp,NULL,procSmpCnt,gd0Args)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Gate detect create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an output file to hold the results of the gate detector
|
||||||
|
if( (p->mfp = cmBinMtxFileAlloc(p->ctxp,NULL,outMtx0Fn)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kProcFailPuRC,"Binary matrix file create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the label file and create p->chArray
|
||||||
|
if((rc = _cmPuReadLabelsAndCreateArray(p, labelFn, p->afrp->info.srate)) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
chp = p->chArray;
|
||||||
|
segSmpIdx = chp->begSmpIdx;
|
||||||
|
|
||||||
|
// for each procSmpCnt samples
|
||||||
|
for(; cmAudioFileRdRead(p->afrp) != cmEofRC; smpIdx += procSmpCnt )
|
||||||
|
{
|
||||||
|
// if this audio frame marks a segment beginning or
|
||||||
|
// the end-of-audio-file will occur on the next frame
|
||||||
|
if( smpIdx+procSmpCnt >= p->afrp->info.frameCnt || (smpIdx <= segSmpIdx && segSmpIdx < smpIdx + procSmpCnt) )
|
||||||
|
{
|
||||||
|
segFl = true;
|
||||||
|
|
||||||
|
// if no ending offset was located then update the gate sum
|
||||||
|
if( gateMax != 0 )
|
||||||
|
{
|
||||||
|
gateSum += gateMax;
|
||||||
|
gateCnt += 1;
|
||||||
|
gateMax = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calc the avg max RMS value for this segment
|
||||||
|
chp->gateMaxAvg = gateCnt == 0 ? 0.0 : gateSum / gateCnt;
|
||||||
|
gateCnt = 0;
|
||||||
|
gateSum = 0;
|
||||||
|
gateMax = 0;
|
||||||
|
|
||||||
|
minRms = rmsMax; // force the segment end to be after the segment beginning
|
||||||
|
|
||||||
|
if((chp = _cmPuIncrCh(p,chp, &segSmpIdx )) == NULL )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shift the new samples into the shift buffer
|
||||||
|
while(cmShiftBufExec(p->sbp,p->afrp->outV,p->afrp->outN))
|
||||||
|
{
|
||||||
|
// update the gate detector
|
||||||
|
cmGateDetectExec2(p->gdp,p->sbp->outV,p->sbp->outN);
|
||||||
|
|
||||||
|
// write the output matrix file
|
||||||
|
if( _cmPuWriteMtxFile(p,segFl) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
segFl = false;
|
||||||
|
|
||||||
|
// if this frame is an RMS minimum or onset or offset
|
||||||
|
// then select it as a possible segment end.
|
||||||
|
// Note that for onsets this will effectively force the end to
|
||||||
|
// come after the onset because the onset will not be an energy minimum
|
||||||
|
// relative to subsequent frames.
|
||||||
|
if( p->gdp->rms < minRms || p->gdp->onFl || p->gdp->offFl )
|
||||||
|
{
|
||||||
|
minRms = p->gdp->rms;
|
||||||
|
chp->endSmpIdx = smpIdx;
|
||||||
|
|
||||||
|
// count onsets
|
||||||
|
if( p->gdp->onFl )
|
||||||
|
++chp->onCnt;
|
||||||
|
|
||||||
|
// count offsets
|
||||||
|
if( p->gdp->offFl )
|
||||||
|
{
|
||||||
|
++chp->offCnt;
|
||||||
|
|
||||||
|
// update the gate sum and count
|
||||||
|
gateSum += gateMax;
|
||||||
|
gateCnt += 1;
|
||||||
|
gateMax = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// track the max RMS value during this gate
|
||||||
|
if( p->gdp->gateFl && p->gdp->rms > gateMax )
|
||||||
|
gateMax = p->gdp->rms;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the channel gains
|
||||||
|
if( rc == kOkPuRC )
|
||||||
|
_cmPuCalcGains(p);
|
||||||
|
|
||||||
|
rc = _cmPuCalcRerunGateDetectors(p,outMtx1Fn,outAudFn,gd1Args,procSmpCnt,wndSmpCnt,hopSmpCnt);
|
||||||
|
|
||||||
|
p->gd0Args = *gd0Args;
|
||||||
|
p->gd1Args = *gd1Args;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( p->mfp != NULL )
|
||||||
|
cmBinMtxFileFree(&p->mfp);
|
||||||
|
|
||||||
|
if( p->gdp != NULL )
|
||||||
|
cmGateDetectFree2(&p->gdp);
|
||||||
|
|
||||||
|
if( p->sbp != NULL )
|
||||||
|
cmShiftBufFree(&p->sbp);
|
||||||
|
|
||||||
|
if( p->afrp != NULL )
|
||||||
|
cmAudioFileRdFree(&p->afrp);
|
||||||
|
|
||||||
|
if( p->ctxp != NULL )
|
||||||
|
cmCtxFree(&p->ctxp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t cmPuAutoGainExec( cmPuH_t h, const cmChar_t* fileDir, unsigned procSmpCnt )
|
||||||
|
{
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(h);
|
||||||
|
const cmChar_t* inAudFn = cmFsMakeFn(fileDir, p->inAudFn, NULL, NULL );
|
||||||
|
const cmChar_t* inLabelFn = cmFsMakeFn(fileDir, p->inLabelFn, NULL, NULL );
|
||||||
|
const cmChar_t* outMtx0Fn = cmFsMakeFn(fileDir, p->outMtx0Fn, NULL, NULL );
|
||||||
|
const cmChar_t* outMtx1Fn = cmFsMakeFn(fileDir, p->outMtx1Fn, NULL, NULL );
|
||||||
|
const cmChar_t* outAudFn = cmFsMakeFn(fileDir, p->outAudFn, NULL, NULL );
|
||||||
|
|
||||||
|
cmPuRC_t rc = cmPuAutoGainCfg(h,inAudFn,inLabelFn,outMtx0Fn,outMtx1Fn,outAudFn,procSmpCnt,p->hopMs,&p->gd0Args,&p->gd1Args);
|
||||||
|
|
||||||
|
cmFsFreeFn(outAudFn);
|
||||||
|
cmFsFreeFn(outMtx1Fn);
|
||||||
|
cmFsFreeFn(outMtx0Fn);
|
||||||
|
cmFsFreeFn(inLabelFn);
|
||||||
|
cmFsFreeFn(inAudFn);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t cmPuAutoGainCfgFromJson(
|
||||||
|
cmPuH_t h,
|
||||||
|
const cmChar_t* cfgDir,
|
||||||
|
cmJsonH_t jsH,
|
||||||
|
cmJsonNode_t* onp,
|
||||||
|
unsigned procSmpCnt )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc;
|
||||||
|
if((rc = cmPuReadJson(h,jsH,onp)) != kOkPuRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
|
||||||
|
return cmPuAutoGainExec(h,cfgDir,procSmpCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmPuReport( cmPuH_t h, cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(h);
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<p->chCnt; ++i)
|
||||||
|
{
|
||||||
|
const cmPuCh_t* chp = p->chArray + i;
|
||||||
|
cmRptPrintf(rpt,"beg:%i end:%i on:%i off:%i max:%f gain:%f\n",
|
||||||
|
chp->begSmpIdx, chp->endSmpIdx, chp->onCnt, chp->offCnt, chp->gateMaxAvg, chp->gain );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuJsonGainRead( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmJsonNode_t* arp;
|
||||||
|
|
||||||
|
// locate the JSON 'gain' array
|
||||||
|
if(( arp = cmJsonFindValue(jsH,label,onp,kArrayTId)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON array node %s.",cmStringNullGuard(label));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the count of elements in the 'gain' array
|
||||||
|
unsigned arrCnt = cmJsonChildCount(arp);
|
||||||
|
cmPuCh_t* arr = NULL;
|
||||||
|
|
||||||
|
if( arrCnt > 0 )
|
||||||
|
{
|
||||||
|
arr = cmMemAllocZ(cmPuCh_t,arrCnt);
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<arrCnt; ++i)
|
||||||
|
{
|
||||||
|
if( i<p->chCnt )
|
||||||
|
arr[i] = p->chArray[i];
|
||||||
|
|
||||||
|
if( cmJsonRealValue( cmJsonArrayElement(arp,i), &arr[i].gain ) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"An error occurred while accessing a JSON 'gain' element.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
|
||||||
|
if( rc != kOkPuRC )
|
||||||
|
cmMemPtrFree(&arr);
|
||||||
|
|
||||||
|
cmMemPtrFree(&p->chArray);
|
||||||
|
p->chArray = arr;
|
||||||
|
p->chCnt = arrCnt;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuJsonGdParmsRead( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label, cmGateDetectParams* gdParms )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmJsonNode_t* gdp;
|
||||||
|
const char* errLabelPtr;
|
||||||
|
if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmJsonMemberValues(gdp, &errLabelPtr,
|
||||||
|
"medCnt", kIntTId, &gdParms->medCnt,
|
||||||
|
"avgCnt", kIntTId, &gdParms->avgCnt,
|
||||||
|
"suprCnt", kIntTId, &gdParms->suprCnt,
|
||||||
|
"offCnt", kIntTId, &gdParms->offCnt,
|
||||||
|
"suprCoeff", kRealTId, &gdParms->suprCoeff,
|
||||||
|
"onThreshDb", kRealTId, &gdParms->onThreshDb,
|
||||||
|
"offThreshDb", kRealTId, &gdParms->offThreshDb,
|
||||||
|
NULL ) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Gate detect parameter restore failed for '%s'.",cmStringNullGuard(label));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuJsonCfgParmsRead(cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp)
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmJsonNode_t* gdp = onp;
|
||||||
|
const char* errLabelPtr;
|
||||||
|
|
||||||
|
|
||||||
|
//if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
|
||||||
|
//{
|
||||||
|
// rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
|
||||||
|
// goto errLabel;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if( cmJsonMemberValues(gdp, &errLabelPtr,
|
||||||
|
"audioFn", kStringTId, &p->inAudFn,
|
||||||
|
"labelFn", kStringTId, &p->inLabelFn,
|
||||||
|
"outMtx0Fn", kStringTId, &p->outMtx0Fn,
|
||||||
|
"outMtx1Fn", kStringTId, &p->outMtx1Fn,
|
||||||
|
"outAudFn", kStringTId, &p->outAudFn,
|
||||||
|
"hopMs", kRealTId, &p->hopMs,
|
||||||
|
NULL ) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Autotune cfg parameter read failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t cmPuReadJson( cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(h);
|
||||||
|
cmJsonNode_t* atp;
|
||||||
|
|
||||||
|
if(( atp = cmJsonFindValue(jsH,"cfg",onp,kObjectTId)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"The JSON 'autotune' object was not found.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = _cmPuJsonCfgParmsRead(p,jsH,atp)) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
if((rc = _cmPuJsonGdParmsRead(p,jsH,atp,"gdParms0",&p->gd0Args)) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
if((rc = _cmPuJsonGdParmsRead(p,jsH,atp,"gdParms1",&p->gd1Args)) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
if((rc = _cmPuJsonGainRead(p,jsH,atp,"gain")) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmPuChannelCount( cmPuH_t h )
|
||||||
|
{
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(h);
|
||||||
|
return p->chCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmPuCh_t* cmPuChannel( cmPuH_t h, unsigned chIdx )
|
||||||
|
{
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(h);
|
||||||
|
assert( chIdx < p->chCnt );
|
||||||
|
return p->chArray + chIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuJsonSetInt( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* gdp, const cmChar_t* label, int val )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
|
||||||
|
if( cmJsonReplacePairInt(jsH, gdp, label, kIntTId | kRealTId, val ) != kOkJsRC )
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Error setting integer JSON field: %s.",cmStringNullGuard(label));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuJsonSetReal( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* gdp, const cmChar_t* label, cmReal_t val )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
|
||||||
|
if( cmJsonReplacePairReal(jsH, gdp, label, kIntTId | kRealTId, val ) != kOkJsRC )
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Error setting real JSON field: %s.",cmStringNullGuard(label));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuJsonGdParmsUpdate( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label, const cmGateDetectParams* gdParms )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmJsonNode_t* gdp;
|
||||||
|
|
||||||
|
if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cmPuJsonSetInt( p, jsH, gdp, "medCnt", gdParms->medCnt);
|
||||||
|
_cmPuJsonSetInt( p, jsH, gdp, "avgCnt", gdParms->avgCnt);
|
||||||
|
_cmPuJsonSetInt( p, jsH, gdp, "suprCnt", gdParms->suprCnt);
|
||||||
|
_cmPuJsonSetInt( p, jsH, gdp, "offCnt", gdParms->offCnt);
|
||||||
|
_cmPuJsonSetReal( p, jsH, gdp, "suprCoeff", gdParms->suprCoeff);
|
||||||
|
_cmPuJsonSetReal( p, jsH, gdp, "onThreshDb", gdParms->onThreshDb);
|
||||||
|
_cmPuJsonSetReal( p, jsH, gdp, "offThresDb", gdParms->offThreshDb);
|
||||||
|
|
||||||
|
rc = cmErrLastRC(&p->err);
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t _cmPuJsonGainUpdate( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmJsonNode_t* arp;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// locate the JSON 'gain' array
|
||||||
|
if(( arp = cmJsonFindValue(jsH,label,onp,kArrayTId)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON array node %s.",cmStringNullGuard(label));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the count of elements in the 'gain' array
|
||||||
|
unsigned arrCnt = cmJsonChildCount(arp);
|
||||||
|
|
||||||
|
// update the existing 'gain' array elmements from p->chArray[]
|
||||||
|
for(i=0; i<arrCnt && i<p->chCnt; ++i)
|
||||||
|
{
|
||||||
|
if(cmJsonSetReal( jsH, cmJsonArrayElement(arp,i), p->chArray[i].gain ) != kOkPuRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"Set JSON 'gain' array elment failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new elements if the array was not long enough
|
||||||
|
for(; i<p->chCnt; ++i)
|
||||||
|
if( cmJsonCreateReal(jsH,arp,p->chArray[i].gain) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON 'gain' element create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove elements if the array begain with extra elements.
|
||||||
|
if( arrCnt > p->chCnt )
|
||||||
|
{
|
||||||
|
if( cmJsonRemoveNode( jsH, cmJsonArrayElement(arp,i), true ) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON 'gain' element removal failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t cmPuWriteJson( cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(h);
|
||||||
|
cmJsonNode_t* atp;
|
||||||
|
|
||||||
|
if(( atp = cmJsonFindValue(jsH,"autoTune",onp,kObjectTId)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"The JSON 'autotune' object was not found.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = _cmPuJsonGdParmsUpdate(p,jsH,atp,"gdParms0",&p->gd0Args)) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
if((rc = _cmPuJsonGdParmsUpdate(p,jsH,atp,"gdParms1",&p->gd1Args)) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
if((rc = _cmPuJsonGainUpdate(p,jsH,atp,"gain")) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmPuRC_t cmPuWriteJsonFile( cmPuH_t h, const cmChar_t* jsonFn )
|
||||||
|
{
|
||||||
|
cmPuRC_t rc = kOkPuRC;
|
||||||
|
cmJsonH_t jsH = cmJsonNullHandle;
|
||||||
|
cmPu_t* p = _cmPuHandleToPtr(h);
|
||||||
|
|
||||||
|
// initialize a JSON tree from 'jsonFn'.
|
||||||
|
if( cmJsonInitializeFromFile(&jsH,jsonFn,&p->ctx) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file initialization failed on '%s'.",cmStringNullGuard(jsonFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the 'autoTune' object in the JSON tree
|
||||||
|
if((rc = cmPuWriteJson(h,jsH,cmJsonRoot(jsH))) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
// write the JSON tree back to 'jsonFn'.
|
||||||
|
if( cmJsonWrite(jsH,cmJsonRoot(jsH),jsonFn) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file save failed on '%s'.",cmStringNullGuard(jsonFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
// release the JSON tree
|
||||||
|
if( cmJsonFinalize(&jsH) != kOkJsRC )
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file finalization failed on '%s'.",cmStringNullGuard(jsonFn));
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmPuTest(cmCtx_t* ctx)
|
||||||
|
{
|
||||||
|
cmPuH_t h = cmPuNullHandle;
|
||||||
|
cmGateDetectParams gd0Args;
|
||||||
|
cmGateDetectParams gd1Args;
|
||||||
|
const cmChar_t* audioFn = "/home/kevin/media/audio/gate_detect/gate_detect0.aif";
|
||||||
|
const cmChar_t* labelFn = "/home/kevin/media/audio/gate_detect/gate_detect0_labels.txt";
|
||||||
|
const cmChar_t* outMtx0Fn = "/home/kevin/media/audio/gate_detect/gd0.mtx";
|
||||||
|
const cmChar_t* outMtx1Fn = "/home/kevin/media/audio/gate_detect/gd1.mtx";
|
||||||
|
const cmChar_t* outAudFn = "/home/kevin/media/audio/gate_detect/gd_gain0.aif";
|
||||||
|
const cmChar_t* jsonFn = "/home/kevin/src/kc/src/data/rsrc1.txt";
|
||||||
|
unsigned procSmpCnt = 64;
|
||||||
|
cmReal_t hopMs = 10;
|
||||||
|
|
||||||
|
gd0Args.medCnt = 5;
|
||||||
|
gd0Args.avgCnt = 9;
|
||||||
|
gd0Args.suprCnt = 6;
|
||||||
|
gd0Args.offCnt = 3;
|
||||||
|
gd0Args.suprCoeff = 1.4;
|
||||||
|
gd0Args.onThreshDb = -53.0;
|
||||||
|
gd0Args.offThreshDb = -80.0;
|
||||||
|
|
||||||
|
gd1Args = gd0Args;
|
||||||
|
gd1Args.onThreshDb = -45;
|
||||||
|
|
||||||
|
if( cmPuAlloc(ctx, &h ) != kOkPuRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
if( cmPuAutoGainCfg(h,audioFn,labelFn,outMtx0Fn,outMtx1Fn,outAudFn,procSmpCnt,hopMs,&gd0Args,&gd1Args) == kOkPuRC )
|
||||||
|
{
|
||||||
|
cmPuReport(h,&ctx->rpt);
|
||||||
|
|
||||||
|
cmPuWriteJsonFile( h, jsonFn );
|
||||||
|
}
|
||||||
|
errLabel:
|
||||||
|
cmPuFree(&h);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
96
app/cmPickup.h
Normal file
96
app/cmPickup.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#ifndef cmPickup_h
|
||||||
|
#define cmPickup_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkPuRC = cmOkRC,
|
||||||
|
kProcFailPuRC,
|
||||||
|
kJsonFailPuRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmPuRC_t;
|
||||||
|
typedef cmHandle_t cmPuH_t;
|
||||||
|
|
||||||
|
// This record holds information which is maintained on a per-pickup basis
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned begSmpIdx; // during auto-gain cfg set to the first sample in the audio example file where the group of notes from this pickup begin.
|
||||||
|
unsigned endSmpIdx; // ... end of the example notes for this pickup
|
||||||
|
unsigned midiPitch; // midi pitch associated with this pickup
|
||||||
|
unsigned onCnt; // during auto-gain cfg set to the count of onsets detected for this pickup
|
||||||
|
unsigned offCnt; // ... offset detected
|
||||||
|
cmReal_t gateMaxAvg; // avg of the the max gate RMS values for all detected notes
|
||||||
|
cmReal_t gain; // auto-gain coeff for this pickup
|
||||||
|
|
||||||
|
} cmPuCh_t;
|
||||||
|
|
||||||
|
|
||||||
|
extern cmPuH_t cmPuNullHandle;
|
||||||
|
|
||||||
|
cmPuRC_t cmPuAlloc( cmCtx_t* ctx, cmPuH_t* hp );
|
||||||
|
cmPuRC_t cmPuFree( cmPuH_t* hp );
|
||||||
|
bool cmPuIsValid( cmPuH_t h );
|
||||||
|
|
||||||
|
// Given a recorded audio file containing a set of notes recorded from each pickup,
|
||||||
|
// an Audacity label file which marks the beginning of teach set of notes,
|
||||||
|
// and a set of gate detector parameters attempt to calculate a set of gain
|
||||||
|
// coefficients to equalize the relative gain of all the pickup channels.
|
||||||
|
// This algorithm works in two passes: In the first pass the gain coefficient
|
||||||
|
// is established by finding an average RMS value among the examples and
|
||||||
|
// then increasing and decreasing the pickups to move the examples closer
|
||||||
|
// to the average. The gain is then adjusted and a second pass is made to
|
||||||
|
// examine how well the gate detectors work with the new gain setttings.
|
||||||
|
cmPuRC_t cmPuAutoGainCfg(
|
||||||
|
cmPuH_t h,
|
||||||
|
const cmChar_t* audioFn, // audio file containing a set of examples for each note
|
||||||
|
const cmChar_t* labelFn, // audicity label file with one marker preceding each set of notes
|
||||||
|
const cmChar_t* outMtx0Fn, // octave binary matrix file containing the intermediate and final results of the gate detector analysis after the first pass
|
||||||
|
const cmChar_t* outMtx1Fn, // octave binary matrix file containing the intermediate and final results of the gate detector analysis after the second pass
|
||||||
|
const cmChar_t* outAudFn, // audioFn rewritten with auto-gain applied
|
||||||
|
unsigned procSmpCnt, // analysis DSP frame size in samples
|
||||||
|
cmReal_t hopMs, // analysis window hop size in milliseconds
|
||||||
|
const cmGateDetectParams* gd0Args, // first pass gate detector args
|
||||||
|
const cmGateDetectParams* gd1Args ); // second pass gate detector args
|
||||||
|
|
||||||
|
// Calls cmPuReadJson() and then prepends the fileDir to each of the
|
||||||
|
// file names. All the files are then read and written to this directory.
|
||||||
|
// and calls cmPuAutoGainCfg().
|
||||||
|
cmPuRC_t cmPuAutoGainCfgFromJson(
|
||||||
|
cmPuH_t h,
|
||||||
|
const cmChar_t* fileDir,
|
||||||
|
cmJsonH_t jsH,
|
||||||
|
cmJsonNode_t* onp,
|
||||||
|
unsigned procSmpCnt );
|
||||||
|
|
||||||
|
|
||||||
|
cmPuRC_t cmPuAutoGainExec( cmPuH_t h, const cmChar_t* fileDir, unsigned procSmpCnt );
|
||||||
|
|
||||||
|
// Load the 'parmsCfg', 'gdArgs' and 'gain' array from the JSON
|
||||||
|
// 'autoTune' object contained in the JSON object 'onp'.
|
||||||
|
cmPuRC_t cmPuReadJson( cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp );
|
||||||
|
|
||||||
|
unsigned cmPuChannelCount( cmPuH_t h );
|
||||||
|
const cmPuCh_t* cmPuChannel( cmPuH_t h, unsigned chIdx );
|
||||||
|
|
||||||
|
|
||||||
|
// Update the 'autoTune' JSON object contained the JSON object 'onp'.
|
||||||
|
cmPuRC_t cmPuWriteJson( cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp );
|
||||||
|
|
||||||
|
// Update the 'autoTune' JSON object in the JSON file 'jsonFn'.
|
||||||
|
// (Same as cmPuWriteJson() except the JSON tree to update is contained in a file.)
|
||||||
|
cmPuRC_t cmPuWriteJsonFile( cmPuH_t h, const cmChar_t* jsonFn );
|
||||||
|
|
||||||
|
void cmPuReport( cmPuH_t h, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
void cmPuTest(cmCtx_t* ctx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
635
app/cmScore.c
Normal file
635
app/cmScore.c
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmMidi.h"
|
||||||
|
#include "cmLex.h"
|
||||||
|
#include "cmCsv.h"
|
||||||
|
#include "cmMidiFile.h"
|
||||||
|
#include "cmAudioFile.h"
|
||||||
|
#include "cmTimeLine.h"
|
||||||
|
#include "cmScore.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "cmComplexTypes.h"
|
||||||
|
#include "cmLinkedHeap.h"
|
||||||
|
#include "cmSymTbl.h"
|
||||||
|
#include "cmProcObj.h"
|
||||||
|
#include "cmProc.h"
|
||||||
|
#include "cmProcTemplate.h"
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cmVectOpsTemplateMain.h"
|
||||||
|
|
||||||
|
cmScH_t cmScNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kLabelCharCnt = 7,
|
||||||
|
|
||||||
|
kInvalidDynScId = 0,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kTypeLabelColScIdx = 3,
|
||||||
|
kDSecsColScIdx = 5,
|
||||||
|
kPitchColScIdx = 11,
|
||||||
|
kBarColScIdx = 13,
|
||||||
|
kSkipColScIdx = 14,
|
||||||
|
kEvenColScIdx = 15,
|
||||||
|
kTempoColScIdx = 16,
|
||||||
|
kDynColScIdx = 17
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned id;
|
||||||
|
cmChar_t label[ kLabelCharCnt + 1 ];
|
||||||
|
} cmScEvtRef_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmScoreEvt_t* array;
|
||||||
|
unsigned cnt;
|
||||||
|
cmCsvH_t cH;
|
||||||
|
} cmSc_t;
|
||||||
|
|
||||||
|
cmScEvtRef_t _cmScEvtRefArray[] =
|
||||||
|
{
|
||||||
|
{ kTimeSigEvtScId, "tsg" },
|
||||||
|
{ kKeySigEvtScId, "ksg" },
|
||||||
|
{ kTempoEvtScId, "tmp" },
|
||||||
|
{ kTrackEvtScId, "trk" },
|
||||||
|
{ kTextEvtScId, "txt" },
|
||||||
|
{ kEOTrackEvtScId, "eot" },
|
||||||
|
{ kCopyEvtScId, "cpy"},
|
||||||
|
{ kBlankEvtScId, "blk"},
|
||||||
|
{ kBarEvtScId, "bar"},
|
||||||
|
{ kPgmEvtScId, "pgm" },
|
||||||
|
{ kCtlEvtScId, "ctl" },
|
||||||
|
{ kNonEvtScId, "non" },
|
||||||
|
{ kInvalidEvtScId, "***" }
|
||||||
|
};
|
||||||
|
|
||||||
|
cmScEvtRef_t _cmScDynRefArray[] =
|
||||||
|
{
|
||||||
|
{ 1, "pppp" },
|
||||||
|
{ 2, "ppp" },
|
||||||
|
{ 3, "pp" },
|
||||||
|
{ 4, "p" },
|
||||||
|
{ 5, "mp" },
|
||||||
|
{ 6, "m" },
|
||||||
|
{ 7, "mf" },
|
||||||
|
{ 8, "f" },
|
||||||
|
{ 9, "ff" },
|
||||||
|
{ 10, "fff" },
|
||||||
|
{ 11, "ffff"},
|
||||||
|
{ kInvalidDynScId, "***" },
|
||||||
|
};
|
||||||
|
|
||||||
|
cmSc_t* _cmScHandleToPtr( cmScH_t h )
|
||||||
|
{
|
||||||
|
cmSc_t* p = (cmSc_t*)h.h;
|
||||||
|
assert( p != NULL );
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _cmScEvtTypeLabelToId( const cmChar_t* label )
|
||||||
|
{
|
||||||
|
cmScEvtRef_t* r = _cmScEvtRefArray;
|
||||||
|
for(; r->id != kInvalidEvtScId; ++r )
|
||||||
|
if( strcmp(label,r->label) == 0 )
|
||||||
|
return r->id;
|
||||||
|
return kInvalidEvtScId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmChar_t* _cmScEvtTypeIdToLabel( unsigned id )
|
||||||
|
{
|
||||||
|
cmScEvtRef_t* r = _cmScEvtRefArray;
|
||||||
|
for(; r->id != kInvalidEvtScId; ++r )
|
||||||
|
if( r->id == id )
|
||||||
|
return r->label;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _cmScDynLabelToId( const cmChar_t* label )
|
||||||
|
{
|
||||||
|
cmScEvtRef_t* r = _cmScDynRefArray;
|
||||||
|
for(; r->id != kInvalidEvtScId; ++r )
|
||||||
|
if( strcmp(label,r->label) == 0 )
|
||||||
|
return r->id;
|
||||||
|
return kInvalidDynScId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmChar_t* _cmScDynIdToLabel( unsigned id )
|
||||||
|
{
|
||||||
|
cmScEvtRef_t* r = _cmScDynRefArray;
|
||||||
|
for(; r->id != kInvalidDynScId; ++r )
|
||||||
|
if( r->id == id )
|
||||||
|
return r->label;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _cmScLexSciPitchMatcher( const cmChar_t* cp, unsigned cn )
|
||||||
|
{
|
||||||
|
// first char must be "A-G"
|
||||||
|
if( strspn(cp,"ABCDEFG") != 1 )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned i = 1;
|
||||||
|
|
||||||
|
// next char could be accidental
|
||||||
|
if( cp[i] == '#' || cp[i] == 'b' )
|
||||||
|
++i; // i==2
|
||||||
|
|
||||||
|
// the 2nd or 3rd char must be a digit
|
||||||
|
if( isdigit(cp[i]) == false )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
++i; // i==2 or i==3
|
||||||
|
|
||||||
|
// the 3rd or 4th char must be a digit or EOS
|
||||||
|
if( isdigit(cp[i]) == false )
|
||||||
|
return i;
|
||||||
|
|
||||||
|
++i;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmScRC_t _cmScFinalize( cmSc_t* p )
|
||||||
|
{
|
||||||
|
cmScRC_t rc = kOkScRC;
|
||||||
|
|
||||||
|
if( cmCsvFinalize(&p->cH) != kOkCsvRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmMemFree(p->array);
|
||||||
|
cmMemFree(p);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmScRC_t _cmScParseBar( cmSc_t* p, unsigned rowIdx, int* barNumb )
|
||||||
|
{
|
||||||
|
if((*barNumb = cmCsvCellInt(p->cH,rowIdx,kBarColScIdx)) == INT_MAX )
|
||||||
|
return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to parse the bar number.");
|
||||||
|
return kOkScRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmScRC_t _cmScParseNoteOn( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int barNumb, unsigned barNoteIdx )
|
||||||
|
{
|
||||||
|
cmScRC_t rc = kOkScRC;
|
||||||
|
unsigned flags = 0;
|
||||||
|
unsigned dynVal = kInvalidDynScId;
|
||||||
|
const cmChar_t* sciPitch;
|
||||||
|
cmMidiByte_t midiPitch;
|
||||||
|
const cmChar_t* attr;
|
||||||
|
double dsecs;
|
||||||
|
|
||||||
|
if((sciPitch = cmCsvCellText(p->cH,rowIdx,kPitchColScIdx)) == NULL )
|
||||||
|
return cmErrMsg(&p->err,kSyntaxErrScRC,"Expected a scientific pitch value");
|
||||||
|
|
||||||
|
if((midiPitch = cmSciPitchToMidi(sciPitch)) == kInvalidMidiPitch)
|
||||||
|
return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to convert the scientific pitch '%s' to a MIDI value. ");
|
||||||
|
|
||||||
|
// it is possible that note delta-secs field is empty - so default to 0
|
||||||
|
if((dsecs = cmCsvCellDouble(p->cH, rowIdx, kDSecsColScIdx )) == DBL_MAX) // Returns DBL_MAX on error.
|
||||||
|
dsecs = 0;
|
||||||
|
|
||||||
|
if((attr = cmCsvCellText(p->cH,rowIdx,kSkipColScIdx)) != NULL && *attr == 's' )
|
||||||
|
flags += kSkipScFl;
|
||||||
|
|
||||||
|
if((attr = cmCsvCellText(p->cH,rowIdx,kEvenColScIdx)) != NULL && *attr == 'e' )
|
||||||
|
flags += kEvenScFl;
|
||||||
|
|
||||||
|
if((attr = cmCsvCellText(p->cH,rowIdx,kTempoColScIdx)) != NULL && *attr == 't' )
|
||||||
|
flags += kTempoScFl;
|
||||||
|
|
||||||
|
if((attr = cmCsvCellText(p->cH,rowIdx,kDynColScIdx)) != NULL )
|
||||||
|
{
|
||||||
|
if((dynVal = _cmScDynLabelToId(attr)) == kInvalidDynScId )
|
||||||
|
return cmErrMsg(&p->err,kSyntaxErrScRC,"Unknown dynamic label '%s'.",cmStringNullGuard(attr));
|
||||||
|
|
||||||
|
flags += kDynScFl;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->type = kNonEvtScId;
|
||||||
|
s->pitch = midiPitch;
|
||||||
|
s->flags = flags;
|
||||||
|
s->dynVal = dynVal;
|
||||||
|
s->barNumb = barNumb;
|
||||||
|
s->barNoteIdx = barNoteIdx;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmScRC_t _cmScParseFile( cmSc_t* p, cmCtx_t* ctx, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmScRC_t rc = kOkScRC;
|
||||||
|
unsigned barNoteIdx;
|
||||||
|
int barNumb;
|
||||||
|
|
||||||
|
if( cmCsvInitialize(&p->cH, ctx ) != kOkCsvRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kCsvFailScRC,"Score file initialization failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmCsvLexRegisterMatcher(p->cH, cmCsvLexNextAvailId(p->cH), _cmScLexSciPitchMatcher ) != kOkCsvRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kCsvFailScRC,"CSV token matcher registration failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmCsvParseFile(p->cH, fn, 0 ) != kOkCsvRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kCsvFailScRC,"CSV file parsing failed on the file '%s'.",cmStringNullGuard(fn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->cnt = cmCsvRowCount(p->cH);
|
||||||
|
p->array = cmMemAllocZ(cmScoreEvt_t,p->cnt);
|
||||||
|
|
||||||
|
unsigned i,j;
|
||||||
|
|
||||||
|
// skip labels line - start on line 1
|
||||||
|
for(i=1,j=0; i<p->cnt && rc==kOkScRC; ++i)
|
||||||
|
{
|
||||||
|
// get the row 'type' label
|
||||||
|
const char* typeLabel;
|
||||||
|
if((typeLabel = cmCsvCellText(p->cH,i,kTypeLabelColScIdx)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kSyntaxErrScRC,"No type label.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the row 'type' label to an id
|
||||||
|
unsigned tid;
|
||||||
|
if((tid = _cmScEvtTypeLabelToId(typeLabel)) == kInvalidEvtScId)
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kSyntaxErrScRC,"Unknown type '%s'.",cmStringNullGuard(typeLabel));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch(tid)
|
||||||
|
{
|
||||||
|
case kBarEvtScId:
|
||||||
|
// parse bar lines
|
||||||
|
if((rc = _cmScParseBar(p,i,&barNumb)) == kOkScRC )
|
||||||
|
barNoteIdx = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kNonEvtScId:
|
||||||
|
// parse note-on events
|
||||||
|
if((rc = _cmScParseNoteOn(p, i, p->array + j, barNumb, barNoteIdx )) == kOkScRC )
|
||||||
|
{
|
||||||
|
if( cmIsFlag(p->array[j].flags,kSkipScFl) == false )
|
||||||
|
++j;
|
||||||
|
|
||||||
|
++barNoteIdx;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( rc == kSyntaxErrScRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,rc,"Syntax error on line %i in '%s'.",i+1,cmStringNullGuard(fn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->cnt = i;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmScRC_t rc = kOkScRC;
|
||||||
|
if((rc = cmScoreFinalize(hp)) != kOkScRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmSc_t* p = cmMemAllocZ(cmSc_t,1);
|
||||||
|
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"Score");
|
||||||
|
|
||||||
|
if((rc = _cmScParseFile(p,ctx,fn)) != kOkScRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( rc != kOkScRC )
|
||||||
|
_cmScFinalize(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmScRC_t cmScoreFinalize( cmScH_t* hp )
|
||||||
|
{
|
||||||
|
cmScRC_t rc = kOkScRC;
|
||||||
|
|
||||||
|
if( hp == NULL || cmScoreIsValid(*hp) == false )
|
||||||
|
return kOkScRC;
|
||||||
|
|
||||||
|
cmSc_t* p = _cmScHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if((rc = _cmScFinalize(p)) != kOkScRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmScoreIsValid( cmScH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
unsigned cmScoreEvtCount( cmScH_t h )
|
||||||
|
{
|
||||||
|
cmSc_t* p = _cmScHandleToPtr(h);
|
||||||
|
return p->cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmScoreEvt_t* cmScoreEvt( cmScH_t h, unsigned idx )
|
||||||
|
{
|
||||||
|
cmSc_t* p = _cmScHandleToPtr(h);
|
||||||
|
if( idx >= p->cnt )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kInvalidIdxScRC,"%i is an invalid index for %i records.",idx,p->cnt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return p->array + idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmScorePrint( cmScH_t h, cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmSc_t* p = _cmScHandleToPtr(h);
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<20 /*p->cnt*/; ++i)
|
||||||
|
{
|
||||||
|
cmScoreEvt_t* r = p->array + i;
|
||||||
|
switch(r->type)
|
||||||
|
{
|
||||||
|
case kNonEvtScId:
|
||||||
|
cmRptPrintf(rpt,"%5i %3i %3i %s 0x%2x %c%c%c %s\n",
|
||||||
|
i,
|
||||||
|
r->barNumb,
|
||||||
|
r->barNoteIdx,
|
||||||
|
_cmScEvtTypeIdToLabel(r->type),
|
||||||
|
r->pitch,
|
||||||
|
cmIsFlag(r->flags,kEvenScFl) ? 'e' : ' ',
|
||||||
|
cmIsFlag(r->flags,kTempoScFl) ? 't' : ' ',
|
||||||
|
cmIsFlag(r->flags,kDynScFl) ? 'd' : ' ',
|
||||||
|
cmIsFlag(r->flags,kDynScFl) ? _cmScDynIdToLabel(r->dynVal) : "");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each time line note-on object is decorated (via cmTlObj_t.userDataPtr) with a
|
||||||
|
// cmScSyncState_t record.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned cnt; // count of candidate sync locations
|
||||||
|
double dist; // edit distance to the closest sync location
|
||||||
|
unsigned scEvtIdx; // score record this note-on is assigned to
|
||||||
|
} cmScSyncState_t;
|
||||||
|
|
||||||
|
void _cmScSyncTimeLineAllocFree( cmTlH_t tlH, bool allocFl )
|
||||||
|
{
|
||||||
|
cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
|
||||||
|
|
||||||
|
for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId))
|
||||||
|
if( mep->msg->status == kNoteOnMdId )
|
||||||
|
{
|
||||||
|
if( allocFl )
|
||||||
|
mep->obj.userDataPtr = cmMemAllocZ(cmScSyncState_t,1);
|
||||||
|
else
|
||||||
|
cmMemPtrFree(&mep->obj.userDataPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmScPrintSyncState( cmSc_t* p, cmTlH_t tlH )
|
||||||
|
{
|
||||||
|
unsigned i = 0;
|
||||||
|
double sr = cmTimeLineSampleRate(tlH);
|
||||||
|
cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
|
||||||
|
|
||||||
|
for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId))
|
||||||
|
if( mep->msg->status == kNoteOnMdId )
|
||||||
|
{
|
||||||
|
cmScSyncState_t* ssp = (cmScSyncState_t*)mep->obj.userDataPtr;
|
||||||
|
|
||||||
|
cmRptPrintf(p->err.rpt,"%5.3f pit:0x%2x (%3i) bar:%3i bni:%3i cnt:%3i dst:%1.6f ref:%s\n",
|
||||||
|
(mep->obj.ref->begSmpIdx - mep->obj.begSmpIdx) / (sr*60),
|
||||||
|
mep->msg->u.chMsgPtr->d0,
|
||||||
|
mep->msg->u.chMsgPtr->d0,
|
||||||
|
ssp->cnt ? p->array[ ssp->scEvtIdx ].barNumb : 0,
|
||||||
|
ssp->cnt ? p->array[ ssp->scEvtIdx ].barNoteIdx : 0,
|
||||||
|
ssp->cnt,
|
||||||
|
ssp->dist,
|
||||||
|
cmStringNullGuard(mep->obj.ref->name));
|
||||||
|
|
||||||
|
++i;
|
||||||
|
if( i>=300)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double _cmScWndEditDist( cmSc_t* p, unsigned* mtx, const unsigned* tlWnd, cmScSyncState_t* tlObjWnd[], unsigned wndCnt )
|
||||||
|
{
|
||||||
|
unsigned scWnd[ wndCnt ];
|
||||||
|
unsigned scIdxWnd[ wndCnt ];
|
||||||
|
unsigned i;
|
||||||
|
unsigned wn = 0;
|
||||||
|
double minDist = DBL_MAX;
|
||||||
|
|
||||||
|
// for each note-on score event
|
||||||
|
for(i=0; i<p->cnt; ++i)
|
||||||
|
if( p->array[i].type == kNonEvtScId )
|
||||||
|
{
|
||||||
|
// shift the score event window to the the left
|
||||||
|
memmove(scWnd, scWnd+1, (wndCnt-1)*sizeof(scWnd[0]));
|
||||||
|
memmove(scIdxWnd,scIdxWnd+1,(wndCnt-1)*sizeof(scIdxWnd[0]));
|
||||||
|
|
||||||
|
// insert new score event data on right
|
||||||
|
scWnd[wndCnt-1] = p->array[i].pitch;
|
||||||
|
scIdxWnd[wndCnt-1] = i;
|
||||||
|
++wn;
|
||||||
|
|
||||||
|
// if the window is full
|
||||||
|
if(wn >= wndCnt )
|
||||||
|
{
|
||||||
|
// score the edit distance between the time line window and the edit window
|
||||||
|
double dist = cmVOU_LevEditDist(wndCnt,mtx,scWnd,wndCnt,tlWnd,wndCnt,wndCnt);
|
||||||
|
|
||||||
|
if( dist < minDist )
|
||||||
|
minDist = dist;
|
||||||
|
|
||||||
|
// update the match information in the time line window
|
||||||
|
unsigned j;
|
||||||
|
for(j=0; j<wndCnt; ++j)
|
||||||
|
{
|
||||||
|
// if the pitch matches and the score is less than the previous score
|
||||||
|
if( scWnd[j] == tlWnd[j] && (tlObjWnd[j]->cnt == 0 || dist < tlObjWnd[j]->dist) )
|
||||||
|
{
|
||||||
|
tlObjWnd[j]->cnt += 1;
|
||||||
|
tlObjWnd[j]->dist = dist;
|
||||||
|
tlObjWnd[j]->scEvtIdx = scIdxWnd[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return minDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmScRC_t cmScoreSyncTimeLine( cmScH_t scH, cmTlH_t tlH, unsigned edWndCnt, cmReal_t maxSecs )
|
||||||
|
{
|
||||||
|
cmSc_t* p = _cmScHandleToPtr(scH);
|
||||||
|
unsigned* edWndMtx = cmVOU_LevEditDistAllocMtx(edWndCnt);
|
||||||
|
unsigned maxMicroSecs = floor(maxSecs*1000000);
|
||||||
|
unsigned edWndData[ edWndCnt ];
|
||||||
|
cmScSyncState_t* edWndObj[ edWndCnt ];
|
||||||
|
|
||||||
|
// alloc a sync state record for each MIDI note-on in the time line
|
||||||
|
_cmScSyncTimeLineAllocFree(tlH, true );
|
||||||
|
|
||||||
|
// get the first time line object
|
||||||
|
cmTlObj_t* rfp = cmTimeLineNextTypeObj(tlH,NULL,cmInvalidId,kMidiFileTlId);
|
||||||
|
|
||||||
|
// interate through the time line in search of MIDI file objects
|
||||||
|
for(; rfp != NULL; rfp = cmTimeLineNextTypeObj(tlH,rfp,cmInvalidId,kMidiFileTlId))
|
||||||
|
{
|
||||||
|
cmTlMidiFile_t* mfp = cmTimeLineMidiFileObjPtr(tlH,rfp);
|
||||||
|
unsigned curEdWndCnt = 0;
|
||||||
|
double prog = 0.1;
|
||||||
|
unsigned progIdx = 0;
|
||||||
|
|
||||||
|
cmRptPrintf(p->err.rpt,"MIDI File:%s\n", cmMidiFileName( mfp->h ));
|
||||||
|
|
||||||
|
// get first midi event object
|
||||||
|
cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
|
||||||
|
|
||||||
|
// iterate through the time line in search of MIDI note-on events with belong to mfp
|
||||||
|
for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId) )
|
||||||
|
{
|
||||||
|
if( mep->obj.ref == rfp && mep->msg->status == kNoteOnMdId )
|
||||||
|
{
|
||||||
|
// If this notes inter-onset time is greater than maxMicroSecs
|
||||||
|
// then dispose of the current window and begin refilling it again.
|
||||||
|
if( mep->msg->dtick > maxMicroSecs )
|
||||||
|
curEdWndCnt = 0;
|
||||||
|
|
||||||
|
// shift window one slot to left
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<edWndCnt-1; ++i)
|
||||||
|
{
|
||||||
|
edWndData[i] = edWndData[i+1];
|
||||||
|
edWndObj[i] = edWndObj[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill window on right
|
||||||
|
edWndData[edWndCnt-1] = mep->msg->u.chMsgPtr->d0; // d0=pitch
|
||||||
|
edWndObj[ edWndCnt-1] = (cmScSyncState_t*)mep->obj.userDataPtr;
|
||||||
|
|
||||||
|
++curEdWndCnt;
|
||||||
|
|
||||||
|
// if a complete window exists then update the time-line / score match state
|
||||||
|
if( curEdWndCnt >= edWndCnt )
|
||||||
|
_cmScWndEditDist( p, edWndMtx, edWndData, edWndObj, edWndCnt );
|
||||||
|
|
||||||
|
// print the progress
|
||||||
|
++progIdx;
|
||||||
|
if( progIdx >= prog * mfp->noteOnCnt )
|
||||||
|
{
|
||||||
|
cmRptPrintf(p->err.rpt,"%i ",(unsigned)round(prog*10));
|
||||||
|
prog += 0.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmRptPrintf(p->err.rpt,"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
_cmScPrintSyncState(p,tlH );
|
||||||
|
|
||||||
|
// free sync state records
|
||||||
|
_cmScSyncTimeLineAllocFree(tlH,false);
|
||||||
|
|
||||||
|
cmMemFree(edWndMtx);
|
||||||
|
|
||||||
|
return kOkScRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmScRC_t cmScoreSyncTimeLineTest( cmCtx_t* ctx, const cmChar_t* timeLineJsFn, const cmChar_t* scoreCsvFn )
|
||||||
|
{
|
||||||
|
cmScRC_t rc = kOkScRC;
|
||||||
|
cmTlH_t tlH = cmTimeLineNullHandle;
|
||||||
|
cmScH_t scH = cmScNullHandle;
|
||||||
|
unsigned edWndCnt = 7;
|
||||||
|
cmReal_t maxSecs = 2.0;
|
||||||
|
|
||||||
|
if((rc = cmTimeLineInitialize(ctx,&tlH,NULL,NULL)) != kOkTlRC )
|
||||||
|
return cmErrMsg(&ctx->err,kTimeLineFailScRC,"Time line initialization failed.");;
|
||||||
|
|
||||||
|
if((rc = cmTimeLineReadJson(tlH,timeLineJsFn)) != kOkTlRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&ctx->err,kTimeLineFailScRC,"Time line parse failed.");;
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cmTimeLinePrint(tlH,&ctx->rpt);
|
||||||
|
|
||||||
|
if(1)
|
||||||
|
{
|
||||||
|
if((rc = cmScoreInitialize(ctx,&scH,scoreCsvFn)) != kOkScRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
|
||||||
|
rc = cmScoreSyncTimeLine(scH, tlH, edWndCnt, maxSecs );
|
||||||
|
|
||||||
|
}
|
||||||
|
//cmScorePrint(scH, ctx->err.rpt );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
cmScoreFinalize(&scH);
|
||||||
|
cmTimeLineFinalize(&tlH);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmScH_t h = cmScNullHandle;
|
||||||
|
if( cmScoreInitialize(ctx,&h,fn) != kOkScRC )
|
||||||
|
return;
|
||||||
|
|
||||||
|
cmScorePrint(h,&ctx->rpt);
|
||||||
|
|
||||||
|
cmScoreFinalize(&h);
|
||||||
|
}
|
82
app/cmScore.h
Normal file
82
app/cmScore.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#ifndef cmScore_h
|
||||||
|
#define cmScore_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkScRC = cmOkRC,
|
||||||
|
kCsvFailScRC,
|
||||||
|
kSyntaxErrScRC,
|
||||||
|
kInvalidIdxScRC,
|
||||||
|
kTimeLineFailScRC
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidEvtScId = 0,
|
||||||
|
kTimeSigEvtScId,
|
||||||
|
kKeySigEvtScId,
|
||||||
|
kTempoEvtScId,
|
||||||
|
kTrackEvtScId,
|
||||||
|
kTextEvtScId,
|
||||||
|
kEOTrackEvtScId,
|
||||||
|
kCopyEvtScId,
|
||||||
|
kBlankEvtScId,
|
||||||
|
kBarEvtScId,
|
||||||
|
kPgmEvtScId,
|
||||||
|
kCtlEvtScId,
|
||||||
|
kNonEvtScId
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kEvenScFl = 0x01, // This note is marked for evenness measurement
|
||||||
|
kDynScFl = 0x02, // This note is marked for dynamics measurement
|
||||||
|
kTempoScFl = 0x03, // This note is marked for tempo measurement
|
||||||
|
kSkipScFl = 0x04 // this isn't a real event (e.g. tied note) skip over it
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned type; // Event type
|
||||||
|
double dsecs; //
|
||||||
|
cmMidiByte_t pitch; // MIDI pitch of this note
|
||||||
|
unsigned flags; // Attribute flags for this event
|
||||||
|
unsigned dynVal; // Dynamcis value pppp to ffff (1 to 11) for this note.
|
||||||
|
unsigned barNumb; // bar number of this event
|
||||||
|
unsigned barNoteIdx; // index of this note in this bar
|
||||||
|
} cmScoreEvt_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmRC_t cmScRC_t;
|
||||||
|
typedef cmHandle_t cmScH_t;
|
||||||
|
|
||||||
|
extern cmScH_t cmScNullHandle;
|
||||||
|
|
||||||
|
// Initialize a score object from a CSV File generated from a score spreadsheet.
|
||||||
|
cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn );
|
||||||
|
cmScRC_t cmScoreFinalize( cmScH_t* hp );
|
||||||
|
|
||||||
|
bool cmScoreIsValid( cmScH_t h );
|
||||||
|
|
||||||
|
// Access the score data.
|
||||||
|
unsigned cmScoreEvtCount( cmScH_t h );
|
||||||
|
cmScoreEvt_t* cmScoreEvt( cmScH_t h, unsigned idx );
|
||||||
|
|
||||||
|
void cmScorePrint( cmScH_t h, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
cmScRC_t cmScoreSyncTimeLine( cmScH_t scH, cmTlH_t tlH, unsigned editDistWndCnt, cmReal_t maxNoteOffsetSecs );
|
||||||
|
|
||||||
|
cmScRC_t cmScoreSyncTimeLineTest( cmCtx_t* ctx, const cmChar_t* timeLineJsFn, const cmChar_t* scoreCsvFn );
|
||||||
|
|
||||||
|
void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1542
app/cmTimeLine.c
Normal file
1542
app/cmTimeLine.c
Normal file
File diff suppressed because it is too large
Load Diff
217
app/cmTimeLine.h
Normal file
217
app/cmTimeLine.h
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#ifndef cmTimeLine_h
|
||||||
|
#define cmTimeLine_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmHandle_t cmTlH_t;
|
||||||
|
|
||||||
|
typedef cmRC_t cmTlRC_t;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkTlRC = cmOkRC,
|
||||||
|
kLHeapFailTlRC,
|
||||||
|
kParseFailTlRC,
|
||||||
|
kJsonFailTlRC,
|
||||||
|
kDuplNameTlRC,
|
||||||
|
kRefNotFoundTlRC,
|
||||||
|
kAudioFileFailTlRC,
|
||||||
|
kMidiFileFailTlRC,
|
||||||
|
kTypeCvtFailTlRC,
|
||||||
|
kUnknownRecdTypeTlRC,
|
||||||
|
kFinalizeFailTlRC,
|
||||||
|
kInvalidSeqIdTlRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kMidiFileTlId = 0x01,
|
||||||
|
kMidiEvtTlId = 0x02,
|
||||||
|
kAudioFileTlId = 0x03,
|
||||||
|
kAudioEvtTlId = 0x04,
|
||||||
|
kMarkerTlId = 0x05
|
||||||
|
} cmTlObjTypeId_t;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kReservedTlFl = 0x01,
|
||||||
|
kNoWriteTlFl = 0x02 // do not write this object in cmTimeLineWrite()
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*cmTlCb_t)( void* arg, const void* data, unsigned byteCnt );
|
||||||
|
|
||||||
|
typedef struct cmTlObj_str
|
||||||
|
{
|
||||||
|
void* reserved; // pt's to _cmTlObj_t
|
||||||
|
unsigned seqId; // sequence this object is assigned to
|
||||||
|
const cmChar_t* name; // text name of this object
|
||||||
|
unsigned uid; // generated unique id for this object
|
||||||
|
cmTlObjTypeId_t typeId; // type of the object
|
||||||
|
struct cmTlObj_str* ref; // time reference object
|
||||||
|
int begSmpIdx; // start time of this object as an offset from the start time of the reference object
|
||||||
|
unsigned durSmpCnt; // duration of this object
|
||||||
|
int seqSmpIdx; // absolute start time of this object within the sequence
|
||||||
|
const cmChar_t* text; // points to text assoc'd with this node (file name for audio/midi file, marker text)
|
||||||
|
unsigned flags; // see kXXXTlFl
|
||||||
|
void* userDataPtr; // user customizable data pointer
|
||||||
|
} cmTlObj_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmTlObj_t obj;
|
||||||
|
cmMidiFileH_t h;
|
||||||
|
unsigned noteOnCnt;
|
||||||
|
cmChar_t* fn;
|
||||||
|
} cmTlMidiFile_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmTlObj_t obj;
|
||||||
|
unsigned midiFileObjId;
|
||||||
|
const cmMidiTrackMsg_t* msg; // w/ dticks converted to microsecs
|
||||||
|
} cmTlMidiEvt_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmTlObj_t obj;
|
||||||
|
cmAudioFileH_t h;
|
||||||
|
cmAudioFileInfo_t info;
|
||||||
|
cmChar_t* fn;
|
||||||
|
} cmTlAudioFile_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmTlObj_t obj;
|
||||||
|
cmAudioFileH_t h;
|
||||||
|
unsigned smpIdx;
|
||||||
|
unsigned smpCnt;
|
||||||
|
cmChar_t* text;
|
||||||
|
} cmTlAudioEvt_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmTlObj_t obj;
|
||||||
|
const cmChar_t* text;
|
||||||
|
} cmTlMarker_t;
|
||||||
|
|
||||||
|
extern cmTlH_t cmTimeLineNullHandle;
|
||||||
|
|
||||||
|
//
|
||||||
|
cmTlRC_t cmTimeLineInitialize( cmCtx_t* ctx, cmTlH_t* hp, cmTlCb_t cbFunc, void* cbArg );
|
||||||
|
cmTlRC_t cmTimeLineInitializeFromFile( cmCtx_t* ctx, cmTlH_t* hp, cmTlCb_t cbFunc, void* cbArg, const cmChar_t* fn );
|
||||||
|
|
||||||
|
cmTlRC_t cmTimeLineFinalize( cmTlH_t* hp );
|
||||||
|
|
||||||
|
bool cmTimeLineIsValid( cmTlH_t h );
|
||||||
|
double cmTimeLineSampleRate( cmTlH_t h );
|
||||||
|
|
||||||
|
// Return the object following 'p' assigned to 'seqId'.
|
||||||
|
// If 'p' is NULL then return the first object assigned to seqId.
|
||||||
|
// If 'seqId' is set to cmInvalidId then return the next object on any seq.
|
||||||
|
// If no objects follow 'p' on the specified sequence then return NULL.
|
||||||
|
cmTlObj_t* cmTimeLineNextObj( cmTlH_t h, cmTlObj_t* p, unsigned seqId );
|
||||||
|
|
||||||
|
// Same as cmTimeLineNextObj() but returns next object whose type matches 'typeId'.
|
||||||
|
cmTlObj_t* cmTimeLineNextTypeObj( cmTlH_t h, cmTlObj_t* p, unsigned seqId, unsigned typeId );
|
||||||
|
|
||||||
|
cmTlMidiFile_t* cmTlNextMidiFileObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId );
|
||||||
|
cmTlAudioFile_t* cmTlNextAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId );
|
||||||
|
cmTlMidiEvt_t* cmTlNextMidiEvtObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId );
|
||||||
|
cmTlAudioEvt_t* cmTlNextAudioEvtObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId );
|
||||||
|
cmTlMarker_t* cmTlNextMarkerObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId );
|
||||||
|
|
||||||
|
|
||||||
|
// Cast a genereic cmTlObj_t pointer to a specificy type.
|
||||||
|
cmTlMidiFile_t* cmTimeLineMidiFileObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
cmTlAudioFile_t* cmTimeLineAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
cmTlMidiEvt_t* cmTimeLineMidiEvtObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
cmTlAudioEvt_t* cmTimeLineAudioEvtObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
cmTlMarker_t* cmTimeLineMarkerObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
|
||||||
|
// Same as cmTimeLineXXXObjPtr() but does not generate an error when
|
||||||
|
// 'op' does not point to the correct type. These function quietly
|
||||||
|
// return NULL if the requested type does not match.
|
||||||
|
cmTlMidiFile_t* cmTlMidiFileObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
cmTlAudioFile_t* cmTlAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
cmTlMidiEvt_t* cmTlMidiEvtObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
cmTlAudioEvt_t* cmTlAudioEvtObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
cmTlMarker_t* cmTlMarkerObjPtr( cmTlH_t h, cmTlObj_t* op );
|
||||||
|
|
||||||
|
cmTlAudioFile_t* cmTimeLineFindAudioFile( cmTlH_t h, const cmChar_t* fn );
|
||||||
|
cmTlMidiFile_t* cmTimeLineFindMidiFile( cmTlH_t h, const cmChar_t* fn );
|
||||||
|
|
||||||
|
// 'typeId' = kAudioFileTlId, kMidiFileTId, kMarkerTlId.
|
||||||
|
// 'nameStr' and 'refObjNameStr' may be NULL.
|
||||||
|
cmTlRC_t cmTimeLineInsert(
|
||||||
|
cmTlH_t h,
|
||||||
|
const cmChar_t* nameStr,
|
||||||
|
unsigned typeId,
|
||||||
|
const cmChar_t* fn_or_markerStr,
|
||||||
|
int begSmpIdx,
|
||||||
|
unsigned durSmpCnt,
|
||||||
|
const cmChar_t* refObjNameStr,
|
||||||
|
unsigned seqId );
|
||||||
|
|
||||||
|
// See src/data/tl0.json for an example JSON file.
|
||||||
|
cmTlRC_t cmTimeLineReadJson( cmTlH_t h, const cmChar_t* ifn );
|
||||||
|
|
||||||
|
// Return a count of sequences contained within this timeline.
|
||||||
|
unsigned cmTimeLineSeqCount( cmTlH_t h );
|
||||||
|
|
||||||
|
// Make notifications for all records belonging to the sequence.
|
||||||
|
cmTlRC_t cmTimeLineSeqNotify( cmTlH_t h, unsigned seqId );
|
||||||
|
|
||||||
|
cmTlRC_t cmTimeLineWrite( cmTlH_t h, const cmChar_t* fn );
|
||||||
|
|
||||||
|
cmTlRC_t cmTimeLinePrint( cmTlH_t h, cmRpt_t* rpt );
|
||||||
|
cmTlRC_t cmTimeLinePrintFn( cmCtx_t* ctx, const cmChar_t* fn, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
cmTlRC_t cmTimeLineTest( cmCtx_t* ctx, const cmChar_t* jsFn );
|
||||||
|
|
||||||
|
// The time-line notifies listeners of initialization and finalization
|
||||||
|
// events via calling a cmTlCbFunc_t function. The argument to this
|
||||||
|
// function is a serialized cmTlUiMsg_t. The recipient of the callback
|
||||||
|
// can extract information from this message using cmTimeLineDecode()
|
||||||
|
// to form a cmTlUiMsg_t record. Note that all pointers internal to the
|
||||||
|
// cmTlUiMsg_t point into the message buffer itself.
|
||||||
|
|
||||||
|
// id's used to indicate the type of a serialized object
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kInvalidMsgTlId,
|
||||||
|
kInitMsgTlId, // A new time-line object is begin intialized.
|
||||||
|
kFinalMsgTlId, // A time-line object is being finalized.
|
||||||
|
kDoneMsgTlId, // All the objects assoc'd with a time line seq-notify have been sent.
|
||||||
|
kInsertMsgTlId, // A time-line object was inserted.
|
||||||
|
} cmTlUiMsgTypeId_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmTlUiMsgTypeId_t msgId; // See cmTlUiMsgTypeId_t.
|
||||||
|
unsigned objId; // Set to cmTlObj_t.uid
|
||||||
|
unsigned parentObjId; // cmTlObj_t.uid of the object this object's begSmpIdx is set relative to.
|
||||||
|
unsigned seqId; //
|
||||||
|
cmTlObjTypeId_t typeId; //
|
||||||
|
int begSmpIdx; // Time relative to parent.
|
||||||
|
unsigned durSmpCnt; // Duration of the object.
|
||||||
|
const char* label; // Object label (points to memory inside the serialized msg.)
|
||||||
|
double srate; // Only valid with kInitMsgTlId.
|
||||||
|
unsigned seqCnt; // Only valid with kInitMsgTlId.
|
||||||
|
const cmMidiTrackMsg_t* midiTrkMsg; // Only valid for typeId == kMidiEvtTlId. Internal pointers refer to memory inside the serialzed msg. buffer.
|
||||||
|
unsigned midiFileObjId; // Only valid for typeId == kMidiEvtTlId
|
||||||
|
const char* textStr; // filename for kAudioFileTlId and kMidiFileTlId, marker text for kMarkerTlId
|
||||||
|
} cmTlUiMsg_t;
|
||||||
|
|
||||||
|
// Decode a serialized cmTlObj_t as passed to the cmTlCb_t listener
|
||||||
|
// callback function.
|
||||||
|
cmTlRC_t cmTimeLineDecode( const void* msg, unsigned msgByteCnt, cmTlUiMsg_t* uiMsg );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
910
cmApBuf.c
Normal file
910
cmApBuf.c
Normal file
@ -0,0 +1,910 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmAudioPort.h"
|
||||||
|
#include "cmApBuf.h"
|
||||||
|
#include "cmThread.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
This API is in general called by two types of threads:
|
||||||
|
audio devices threads and the client thread. There
|
||||||
|
may be multiple devie threads however there is only
|
||||||
|
one client thread.
|
||||||
|
|
||||||
|
The audio device threads only call cmApBufUpdate().
|
||||||
|
cmApBufUpdate() is never called by any other threads.
|
||||||
|
A call from the audio update threads targets specific channels
|
||||||
|
(cmApCh records). The variables within each channels that
|
||||||
|
it modifies are confined to:
|
||||||
|
on input channels: increments ii and increments fn (data is entering the ch. buffers)
|
||||||
|
on output channels: increments oi and decrements fn (data is leaving the ch. buffers)
|
||||||
|
|
||||||
|
The client picks up incoming audio and provides outgoing audio via
|
||||||
|
cmApBufGet(). It then informs the cmApBuf() that it has completed
|
||||||
|
the audio data transfer by calling cmApBufAdvance().
|
||||||
|
|
||||||
|
cmApBufAdvance() modifies the following internal variables:
|
||||||
|
on input channels: increments oi and decrements fn (data has left the ch buffer)
|
||||||
|
on output channels: increments ii and increments fn (data has enterned the ch. buffer)
|
||||||
|
|
||||||
|
Based on the above scenario the channel ii and oi variables are always thread-safe
|
||||||
|
because they are only changed by a single thread.
|
||||||
|
|
||||||
|
ii oi fn
|
||||||
|
------ ----- ----
|
||||||
|
input ch: audio client both
|
||||||
|
output ch: client audio both
|
||||||
|
|
||||||
|
The fn variable however is not thread-safe and therefore care must be taken as
|
||||||
|
to how it is read and updated.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum { kInApIdx=0, kOutApIdx=1, kIoApCnt=2 };
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned fl; // kChApFl|kToneApFl|kMeterApFl ...
|
||||||
|
cmApSample_t* b; // b[n]
|
||||||
|
unsigned ii; // next in
|
||||||
|
unsigned oi; // next out
|
||||||
|
unsigned fn; // full cnt - count of samples currently in the buffer - incr'd by incoming, decr'd by outgoing
|
||||||
|
unsigned phs; // tone phase
|
||||||
|
double hz; // tone frequency
|
||||||
|
double gain; // channel gain
|
||||||
|
cmApSample_t* m; // m[mn] meter sample sum
|
||||||
|
unsigned mn; // length of m[]
|
||||||
|
unsigned mi; // next ele of m[] to rcv sum
|
||||||
|
} cmApCh;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned chCnt;
|
||||||
|
cmApCh* chArray;
|
||||||
|
|
||||||
|
unsigned n; // length of b[] (multiple of dspFrameCnt) bufCnt*framesPerCycle
|
||||||
|
double srate; // device sample rate;
|
||||||
|
|
||||||
|
unsigned faultCnt;
|
||||||
|
unsigned framesPerCycle;
|
||||||
|
unsigned dspFrameCnt;
|
||||||
|
|
||||||
|
} cmApIO;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// ioArray[] always contains 2 elements - one for input the other for output.
|
||||||
|
cmApIO ioArray[kIoApCnt];
|
||||||
|
} cmApDev;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmApDev* devArray;
|
||||||
|
unsigned devCnt;
|
||||||
|
unsigned meterMs;
|
||||||
|
|
||||||
|
cmApSample_t* zeroBuf; // buffer of zeros
|
||||||
|
unsigned zeroBufCnt; // max of all dspFrameCnt for all devices.
|
||||||
|
} cmApBuf;
|
||||||
|
|
||||||
|
cmApBuf _cmApBuf;
|
||||||
|
|
||||||
|
|
||||||
|
cmApSample_t _cmApMeterValue( const cmApCh* cp )
|
||||||
|
{
|
||||||
|
double sum = 0;
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<cp->mn; ++i)
|
||||||
|
sum += cp->m[i];
|
||||||
|
|
||||||
|
return (cmApSample_t)sqrt(sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmApSine( cmApCh* cp, cmApSample_t* b0, unsigned n0, cmApSample_t* b1, unsigned n1, unsigned stride, float srate )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for(i=0; i<n0; ++i,++cp->phs)
|
||||||
|
b0[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
|
||||||
|
|
||||||
|
for(i=0; i<n1; ++i,++cp->phs)
|
||||||
|
b1[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApSample_t _cmApMeter( const cmApSample_t* b, unsigned bn, unsigned stride )
|
||||||
|
{
|
||||||
|
const cmApSample_t* ep = b + bn;
|
||||||
|
cmApSample_t sum = 0;
|
||||||
|
|
||||||
|
for(; b<ep; b+=stride)
|
||||||
|
sum += *b * *b;
|
||||||
|
|
||||||
|
return sum / bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmApChFinalize( cmApCh* chPtr )
|
||||||
|
{
|
||||||
|
cmMemPtrFree( &chPtr->b );
|
||||||
|
cmMemPtrFree( &chPtr->m );
|
||||||
|
}
|
||||||
|
|
||||||
|
// n=buf sample cnt mn=meter buf smp cnt
|
||||||
|
void _cmApChInitialize( cmApCh* chPtr, unsigned n, unsigned mn )
|
||||||
|
{
|
||||||
|
_cmApChFinalize(chPtr);
|
||||||
|
|
||||||
|
chPtr->b = n==0 ? NULL : cmMemAllocZ( cmApSample_t, n );
|
||||||
|
chPtr->ii = 0;
|
||||||
|
chPtr->oi = 0;
|
||||||
|
chPtr->fn = 0;
|
||||||
|
chPtr->fl = (n!=0 ? kChApFl : 0);
|
||||||
|
chPtr->hz = 1000;
|
||||||
|
chPtr->gain = 1.0;
|
||||||
|
chPtr->mn = mn;
|
||||||
|
chPtr->m = cmMemAllocZ(cmApSample_t,mn);
|
||||||
|
chPtr->mi = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmApIoFinalize( cmApIO* ioPtr )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<ioPtr->chCnt; ++i)
|
||||||
|
_cmApChFinalize( ioPtr->chArray + i );
|
||||||
|
|
||||||
|
cmMemPtrFree(&ioPtr->chArray);
|
||||||
|
ioPtr->chCnt = 0;
|
||||||
|
ioPtr->n = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmApIoInitialize( cmApIO* ioPtr, double srate, unsigned framesPerCycle, unsigned chCnt, unsigned n, unsigned meterBufN, unsigned dspFrameCnt )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
_cmApIoFinalize(ioPtr);
|
||||||
|
|
||||||
|
n += (n % dspFrameCnt); // force buffer size to be a multiple of dspFrameCnt
|
||||||
|
|
||||||
|
ioPtr->chArray = chCnt==0 ? NULL : cmMemAllocZ( cmApCh, chCnt );
|
||||||
|
ioPtr->chCnt = chCnt;
|
||||||
|
ioPtr->n = n;
|
||||||
|
ioPtr->faultCnt = 0;
|
||||||
|
ioPtr->framesPerCycle = framesPerCycle;
|
||||||
|
ioPtr->srate = srate;
|
||||||
|
ioPtr->dspFrameCnt = dspFrameCnt;
|
||||||
|
|
||||||
|
for(i=0; i<chCnt; ++i )
|
||||||
|
_cmApChInitialize( ioPtr->chArray + i, n, meterBufN );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmApDevFinalize( cmApDev* dp )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<kIoApCnt; ++i)
|
||||||
|
_cmApIoFinalize( dp->ioArray+i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmApDevInitialize( cmApDev* dp, double srate, unsigned iFpC, unsigned iChCnt, unsigned iBufN, unsigned oFpC, unsigned oChCnt, unsigned oBufN, unsigned meterBufN, unsigned dspFrameCnt )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
_cmApDevFinalize(dp);
|
||||||
|
|
||||||
|
for(i=0; i<kIoApCnt; ++i)
|
||||||
|
{
|
||||||
|
unsigned chCnt = i==kInApIdx ? iChCnt : oChCnt;
|
||||||
|
unsigned bufN = i==kInApIdx ? iBufN : oBufN;
|
||||||
|
unsigned fpc = i==kInApIdx ? iFpC : oFpC;
|
||||||
|
_cmApIoInitialize( dp->ioArray+i, srate, fpc, chCnt, bufN, meterBufN, dspFrameCnt );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAbRC_t cmApBufInitialize( unsigned devCnt, unsigned meterMs )
|
||||||
|
{
|
||||||
|
cmAbRC_t rc;
|
||||||
|
|
||||||
|
if((rc = cmApBufFinalize()) != kOkAbRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
_cmApBuf.devArray = cmMemAllocZ( cmApDev, devCnt );
|
||||||
|
_cmApBuf.devCnt = devCnt;
|
||||||
|
_cmApBuf.meterMs = meterMs;
|
||||||
|
return kOkAbRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAbRC_t cmApBufFinalize()
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<_cmApBuf.devCnt; ++i)
|
||||||
|
_cmApDevFinalize(_cmApBuf.devArray + i);
|
||||||
|
|
||||||
|
cmMemPtrFree( &_cmApBuf.devArray );
|
||||||
|
cmMemPtrFree( &_cmApBuf.zeroBuf );
|
||||||
|
|
||||||
|
_cmApBuf.devCnt = 0;
|
||||||
|
|
||||||
|
return kOkAbRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAbRC_t cmApBufSetup(
|
||||||
|
unsigned devIdx,
|
||||||
|
double srate,
|
||||||
|
unsigned dspFrameCnt,
|
||||||
|
unsigned bufCnt,
|
||||||
|
unsigned inChCnt,
|
||||||
|
unsigned inFramesPerCycle,
|
||||||
|
unsigned outChCnt,
|
||||||
|
unsigned outFramesPerCycle)
|
||||||
|
{
|
||||||
|
cmApDev* devPtr = _cmApBuf.devArray + devIdx;
|
||||||
|
unsigned iBufN = bufCnt * inFramesPerCycle;
|
||||||
|
unsigned oBufN = bufCnt * outFramesPerCycle;
|
||||||
|
unsigned meterBufN = cmMax(1,floor(srate * _cmApBuf.meterMs / (1000.0 * outFramesPerCycle)));
|
||||||
|
|
||||||
|
_cmApDevInitialize( devPtr, srate, inFramesPerCycle, inChCnt, iBufN, outFramesPerCycle, outChCnt, oBufN, meterBufN, dspFrameCnt );
|
||||||
|
|
||||||
|
if( inFramesPerCycle > _cmApBuf.zeroBufCnt || outFramesPerCycle > _cmApBuf.zeroBufCnt )
|
||||||
|
{
|
||||||
|
_cmApBuf.zeroBufCnt = cmMax(inFramesPerCycle,outFramesPerCycle);
|
||||||
|
_cmApBuf.zeroBuf = cmMemResizeZ(cmApSample_t,_cmApBuf.zeroBuf,_cmApBuf.zeroBufCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkAbRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt )
|
||||||
|
{
|
||||||
|
cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for(i=0; i<iop->chCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApCh* cp = iop->chArray + i;
|
||||||
|
unsigned bn = iop->n * sizeof(cmApSample_t);
|
||||||
|
memset(cp->b,0,bn);
|
||||||
|
cp->oi = 0;
|
||||||
|
cp->ii = iop->framesPerCycle * audioCycleCnt;
|
||||||
|
cp->fn = iop->framesPerCycle * audioCycleCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkAbRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAbRC_t cmApBufUpdate(
|
||||||
|
cmApAudioPacket_t* inPktArray,
|
||||||
|
unsigned inPktCnt,
|
||||||
|
cmApAudioPacket_t* outPktArray,
|
||||||
|
unsigned outPktCnt )
|
||||||
|
{
|
||||||
|
unsigned i,j;
|
||||||
|
|
||||||
|
// copy samples from the packet to the buffer
|
||||||
|
if( inPktArray != NULL )
|
||||||
|
{
|
||||||
|
for(i=0; i<inPktCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApAudioPacket_t* pp = inPktArray + i;
|
||||||
|
cmApIO* ip = _cmApBuf.devArray[pp->devIdx].ioArray + kInApIdx; // dest io recd
|
||||||
|
|
||||||
|
// for each source packet channel and enabled dest channel
|
||||||
|
for(j=0; j<pp->chCnt; ++j)
|
||||||
|
{
|
||||||
|
cmApCh* cp = ip->chArray + pp->begChIdx +j; // dest ch
|
||||||
|
unsigned n0 = ip->n - cp->ii; // first dest segment
|
||||||
|
unsigned n1 = 0; // second dest segment
|
||||||
|
|
||||||
|
assert(pp->begChIdx + j < ip->chCnt );
|
||||||
|
|
||||||
|
// if the incoming samples would overflow the buffer then ignore them
|
||||||
|
if( cp->fn + pp->audioFramesCnt > ip->n )
|
||||||
|
{
|
||||||
|
++ip->faultCnt; // record input overflow
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the incoming samples would go off the end of the buffer then
|
||||||
|
// copy in the samples in two segments (one at the end and another at begin of dest channel)
|
||||||
|
if( n0 < pp->audioFramesCnt )
|
||||||
|
n1 = pp->audioFramesCnt-n0;
|
||||||
|
else
|
||||||
|
n0 = pp->audioFramesCnt;
|
||||||
|
|
||||||
|
bool enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
|
||||||
|
const cmApSample_t* sp = enaFl ? ((cmApSample_t*)pp->audioBytesPtr) + j : _cmApBuf.zeroBuf;
|
||||||
|
unsigned ssn = enaFl ? pp->chCnt : 1; // stride (packet samples are interleaved)
|
||||||
|
cmApSample_t* dp = cp->b + cp->ii;
|
||||||
|
const cmApSample_t* ep = dp + n0;
|
||||||
|
|
||||||
|
|
||||||
|
// update the meter
|
||||||
|
if( cmIsFlag(cp->fl,kMeterApFl) )
|
||||||
|
{
|
||||||
|
cp->m[cp->mi] = _cmApMeter(sp,pp->audioFramesCnt,pp->chCnt);
|
||||||
|
cp->mi = (cp->mi + 1) % cp->mn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the test tone is enabled on this input channel
|
||||||
|
if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
|
||||||
|
{
|
||||||
|
_cmApSine(cp, dp, n0, cp->b, n1, 1, ip->srate );
|
||||||
|
}
|
||||||
|
else // otherwise copy samples from the packet to the buffer
|
||||||
|
{
|
||||||
|
// copy the first segment
|
||||||
|
for(; dp < ep; sp += ssn )
|
||||||
|
*dp++ = cp->gain * *sp;
|
||||||
|
|
||||||
|
// if there is a second segment
|
||||||
|
if( n1 > 0 )
|
||||||
|
{
|
||||||
|
// copy the second segment
|
||||||
|
dp = cp->b;
|
||||||
|
ep = dp + n1;
|
||||||
|
for(; dp<ep; sp += ssn )
|
||||||
|
*dp++ = cp->gain * *sp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// advance the input channel buffer
|
||||||
|
cp->ii = n1>0 ? n1 : cp->ii + n0;
|
||||||
|
//cp->fn += pp->audioFramesCnt;
|
||||||
|
cmThUIntIncr(&cp->fn,pp->audioFramesCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy samples from the buffer to the packet
|
||||||
|
if( outPktArray != NULL )
|
||||||
|
{
|
||||||
|
for(i=0; i<outPktCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApAudioPacket_t* pp = outPktArray + i;
|
||||||
|
cmApIO* op = _cmApBuf.devArray[pp->devIdx].ioArray + kOutApIdx; // dest io recd
|
||||||
|
|
||||||
|
|
||||||
|
// for each dest packet channel and enabled source channel
|
||||||
|
for(j=0; j<pp->chCnt; ++j)
|
||||||
|
{
|
||||||
|
cmApCh* cp = op->chArray + pp->begChIdx + j; // dest ch
|
||||||
|
unsigned n0 = op->n - cp->oi; // first src segment
|
||||||
|
unsigned n1 = 0; // second src segment
|
||||||
|
volatile unsigned fn = cp->fn; // store fn because it may be changed by the client thread
|
||||||
|
|
||||||
|
// if the outgoing samples will underflow the buffer
|
||||||
|
if( pp->audioFramesCnt > fn )
|
||||||
|
{
|
||||||
|
++op->faultCnt; // record an output underflow
|
||||||
|
|
||||||
|
// if the buffer is empty - zero the packet and return
|
||||||
|
if( fn == 0 )
|
||||||
|
{
|
||||||
|
memset( pp->audioBytesPtr, 0, pp->audioFramesCnt*sizeof(cmApSample_t));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... otherwise decrease the count of returned samples
|
||||||
|
pp->audioFramesCnt = fn;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the outgong segments would go off the end of the buffer then
|
||||||
|
// arrange to wrap to the begining of the buffer
|
||||||
|
if( n0 < pp->audioFramesCnt )
|
||||||
|
n1 = pp->audioFramesCnt-n0;
|
||||||
|
else
|
||||||
|
n0 = pp->audioFramesCnt;
|
||||||
|
|
||||||
|
cmApSample_t* dp = ((cmApSample_t*)pp->audioBytesPtr) + j;
|
||||||
|
bool enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
|
||||||
|
|
||||||
|
// if the tone is enabled on this channel
|
||||||
|
if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
|
||||||
|
{
|
||||||
|
_cmApSine(cp, dp, n0, dp + n0*pp->chCnt, n1, pp->chCnt, op->srate );
|
||||||
|
}
|
||||||
|
else // otherwise copy samples from the output buffer to the packet
|
||||||
|
{
|
||||||
|
const cmApSample_t* sp = enaFl ? cp->b + cp->oi : _cmApBuf.zeroBuf;
|
||||||
|
const cmApSample_t* ep = sp + n0;
|
||||||
|
|
||||||
|
// copy the first segment
|
||||||
|
for(; sp < ep; dp += pp->chCnt )
|
||||||
|
*dp = cp->gain * *sp++;
|
||||||
|
|
||||||
|
// if there is a second segment
|
||||||
|
if( n1 > 0 )
|
||||||
|
{
|
||||||
|
// copy the second segment
|
||||||
|
sp = enaFl ? cp->b : _cmApBuf.zeroBuf;
|
||||||
|
ep = sp + n1;
|
||||||
|
for(; sp<ep; dp += pp->chCnt )
|
||||||
|
*dp = cp->gain * *sp++;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the meter
|
||||||
|
if( cmIsFlag(cp->fl,kMeterApFl) )
|
||||||
|
{
|
||||||
|
cp->m[cp->mi] = _cmApMeter(((cmApSample_t*)pp->audioBytesPtr)+j,pp->audioFramesCnt,pp->chCnt);
|
||||||
|
cp->mi = (cp->mi + 1) % cp->mn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance the output channel buffer
|
||||||
|
cp->oi = n1>0 ? n1 : cp->oi + n0;
|
||||||
|
//cp->fn -= pp->audioFramesCnt;
|
||||||
|
cmThUIntDecr(&cp->fn,pp->audioFramesCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kOkAbRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmApBufMeterMs()
|
||||||
|
{ return _cmApBuf.meterMs; }
|
||||||
|
|
||||||
|
unsigned cmApBufChannelCount( unsigned devIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
|
||||||
|
return _cmApBuf.devArray[devIdx].ioArray[ idx ].chCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmApBufSetFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
|
||||||
|
bool enableFl = flags & kEnableApFl ? true : false;
|
||||||
|
unsigned i = chIdx != -1 ? chIdx : 0;
|
||||||
|
unsigned n = chIdx != -1 ? chIdx+1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt;
|
||||||
|
|
||||||
|
for(; i<n; ++i)
|
||||||
|
{
|
||||||
|
cmApCh* cp = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + i;
|
||||||
|
cp->fl = cmEnaFlag(cp->fl, flags & (kChApFl|kToneApFl|kMeterApFl|kMuteApFl|kPassApFl), enableFl );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmApBufIsFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
|
||||||
|
return cmIsFlag(_cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].fl,flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmApBufEnableChannel( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ cmApBufSetFlag(devIdx,chIdx,flags | kChApFl); }
|
||||||
|
|
||||||
|
bool cmApBufIsChannelEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ return cmApBufIsFlag(devIdx, chIdx, flags | kChApFl); }
|
||||||
|
|
||||||
|
void cmApBufEnableTone( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ cmApBufSetFlag(devIdx,chIdx,flags | kToneApFl); }
|
||||||
|
|
||||||
|
bool cmApBufIsToneEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ return cmApBufIsFlag(devIdx,chIdx,flags | kToneApFl); }
|
||||||
|
|
||||||
|
void cmApBufEnableMute( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ cmApBufSetFlag(devIdx,chIdx,flags | kMuteApFl); }
|
||||||
|
|
||||||
|
bool cmApBufIsMuteEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ return cmApBufIsFlag(devIdx,chIdx,flags | kMuteApFl); }
|
||||||
|
|
||||||
|
void cmApBufEnablePass( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ cmApBufSetFlag(devIdx,chIdx,flags | kPassApFl); }
|
||||||
|
|
||||||
|
bool cmApBufIsPassEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ return cmApBufIsFlag(devIdx,chIdx,flags | kPassApFl); }
|
||||||
|
|
||||||
|
void cmApBufEnableMeter( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ cmApBufSetFlag(devIdx,chIdx,flags | kMeterApFl); }
|
||||||
|
|
||||||
|
bool cmApBufIsMeterEnabled(unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{ return cmApBufIsFlag(devIdx,chIdx,flags | kMeterApFl); }
|
||||||
|
|
||||||
|
cmApSample_t cmApBufMeter(unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
|
||||||
|
const cmApCh* cp = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + chIdx;
|
||||||
|
return _cmApMeterValue(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmApBufSetGain( unsigned devIdx, unsigned chIdx, unsigned flags, double gain )
|
||||||
|
{
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
|
||||||
|
unsigned i = chIdx != -1 ? chIdx : 0;
|
||||||
|
unsigned n = i + (chIdx != -1 ? 1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt);
|
||||||
|
|
||||||
|
for(; i<n; ++i)
|
||||||
|
_cmApBuf.devArray[devIdx].ioArray[idx].chArray[i].gain = gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
double cmApBufGain( unsigned devIdx, unsigned chIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
|
||||||
|
return _cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned cmApBufGetStatus( unsigned devIdx, unsigned flags, double* meterArray, unsigned meterCnt, unsigned* faultCntPtr )
|
||||||
|
{
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned ioIdx = cmIsFlag(flags,kInApFl) ? kInApIdx : kOutApIdx;
|
||||||
|
cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + ioIdx;
|
||||||
|
unsigned chCnt = cmMin(iop->chCnt, meterCnt );
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if( faultCntPtr != NULL )
|
||||||
|
*faultCntPtr = iop->faultCnt;
|
||||||
|
|
||||||
|
for(i=0; i<chCnt; ++i)
|
||||||
|
meterArray[i] = _cmApMeterValue(iop->chArray + i);
|
||||||
|
|
||||||
|
return chCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool cmApBufIsDeviceReady( unsigned devIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
//bool iFl = true;
|
||||||
|
//bool oFl = true;
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( flags & kInApFl )
|
||||||
|
{
|
||||||
|
const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
|
||||||
|
for(i=0; i<ioPtr->chCnt; ++i)
|
||||||
|
if( ioPtr->chArray[i].fn < ioPtr->dspFrameCnt )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//iFl = ioPtr->fn > ioPtr->dspFrameCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( flags & kOutApFl )
|
||||||
|
{
|
||||||
|
const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
|
||||||
|
|
||||||
|
for(i=0; i<ioPtr->chCnt; ++i)
|
||||||
|
if( (ioPtr->n - ioPtr->chArray[i].fn) < ioPtr->dspFrameCnt )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
//oFl = (ioPtr->n - ioPtr->fn) > ioPtr->dspFrameCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
//return iFl & oFl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Note that his function returns audio samples but does NOT
|
||||||
|
// change any internal states.
|
||||||
|
void cmApBufGet( unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsigned bufChCnt )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
{
|
||||||
|
for(i=0; i<bufChCnt; ++i)
|
||||||
|
bufArray[i] = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
|
||||||
|
const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + idx;
|
||||||
|
unsigned n = bufChCnt < ioPtr->chCnt ? bufChCnt : ioPtr->chCnt;
|
||||||
|
//unsigned offs = flags & kInApFl ? ioPtr->oi : ioPtr->ii;
|
||||||
|
cmApCh* cp = ioPtr->chArray;
|
||||||
|
|
||||||
|
for(i=0; i<n; ++i,++cp)
|
||||||
|
{
|
||||||
|
unsigned offs = flags & kInApFl ? cp->oi : cp->ii;
|
||||||
|
bufArray[i] = cmIsFlag(cp->fl,kChApFl) ? cp->b + offs : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt )
|
||||||
|
{
|
||||||
|
cmApBufGet( iDevIdx, kInApFl, iBufArray, iBufChCnt );
|
||||||
|
cmApBufGet( oDevIdx, kOutApFl,oBufArray, oBufChCnt );
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
|
if( iDevIdx != cmInvalidIdx && oDevIdx != cmInvalidIdx )
|
||||||
|
{
|
||||||
|
const cmApIO* ip = _cmApBuf.devArray[iDevIdx].ioArray + kInApIdx;
|
||||||
|
const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
|
||||||
|
unsigned minChCnt = cmMin(iBufChCnt,oBufChCnt);
|
||||||
|
unsigned frmCnt = cmMin(ip->dspFrameCnt,op->dspFrameCnt);
|
||||||
|
unsigned byteCnt = frmCnt * sizeof(cmApSample_t);
|
||||||
|
|
||||||
|
for(i=0; i<minChCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApCh* ocp = op->chArray + i;
|
||||||
|
cmApCh* icp = ip->chArray + i;
|
||||||
|
|
||||||
|
if( oBufArray[i] != NULL )
|
||||||
|
{
|
||||||
|
// if either the input or output channel is marked for pass-through
|
||||||
|
if( cmAllFlags(ocp->fl,kPassApFl) || cmAllFlags(icp->fl,kPassApFl) )
|
||||||
|
{
|
||||||
|
memcpy( oBufArray[i], iBufArray[i], byteCnt );
|
||||||
|
|
||||||
|
// set the output buffer to NULL to prevent it being over written by the client
|
||||||
|
oBufArray[i] = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// zero the output buffer
|
||||||
|
memset(oBufArray[i],0,byteCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( oDevIdx != cmInvalidIdx )
|
||||||
|
{
|
||||||
|
const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
|
||||||
|
unsigned byteCnt = op->dspFrameCnt * sizeof(cmApSample_t);
|
||||||
|
|
||||||
|
for(; i<oBufChCnt; ++i)
|
||||||
|
if( oBufArray[i] != NULL )
|
||||||
|
memset( oBufArray[i], 0, byteCnt );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmApBufAdvance( unsigned devIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( flags & kInApFl )
|
||||||
|
{
|
||||||
|
cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
|
||||||
|
|
||||||
|
for(i=0; i<ioPtr->chCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApCh* cp = ioPtr->chArray + i;
|
||||||
|
cp->oi = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n;
|
||||||
|
//cp->fn -= ioPtr->dspFrameCnt;
|
||||||
|
cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//ioPtr->oi = (ioPtr->oi + ioPtr->dspFrameCnt) % ioPtr->n;
|
||||||
|
//ioPtr->fn -= ioPtr->dspFrameCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( flags & kOutApFl )
|
||||||
|
{
|
||||||
|
cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
|
||||||
|
for(i=0; i<ioPtr->chCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApCh* cp = ioPtr->chArray + i;
|
||||||
|
cp->ii = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n;
|
||||||
|
//cp->fn += ioPtr->dspFrameCnt;
|
||||||
|
cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//ioPtr->ii = (ioPtr->ii + ioPtr->dspFrameCnt) % ioPtr->n;
|
||||||
|
//ioPtr->fn += ioPtr->dspFrameCnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmApBufInputToOutput( unsigned iDevIdx, unsigned oDevIdx )
|
||||||
|
{
|
||||||
|
if( iDevIdx == cmInvalidIdx || oDevIdx == cmInvalidIdx )
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned iChCnt = cmApBufChannelCount( iDevIdx, kInApFl );
|
||||||
|
unsigned oChCnt = cmApBufChannelCount( oDevIdx, kOutApFl );
|
||||||
|
unsigned chCnt = iChCnt < oChCnt ? iChCnt : oChCnt;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
cmApSample_t* iBufPtrArray[ iChCnt ];
|
||||||
|
cmApSample_t* oBufPtrArray[ oChCnt ];
|
||||||
|
|
||||||
|
|
||||||
|
while( cmApBufIsDeviceReady( iDevIdx, kInApFl ) && cmApBufIsDeviceReady( oDevIdx, kOutApFl ) )
|
||||||
|
{
|
||||||
|
cmApBufGet( iDevIdx, kInApFl, iBufPtrArray, iChCnt );
|
||||||
|
cmApBufGet( oDevIdx, kOutApFl, oBufPtrArray, oChCnt );
|
||||||
|
|
||||||
|
// Warning: buffer pointers to disabled channels will be set to NULL
|
||||||
|
|
||||||
|
for(i=0; i<chCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApIO* ip = _cmApBuf.devArray[iDevIdx ].ioArray + kInApIdx;
|
||||||
|
cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
|
||||||
|
|
||||||
|
assert( ip->dspFrameCnt == op->dspFrameCnt );
|
||||||
|
|
||||||
|
unsigned byteCnt = ip->dspFrameCnt * sizeof(cmApSample_t);
|
||||||
|
|
||||||
|
if( oBufPtrArray[i] != NULL )
|
||||||
|
{
|
||||||
|
// the input channel is not disabled
|
||||||
|
if( iBufPtrArray[i]!=NULL )
|
||||||
|
memcpy(oBufPtrArray[i],iBufPtrArray[i],byteCnt);
|
||||||
|
else
|
||||||
|
// the input channel is disabled but the output is not - so fill the output with zeros
|
||||||
|
memset(oBufPtrArray[i],0,byteCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApBufAdvance( iDevIdx, kInApFl );
|
||||||
|
cmApBufAdvance( oDevIdx, kOutApFl );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmApBufReport( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
unsigned i,j,k;
|
||||||
|
for(i=0; i<_cmApBuf.devCnt; ++i)
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"%i ",i);
|
||||||
|
|
||||||
|
for(j=0; j<kIoApCnt; ++j)
|
||||||
|
{
|
||||||
|
cmApIO* ip = _cmApBuf.devArray[i].ioArray + j;
|
||||||
|
|
||||||
|
unsigned ii = 0;
|
||||||
|
unsigned oi = 0;
|
||||||
|
unsigned fn = 0;
|
||||||
|
for(k=0; k<ip->chCnt; ++k)
|
||||||
|
{
|
||||||
|
cmApCh* cp = ip->chArray + i;
|
||||||
|
ii += cp->ii;
|
||||||
|
oi += cp->oi;
|
||||||
|
fn += cp->fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRptPrintf(rpt,"%s - i:%7i o:%7i f:%7i n:%7i err %s:%7i ",
|
||||||
|
j==0?"IN":"OUT",
|
||||||
|
ii,oi,fn,ip->n, (j==0?"over":"under"), ip->faultCnt);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRptPrintf(rpt,"\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [cmApBufExample]
|
||||||
|
|
||||||
|
void cmApBufTest( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
unsigned devIdx = 0;
|
||||||
|
unsigned devCnt = 1 ;
|
||||||
|
unsigned dspFrameCnt = 10;
|
||||||
|
unsigned cycleCnt = 3;
|
||||||
|
unsigned framesPerCycle = 25;
|
||||||
|
unsigned inChCnt = 2;
|
||||||
|
unsigned outChCnt = inChCnt;
|
||||||
|
unsigned sigN = cycleCnt*framesPerCycle*inChCnt;
|
||||||
|
double srate = 44100.0;
|
||||||
|
unsigned meterMs = 50;
|
||||||
|
|
||||||
|
unsigned bufChCnt= inChCnt;
|
||||||
|
cmApSample_t* inBufArray[ bufChCnt ];
|
||||||
|
cmApSample_t* outBufArray[ bufChCnt ];
|
||||||
|
cmApSample_t iSig[ sigN ];
|
||||||
|
cmApSample_t oSig[ sigN ];
|
||||||
|
cmApSample_t* os = oSig;
|
||||||
|
cmApAudioPacket_t pkt;
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
// create a simulated signal
|
||||||
|
for(i=0; i<sigN; ++i)
|
||||||
|
{
|
||||||
|
iSig[i] = i;
|
||||||
|
oSig[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt.devIdx = 0;
|
||||||
|
pkt.begChIdx = 0;
|
||||||
|
pkt.chCnt = inChCnt;
|
||||||
|
pkt.audioFramesCnt = framesPerCycle;
|
||||||
|
pkt.bitsPerSample = 32;
|
||||||
|
pkt.flags = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// initialize a the audio buffer
|
||||||
|
cmApBufInitialize(devCnt,meterMs);
|
||||||
|
|
||||||
|
// setup the buffer with the specific parameters to by used by the host audio ports
|
||||||
|
cmApBufSetup(devIdx,srate,dspFrameCnt,cycleCnt,inChCnt,framesPerCycle,outChCnt,framesPerCycle);
|
||||||
|
|
||||||
|
// simulate cylcing through sigN buffer transactions
|
||||||
|
for(i=0; i<sigN; i+=framesPerCycle*inChCnt)
|
||||||
|
{
|
||||||
|
// setup an incoming audio packet
|
||||||
|
pkt.audioFramesCnt = framesPerCycle;
|
||||||
|
pkt.audioBytesPtr = iSig+i;
|
||||||
|
|
||||||
|
// simulate a call from the audio port with incoming samples
|
||||||
|
// (fill the audio buffers internal input buffers)
|
||||||
|
cmApBufUpdate(&pkt,1,NULL,0);
|
||||||
|
|
||||||
|
// if all devices need to be serviced
|
||||||
|
while( cmApBufIsDeviceReady( devIdx, kInApFl | kOutApFl ))
|
||||||
|
{
|
||||||
|
// get pointers to full internal input buffers
|
||||||
|
cmApBufGet(devIdx, kInApFl, inBufArray, bufChCnt );
|
||||||
|
|
||||||
|
// get pointers to empty internal output buffers
|
||||||
|
cmApBufGet(devIdx, kOutApFl, outBufArray, bufChCnt );
|
||||||
|
|
||||||
|
// Warning: pointers to disabled channels will be set to NULL.
|
||||||
|
|
||||||
|
// simulate a play through by copying the incoming samples to the outgoing buffers.
|
||||||
|
for(j=0; j<bufChCnt; ++j)
|
||||||
|
if( outBufArray[j] != NULL )
|
||||||
|
{
|
||||||
|
// if the input is disabled - but the output is not then zero the output buffer
|
||||||
|
if( inBufArray[j] == NULL )
|
||||||
|
memset( outBufArray[j], 0, dspFrameCnt*sizeof(cmApSample_t));
|
||||||
|
else
|
||||||
|
// copy the input to the output
|
||||||
|
memcpy( outBufArray[j], inBufArray[j], dspFrameCnt*sizeof(cmApSample_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// signal the buffer that this cycle is complete.
|
||||||
|
// (marks current internal input/output buffer empty/full)
|
||||||
|
cmApBufAdvance( devIdx, kInApFl | kOutApFl );
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt.audioBytesPtr = os;
|
||||||
|
|
||||||
|
// simulate a call from the audio port picking up outgoing samples
|
||||||
|
// (empties the audio buffers internal output buffers)
|
||||||
|
cmApBufUpdate(NULL,0,&pkt,1);
|
||||||
|
|
||||||
|
os += pkt.audioFramesCnt * pkt.chCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0; i<sigN; ++i)
|
||||||
|
cmRptPrintf(rpt,"%f ",oSig[i]);
|
||||||
|
cmRptPrintf(rpt,"\n");
|
||||||
|
|
||||||
|
cmApBufFinalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [cmApBufExample]
|
||||||
|
|
||||||
|
|
229
cmApBuf.h
Normal file
229
cmApBuf.h
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/// \file cmApBuf.h
|
||||||
|
/// \brief Thread-safe audio buffer class.
|
||||||
|
///
|
||||||
|
/// This file defines an audio buffer class which handles
|
||||||
|
/// buffering incoming (recording) and outgoing (playback)
|
||||||
|
/// samples in a thread-safe manner.
|
||||||
|
///
|
||||||
|
/// Usage example and testing code:
|
||||||
|
/// See cmApBufTest() and cmAudioSysTest().
|
||||||
|
/// \snippet cmApBuf.c cmApBufExample
|
||||||
|
///
|
||||||
|
/// Notes on channel flags:
|
||||||
|
/// Disabled channels: kChFl is cleared
|
||||||
|
/// cmApBufGet()
|
||||||
|
/// in - return NULL buffer pointers
|
||||||
|
/// out - return NULL buffer points
|
||||||
|
///
|
||||||
|
/// cmApBufUpdate()
|
||||||
|
/// in - incoming samples are set to 0.
|
||||||
|
/// out - outgoing samples are set to 0.
|
||||||
|
///
|
||||||
|
/// Muted channels: kMuteFl is set
|
||||||
|
/// cmApBufUpdate()
|
||||||
|
/// in - incoming samples are set to 0.
|
||||||
|
/// out - outgoing samples are set to 0.
|
||||||
|
///
|
||||||
|
/// Tone channels: kToneFl is set
|
||||||
|
/// cmApBufUpdate()
|
||||||
|
/// in - incoming samples are filled with a 1k sine tone
|
||||||
|
/// out - outgoing samples are filled with a 1k sine tone
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef cmApBuf_h
|
||||||
|
#define cmApBuf_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef cmRC_t cmAbRC_t; ///< Result code type
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAbRC = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Allocate and initialize an audio buffer.
|
||||||
|
/// devCnt - count of devices this buffer will handle.
|
||||||
|
/// meterMs - length of the meter buffers in milliseconds
|
||||||
|
cmAbRC_t cmApBufInitialize( unsigned devCnt, unsigned meterMs );
|
||||||
|
|
||||||
|
/// Deallocate and release any resource held by an audio buffer allocated via cmApBufInitialize().
|
||||||
|
cmAbRC_t cmApBufFinalize();
|
||||||
|
|
||||||
|
/// Configure a buffer for a given device.
|
||||||
|
cmAbRC_t cmApBufSetup(
|
||||||
|
unsigned devIdx, ///< device to setup
|
||||||
|
double srate, ///< device sample rate (only required for synthesizing the correct test-tone frequency)
|
||||||
|
unsigned dspFrameCnt, /// dspFrameCnt - count of samples in channel buffers returned via cmApBufGet()
|
||||||
|
unsigned cycleCnt, ///< number of audio port cycles to store
|
||||||
|
unsigned inChCnt, ///< input channel count on this device
|
||||||
|
unsigned inFramesPerCycle, ///< maximum number of incoming sample frames on an audio port cycle
|
||||||
|
unsigned outChCnt, ///< output channel count on this device
|
||||||
|
unsigned outFramesPerCycle ///< maximum number of outgoing sample frames in an audio port cycle
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prime the buffer with 'audioCycleCnt' * outFramesPerCycle samples ready to be played
|
||||||
|
cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt );
|
||||||
|
|
||||||
|
/// This function is called asynchronously by the audio device driver to transfer incoming samples to the
|
||||||
|
/// the buffer and to send outgoing samples to the DAC. This function is
|
||||||
|
/// intended to be called from the audio port callback function (\see cmApCallbackPtr_t).
|
||||||
|
/// This function is thread-safe under the condition where the audio device uses
|
||||||
|
/// different threads for input and output.
|
||||||
|
///
|
||||||
|
/// Enable Flag:
|
||||||
|
/// Input: If an input channel is disabled then the incoming samples are replaced with zeros.
|
||||||
|
/// Output: If an output channel is disabled then the packet samples are set to zeros.
|
||||||
|
///
|
||||||
|
/// Tone Flag:
|
||||||
|
/// Input: If the tone flag is set on an input channel then the incoming samples are set to a sine tone.
|
||||||
|
/// Output: If the tone flag is set on an output channel then the packet samples are set to a sine tone.
|
||||||
|
///
|
||||||
|
/// The enable flag has higher precedence than the tone flag therefore disabled channels
|
||||||
|
/// will be set to zero even if the tone flag is set.
|
||||||
|
cmAbRC_t cmApBufUpdate(
|
||||||
|
cmApAudioPacket_t* inPktArray, ///< full audio packets from incoming audio (from ADC)
|
||||||
|
unsigned inPktCnt, ///< count of incoming audio packets
|
||||||
|
cmApAudioPacket_t* outPktArray, ///< empty audio packet for outgoing audio (to DAC)
|
||||||
|
unsigned outPktCnt ///< count of outgoing audio packets
|
||||||
|
);
|
||||||
|
/// Channel flags
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInApFl = 0x01, ///< Identify an input channel
|
||||||
|
kOutApFl = 0x02, ///< Identify an output channel
|
||||||
|
kEnableApFl = 0x04, ///< Set to enable a channel, Clear to disable.
|
||||||
|
|
||||||
|
kChApFl = 0x08, ///< Used to enable/disable a channel
|
||||||
|
kMuteApFl = 0x10, ///< Mute this channel
|
||||||
|
kToneApFl = 0x20, ///< Generate a tone on this channel
|
||||||
|
kMeterApFl = 0x40, ///< Turn meter's on/off
|
||||||
|
kPassApFl = 0x80 ///< Pass input channels throught to the output. Must use cmApBufGetIO() to implement this functionality.
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Return the meter window period as set by cmApBufInitialize()
|
||||||
|
unsigned cmApBufMeterMs();
|
||||||
|
|
||||||
|
/// Returns the channel count set via cmApBufSetup().
|
||||||
|
unsigned cmApBufChannelCount( unsigned devIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Set chIdx to -1 to enable all channels on this device.
|
||||||
|
/// Set flags to {kInApFl | kOutApFl} | {kChApFl | kToneApFl | kMeterFl} | { kEnableApFl=on | 0=off }
|
||||||
|
void cmApBufSetFlag( unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Return true if the the flags is set.
|
||||||
|
bool cmApBufIsFlag( unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Set chIdx to -1 to enable all channels on this device.
|
||||||
|
void cmApBufEnableChannel( unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Returns true if an input/output channel is enabled on the specified device.
|
||||||
|
bool cmApBufIsChannelEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Set the state of the tone generator on the specified channel.
|
||||||
|
/// Set chIdx to -1 to apply the change to all channels on this device.
|
||||||
|
/// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
|
||||||
|
void cmApBufEnableTone( unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Returns true if an input/output tone is enabled on the specified device.
|
||||||
|
bool cmApBufIsToneEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Mute a specified channel.
|
||||||
|
/// Set chIdx to -1 to apply the change to all channels on this device.
|
||||||
|
/// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
|
||||||
|
void cmApBufEnableMute( unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Returns true if an input/output channel is muted on the specified device.
|
||||||
|
bool cmApBufIsMuteEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Set the specified channel to pass through.
|
||||||
|
/// Set chIdx to -1 to apply the change to all channels on this device.
|
||||||
|
/// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
|
||||||
|
void cmApBufEnablePass( unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Returns true if pass through is enabled on the specified channel.
|
||||||
|
bool cmApBufIsPassEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Turn meter data collection on and off.
|
||||||
|
/// Set chIdx to -1 to apply the change to all channels on this device.
|
||||||
|
/// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
|
||||||
|
void cmApBufEnableMeter( unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Returns true if an input/output tone is enabled on the specified device.
|
||||||
|
bool cmApBufIsMeterEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Return the meter value for the requested channel.
|
||||||
|
/// Set flags to kInApFl | kOutApFl.
|
||||||
|
cmApSample_t cmApBufMeter(unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Set chIdx to -1 to apply the gain to all channels on the specified device.
|
||||||
|
void cmApBufSetGain( unsigned devIdx, unsigned chIdx, unsigned flags, double gain );
|
||||||
|
|
||||||
|
/// Return the current gain seting for the specified channel.
|
||||||
|
double cmApBufGain( unsigned devIdx, unsigned chIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Get the meter and fault status of the channel input or output channel array of a device.
|
||||||
|
/// Set 'flags' to { kInApFl | kOutApFl }.
|
||||||
|
/// The returns value is the count of channels actually written to meterArray.
|
||||||
|
/// If 'faultCntPtr' is non-NULL then it is set to the faultCnt of the associated devices input or output buffer.
|
||||||
|
unsigned cmApBufGetStatus( unsigned devIdx, unsigned flags, double* meterArray, unsigned meterCnt, unsigned* faultCntPtr );
|
||||||
|
|
||||||
|
/// Do all enabled input/output channels on this device have samples available?
|
||||||
|
/// 'flags' can be set to either or both kInApFl and kOutApFl
|
||||||
|
bool cmApBufIsDeviceReady( unsigned devIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// This function is called by the application to get full incoming sample buffers and
|
||||||
|
/// to fill empty outgoing sample buffers.
|
||||||
|
/// Upon return each element in bufArray[bufChCnt] holds a pointer to a buffer assoicated
|
||||||
|
/// with an audio channel or to NULL if the channel is disabled.
|
||||||
|
/// 'flags' can be set to kInApFl or kOutApFl but not both.
|
||||||
|
/// The buffers pointed to by bufArray[] each contain 'dspFrameCnt' samples. Where
|
||||||
|
/// 'dspFrameCnt' was set in the earlier call to cmApBufSetup() for this device.
|
||||||
|
/// (see cmApBufInitialize()).
|
||||||
|
/// Note that this function just returns audio information it does not
|
||||||
|
/// change any cmApBuf() internal states.
|
||||||
|
void cmApBufGet( unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsigned bufChCnt );
|
||||||
|
|
||||||
|
/// This function replaces calls to cmApBufGet() and implements pass-through and output
|
||||||
|
/// buffer zeroing:
|
||||||
|
///
|
||||||
|
/// 1) cmApBufGet(in);
|
||||||
|
/// 2) cmApBufGet(out);
|
||||||
|
/// 3) Copy through channels marked for 'pass' and set the associated oBufArray[i] channel to NULL.
|
||||||
|
/// 4) Zero all other enabled output channels.
|
||||||
|
///
|
||||||
|
/// Notes:
|
||||||
|
/// 1) The oBufArray[] channels that are disabled or marked for pass-through will
|
||||||
|
/// be set to NULL.
|
||||||
|
/// 2) The client is required to use this function to implement pass-through internally.
|
||||||
|
/// 3) This function just returns audio information it does not
|
||||||
|
/// change any cmApBuf() internal states.
|
||||||
|
void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt );
|
||||||
|
|
||||||
|
/// The application calls this function each time it completes processing of a bufArray[]
|
||||||
|
/// returned from cmApBufGet(). 'flags' can be set to either or both kInApFl and kOutApFl.
|
||||||
|
/// This function should only be called from the client thread.
|
||||||
|
void cmApBufAdvance( unsigned devIdx, unsigned flags );
|
||||||
|
|
||||||
|
/// Copy all available samples incoming samples from an input device to an output device.
|
||||||
|
/// The source code for this example is a good example of how an application should use cmApBufGet()
|
||||||
|
/// and cmApBufAdvance().
|
||||||
|
void cmApBufInputToOutput( unsigned inDevIdx, unsigned outDevIdx );
|
||||||
|
|
||||||
|
/// Print the current buffer state.
|
||||||
|
void cmApBufReport( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
/// Run a buffer usage simulation to test the class. cmAudioPortTest.c calls this function.
|
||||||
|
void cmApBufTest( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1224
cmAudDsp.c
Normal file
1224
cmAudDsp.c
Normal file
File diff suppressed because it is too large
Load Diff
55
cmAudDsp.h
Normal file
55
cmAudDsp.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#ifndef cmAudDsp_h
|
||||||
|
#define cmAudDsp_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This API supports a serialized interface to an internal instance of
|
||||||
|
// cmAudioSys and cmDspSys.
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAdRC = cmOkRC,
|
||||||
|
kAudioPortFailAdRC,
|
||||||
|
kAudioSysFailAdRC,
|
||||||
|
kMidiSysFailAdRC,
|
||||||
|
kDspSysFailAdRC,
|
||||||
|
kFileSysFailAdRC,
|
||||||
|
kJsonFailAdRC,
|
||||||
|
kSendMsgFailAdRC,
|
||||||
|
kInvalidCfgIdxAdRC,
|
||||||
|
kNoPgmLoadedAdRC,
|
||||||
|
kInvalidSubSysIdxAdRC,
|
||||||
|
kUnknownMsgTypeAdRC,
|
||||||
|
kAggDevSysFailAdRC,
|
||||||
|
kAggDevCreateFailAdRC,
|
||||||
|
kNrtDevSysFailAdRC,
|
||||||
|
kNetSysFailAdRC
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmRC_t cmAdRC_t;
|
||||||
|
typedef cmHandle_t cmAdH_t;
|
||||||
|
|
||||||
|
extern cmAdH_t cmAdNullHandle;
|
||||||
|
|
||||||
|
// Create a audio dsp engine and send device and program information to the
|
||||||
|
// host application.
|
||||||
|
// cbPtr provides a function used by cmAudDsp to send messages to the client.
|
||||||
|
cmAdRC_t cmAudDspAlloc( cmCtx_t* ctx, cmAdH_t* hp, cmMsgSendFuncPtr_t cbPtr, void* cbDataPtr );
|
||||||
|
cmAdRC_t cmAudDspFree( cmAdH_t* hp );
|
||||||
|
|
||||||
|
bool cmAudDspIsValid( cmAdH_t h );
|
||||||
|
|
||||||
|
// This function provides the primary interface for communication from the
|
||||||
|
// client program to the aud_dsp system.
|
||||||
|
cmAdRC_t cmAudDspReceiveClientMsg( cmAdH_t h, unsigned msgBytecnt, const void* msg );
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
291
cmAudDspIF.c
Normal file
291
cmAudDspIF.c
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmFileSys.h"
|
||||||
|
#include "cmJson.h"
|
||||||
|
#include "cmThread.h"
|
||||||
|
#include "dsp/cmDspValue.h"
|
||||||
|
#include "cmMsgProtocol.h"
|
||||||
|
#include "cmAudDspIF.h"
|
||||||
|
|
||||||
|
|
||||||
|
cmAiH_t cmAiNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmAdIfParm_t parms;
|
||||||
|
cmJsonH_t jsH;
|
||||||
|
} cmAi_t;
|
||||||
|
|
||||||
|
cmAi_t* _cmAiHandleToPtr( cmAiH_t h )
|
||||||
|
{
|
||||||
|
cmAi_t* p = (cmAi_t*)h.h;
|
||||||
|
assert(p != NULL);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Dispatch a message to the client application.
|
||||||
|
// This function is called from within cmTsQueueDequeueMsg() which is called
|
||||||
|
// by cmAdIfDispatchMsgToHost().
|
||||||
|
cmRC_t _cmAiDispatchMsgToClient(void* cbDataPtr, unsigned msgByteCnt, const void* msgDataPtr )
|
||||||
|
{
|
||||||
|
cmAi_t* p = (cmAi_t*)cbDataPtr;
|
||||||
|
cmDspUiHdr_t* m = (cmDspUiHdr_t*)msgDataPtr;
|
||||||
|
cmRC_t rc = cmOkRC;
|
||||||
|
|
||||||
|
switch( m->uiId )
|
||||||
|
{
|
||||||
|
case kStatusSelAsId:
|
||||||
|
{
|
||||||
|
// handle a status mesage
|
||||||
|
const char* base = (const char*)msgDataPtr;
|
||||||
|
const cmAudioSysStatus_t* st = (const cmAudioSysStatus_t*)(base + (2 * sizeof(unsigned)));
|
||||||
|
const double* iMeterArray = (const double*)(st + 1);
|
||||||
|
const double* oMeterArray = iMeterArray + st->iMeterCnt;
|
||||||
|
rc = p->parms.dispatchRecd.statusFunc(p->parms.dispatchRecd.cbDataPtr, st, iMeterArray, oMeterArray );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kSsInitSelAsId:
|
||||||
|
{
|
||||||
|
// handle an ssInit message
|
||||||
|
const cmAudioSysSsInitMsg_t* sip = (const cmAudioSysSsInitMsg_t*)msgDataPtr;
|
||||||
|
const char* iDevLabel = (const char*)(sip+1);
|
||||||
|
const char* oDevLabel = iDevLabel + strlen(iDevLabel) + 1;
|
||||||
|
rc = p->parms.dispatchRecd.ssInitFunc(p->parms.dispatchRecd.cbDataPtr, sip, iDevLabel, oDevLabel );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kUiSelAsId:
|
||||||
|
{
|
||||||
|
bool jsFl = false;
|
||||||
|
cmDsvRC_t rc = kOkDsvRC;
|
||||||
|
|
||||||
|
// if the value associated with this msg is a mtx then set
|
||||||
|
// its mtx data area pointer to just after the msg header.
|
||||||
|
if( cmDsvIsJson(&m->value) )
|
||||||
|
{
|
||||||
|
rc = cmDsvDeserializeJson(&m->value,p->jsH);
|
||||||
|
jsFl = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rc = cmDsvDeserializeInPlace(&m->value,msgByteCnt-sizeof(cmDspUiHdr_t));
|
||||||
|
|
||||||
|
if( rc != kOkDsvRC )
|
||||||
|
cmErrMsg(&p->err,kDeserialFailAiRC,"Deserialize failed.");
|
||||||
|
else
|
||||||
|
rc = p->parms.dispatchRecd.uiFunc(p->parms.dispatchRecd.cbDataPtr,m);
|
||||||
|
|
||||||
|
if( jsFl )
|
||||||
|
cmJsonClearTree(p->jsH);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cmErrMsg(&p->err,kUnknownMsgTypeAiRC,"The message type %i is unknown.",m->uiId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAiRC_t _cmAdIfReadCfgFile( cmAi_t* p, cmCtx_t* ctx )
|
||||||
|
{
|
||||||
|
cmAiRC_t rc = kOkAiRC;
|
||||||
|
const cmChar_t* sysJsFn = NULL;
|
||||||
|
cmJsonH_t jsH = cmJsonNullHandle;
|
||||||
|
cmJsonNode_t* audDspNodePtr = NULL;
|
||||||
|
//const cmChar_t* errLabelPtr = NULL;
|
||||||
|
|
||||||
|
// form the audio dsp resource file name
|
||||||
|
if((sysJsFn = cmFsMakeFn( cmFsPrefsDir(),cmAudDspSys_FILENAME,NULL,NULL)) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kFileSysFailAiRC,"Unable to form the audio dsp system resource file name.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the audio dsp resource file
|
||||||
|
if(cmJsonInitializeFromFile(&jsH,sysJsFn,ctx) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailAiRC,"Unable to open the audio dsp resource file: '%s'.",cmStringNullGuard(sysJsFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// locate the aud_dsp container object
|
||||||
|
if( cmJsonNodeMember( cmJsonRoot(jsH), "aud_dsp", &audDspNodePtr ) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailAiRC,"The audio DSP system resource file '%s' does not contain an 'aud_dsp' object.",cmStringNullGuard(sysJsFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// locate the read the aud_dsp sub-elements
|
||||||
|
if( cmJsonMemberValues( audDspNodePtr, &errLabelPtr,
|
||||||
|
"msgQueueByteCnt", kIntTId, &p->msgQueueByteCnt,
|
||||||
|
NULL ) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailAiRC,"Syntax error while parsing the top level fields in the audio DSP system resource file:%s.",cmStringNullGuard(sysJsFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( cmJsonFinalize(&jsH) != kOkJsRC )
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON finalization failed.");
|
||||||
|
|
||||||
|
if( sysJsFn != NULL )
|
||||||
|
cmFsFreeFn(sysJsFn);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmAiRC_t _cmAdIfSendIntMsg(cmAiH_t h, unsigned selId, unsigned asSubIdx, unsigned flags, unsigned iv, double dv )
|
||||||
|
{
|
||||||
|
cmAi_t* p = _cmAiHandleToPtr( h );
|
||||||
|
cmDspValue_t v;
|
||||||
|
|
||||||
|
if(iv == cmInvalidIdx )
|
||||||
|
cmDsvSetDouble(&v,dv);
|
||||||
|
else
|
||||||
|
cmDsvSetUInt(&v,iv);
|
||||||
|
|
||||||
|
if( cmMsgSend(&p->err,asSubIdx,kUiSelAsId,selId,flags,cmInvalidId,cmInvalidId,&v,p->parms.audDspFunc,p->parms.audDspFuncDataPtr) != kOkMsgRC )
|
||||||
|
return cmErrMsg(&p->err,kSendFailAiRC,"The integer message sel id:%i value:%i transmission failed.",selId,iv);
|
||||||
|
|
||||||
|
return kOkAiRC;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAiRC_t _cmAdIfFree( cmAi_t* p )
|
||||||
|
{
|
||||||
|
cmAiRC_t rc = kOkAiRC;
|
||||||
|
|
||||||
|
if( cmJsonFinalize(&p->jsH) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON finalization failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemFree(p);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfAllocate( cmCtx_t* ctx, cmAiH_t* hp, const cmAdIfParm_t* parms )
|
||||||
|
{
|
||||||
|
cmAiRC_t rc;
|
||||||
|
if((rc = cmAdIfFree(hp)) != kOkAiRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmAi_t* p = cmMemAllocZ(cmAi_t,1);
|
||||||
|
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"Audio DSP Interface");
|
||||||
|
|
||||||
|
p->parms = *parms;
|
||||||
|
|
||||||
|
// read the system configuration file
|
||||||
|
if((rc = _cmAdIfReadCfgFile(p, ctx )) != kOkAiRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
|
||||||
|
// initialize a JSON tree for use in deserializing JSON messages
|
||||||
|
if((rc = cmJsonInitialize( &p->jsH, ctx )) != kOkJsRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON initialization failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( rc != kOkAiRC )
|
||||||
|
_cmAdIfFree(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfFree( cmAiH_t* hp )
|
||||||
|
{
|
||||||
|
cmAiRC_t rc = kOkAiRC;
|
||||||
|
|
||||||
|
if( hp==NULL || cmAdIfIsValid(*hp)==false )
|
||||||
|
return kOkAiRC;
|
||||||
|
|
||||||
|
cmAi_t* p = _cmAiHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if((rc = _cmAdIfFree(p)) != kOkAiRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hp->h = NULL;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmAdIfIsValid( cmAiH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfRecvAudDspMsg( cmAiH_t h, unsigned msgByteCnt, const void* msg )
|
||||||
|
{
|
||||||
|
cmAi_t* p = _cmAiHandleToPtr(h);
|
||||||
|
cmAiRC_t rc = kOkAiRC;
|
||||||
|
|
||||||
|
_cmAiDispatchMsgToClient(p,msgByteCnt,msg);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfDeviceReport( cmAiH_t h )
|
||||||
|
{ return _cmAdIfSendIntMsg(h,kDevReportDuiId,cmInvalidIdx,0,cmInvalidIdx,0.0); }
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfSetAudioSysCfg( cmAiH_t h, unsigned asCfgIdx )
|
||||||
|
{ return _cmAdIfSendIntMsg(h,kSetAudioCfgDuiId,cmInvalidIdx,0,asCfgIdx,0.0); }
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfSetAudioDevice( cmAiH_t h, unsigned asSubIdx, bool inputFl, unsigned devIdx )
|
||||||
|
{ return _cmAdIfSendIntMsg(h,kSetAudioDevDuiId,asSubIdx,inputFl,devIdx,0.0); }
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfSetSampleRate( cmAiH_t h, unsigned asSubIdx, double srate )
|
||||||
|
{ return _cmAdIfSendIntMsg(h,kSetSampleRateDuiId,asSubIdx,0,cmInvalidIdx,srate); }
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfLoadProgram( cmAiH_t h, unsigned asSubIdx, unsigned pgmIdx )
|
||||||
|
{ return _cmAdIfSendIntMsg(h,kSetPgmDuiId,asSubIdx,0,pgmIdx,0.0); }
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfEnableAudio( cmAiH_t h, bool enableFl )
|
||||||
|
{ return _cmAdIfSendIntMsg(h,kEnableDuiId,cmInvalidIdx,enableFl,cmInvalidIdx,0.0); }
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfEnableStatusNotify( cmAiH_t h, bool enableFl )
|
||||||
|
{ return _cmAdIfSendIntMsg(h,kSetNotifyEnableDuiId,cmInvalidIdx,enableFl,cmInvalidIdx,0.0); }
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfSendMsgToAudioDSP(
|
||||||
|
cmAiH_t h,
|
||||||
|
unsigned asSubIdx,
|
||||||
|
unsigned msgTypeId,
|
||||||
|
unsigned selId,
|
||||||
|
unsigned flags,
|
||||||
|
unsigned instId,
|
||||||
|
unsigned instVarId,
|
||||||
|
const cmDspValue_t* valPtr )
|
||||||
|
{
|
||||||
|
cmAiRC_t rc = kOkAiRC;
|
||||||
|
cmAi_t* p = _cmAiHandleToPtr(h);
|
||||||
|
|
||||||
|
if( cmMsgSend( &p->err, asSubIdx, msgTypeId,selId,flags,instId,instVarId,valPtr, p->parms.audDspFunc, p->parms.audDspFuncDataPtr ) != kOkMsgRC )
|
||||||
|
rc = cmErrMsg(&p->err, kSendFailAiRC, "A UI message intened for the the audio DSP system was not successfully delivered.");
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfDispatchMsgToHost( cmAiH_t h )
|
||||||
|
{ return _cmAdIfSendIntMsg(h,kClientMsgPollDuiId,cmInvalidIdx,0,cmInvalidIdx,0.0); }
|
||||||
|
|
167
cmAudDspIF.h
Normal file
167
cmAudDspIF.h
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
#ifndef cmAudDspIF_h
|
||||||
|
#define cmAudDspIF_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This API has two basic responsibilities:
|
||||||
|
//
|
||||||
|
// 1) Provides a function based interface to the audio DSP system for the
|
||||||
|
// client application.
|
||||||
|
// The client calls these API functions to send commands to the audio DSP
|
||||||
|
// system. Internally the cmAdIfxxx functions converts the commands to
|
||||||
|
// raw message packets and passes them to a transmission service
|
||||||
|
// via cmAdIfParm_t audioDspFunc().
|
||||||
|
//
|
||||||
|
// 2) Acts as the receiver of raw message streams from whatever external
|
||||||
|
// service (e.g. cmAudDspLocal, cmAudDspUdp) is receiving raw message packets
|
||||||
|
// from audio DSP system.
|
||||||
|
//
|
||||||
|
// This process is driven by periodic calls from the client to
|
||||||
|
// cmAdIfDispatchMsgToHost().
|
||||||
|
// cmAdIfDispatchMsgToHost() then generates an internal
|
||||||
|
// 'kClientMsgPollDuiId' msg which is passed toward the
|
||||||
|
// cmAudDsp system.
|
||||||
|
// When the msg encounters a sub-system with queued msgs waiting
|
||||||
|
// for the client a callback chain ensues which eventually
|
||||||
|
// calls cmAdIfRecvAudDspMsg() which in turn calls the appropriate
|
||||||
|
// client provided cmAdIfDispatch_t function (ssInitFunc,statusFunc or uiFunc).
|
||||||
|
// Note that this entire chain of calls occurs in the client thread
|
||||||
|
// and in the context of the cmAdIfDispatchMsgToHost() procedure.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAiRC = cmOkRC,
|
||||||
|
kAudDspFailAiRC,
|
||||||
|
kLHeapFailAiRC,
|
||||||
|
kUnknownMsgTypeAiRC,
|
||||||
|
kMsgCorruptAiRC,
|
||||||
|
kSendFailAiRC,
|
||||||
|
kQueueFailAiRC,
|
||||||
|
kNoMsgAiRC,
|
||||||
|
kJsonFailAiRC,
|
||||||
|
kDeserialFailAiRC,
|
||||||
|
kFileSysFailAiRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmAiRC_t;
|
||||||
|
|
||||||
|
typedef cmHandle_t cmAiH_t;
|
||||||
|
|
||||||
|
// These functions are provided by the client to receive messages
|
||||||
|
// from the audio DSP sytem. These functions are only called from the client thread
|
||||||
|
// from within cmAdIfDispatchMsgToHost().
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
void* cbDataPtr; // data to send as the first arg. to app. callbacks
|
||||||
|
|
||||||
|
cmRC_t (*ssInitFunc)( void* cbDataPtr, const cmAudioSysSsInitMsg_t* r, const char* iDevLabel, const char* oDevLabel );
|
||||||
|
cmRC_t (*statusFunc)( void* cbDataPtr, const cmAudioSysStatus_t* r, const double* iMeterArray, const double* oMeterArray );
|
||||||
|
cmRC_t (*uiFunc)( void* cbDataPtr, const cmDspUiHdr_t* r );
|
||||||
|
} cmAdIfDispatch_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmAdIfDispatch_t dispatchRecd; // client application callback pointers
|
||||||
|
cmMsgSendFuncPtr_t audDspFunc; // the cmAdIfXXX functions use the callback to send msgs to the audio DSP system.
|
||||||
|
void* audDspFuncDataPtr; // data to send with the audio DSP callback function
|
||||||
|
} cmAdIfParm_t;
|
||||||
|
|
||||||
|
|
||||||
|
extern cmAiH_t cmAiNullHandle;
|
||||||
|
|
||||||
|
cmAiRC_t cmAdIfAllocate( cmCtx_t* ctx, cmAiH_t* hp, const cmAdIfParm_t* parms );
|
||||||
|
cmAiRC_t cmAdIfFree( cmAiH_t* hp );
|
||||||
|
|
||||||
|
bool cmAdIfIsValid( cmAiH_t h );
|
||||||
|
|
||||||
|
// Receive a msg from the audio DSP system. This is the main point of entry
|
||||||
|
// for all calls from the audio DSP system to the client.
|
||||||
|
// This function is provided as a callback to the owner of this cmAudDspIF
|
||||||
|
// (e.g. cmAudDspLocal, cmAudDspUdpClient) it should never need to be called
|
||||||
|
// by the client.
|
||||||
|
cmAiRC_t cmAdIfRecvAudDspMsg( cmAiH_t h, unsigned msgByteCnt, const void* msgDataPtr);
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// The functions below are used to send commands to the audio DSP system
|
||||||
|
// from the client application.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Print a hardware report.
|
||||||
|
cmAiRC_t cmAdIfDeviceReport( cmAiH_t h );
|
||||||
|
|
||||||
|
// Select a audio system configuration. This must be done prior to
|
||||||
|
// sending any other commands.
|
||||||
|
cmAiRC_t cmAdIfSetAudioSysCfg( cmAiH_t h, unsigned asCfgIdx );
|
||||||
|
|
||||||
|
// Select an audio input or output device for a given audio sub-system.
|
||||||
|
// An audio configuration must have been selected via cmAdIfSetAudioSysCfg()
|
||||||
|
// prior to calling this function.
|
||||||
|
cmAiRC_t cmAdIfSetAudioDevice( cmAiH_t h, unsigned asSubIdx, bool inputFl, unsigned devIdx );
|
||||||
|
|
||||||
|
// Set the sample rate for a given audio sub-system or the entire audio system.
|
||||||
|
// Set asSubIdx to cmInvalidIdx to assign the sample rate to all sub-systems
|
||||||
|
// of the current audio system configuration.
|
||||||
|
// An audio configuration must have been selected via cmAdIfSetAudioSysCfg()
|
||||||
|
// prior to calling this function.
|
||||||
|
cmAiRC_t cmAdIfSetSampleRate( cmAiH_t h, unsigned asSubIdx, double srate );
|
||||||
|
|
||||||
|
// Select a DSP program for a given audio sub-system or the entire audio system.
|
||||||
|
// Set asSubIdx to cmInvalidIdx to load the program on all sub-systems
|
||||||
|
// of the current audio system configuration.
|
||||||
|
// An audio configuration must have been selected via cmAdIfSetAudioSysCfg()
|
||||||
|
// prior to calling this function.
|
||||||
|
cmAiRC_t cmAdIfLoadProgram( cmAiH_t h, unsigned asSubIdx, unsigned pgmIdx );
|
||||||
|
|
||||||
|
// Start the audio streaming.
|
||||||
|
// An audio configuration must have been selected via cmAdIfSetAudioSysCfg()
|
||||||
|
// and a DSP program must have been selected via cmAdIfLoadProgram()
|
||||||
|
// prior to calling this function.
|
||||||
|
cmAiRC_t cmAdIfEnableAudio( cmAiH_t h, bool enableFl );
|
||||||
|
|
||||||
|
// Enable/disable periodic audio system status notifications.
|
||||||
|
cmAiRC_t cmAdIfEnableStatusNotify( cmAiH_t h, bool enableFl );
|
||||||
|
|
||||||
|
// Send a kUiSelAsId style message to the audio DSP system.
|
||||||
|
cmAiRC_t cmAdIfSendMsgToAudioDSP(
|
||||||
|
cmAiH_t h,
|
||||||
|
unsigned asSubIdx,
|
||||||
|
unsigned msgTypeId,
|
||||||
|
unsigned selId,
|
||||||
|
unsigned flags,
|
||||||
|
unsigned instId,
|
||||||
|
unsigned instVarId,
|
||||||
|
const cmDspValue_t* valPtr );
|
||||||
|
|
||||||
|
// The client application must periodically call this function to
|
||||||
|
// receive pending messages from the audio DSP system. The
|
||||||
|
// messages are delivered via callbacks provided by cmAdIfDispatch_t.
|
||||||
|
// This function should only be called from the client thread.
|
||||||
|
cmAiRC_t cmAdIfDispatchMsgToHost( cmAiH_t h );
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local call chain:
|
||||||
|
cmAdIfDispatchMsgToHost() -> p->parms.audDspFunc = cmAudDspLocal::_cmAdlAudDspSendFunc()
|
||||||
|
-> cmAudioDsp::cmAudDspReceiveClientMsg()
|
||||||
|
-> cmAudioDsp::_cmAudDspClientMsgPoll()
|
||||||
|
-> cmAudioSys::cmAudioSysReceiveMsg()
|
||||||
|
-> cmThread::cmTs1p1cDequeueMsg()
|
||||||
|
-> cmAudioSysCfg_t::clientCbFunc = cmAudDsp::_cmAudioSysToClientCallback()
|
||||||
|
-> cmAudDsp::cmAd_t::cbFunc = cmAudDspLocal::_cmAudDspLocalCallback()
|
||||||
|
-> cmAudDspIF::cmAdIfRecvAudDspMsg()
|
||||||
|
-> cmAudDspIF::_cmAiDispatchMsgToClient()
|
||||||
|
-> cmAudDspIF::cmAdIfDispatch_t.uiFunc = kcApp::_s_handleUiMsg()
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
147
cmAudDspLocal.c
Normal file
147
cmAudDspLocal.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmJson.h"
|
||||||
|
#include "dsp/cmDspValue.h"
|
||||||
|
#include "cmMsgProtocol.h"
|
||||||
|
#include "cmAudDsp.h"
|
||||||
|
#include "cmAudDspIF.h"
|
||||||
|
#include "cmAudDspLocal.h"
|
||||||
|
|
||||||
|
|
||||||
|
cmAdlH_t cmAdlNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmAiH_t aiH;
|
||||||
|
cmAdH_t adH;
|
||||||
|
} cmAdl_t;
|
||||||
|
|
||||||
|
cmAdl_t* _cmAdlHandleToPtr( cmAdlH_t h )
|
||||||
|
{
|
||||||
|
cmAdl_t* p = (cmAdl_t*)h.h;
|
||||||
|
assert( p != NULL );
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward messages coming from the audio DSP system to the audio DSP IF
|
||||||
|
// for later dispatch to the client application.
|
||||||
|
cmMsgRC_t _cmAudDspLocalCallback(void* cbDataPtr, unsigned msgByteCnt, const void* msg )
|
||||||
|
{
|
||||||
|
cmMsgRC_t rc = kOkMsgRC;
|
||||||
|
cmAdl_t* p = (cmAdl_t*)cbDataPtr;
|
||||||
|
|
||||||
|
if( cmAdIfRecvAudDspMsg(p->aiH, msgByteCnt, msg ) != kOkAiRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kAudDspIfFailAdlRC,"Message transmission to the audio DSP interface failed.");
|
||||||
|
rc = kSendFailMsgRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward messages from the audio DSP interface to the audio DSP system.
|
||||||
|
cmMsgRC_t _cmAdlAudDspSendFunc( void* cbDataPtr, unsigned msgByteCnt, const void* msg )
|
||||||
|
{
|
||||||
|
cmMsgRC_t rc = kOkMsgRC;
|
||||||
|
cmAdl_t* p = (cmAdl_t*)cbDataPtr;
|
||||||
|
|
||||||
|
if( cmAudDspReceiveClientMsg( p->adH, msgByteCnt, msg ) != kOkAdRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kAudDspFailAdlRC,"Message transmission the audio DSP system failed.");
|
||||||
|
rc = kSendFailMsgRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAdlRC_t _cmAudDspLocalFree( cmAdl_t* p )
|
||||||
|
{
|
||||||
|
cmAdlRC_t rc = kOkAdlRC;
|
||||||
|
|
||||||
|
if( cmAdIfFree(&p->aiH) != kOkAiRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAudDspIfFailAdlRC,"The audio DSP interface release failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmAudDspFree(&p->adH) != kOkAdRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAudDspFailAdlRC,"The audio DSP release failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemFree(p);
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cmAdlRC_t cmAudDspLocalAllocate( cmCtx_t* ctx, cmAdlH_t* hp, const cmAdIfDispatch_t* recd )
|
||||||
|
{
|
||||||
|
cmAdlRC_t rc;
|
||||||
|
if((rc = cmAudDspLocalFree(hp)) != kOkAdlRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmAdl_t* p = cmMemAllocZ(cmAdl_t,1);
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"Audio DSP Local");
|
||||||
|
|
||||||
|
cmAdIfParm_t parms;
|
||||||
|
parms.dispatchRecd = *recd;
|
||||||
|
parms.audDspFunc = _cmAdlAudDspSendFunc;
|
||||||
|
parms.audDspFuncDataPtr = p;
|
||||||
|
|
||||||
|
if( cmAdIfAllocate(ctx, &p->aiH, &parms ) != kOkAiRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAudDspIfFailAdlRC,"The audio DSP interface system allocation failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmAudDspAlloc(ctx, &p->adH, _cmAudDspLocalCallback, p ) != kOkAdRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAudDspFailAdlRC,"The audio DSP system allocation failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( rc != kOkAdlRC )
|
||||||
|
_cmAudDspLocalFree(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAdlRC_t cmAudDspLocalFree( cmAdlH_t* hp )
|
||||||
|
{
|
||||||
|
cmAdlRC_t rc = kOkAdlRC;
|
||||||
|
|
||||||
|
if( hp == NULL || cmAudDspLocalIsValid(*hp) == false )
|
||||||
|
return kOkAdlRC;
|
||||||
|
|
||||||
|
cmAdl_t* p = _cmAdlHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if((rc = _cmAudDspLocalFree(p)) != kOkAdlRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hp->h = NULL;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmAudDspLocalIsValid( cmAdlH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
cmAiH_t cmAudDspLocalIF_Handle( cmAdlH_t h )
|
||||||
|
{
|
||||||
|
cmAdl_t* p = _cmAdlHandleToPtr(h);
|
||||||
|
return p->aiH;
|
||||||
|
}
|
||||||
|
|
38
cmAudDspLocal.h
Normal file
38
cmAudDspLocal.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef cmAudDspLocal_h
|
||||||
|
#define cmAudDspLocal_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAdlRC = cmOkRC,
|
||||||
|
kAudDspIfFailAdlRC,
|
||||||
|
kAudDspFailAdlRC,
|
||||||
|
kFileSysFailAdlRC,
|
||||||
|
kJsonFailAdlRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmAdlRC_t;
|
||||||
|
typedef cmHandle_t cmAdlH_t;
|
||||||
|
|
||||||
|
extern cmAdlH_t cmAdlNullHandle;
|
||||||
|
|
||||||
|
cmAdlRC_t cmAudDspLocalAllocate(
|
||||||
|
cmCtx_t* ctx,
|
||||||
|
cmAdlH_t* hp,
|
||||||
|
const cmAdIfDispatch_t* recd );
|
||||||
|
|
||||||
|
cmAdlRC_t cmAudDspLocalFree( cmAdlH_t* hp );
|
||||||
|
|
||||||
|
bool cmAudDspLocalIsValid( cmAdlH_t h );
|
||||||
|
|
||||||
|
cmAiH_t cmAudDspLocalIF_Handle( cmAdlH_t h );
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
316
cmAudLabelFile.c
Normal file
316
cmAudLabelFile.c
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmLinkedHeap.h"
|
||||||
|
#include "cmFile.h"
|
||||||
|
#include "cmAudLabelFile.h"
|
||||||
|
|
||||||
|
cmAlfH_t cmAlfNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
typedef struct cmAlfRecd_str
|
||||||
|
{
|
||||||
|
cmAlfLabel_t r;
|
||||||
|
struct cmAlfRecd_str* link;
|
||||||
|
} cmAlfRecd_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmLHeapH_t lH;
|
||||||
|
cmFileH_t fH;
|
||||||
|
cmAlfRecd_t* list;
|
||||||
|
int cnt;
|
||||||
|
} cmAlf_t;
|
||||||
|
|
||||||
|
cmAlf_t* _cmAlfHandleToPtr( cmAlfH_t h )
|
||||||
|
{
|
||||||
|
cmAlf_t* p = (cmAlf_t*)h.h;
|
||||||
|
assert( p != NULL );
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAlfRC_t _cmAlfFree( cmAlf_t* p )
|
||||||
|
{
|
||||||
|
cmAlfRC_t rc = kOkAlfRC;
|
||||||
|
|
||||||
|
cmLHeapDestroy(&p->lH);
|
||||||
|
|
||||||
|
cmMemPtrFree(&p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileAlloc( cmCtx_t* ctx, cmAlfH_t* hp )
|
||||||
|
{
|
||||||
|
cmAlfRC_t rc;
|
||||||
|
if((rc = cmAudLabelFileFree(hp)) != kOkAlfRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmAlf_t* p = cmMemAllocZ(cmAlf_t,1);
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"Audio Label File");
|
||||||
|
|
||||||
|
if(!cmLHeapIsValid( p->lH = cmLHeapCreate(1024,ctx)))
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kLHeapFailAlfRC,"Linked heap create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileAllocOpen( cmCtx_t* ctx, cmAlfH_t* hp, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmAlfRC_t rc;
|
||||||
|
if((rc = cmAudLabelFileAlloc(ctx,hp)) != kOkAlfRC)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return cmAudLabelFileOpen(*hp,fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileFree( cmAlfH_t* hp )
|
||||||
|
{
|
||||||
|
cmAlfRC_t rc = kOkAlfRC;
|
||||||
|
|
||||||
|
if( hp == NULL || cmAudLabelFileIsValid(*hp)==false )
|
||||||
|
return kOkAlfRC;
|
||||||
|
|
||||||
|
cmAlf_t* p = _cmAlfHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if((rc = _cmAlfFree(p)) != kOkAlfRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmAudLabelFileIsValid( cmAlfH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
void _cmAlfInsert( cmAlf_t* p, cmReal_t begSecs, cmReal_t endSecs, const cmChar_t* label )
|
||||||
|
{
|
||||||
|
cmAlfRecd_t* np = p->list;
|
||||||
|
cmAlfRecd_t* pp = NULL;
|
||||||
|
cmAlfRecd_t* ip = cmLhAllocZ(p->lH,cmAlfRecd_t,1);
|
||||||
|
|
||||||
|
ip->r.begSecs = begSecs;
|
||||||
|
ip->r.endSecs = endSecs;
|
||||||
|
ip->r.label = label==NULL || strlen(label)==0 ? NULL : cmLhAllocStr(p->lH,label);
|
||||||
|
|
||||||
|
// set np to the next recd and
|
||||||
|
// set pp to the prev recd
|
||||||
|
while(np != NULL )
|
||||||
|
{
|
||||||
|
if( np->r.begSecs > begSecs )
|
||||||
|
break;
|
||||||
|
|
||||||
|
pp = np;
|
||||||
|
np = np->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip->link = np;
|
||||||
|
|
||||||
|
// if the new recd is first on the list
|
||||||
|
if( pp == NULL )
|
||||||
|
p->list = ip;
|
||||||
|
else
|
||||||
|
pp->link = ip;
|
||||||
|
|
||||||
|
// incr the recd count
|
||||||
|
++p->cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the record just after pp
|
||||||
|
void _cmAlfRemove( cmAlf_t* p, cmAlfRecd_t* pp )
|
||||||
|
{
|
||||||
|
// if the list is already empty
|
||||||
|
if( p->list == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if the first recd should be removed
|
||||||
|
if( pp == NULL )
|
||||||
|
{
|
||||||
|
p->list = p->list->link;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if pp points to the last recd
|
||||||
|
if( pp->link == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// remove pp->link from the list
|
||||||
|
pp->link = pp->link->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( p->cnt != 0 );
|
||||||
|
--p->cnt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileOpen( cmAlfH_t h, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmAlfRC_t rc;
|
||||||
|
cmAlf_t* p = _cmAlfHandleToPtr(h);
|
||||||
|
cmChar_t* lineBuf = NULL;
|
||||||
|
unsigned lineBufByteCnt = 0;
|
||||||
|
unsigned line = 1;
|
||||||
|
cmFileH_t fH = cmFileNullHandle;
|
||||||
|
|
||||||
|
// open the label file
|
||||||
|
if( cmFileOpen(&fH,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label file '%s' could not be openend.",cmStringNullGuard(fn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read each line
|
||||||
|
while( cmFileGetLineAuto(fH,&lineBuf,&lineBufByteCnt) == kOkFileRC )
|
||||||
|
{
|
||||||
|
cmReal_t begSecs;
|
||||||
|
cmReal_t endSecs;
|
||||||
|
cmChar_t* label = NULL;
|
||||||
|
cmChar_t* begPtr = lineBuf;
|
||||||
|
cmChar_t* endPtr = NULL;
|
||||||
|
|
||||||
|
// parse the start time in seconds
|
||||||
|
errno = 0;
|
||||||
|
begSecs = strtod(begPtr,&endPtr);
|
||||||
|
if( errno != 0 )
|
||||||
|
return cmErrMsg(&p->err,kSyntaxErrAlfRC,"Begin time conversion error on line %i in '%s'.",line,cmFileName(fH));
|
||||||
|
|
||||||
|
// parse the end time in seconds
|
||||||
|
begPtr = endPtr;
|
||||||
|
endSecs = strtod(begPtr,&endPtr);
|
||||||
|
if( errno != 0 )
|
||||||
|
return cmErrMsg(&p->err,kSyntaxErrAlfRC,"End time conversion error on line %i in '%s'.",line,cmFileName(fH));
|
||||||
|
|
||||||
|
label = endPtr;
|
||||||
|
|
||||||
|
// eat any leading white space off the label
|
||||||
|
while( *label )
|
||||||
|
{
|
||||||
|
if( isspace(*label) )
|
||||||
|
++label;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim trailing space and '\n' from the label.
|
||||||
|
int i = strlen(label)-1;
|
||||||
|
for(; i>=0; --i)
|
||||||
|
{
|
||||||
|
if( isspace(label[i]) )
|
||||||
|
label[i]=0;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the label does not exist
|
||||||
|
if( strlen(label)==0 )
|
||||||
|
label = NULL;
|
||||||
|
|
||||||
|
// insert a new recd
|
||||||
|
_cmAlfInsert(p,begSecs,endSecs,label);
|
||||||
|
|
||||||
|
++line;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemPtrFree(&lineBuf);
|
||||||
|
|
||||||
|
if( cmFileClose(&fH) != kOkFileRC )
|
||||||
|
rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label file close failed.");
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileInsert( cmAlfH_t h, cmReal_t begSecs, cmReal_t endSecs, const cmChar_t* label )
|
||||||
|
{
|
||||||
|
cmAlfRC_t rc = kOkAlfRC;
|
||||||
|
cmAlf_t* p = _cmAlfHandleToPtr(h);
|
||||||
|
_cmAlfInsert(p,begSecs,endSecs,label);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmAudLabelFileCount( cmAlfH_t h )
|
||||||
|
{
|
||||||
|
cmAlf_t* p = _cmAlfHandleToPtr(h);
|
||||||
|
return p->cnt;
|
||||||
|
}
|
||||||
|
const cmAlfLabel_t* cmAudLabelFileLabel( cmAlfH_t h, unsigned idx )
|
||||||
|
{
|
||||||
|
cmAlf_t* p = _cmAlfHandleToPtr(h);
|
||||||
|
cmAlfRecd_t* lp = p->list;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for(i=0; lp!=NULL && i<idx; ++i)
|
||||||
|
lp = lp->link;
|
||||||
|
|
||||||
|
return &lp->r;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileWrite( cmAlfH_t h, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmAlfRC_t rc = kOkAlfRC;
|
||||||
|
cmAlf_t* p = _cmAlfHandleToPtr(h);
|
||||||
|
cmAlfRecd_t* lp = p->list;
|
||||||
|
cmFileH_t fH = cmFileNullHandle;
|
||||||
|
|
||||||
|
if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label output file '%s' could not be created.",cmStringNullGuard(fn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; lp!=NULL; lp=lp->link)
|
||||||
|
{
|
||||||
|
if( cmFilePrintf(fH,"%f %f %s",lp->r.begSecs,lp->r.endSecs,lp->r.label == NULL ? "" : lp->r.label) != kOkFileRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label output file write failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( cmFileClose(&fH) != kOkFileRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label output file '%s' close failed.",cmStringNullGuard(fn));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmAudLabelFileTest( cmCtx_t* ctx )
|
||||||
|
{
|
||||||
|
const cmChar_t* fn = "/home/kevin/temp/labels.txt";
|
||||||
|
const cmChar_t* ofn = "/home/kevin/temp/labels_out.txt";
|
||||||
|
cmAlfH_t h = cmAlfNullHandle;
|
||||||
|
|
||||||
|
if( cmAudLabelFileAllocOpen(ctx,&h,fn) == kOkAlfRC )
|
||||||
|
{
|
||||||
|
unsigned n = cmAudLabelFileCount(h);
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<n; ++i)
|
||||||
|
{
|
||||||
|
const cmAlfLabel_t* lp;
|
||||||
|
if((lp = cmAudLabelFileLabel(h,i)) != NULL )
|
||||||
|
cmRptPrintf(&ctx->rpt,"%f %f %s\n",lp->begSecs,lp->endSecs,lp->label);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAudLabelFileWrite(h,ofn);
|
||||||
|
|
||||||
|
cmAudLabelFileFree(&h);
|
||||||
|
}
|
||||||
|
}
|
50
cmAudLabelFile.h
Normal file
50
cmAudLabelFile.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef cmAudLabelFile_h
|
||||||
|
#define cmAudLabelFile_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAlfRC = cmOkRC,
|
||||||
|
kLHeapFailAlfRC,
|
||||||
|
kFileFailAlfRC,
|
||||||
|
kSyntaxErrAlfRC,
|
||||||
|
kAlfFileFailPuRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmAlfRC_t;
|
||||||
|
typedef cmHandle_t cmAlfH_t;
|
||||||
|
|
||||||
|
extern cmAlfH_t cmAlfNullHandle;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmReal_t begSecs;
|
||||||
|
cmReal_t endSecs;
|
||||||
|
const cmChar_t* label;
|
||||||
|
} cmAlfLabel_t;
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileAlloc( cmCtx_t* ctx, cmAlfH_t* hp );
|
||||||
|
cmAlfRC_t cmAudLabelFileAllocOpen( cmCtx_t* ctx, cmAlfH_t* hp, const cmChar_t* fn );
|
||||||
|
cmAlfRC_t cmAudLabelFileFree( cmAlfH_t* hp );
|
||||||
|
|
||||||
|
bool cmAudLabelFileIsValid( cmAlfH_t h );
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileOpen( cmAlfH_t h, const cmChar_t* fn );
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileInsert( cmAlfH_t h, cmReal_t begSecs, cmReal_t endSecs, const cmChar_t* label );
|
||||||
|
|
||||||
|
unsigned cmAudLabelFileCount( cmAlfH_t h );
|
||||||
|
const cmAlfLabel_t* cmAudLabelFileLabel( cmAlfH_t h, unsigned idx );
|
||||||
|
|
||||||
|
cmAlfRC_t cmAudLabelFileWrite( cmAlfH_t h, const cmChar_t* fn );
|
||||||
|
|
||||||
|
void cmAudLabelFileTest( cmCtx_t* ctx );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
942
cmAudioAggDev.c
Normal file
942
cmAudioAggDev.c
Normal file
@ -0,0 +1,942 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmAudioPort.h"
|
||||||
|
#include "cmAudioAggDev.h"
|
||||||
|
#include "cmThread.h" // cmThUIntIncr()
|
||||||
|
|
||||||
|
#include "cmApBuf.h" // only needed for cmApBufTest().
|
||||||
|
|
||||||
|
//#include <unistd.h> // usleep
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kBufArrayCnt = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cmApAgg_str;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned physDevIdx;
|
||||||
|
struct cmApAgg_str* ap;
|
||||||
|
|
||||||
|
unsigned iChIdx;
|
||||||
|
unsigned iChCnt;
|
||||||
|
|
||||||
|
unsigned oChIdx;
|
||||||
|
unsigned oChCnt;
|
||||||
|
} cmApAggDev_t;
|
||||||
|
|
||||||
|
typedef struct cmApAgg_str
|
||||||
|
{
|
||||||
|
cmChar_t* label; // agg. device label
|
||||||
|
unsigned aggDevIdx; // agg. device index
|
||||||
|
unsigned sysDevIdx; // system device index
|
||||||
|
unsigned devCnt; // count of phys devices
|
||||||
|
cmApAggDev_t* devArray; // devArray[ devCnt ] - physical device array
|
||||||
|
unsigned iChCnt; // sum of phys device input channels
|
||||||
|
unsigned oChCnt; // sum of phys device output channels
|
||||||
|
double srate; // agg. dev sample rate
|
||||||
|
unsigned framesPerCycle; // agg. dev frames per cycle
|
||||||
|
unsigned flags; // kAgInFl | kAgOutFl
|
||||||
|
cmApCallbackPtr_t cbFunc; // client supplied callback func
|
||||||
|
void* cbArg; // client supplied callback func arg.
|
||||||
|
bool startedFl; // true if the agg. device is started
|
||||||
|
struct cmApAgg_str* link; // _cmAg.list link
|
||||||
|
} cmApAgg_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmApAgg_t* list;
|
||||||
|
} cmApAggMain_t;
|
||||||
|
|
||||||
|
cmApAggMain_t _cmAg;
|
||||||
|
|
||||||
|
|
||||||
|
void _cmApAggCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
cmApAudioPacket_t pkt;
|
||||||
|
|
||||||
|
for(i=0; i<inPktCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApAggDev_t* dp = (cmApAggDev_t*)inPktArray[i].userCbPtr;
|
||||||
|
pkt = inPktArray[i];
|
||||||
|
pkt.devIdx = dp->ap->sysDevIdx;
|
||||||
|
pkt.begChIdx = dp->iChIdx;
|
||||||
|
pkt.userCbPtr = dp->ap->cbArg;
|
||||||
|
dp->ap->cbFunc( &pkt, 1, NULL, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0; i<outPktCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApAggDev_t* dp = (cmApAggDev_t*)outPktArray[i].userCbPtr;
|
||||||
|
pkt = outPktArray[i];
|
||||||
|
pkt.devIdx = dp->ap->sysDevIdx;
|
||||||
|
pkt.begChIdx = dp->oChIdx;
|
||||||
|
pkt.userCbPtr = dp->ap->cbArg;
|
||||||
|
dp->ap->cbFunc( NULL, 0, &pkt, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _cmApAgDeleteAggDev( cmApAgg_t* ap )
|
||||||
|
{
|
||||||
|
cmApAgg_t* cp = _cmAg.list;
|
||||||
|
cmApAgg_t* pp = NULL;
|
||||||
|
while( cp != NULL )
|
||||||
|
{
|
||||||
|
if( cp == ap )
|
||||||
|
{
|
||||||
|
if( pp == NULL )
|
||||||
|
_cmAg.list = cp->link;
|
||||||
|
else
|
||||||
|
pp->link = cp->link;
|
||||||
|
|
||||||
|
cmMemFree(ap->label);
|
||||||
|
cmMemFree(ap->devArray);
|
||||||
|
cmMemFree(ap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pp = cp;
|
||||||
|
cp = cp->link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggAllocate( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmAgRC_t rc = kOkAgRC;
|
||||||
|
|
||||||
|
cmErrSetup(&_cmAg.err,rpt,"cmAudioAggDev");
|
||||||
|
|
||||||
|
_cmAg.list = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggFree()
|
||||||
|
{
|
||||||
|
cmAgRC_t rc = kOkAgRC;
|
||||||
|
|
||||||
|
while( _cmAg.list != NULL )
|
||||||
|
_cmApAgDeleteAggDev(_cmAg.list );
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap = _cmAg.list;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
assert( baseApDevIdx == cmApDeviceCount() );
|
||||||
|
|
||||||
|
for(i=0; ap!=NULL; ap=ap->link,++i)
|
||||||
|
{
|
||||||
|
ap->sysDevIdx = cmApDeviceCount() + i;
|
||||||
|
ap->iChCnt = 0;
|
||||||
|
ap->oChCnt = 0;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<ap->devCnt; ++i)
|
||||||
|
{
|
||||||
|
ap->devArray[i].iChIdx = ap->iChCnt;
|
||||||
|
ap->devArray[i].oChIdx = ap->oChCnt;
|
||||||
|
ap->devArray[i].iChCnt = cmApDeviceChannelCount(ap->devArray[i].physDevIdx,true);
|
||||||
|
ap->devArray[i].oChCnt = cmApDeviceChannelCount(ap->devArray[i].physDevIdx,false);
|
||||||
|
ap->iChCnt += ap->devArray[i].iChCnt;
|
||||||
|
ap->oChCnt += ap->devArray[i].oChCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkAgRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggFinalize()
|
||||||
|
{ return kOkAgRC; }
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggCreateDevice(
|
||||||
|
const cmChar_t* label,
|
||||||
|
unsigned devCnt,
|
||||||
|
const unsigned physDevIdxArray[],
|
||||||
|
unsigned flags )
|
||||||
|
{
|
||||||
|
cmAgRC_t rc = kOkAgRC;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if( devCnt < 2 )
|
||||||
|
return cmErrMsg(&_cmAg.err,kMustAggTwoAgRC,"Cannot aggregate less than two devices.");
|
||||||
|
/*
|
||||||
|
|
||||||
|
for(i=0; i<devCnt; ++i)
|
||||||
|
{
|
||||||
|
unsigned physDevIdx = physDevIdxArray[i];
|
||||||
|
|
||||||
|
if( cmApAggIsDeviceAggregated(physDevIdx) )
|
||||||
|
return cmErrMsg(&_cmAg.err,kDevAlreadyAggAgRC,"The physical device associated with index '%i' ('%s') has already been assigned to another aggregated device.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
|
||||||
|
|
||||||
|
if( cmApDeviceIsStarted(physDevIdx) )
|
||||||
|
return cmErrMsg(&_cmAg.err,kCantUseStartedDevAgRC,"The physical device associated with index '%i' ('%s') cannot be aggregated while it is running.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
cmApAgg_t* ap = cmMemAllocZ(cmApAgg_t,1);
|
||||||
|
ap->label = cmMemAllocStr(label==NULL?"Aggregated Device":label);
|
||||||
|
ap->devArray = cmMemAllocZ(cmApAggDev_t,devCnt);
|
||||||
|
ap->aggDevIdx = cmApAggDeviceCount();
|
||||||
|
ap->sysDevIdx = cmInvalidIdx;
|
||||||
|
ap->devCnt = devCnt;
|
||||||
|
ap->iChCnt = 0;
|
||||||
|
ap->oChCnt = 0;
|
||||||
|
|
||||||
|
for(i=0; i<devCnt; ++i)
|
||||||
|
{
|
||||||
|
ap->devArray[i].ap = ap;
|
||||||
|
ap->devArray[i].physDevIdx = physDevIdxArray[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
ap->link = _cmAg.list;
|
||||||
|
_cmAg.list = ap;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApAgg_t* _cmApAggDevIdxToPtr( unsigned aggDevIdx )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap = _cmAg.list;
|
||||||
|
unsigned i = 0;
|
||||||
|
for(; ap!=NULL; ap=ap->link,++i)
|
||||||
|
if( ap->aggDevIdx == aggDevIdx )
|
||||||
|
return ap;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t _cmApAggGetAgg( unsigned aggDevIdx, cmApAgg_t** retPtrPtr )
|
||||||
|
{
|
||||||
|
if((*retPtrPtr = _cmApAggDevIdxToPtr(aggDevIdx)) == NULL )
|
||||||
|
return cmErrMsg(&_cmAg.err,kInvalidDevIdxAgRC,"The aggregate system device index '%i' is invalid.");
|
||||||
|
return kOkAgRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool cmApAggIsDeviceAggregated( unsigned physDevIdx )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap = _cmAg.list;
|
||||||
|
for(; ap!=NULL; ap=ap->link)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<ap->devCnt; ++i)
|
||||||
|
if( ap->devArray[i].physDevIdx == physDevIdx )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggDeviceCount()
|
||||||
|
{
|
||||||
|
unsigned devCnt=0;
|
||||||
|
cmApAgg_t* ap = _cmAg.list;
|
||||||
|
for(; ap!=NULL; ap=ap->link)
|
||||||
|
++devCnt;
|
||||||
|
|
||||||
|
return devCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* cmApAggDeviceLabel( unsigned aggDevIdx )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap;
|
||||||
|
cmAgRC_t rc;
|
||||||
|
if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
|
||||||
|
return ap->label;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmApAggDeviceChannelCount( unsigned aggDevIdx, bool inputFl )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap;
|
||||||
|
cmAgRC_t rc;
|
||||||
|
if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
|
||||||
|
return inputFl ? ap->iChCnt : ap->oChCnt;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double cmApAggDeviceSampleRate( unsigned aggDevIdx )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap;
|
||||||
|
cmAgRC_t rc;
|
||||||
|
if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
|
||||||
|
return ap->srate;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmApAggDeviceFramesPerCycle( unsigned aggDevIdx, bool inputFl )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap;
|
||||||
|
cmAgRC_t rc;
|
||||||
|
if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
|
||||||
|
return ap->framesPerCycle;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggDeviceSetup(
|
||||||
|
unsigned aggDevIdx,
|
||||||
|
double srate,
|
||||||
|
unsigned framesPerCycle,
|
||||||
|
cmApCallbackPtr_t callbackPtr,
|
||||||
|
void* userCbPtr )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap;
|
||||||
|
cmAgRC_t rc;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if((rc = cmApAggDeviceStop(aggDevIdx)) != kOkAgRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
for(i=0; i<ap->devCnt; ++i)
|
||||||
|
{
|
||||||
|
unsigned physDevIdx = ap->devArray[i].physDevIdx;
|
||||||
|
cmApAggDev_t* devPtr = ap->devArray + i;
|
||||||
|
|
||||||
|
if( cmApDeviceSetup( physDevIdx, srate, framesPerCycle, _cmApAggCb, devPtr ) != kOkApRC )
|
||||||
|
rc = cmErrMsg(&_cmAg.err,kPhysDevSetupFailAgRC,"The physical device (index:%i '%s') setup failed for sample rate:%f frames-per-cycle:%i.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)),srate,framesPerCycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( rc == kOkAgRC )
|
||||||
|
{
|
||||||
|
ap->cbFunc = callbackPtr;
|
||||||
|
ap->cbArg = userCbPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggDeviceStart( unsigned aggDevIdx )
|
||||||
|
{
|
||||||
|
cmAgRC_t rc = kOkAgRC;
|
||||||
|
cmApAgg_t* ap;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
for(i=0; i<ap->devCnt; ++i)
|
||||||
|
{
|
||||||
|
unsigned physDevIdx = ap->devArray[i].physDevIdx;
|
||||||
|
|
||||||
|
if( cmApDeviceStart( physDevIdx ) != kOkApRC )
|
||||||
|
return cmErrMsg(&_cmAg.err,kPhysDevStartFailAgRC,"The physical device (index:%i '%s') start failed.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
|
||||||
|
|
||||||
|
//usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
ap->startedFl = true;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAgRC_t cmApAggDeviceStop( unsigned aggDevIdx )
|
||||||
|
{
|
||||||
|
cmAgRC_t rc = kOkAgRC;
|
||||||
|
cmApAgg_t* ap;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
for(i=0; i<ap->devCnt; ++i)
|
||||||
|
{
|
||||||
|
unsigned physDevIdx = ap->devArray[i].physDevIdx;
|
||||||
|
|
||||||
|
if( cmApDeviceStop( physDevIdx ) != kOkApRC )
|
||||||
|
return cmErrMsg(&_cmAg.err,kPhysDevStartFailAgRC,"The physical device (index:%i '%s') start failed.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ap->startedFl = false;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmApAggDeviceIsStarted( unsigned aggDevIdx )
|
||||||
|
{
|
||||||
|
cmApAgg_t* ap;
|
||||||
|
|
||||||
|
if(_cmApAggGetAgg(aggDevIdx, &ap ) != kOkAgRC )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ap->startedFl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
bool synthFl;
|
||||||
|
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
|
||||||
|
|
||||||
|
unsigned cbCnt; // count the callback
|
||||||
|
unsigned underunCnt; //
|
||||||
|
unsigned overunCnt;
|
||||||
|
|
||||||
|
double* iMeter; // iMeter[ chCnt ]
|
||||||
|
|
||||||
|
FILE* ifp;
|
||||||
|
FILE* ofp;
|
||||||
|
} cmApAggPortTestRecd;
|
||||||
|
|
||||||
|
// 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 _cmApAggDeviceToBuffer( 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 _cmApAggSynthSine( cmApAggPortTestRecd* 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 = _cmApAggDeviceToBuffer( &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 _cmApAggCopyIn( cmApAggPortTestRecd* r, const cmApSample_t* sp, unsigned srcChIdx, unsigned srcChCnt, unsigned srcFrameCnt )
|
||||||
|
{
|
||||||
|
unsigned i,j;
|
||||||
|
|
||||||
|
unsigned chCnt = cmMin(r->chCnt,srcChCnt);
|
||||||
|
|
||||||
|
// write the incoming sample to an output file for debugging
|
||||||
|
if( r->ifp != NULL )
|
||||||
|
if( fwrite(sp,sizeof(cmApSample_t),srcChCnt*srcFrameCnt,r->ifp) != srcChCnt*srcFrameCnt )
|
||||||
|
printf("file write fail.\n");
|
||||||
|
|
||||||
|
// zero the input meter array
|
||||||
|
for(i=0; i<r->chCnt; ++i)
|
||||||
|
r->iMeter[i] = 0;
|
||||||
|
|
||||||
|
for(i=0; i<srcFrameCnt; ++i)
|
||||||
|
{
|
||||||
|
// copy samples from the src to the buffer - both src and buffer are interleaved
|
||||||
|
for(j=0; j<chCnt; ++j)
|
||||||
|
{
|
||||||
|
r->buf[ r->bufInIdx + j ] = sp[ (i*srcChCnt) + srcChIdx + j ];
|
||||||
|
|
||||||
|
// record the max value in the input meter array
|
||||||
|
if( r->buf[ r->bufInIdx + j ] > r->iMeter[j] )
|
||||||
|
r->iMeter[j] = r->buf[ r->bufInIdx + j ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero channels that are not used in the buffer
|
||||||
|
for(; j<r->chCnt; ++j)
|
||||||
|
r->buf[ r->bufInIdx + j ] = 0;
|
||||||
|
|
||||||
|
// advance to the next frame
|
||||||
|
r->bufInIdx = (r->bufInIdx+r->chCnt) % r->bufFrmCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
//r->bufFullCnt = (r->bufFullCnt + srcFrameCnt) % r->bufFrmCnt;
|
||||||
|
cmThUIntIncr(&r->bufFullCnt,srcFrameCnt);
|
||||||
|
|
||||||
|
if( r->bufFullCnt > r->bufFrmCnt )
|
||||||
|
{
|
||||||
|
//printf("Input buffer overrun.\n");
|
||||||
|
++r->overunCnt;
|
||||||
|
r->bufFullCnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy audio samples out of the internal record buffer into dp[dstChCnt*dstFrameCnt].
|
||||||
|
void _cmApAggCopyOut( cmApAggPortTestRecd* 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 %i < %i\n",r->bufFullCnt,dstFrameCnt);
|
||||||
|
memset( dp, 0, dstFrameCnt*dstChCnt*sizeof(cmApSample_t) );
|
||||||
|
++r->underunCnt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned i,j;
|
||||||
|
unsigned chCnt = cmMin(dstChCnt,r->chCnt);
|
||||||
|
|
||||||
|
for(i=0; i<dstFrameCnt; ++i)
|
||||||
|
{
|
||||||
|
// copy the stored buffer samples to the dst buffer
|
||||||
|
for(j=0; j<chCnt; ++j)
|
||||||
|
dp[ (i*dstChCnt) + dstChIdx + j ] = r->buf[ r->bufOutIdx + j ];
|
||||||
|
|
||||||
|
// zero unset channels in the dst buffer
|
||||||
|
for(; j<dstChCnt; ++j)
|
||||||
|
dp[ (i*dstChCnt) + dstChIdx + j ] = 0;
|
||||||
|
|
||||||
|
r->bufOutIdx = (r->bufOutIdx + r->chCnt) % r->bufFrmCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmThUIntDecr(&r->bufFullCnt,dstFrameCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( r->ofp != NULL )
|
||||||
|
fwrite(dp,sizeof(cmApSample_t),dstChCnt*dstFrameCnt,r->ofp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio port callback function called from the audio device thread.
|
||||||
|
void _cmApAggPortCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// for each incoming audio packet
|
||||||
|
|
||||||
|
for(i=0; i<inPktCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)inPktArray[i].userCbPtr;
|
||||||
|
|
||||||
|
if( r->synthFl==false && inPktArray[i].devIdx == r->inDevIdx )
|
||||||
|
{
|
||||||
|
// copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut().
|
||||||
|
_cmApAggCopyIn( 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)
|
||||||
|
{
|
||||||
|
cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)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 = _cmApAggSynthSine( 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
|
||||||
|
_cmApAggCopyOut( 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// print the usage message for cmAudioPortTest.c
|
||||||
|
void _cmApAggPrintUsage( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
char msg[] =
|
||||||
|
"cmApAggPortTest() 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 _cmApAggGetOpt( 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _cmApBufShowMeter( cmRpt_t* rpt, unsigned devIdx )
|
||||||
|
{
|
||||||
|
unsigned faultCnt = 0;
|
||||||
|
unsigned meterCnt = cmApBufChannelCount(devIdx,kInApFl);
|
||||||
|
double meterArray[ meterCnt ];
|
||||||
|
|
||||||
|
unsigned n = cmApBufGetStatus(devIdx, kInApFl, meterArray, meterCnt, &faultCnt );
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
cmRptPrintf(rpt,"In: actual:%i fault: %i : ",n,faultCnt);
|
||||||
|
for(i=0; i<meterCnt; ++i)
|
||||||
|
cmRptPrintf(rpt,"%i:%f ",i,meterArray[i]);
|
||||||
|
cmRptPrintf(rpt,"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned _cmAggGlobalInDevIdx = 0;
|
||||||
|
unsigned _cmAggGlobalOutDevIdx = 0;
|
||||||
|
|
||||||
|
void _cmApAggPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
|
||||||
|
{
|
||||||
|
|
||||||
|
cmApBufInputToOutput( _cmAggGlobalInDevIdx, _cmAggGlobalOutDevIdx );
|
||||||
|
|
||||||
|
cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void recdPrint();
|
||||||
|
|
||||||
|
// Audio Port testing function
|
||||||
|
int cmApAggTest( bool runFl, cmCtx_t* ctx, int argc, const char* argv[] )
|
||||||
|
{
|
||||||
|
cmApAggPortTestRecd r;
|
||||||
|
unsigned i;
|
||||||
|
cmRpt_t* rpt = &ctx->rpt;
|
||||||
|
|
||||||
|
if( _cmApAggGetOpt(argc,argv,"-h",0,true) )
|
||||||
|
_cmApAggPrintUsage(rpt);
|
||||||
|
|
||||||
|
|
||||||
|
runFl = _cmApAggGetOpt(argc,argv,"-p",!runFl,true)?false:true;
|
||||||
|
r.chIdx = _cmApAggGetOpt(argc,argv,"-a",0,false);
|
||||||
|
r.chCnt = _cmApAggGetOpt(argc,argv,"-c",2,false);
|
||||||
|
r.bufCnt = _cmApAggGetOpt(argc,argv,"-b",3,false);
|
||||||
|
r.framesPerCycle = _cmApAggGetOpt(argc,argv,"-f",512,false);
|
||||||
|
r.bufFrmCnt = (r.bufCnt*r.framesPerCycle);
|
||||||
|
r.bufSmpCnt = (r.chCnt * r.bufFrmCnt);
|
||||||
|
r.synthFl = false;
|
||||||
|
r.meterMs = 50;
|
||||||
|
|
||||||
|
cmApSample_t buf[r.bufSmpCnt];
|
||||||
|
double imeter[r.chCnt];
|
||||||
|
|
||||||
|
r.iMeter = imeter;
|
||||||
|
|
||||||
|
r.inDevIdx = _cmAggGlobalInDevIdx = _cmApAggGetOpt(argc,argv,"-i",0,false);
|
||||||
|
r.outDevIdx = _cmAggGlobalOutDevIdx = _cmApAggGetOpt(argc,argv,"-o",2,false);
|
||||||
|
r.phase = 0;
|
||||||
|
r.frqHz = 2000;
|
||||||
|
r.srate = 44100;
|
||||||
|
r.bufInIdx = 0;
|
||||||
|
r.bufOutIdx = 0;
|
||||||
|
r.bufFullCnt = 0;
|
||||||
|
|
||||||
|
r.buf = buf;
|
||||||
|
r.cbCnt = 0;
|
||||||
|
r.underunCnt = 0;
|
||||||
|
r.overunCnt = 0;
|
||||||
|
r.ifp = NULL;
|
||||||
|
r.ofp = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
if(0)
|
||||||
|
{
|
||||||
|
if((r.ifp = fopen("/home/kevin/temp/itemp0.bin","wb")) == NULL )
|
||||||
|
cmRptPrintf(rpt,"File open failed.\n");
|
||||||
|
|
||||||
|
if((r.ofp = fopen("/home/kevin/temp/otemp0.bin","wb")) == NULL )
|
||||||
|
cmRptPrintf(rpt,"File open failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// allocate the aggregate device system
|
||||||
|
if( cmApAggAllocate(rpt) != kOkAgRC )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"The aggregate device system allocation failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned physDevIdxArray[] = { 0, 1 };
|
||||||
|
unsigned physDevCnt = sizeof(physDevIdxArray)/sizeof(physDevIdxArray[0]);
|
||||||
|
if( cmApAggCreateDevice("aggdev",physDevCnt,physDevIdxArray,kInAggFl | kOutAggFl) != kOkAgRC )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"The aggregate device creation failed.n");
|
||||||
|
goto doneLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// initialize the audio device interface
|
||||||
|
if( cmApInitialize(rpt) != kOkApRC )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"Initialize failed.\n");
|
||||||
|
goto doneLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 input device
|
||||||
|
if( cmApDeviceSetup(r.inDevIdx,r.srate,r.framesPerCycle,_cmApAggPortCb2,&r) != kOkApRC )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"In device setup failed.\n");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup an output device
|
||||||
|
if( r.inDevIdx != r.outDevIdx )
|
||||||
|
{
|
||||||
|
if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApAggPortCb2,&r) != kOkApRC )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"Out device setup failed.\n");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the input device
|
||||||
|
if( cmApDeviceStart(r.inDevIdx) != kOkApRC )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"In device start failed.\n");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( r.inDevIdx != r.outDevIdx )
|
||||||
|
{
|
||||||
|
// start the output device
|
||||||
|
if( cmApDeviceStart(r.outDevIdx) != kOkApRC )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"Out Device start failed.\n");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApBufEnableChannel(r.inDevIdx, -1, kInApFl | kEnableApFl );
|
||||||
|
cmApBufEnableChannel(r.outDevIdx, -1, kOutApFl | kEnableApFl );
|
||||||
|
cmApBufEnableMeter(r.inDevIdx, -1, kInApFl | kEnableApFl );
|
||||||
|
|
||||||
|
cmRptPrintf(rpt,"q=quit O/o output tone, I/i input tone P/p pass\n");
|
||||||
|
char c;
|
||||||
|
while((c=getchar()) != 'q')
|
||||||
|
{
|
||||||
|
//cmApDeviceRtReport(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;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
_cmApBufShowMeter(rpt,_cmAggGlobalInDevIdx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
cmRptPrintf(rpt,"iMeter: ");
|
||||||
|
for(i=0; i<r.chCnt; ++i)
|
||||||
|
cmRptPrintf(rpt,"%f ",r.iMeter[i]);
|
||||||
|
cmRptPrintf(rpt,"\n");
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
recdPrint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cmRptPrintf(rpt,"cb:%i\n",r.cbCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
doneLabel:
|
||||||
|
|
||||||
|
// report the count of audio buffer callbacks
|
||||||
|
cmRptPrintf(rpt,"cb:%i under:%i over:%i\n", r.cbCnt, r.underunCnt, r.overunCnt );
|
||||||
|
|
||||||
|
|
||||||
|
// release any resources held by the audio port interface
|
||||||
|
if( cmApFinalize() != kOkApRC )
|
||||||
|
cmRptPrintf(rpt,"Finalize failed.\n");
|
||||||
|
|
||||||
|
if( cmApAggFree() != kOkAgRC )
|
||||||
|
cmRptPrintf(rpt,"Agg device system free failed.");
|
||||||
|
|
||||||
|
if(r.ifp != NULL)
|
||||||
|
fclose(r.ifp);
|
||||||
|
|
||||||
|
if(r.ofp != NULL)
|
||||||
|
fclose(r.ofp);
|
||||||
|
|
||||||
|
cmApBufFinalize();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
104
cmAudioAggDev.h
Normal file
104
cmAudioAggDev.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#ifndef cmAudioAggDev_h
|
||||||
|
#define cmAudioAggDev_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAgRC = cmOkRC,
|
||||||
|
kMustAggTwoAgRC,
|
||||||
|
kCantUseStartedDevAgRC,
|
||||||
|
kDevAlreadyAggAgRC,
|
||||||
|
kInvalidDevIdxAgRC,
|
||||||
|
kPhysDevSetupFailAgRC,
|
||||||
|
kPhysDevStartFailAgRC,
|
||||||
|
kPhysDevStopFailAgRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmAgRC_t;
|
||||||
|
|
||||||
|
/// Allocate/free the aggregate device management system
|
||||||
|
cmAgRC_t cmApAggAllocate( cmRpt_t* rpt );
|
||||||
|
cmAgRC_t cmApAggFree();
|
||||||
|
|
||||||
|
|
||||||
|
/// Called by cmAudioPort() driver to notify the aggregate device
|
||||||
|
/// system that the hardware ports have been initialized.
|
||||||
|
/// Setup the aggregate audio device management object for this machine.
|
||||||
|
cmAgRC_t cmApAggInitialize( cmRpt_t* rpt, unsigned baseApDevIdx );
|
||||||
|
|
||||||
|
/// Called by cmAudioPort() driver to notify the aggregate device
|
||||||
|
/// system that the hardware ports have been finalized.
|
||||||
|
/// Stop all aggregate audio devices and release any resources held
|
||||||
|
/// by the agg. audio dev. management object.
|
||||||
|
cmAgRC_t cmApAggFinalize();
|
||||||
|
|
||||||
|
/// Create an aggregate device from physical devices.
|
||||||
|
/// Set flags to kInApFl, kOutApFl or both to indicate whether the
|
||||||
|
/// device should aggregate input audio, output audio or both.
|
||||||
|
enum { kInAggFl = 0x01, kOutAggFl = 0x02 };
|
||||||
|
cmAgRC_t cmApAggCreateDevice(
|
||||||
|
const cmChar_t* label,
|
||||||
|
unsigned devCnt,
|
||||||
|
const unsigned physDevIdxArray[],
|
||||||
|
unsigned flags );
|
||||||
|
|
||||||
|
|
||||||
|
// Return true if the specified physical device is included
|
||||||
|
// in an aggregated device.
|
||||||
|
bool cmApAggIsDeviceAggregated( unsigned physDevIdx );
|
||||||
|
|
||||||
|
|
||||||
|
/// Return the count of aggregate audio devices attached to this machine.
|
||||||
|
cmAgRC_t cmApAggDeviceCount();
|
||||||
|
|
||||||
|
/// Get a textual description of the device at index 'aggDevIdx'.
|
||||||
|
const char* cmApAggDeviceLabel( unsigned aggDevIdx );
|
||||||
|
|
||||||
|
/// Get the count of audio input or output channels on device at index 'aggDevIdx'.
|
||||||
|
unsigned cmApAggDeviceChannelCount( unsigned aggDevIdx, bool inputFl );
|
||||||
|
|
||||||
|
/// Get the current sample rate of a device. Note that if the device has both
|
||||||
|
/// input and output capability then the sample rate is the same for both.
|
||||||
|
double cmApAggDeviceSampleRate( unsigned aggDevIdx );
|
||||||
|
|
||||||
|
/// Get the count of samples per callback for the input or output for this device.
|
||||||
|
unsigned cmApAggDeviceFramesPerCycle( unsigned aggDevIdx, bool inputFl );
|
||||||
|
|
||||||
|
/// Configure a device.
|
||||||
|
/// All devices must be setup before they are started.
|
||||||
|
/// framesPerCycle is the requested number of samples per audio callback. The
|
||||||
|
/// actual number of samples made from a callback may be smaller. See the note
|
||||||
|
/// regarding this in cmApAggAudioPacket_t.
|
||||||
|
/// If the device cannot support the requested configuration then the function
|
||||||
|
/// will return an error code.
|
||||||
|
/// If the device is started when this function is called then it will be
|
||||||
|
/// automatically stopped and then restarted following the reconfiguration.
|
||||||
|
/// If the reconfiguration fails then the device may not be restared.
|
||||||
|
cmAgRC_t cmApAggDeviceSetup(
|
||||||
|
unsigned aggDevIdx,
|
||||||
|
double srate,
|
||||||
|
unsigned framesPerCycle,
|
||||||
|
cmApCallbackPtr_t callbackPtr,
|
||||||
|
void* userCbPtr );
|
||||||
|
|
||||||
|
/// Start a device. Note that the callback may be made prior to this function returning.
|
||||||
|
cmAgRC_t cmApAggDeviceStart( unsigned aggDevIdx );
|
||||||
|
|
||||||
|
/// Stop a device.
|
||||||
|
cmAgRC_t cmApAggDeviceStop( unsigned aggDevIdx );
|
||||||
|
|
||||||
|
/// Return true if the device is currently started.
|
||||||
|
bool cmApAggDeviceIsStarted( unsigned aggDevIdx );
|
||||||
|
|
||||||
|
int cmApAggTest( bool runFl, cmCtx_t* ctx, int argc, const char* argv[] );
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
285
cmAudioBuf.c
Normal file
285
cmAudioBuf.c
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmAudioPort.h"
|
||||||
|
#include "cmAudioBuf.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInIdx = 0;
|
||||||
|
kOutIdx = 1;
|
||||||
|
kIoCnt = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmApAudioPacket_t pkt; //
|
||||||
|
char* buf; // buf[bufByteCnt]
|
||||||
|
unsigned bufByteCnt; //
|
||||||
|
} cmAudioBufDevBuf_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmAudioBufSubDev_t* subDevArray;
|
||||||
|
unsigned subDevCnt;
|
||||||
|
unsigned readyCnt;
|
||||||
|
} cmAudioBufCycle_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned chCnt; // device channel count
|
||||||
|
cmAudioBufCycle_t* cycleArray; // subBufArray[ subBufCnt ]
|
||||||
|
unsigned cycleCnt; // set by cmAudioBufSetup().subBufCnt
|
||||||
|
unsigned faultCnt; // count of overruns (input) underruns (output)
|
||||||
|
unsigned iCycleIdx; // in: next buf to rcv on abUpdate() out: rcv on abGet()
|
||||||
|
unsigned oCycleIdx; // in: next buf to send on abGet() out: send on abUpdate()
|
||||||
|
unsigned fullCnt; // in: cnt of bufs ready for abGet() out: cnt of bufs ready for abUpdate()
|
||||||
|
} cmAudioBufIO_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmAudioBufIO_t ioArray[kIoCnt];
|
||||||
|
} cmAudioBufDev_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
unsigned devCnt;
|
||||||
|
cmAudioBufDev_t* devArray; // devArray[ devCnt ]
|
||||||
|
unsigned zeroBufByteCnt; // max of all possible buf sizes for all devices
|
||||||
|
char* zeroBuf; // zeroBuf[ zeroBufByteCnt ]
|
||||||
|
unsigned updateCnt;
|
||||||
|
unsigned nextSeqId;
|
||||||
|
} cmAudioBuf_t;
|
||||||
|
|
||||||
|
cmAudioBuf_t _cmBa;
|
||||||
|
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufInit( cmCtx_t* ctx, unsigned devCnt )
|
||||||
|
{
|
||||||
|
cmBaRC_t rc = kOkBaRC;
|
||||||
|
|
||||||
|
cmErrSetup(_cmBa.err,&ctx->rpt,"Audio Buf");
|
||||||
|
|
||||||
|
_cmBa.devArray = cmMemAllocZ(cmAudioBufDev_t,devCnt);
|
||||||
|
_cmBa.devCnt = devCnt;
|
||||||
|
_cmBa.zeroBufByteCnt = 0;
|
||||||
|
_cmBa.zeroBuf = NULL;
|
||||||
|
_cmBa.updateCnt = 0;
|
||||||
|
_cmBa.nextSeqId = 0;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmBaRC_t _cmAudioBufIoFree( cmAudioBufIO_t* iop )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<iop->subBufCnt; ++i)
|
||||||
|
cmMemPtrFree(&iop->subBufArray[i].buf);
|
||||||
|
|
||||||
|
cmMemPtrFree(&iop->subBufArray);
|
||||||
|
|
||||||
|
return kOkBaRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufFinal()
|
||||||
|
{
|
||||||
|
cmBaRC_t rc = kOkBaRC;
|
||||||
|
unsigned i,j;
|
||||||
|
for(i=0; i<_cmBa.devCnt; ++i)
|
||||||
|
for(j=0; j<kIoCnt; ++j)
|
||||||
|
_cmAudioBufIoFree( _cmBa.devArray[i].ioArray + j );
|
||||||
|
|
||||||
|
cmMemPtrFree(&_cmBa.devArray);
|
||||||
|
_cmBa.devCnt = 0;
|
||||||
|
cmMemPtrFree(&_cmBa.zeroBuf);
|
||||||
|
_cmBa.zeroBufByteCnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufSetup(
|
||||||
|
unsigned devIdx,
|
||||||
|
unsigned cycleCnt,
|
||||||
|
unsigned inSubDevCnt,
|
||||||
|
unsigned inChCnt,
|
||||||
|
unsigned inFrameCnt,
|
||||||
|
unsigned outChCnt,
|
||||||
|
unsigned outFrameCnt,
|
||||||
|
unsigned outSubDevCnt )
|
||||||
|
{
|
||||||
|
assert(devIdx < _cmBa.devCnt );
|
||||||
|
cmAudioBufDev_t* dp = _cmBa.devArray + devIdx;
|
||||||
|
unsigned i,j;
|
||||||
|
for(i=0; i<kIoCnt; ++i)
|
||||||
|
{
|
||||||
|
cmAudioBufIO_t* iop = dp->ioArray + i;
|
||||||
|
|
||||||
|
_cmAudioBufIoFree(iop);
|
||||||
|
|
||||||
|
iop->subBufArray = cmMemAllocZ(cmAudioBufSubBuf_t,subBufCnt);
|
||||||
|
iop->subBufCnt = 0;
|
||||||
|
|
||||||
|
unsigned maxSampleWidthByteCnt = 4;
|
||||||
|
|
||||||
|
// max size of any buffer arriving via cmAudioBufUpdate() for this device/direction
|
||||||
|
unsigned bufByteCnt = frameCnt*chCnt*maxSampleWidthByteCnt;
|
||||||
|
|
||||||
|
// initialize the sub-buf array for this device/direction
|
||||||
|
for(j=0; j<subBufCnt; ++j)
|
||||||
|
{
|
||||||
|
iop->subBufArray[j].buf = cmMemAllocZ(char,bufByteCnt);
|
||||||
|
iop->subBufArray[j].bufByteCnt = bufByteCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// track the largest buffer size and make _cmBa.zeroBuf[] that size
|
||||||
|
if( bufByteCnt > _cmBa.zeroBufByteCnt )
|
||||||
|
{
|
||||||
|
cmMemResizeZ(char,_cmBa.zeroBuf,bufByteCnt);
|
||||||
|
_cmBa.zeroBufByteCnt = bufByteCnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from the audio driver within incoming samples to store (inPktArray)
|
||||||
|
// and empty buffers (outPktArray) to fill with outgoin samples.
|
||||||
|
cmBaRC_t cmAudioBufUpdate(
|
||||||
|
cmApAudioPacket_t* inPktArray, ///< full audio packets from incoming audio (from ADC)
|
||||||
|
unsigned inPktCnt, ///< count of incoming audio packets
|
||||||
|
cmApAudioPacket_t* outPktArray, ///< empty audio packet for outgoing audio (to DAC)
|
||||||
|
unsigned outPktCnt ///< count of outgoing audio packets
|
||||||
|
)
|
||||||
|
{
|
||||||
|
cmBaRC_t rc = kOkBaRC;
|
||||||
|
|
||||||
|
++_cmBa.updateCnt;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<inPktCnt && inPktArray!=NULL; ++i)
|
||||||
|
{
|
||||||
|
cmApAudioPacket_t* ipp = inPktArray + i;
|
||||||
|
|
||||||
|
// get a pointer to the device/direction recd
|
||||||
|
cmAudioBufIO_t* iop = _cmBa.devArray[ ipp->devIdx ].ioArray + kInIdx;
|
||||||
|
|
||||||
|
// check for overruns
|
||||||
|
if( iop->fullCnt == iop->subBufCnt )
|
||||||
|
{
|
||||||
|
// input overrun
|
||||||
|
++ip->faultCnt;
|
||||||
|
rc = cmErrMsg(&_cmBa.err,kBufOverrunBaRC,"Input buffer overrun.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// get the next available sub-buf
|
||||||
|
cmAudioBufSubBuf_t* sbp = iop->subBufArray + iop->iSubBufIdx;
|
||||||
|
|
||||||
|
// store the packet header
|
||||||
|
sbp->pkt = *ipp;
|
||||||
|
sbp->audioBytesPtr = sbp->buf;
|
||||||
|
|
||||||
|
// calc the count of bytes of incoming packet audio
|
||||||
|
unsigned pktBufByteCnt = ipp->chCnt * ipp->audioFramesCnt * (bitsPerSample/8);
|
||||||
|
assert( pktBufByteCnt <= sbp->bufByteCnt );
|
||||||
|
|
||||||
|
// copy the samples into the buffer
|
||||||
|
memcpy(sbp->buf,ipp->audioBytesPtr,pktBufByteCnt);
|
||||||
|
|
||||||
|
// advance the input sub-buffer
|
||||||
|
iop->iSubBufIdx = (iop->iSubBufIdx + 1) % iop->subBufCnt;
|
||||||
|
|
||||||
|
iop->fullCnt+=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0; i<outPktCnt && outPktArray!=NULL; ++i)
|
||||||
|
{
|
||||||
|
unsigned j;
|
||||||
|
cmApAudioPacket_t* opp = outPktArray + i;
|
||||||
|
|
||||||
|
// get a pointer to the device/direction recd
|
||||||
|
cmAudioBufIO_t* iop = _cmBa.devArray[ opp->devIdx ].ioArray + kOutIdx;
|
||||||
|
|
||||||
|
// calc the number of requested bytes
|
||||||
|
unsigned pktBufByteCnt = opp->chCnt * opp->audioFramesCnt * (bitsPerSample/8);
|
||||||
|
|
||||||
|
// locate the oldest sub-buf which matches the pkt begin channel index
|
||||||
|
cmAudioBufSubBuf_t* sbp = NULL;
|
||||||
|
cmAudioBufSubBuf_t* tsbp = iop->subBufArray;
|
||||||
|
for(j=0; j<iop->subBufCnt; ++j,++tsbp)
|
||||||
|
if( tsbp->fullFl && (tsbp->pkt.begChIdx == opp->pkt.begChIdx) )
|
||||||
|
if( sbp==NULL || tsbp->seqId < sbp->seqId )
|
||||||
|
sbp = tsbp;
|
||||||
|
|
||||||
|
if( sbp == NULL )
|
||||||
|
{
|
||||||
|
++opp->faultCnt;
|
||||||
|
rc = cmErrMsg(&_cmBa.err,kBufOverrunBaRC,"Output buffer underrun.");
|
||||||
|
|
||||||
|
// zero the pkt buffer
|
||||||
|
memset(opp->audioBytePtr,0,pktBufByteCnt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the channel count assoc'd with a given begin channel index should always match
|
||||||
|
assert( sbp->pkt.chCnt == opp->chCnt );
|
||||||
|
|
||||||
|
// we guarantee that the sample word width will always match
|
||||||
|
assert( sbp->pkt.bitsPerSample == opp->bitsPerSample);
|
||||||
|
|
||||||
|
// we don't want to deal with the case where the requested number of samples
|
||||||
|
// is less than the number available from a single stored buffer - this would
|
||||||
|
// require sending out a partial buffer
|
||||||
|
assert( opp->audioFrameCnt >= sbp->pkt.audioFrameCnt );
|
||||||
|
|
||||||
|
// calc the number of bytes to copy out
|
||||||
|
unsigned bufByteCnt = sbp->pkt.chCnt * sbp->pkt.audioFramesCnt * (sbp->pkt.bitsPerSample/8);
|
||||||
|
|
||||||
|
assert(bufByteCnt <= pktBufByteCnt );
|
||||||
|
|
||||||
|
// copy out the requested samples
|
||||||
|
memcpy(opp->audioBytesPtr,sbp->buf,cmMin(bufByteCnt,pktBufByteCnt));
|
||||||
|
|
||||||
|
opp->audioFramesCnt = sbp->pkt.audioFramesCnt;
|
||||||
|
|
||||||
|
// mark the sub-buffer as available
|
||||||
|
sbp->fullFl = false;
|
||||||
|
iop->fullCnt -= 1;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
returnr c;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmAudioBufIsDeviceReady( unsigned devIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
assert( devIdx < _cmBa.devCnt );
|
||||||
|
|
||||||
|
//
|
||||||
|
if( cmIsFlag(flags,kInBaFl) )
|
||||||
|
if( _cmBa.devArray[devIdx].ioArray[kInIdx].fullCnt==0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( cmIsFlag(flags,kOutBaFl) )
|
||||||
|
if( _cmBa.devArray[devIdx].ioArray[kOutIdx].fullCnt == _cmBa.devArray[devIdx].ioArray[kOutIdx].subBufCnt )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufGet(
|
||||||
|
unsigned devIdx,
|
||||||
|
unsigned flags,
|
||||||
|
cmApAudioPacket_t* pktArray[],
|
||||||
|
unsigned pktCnt )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufAdvance( unsigned devIdx, unsigned flags )
|
||||||
|
{
|
||||||
|
}
|
63
cmAudioBuf.h
Normal file
63
cmAudioBuf.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef cmAudioBuf_h
|
||||||
|
#define cmAudioBuf_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkBaRC = cmOkRC
|
||||||
|
kBufOverunBaRC,
|
||||||
|
kBufUnderunBaRC
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInBaFl = 0x01,
|
||||||
|
kOutBaFl = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmBaRC_t;
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufInit( cmCtx_t* ctx, unsigned devCnt );
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufFinal();
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufSetup(
|
||||||
|
unsigned devIdx,
|
||||||
|
unsigned cycleCnt,
|
||||||
|
unsigned inSubDevCnt,
|
||||||
|
unsigned inChCnt,
|
||||||
|
unsigned inFrameCnt,
|
||||||
|
unsigned outChCnt,
|
||||||
|
unsigned outFrameCnt,
|
||||||
|
unsigned outSubDevCnt );
|
||||||
|
|
||||||
|
// Called from the audio driver within incoming samples to store (inPktArray)
|
||||||
|
// and empty buffers (outPktArray) to fill with outgoin samples.
|
||||||
|
cmBaRC_t cmAudioBufUpdate(
|
||||||
|
cmApAudioPacket_t* inPktArray, ///< full audio packets from incoming audio (from ADC)
|
||||||
|
unsigned inPktCnt, ///< count of incoming audio packets
|
||||||
|
cmApAudioPacket_t* outPktArray, ///< empty audio packet for outgoing audio (to DAC)
|
||||||
|
unsigned outPktCnt ///< count of outgoing audio packets
|
||||||
|
);
|
||||||
|
|
||||||
|
bool cmAudioBufIsDeviceReady( unsigned devIdx, unsigned flags );
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufGet(
|
||||||
|
unsigned devIdx,
|
||||||
|
unsigned flags,
|
||||||
|
cmApAudioPacket_t* pktArray[],
|
||||||
|
unsigned pktCnt );
|
||||||
|
|
||||||
|
cmBaRC_t cmAudioBufAdvance( unsigned devIdx, unsigned flags );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1696
cmAudioFile.c
Normal file
1696
cmAudioFile.c
Normal file
File diff suppressed because it is too large
Load Diff
305
cmAudioFile.h
Normal file
305
cmAudioFile.h
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/// \file cmAudioFile.h
|
||||||
|
/// \brief Audio file reader/writer class.
|
||||||
|
///
|
||||||
|
/// This class supports reading uncompressed AIFF and WAV files and writing uncompressed AIFF files.
|
||||||
|
/// The reading and writing routines are known to work with 8,16,24, and 32 bit integer sample formats.
|
||||||
|
///
|
||||||
|
/// Testing and example usage for this API can be found in cmProcTest.c cmAudioReadWriteTest().
|
||||||
|
///
|
||||||
|
/// Usage example:
|
||||||
|
/// \snippet cmAudioFile.c cmAudioFileExample
|
||||||
|
|
||||||
|
#ifndef cmAudioFile_h
|
||||||
|
#define cmAudioFile_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef cmAudioFile_MAX_FRAME_READ_CNT
|
||||||
|
/// Maximum number of samples which will be read in one call to fread().
|
||||||
|
/// This value is only significant in that an internal buffer is created on the stack
|
||||||
|
/// whose size must be limited to prevent stack overflows.
|
||||||
|
#define cmAudioFile_MAX_FRAME_READ_CNT (8192)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// Audio file result codes.
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAfRC = 0,
|
||||||
|
kOpenFailAfRC,
|
||||||
|
kReadFailAfRC,
|
||||||
|
kWriteFailAfRC,
|
||||||
|
kSeekFailAfRC,
|
||||||
|
kCloseFailAfRC,
|
||||||
|
kNotAiffAfRC,
|
||||||
|
kInvalidBitWidthAfRC,
|
||||||
|
kInvalidFileModeAfRC,
|
||||||
|
kInvalidHandleAfRC,
|
||||||
|
kUnknownErrAfRC
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Informational flags used by audioFileInfo
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kAiffAfFl = 0x01, ///< this is an AIFF file
|
||||||
|
kWavAfFl = 0x02, ///< this is a WAV file
|
||||||
|
kSwapAfFl = 0x04, ///< file header bytes must be swapped
|
||||||
|
kAifcAfFl = 0x08, ///< this is an AIFC file
|
||||||
|
kSwapSamplesAfFl = 0x10 ///< file sample bytes must be swapped
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Constants
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kAudioFileLabelCharCnt = 256,
|
||||||
|
|
||||||
|
kAfBextDescN = 256,
|
||||||
|
kAfBextOriginN = 32,
|
||||||
|
kAfBextOriginRefN = 32,
|
||||||
|
kAfBextOriginDateN = 10,
|
||||||
|
kAfBextOriginTimeN = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Aiff marker record
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned id;
|
||||||
|
unsigned frameIdx;
|
||||||
|
char label[kAudioFileLabelCharCnt];
|
||||||
|
} cmAudioFileMarker_t;
|
||||||
|
|
||||||
|
/// Broadcast WAV header record As used by ProTools audio files. See http://en.wikipedia.org/wiki/Broadcast_Wave_Format
|
||||||
|
/// When generated from Protools the timeRefLow/timeRefHigh values appear to actually refer
|
||||||
|
/// to the position on the Protools time-line rather than the wall clock time.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char desc[ kAfBextDescN + 1 ];
|
||||||
|
char origin[ kAfBextOriginN + 1 ];
|
||||||
|
char originRef[ kAfBextOriginRefN + 1 ];
|
||||||
|
char originDate[kAfBextOriginDateN + 1 ];
|
||||||
|
char originTime[kAfBextOriginTimeN + 1 ];
|
||||||
|
unsigned timeRefLow; // sample count since midnight low word
|
||||||
|
unsigned timeRefHigh; // sample count since midnight high word
|
||||||
|
} cmAudioFileBext_t;
|
||||||
|
|
||||||
|
/// Audio file information record used by audioFileNew and audioFileOpen
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned bits; ///< bits per sample
|
||||||
|
unsigned chCnt; ///< count of audio file channels
|
||||||
|
double srate; ///< audio file sample rate in samples per second
|
||||||
|
unsigned frameCnt; ///< total number of sample frames in the audio file
|
||||||
|
unsigned flags; ///< informational flags
|
||||||
|
unsigned markerCnt; ///< count of markers in markerArray
|
||||||
|
cmAudioFileMarker_t* markerArray; ///< array of markers
|
||||||
|
cmAudioFileBext_t bextRecd; ///< only used with Broadcast WAV files
|
||||||
|
} cmAudioFileInfo_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmHandle_t cmAudioFileH_t; ///< opaque audio file handle
|
||||||
|
extern cmAudioFileH_t cmNullAudioFileH; ///< NULL audio file handle
|
||||||
|
|
||||||
|
/// Create an audio file handle and optionally use the handle to open an audio file.
|
||||||
|
///
|
||||||
|
/// \param fn The audio file name to open or NULL to create the audio file handle without opening the file.
|
||||||
|
/// \param infoPtr A pointer to an audioFileInfo record to be filled when the file is open or NULL to ignore.
|
||||||
|
/// \param rcPtr A pointer to a result code to be set in the event of a runtime error or NULL to ignore.
|
||||||
|
/// \param rpt A pointer to a cmRpt_t object which error messages from this class will be directed to.
|
||||||
|
/// \retval cmAudioFileH_t A new audio file handle.
|
||||||
|
///
|
||||||
|
cmAudioFileH_t cmAudioFileNewOpen( const cmChar_t* fn, cmAudioFileInfo_t* infoPtr, cmRC_t* rcPtr, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
/// Open an audio file for writing
|
||||||
|
cmAudioFileH_t cmAudioFileNewCreate( const cmChar_t* fn, double srate, unsigned bits, unsigned chCnt, cmRC_t* rcPtr, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
|
||||||
|
/// Open an audio file for reading using a handle returned from an earlier call to audioFileNewXXX().
|
||||||
|
///
|
||||||
|
/// \param h A file handle returned from and earlier call to cmAudioFileNewOpen() or cmAudioFileNewCreate().
|
||||||
|
/// \param fn The audio file name to open or NULL to create the audio file handle without opening the file.
|
||||||
|
/// \param infoPtr A pointer to an audioFileInfo record to be filled when the file is open or NULL to ignore.
|
||||||
|
/// \retval Returns an cmRC_t value indicating the success (kOkAfRC) or failure of the call.
|
||||||
|
///
|
||||||
|
/// If the audio file handle 'h' refers to an open file then it is automatically closed prior to being
|
||||||
|
/// reopened with the new file.
|
||||||
|
cmRC_t cmAudioFileOpen( cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileInfo_t* infoPtr );
|
||||||
|
|
||||||
|
/// Open an audio file for writing.
|
||||||
|
cmRC_t cmAudioFileCreate(
|
||||||
|
cmAudioFileH_t h, ///< Handle returned from an earlier call to cmAudioFileNewCreate() or cmAudioFileNewOpen().
|
||||||
|
const cmChar_t* fn, ///< File name of the new file.
|
||||||
|
double srate, ///< Sample rate of the new file.
|
||||||
|
unsigned bits, ///< Sample word width for the new file in bits (must be 8,16,24 or 32).
|
||||||
|
unsigned chCnt ///< Audio channel count for the new file.
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Close a the file associated with handle 'h' but do not release the handle.
|
||||||
|
/// If the file was opened for writing (cmAudioFileCreate()) then this function will
|
||||||
|
/// write the file header prior to closing the file.
|
||||||
|
cmRC_t cmAudioFileClose( cmAudioFileH_t* h );
|
||||||
|
|
||||||
|
/// Close the file associated with handle 'h' (via an internal call to
|
||||||
|
/// cmAudioFileClose()) and release the handle and any resources
|
||||||
|
/// associated with it. This is the complement to cmAudioFileOpen/Create().
|
||||||
|
cmRC_t cmAudioFileDelete( cmAudioFileH_t* h );
|
||||||
|
|
||||||
|
/// Return true if the handle is not closed or deleted.
|
||||||
|
bool cmAudioFileIsValid( cmAudioFileH_t h );
|
||||||
|
|
||||||
|
/// Return true if the handle is open.
|
||||||
|
bool cmAudioFileIsOpen( cmAudioFileH_t h );
|
||||||
|
|
||||||
|
/// Return true if the current file position is at the end of the file.
|
||||||
|
bool cmAudioFileIsEOF( cmAudioFileH_t h );
|
||||||
|
|
||||||
|
/// Return the current file position as a frame index.
|
||||||
|
unsigned cmAudioFileTell( cmAudioFileH_t h );
|
||||||
|
|
||||||
|
/// Set the current file position as an offset from the first frame.
|
||||||
|
cmRC_t cmAudioFileSeek( cmAudioFileH_t h, unsigned frmIdx );
|
||||||
|
|
||||||
|
/// \name Sample Reading Functions.
|
||||||
|
///@{
|
||||||
|
/// Fill a user suppled buffer with up to frmCnt samples.
|
||||||
|
/// If less than frmCnt samples are available at the specified audio file location then the unused
|
||||||
|
/// buffer space is set to zero. Check *actualFrmCntPtr for the count of samples actually available
|
||||||
|
/// in the return buffer. Functions which do not include a begFrmIdx argument begin reading from
|
||||||
|
/// the current file location (see cmAudioFileSeek()). The buf argument is always a pointer to an
|
||||||
|
/// array of pointers of length chCnt. Each channel buffer specified in buf[] must contain at least
|
||||||
|
/// frmCnt samples.
|
||||||
|
///
|
||||||
|
/// \param h An audio file handle returned from an earlier call to audioFileNew()
|
||||||
|
/// \param fn The name of the audio file to read.
|
||||||
|
/// \param begFrmIdx The frame index of the first sample to read. Functions that do not use this parameter begin reading at the current file location (See cmAudioFileTell()).
|
||||||
|
/// \param frmCnt The number of samples allocated in buf.
|
||||||
|
/// \param chIdx The index of the first channel to read.
|
||||||
|
/// \param chCnt The count of channels to read.
|
||||||
|
/// \param buf An array containing chCnt pointers to arrays of frmCnt samples.
|
||||||
|
/// \param actualFrmCntPtr The number of frames actually written to the return buffer (ignored if NULL)
|
||||||
|
|
||||||
|
cmRC_t cmAudioFileReadInt( cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, int** buf, unsigned* actualFrmCntPtr );
|
||||||
|
cmRC_t cmAudioFileReadFloat( cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float** buf, unsigned* actualFrmCntPtr );
|
||||||
|
cmRC_t cmAudioFileReadDouble( cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr );
|
||||||
|
|
||||||
|
cmRC_t cmAudioFileGetInt( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, int** buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
|
||||||
|
cmRC_t cmAudioFileGetFloat( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float** buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
|
||||||
|
cmRC_t cmAudioFileGetDouble( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
/// \name Sum the returned samples into the output buffer.
|
||||||
|
///@{
|
||||||
|
cmRC_t cmAudioFileReadSumInt( cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, int** buf, unsigned* actualFrmCntPtr );
|
||||||
|
cmRC_t cmAudioFileReadSumFloat( cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float** buf, unsigned* actualFrmCntPtr );
|
||||||
|
cmRC_t cmAudioFileReadSumDouble( cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr );
|
||||||
|
|
||||||
|
cmRC_t cmAudioFileGetSumInt( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, int** buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
|
||||||
|
cmRC_t cmAudioFileGetSumFloat( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float** buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
|
||||||
|
cmRC_t cmAudioFileGetSumDouble( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
|
||||||
|
///@}
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
/// \name Sample Writing Functions
|
||||||
|
///@{
|
||||||
|
cmRC_t cmAudioFileWriteInt( cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, int** bufPtrPtr );
|
||||||
|
cmRC_t cmAudioFileWriteFloat( cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, float** bufPtrPtr );
|
||||||
|
cmRC_t cmAudioFileWriteDouble( cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, double** bufPtrPtr );
|
||||||
|
|
||||||
|
cmRC_t cmAudioFileWriteFileInt( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, int** bufPtrPtr, cmRpt_t* rpt );
|
||||||
|
cmRC_t cmAudioFileWriteFileFloat( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, float** bufPtrPtr, cmRpt_t* rpt );
|
||||||
|
cmRC_t cmAudioFileWriteFileDouble( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, double** bufPtrPtr, cmRpt_t* rpt );
|
||||||
|
///@}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// \name cmSample_t and cmReal_t Alias Macros
|
||||||
|
///@{
|
||||||
|
/// Alias the cmSample_t and cmReal_t sample reading and writing functions to the appropriate
|
||||||
|
/// type based on #CM_FLOAT_SMP and #CM_FLOAT_REAL.
|
||||||
|
|
||||||
|
#if CM_FLOAT_SMP == 1
|
||||||
|
|
||||||
|
#define cmAudioFileReadSample cmAudioFileReadFloat
|
||||||
|
#define cmAudioFileReadSumSample cmAudioFileReadSumFloat
|
||||||
|
#define cmAudioFileGetSample cmAudioFileGetFloat
|
||||||
|
#define cmAudioFileGetSumSample cmAudioFileGetSumFloat
|
||||||
|
#define cmAudioFileWriteSample cmAudioFileWriteFloat
|
||||||
|
#define cmAudioFileWriteFileSample cmAudioFileWriteFileFloat
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define cmAudioFileReadSample cmAudioFileReadDouble
|
||||||
|
#define cmAudioFileReadSumSample cmAudioFileReadSumDouble
|
||||||
|
#define cmAudioFileGetSample cmAudioFileGetDouble
|
||||||
|
#define cmAudioFileGetSumSample cmAudioFileGetSumDouble
|
||||||
|
#define cmAudioFileWriteSample cmAudioFileWriteDouble
|
||||||
|
#define cmAudioFileWriteFileSample cmAudioFileWriteFileDouble
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CM_FLOAT_REAL == 1
|
||||||
|
|
||||||
|
#define cmAudioFileReadReal cmAudioFileReadFloat
|
||||||
|
#define cmAudioFileReadSumReal cmAudioFileReadSumFloat
|
||||||
|
#define cmAudioFileGetReal cmAudioFileGetFloat
|
||||||
|
#define cmAudioFileGetSumReal cmAudioFileGetSumFloat
|
||||||
|
#define cmAudioFileWriteReal cmAudioFileWriteFloat
|
||||||
|
#define cmAudioFileWriteFileReal cmAudioFileWriteFileFloat
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define cmAudioFileReadReal cmAudioFileReadDouble
|
||||||
|
#define cmAudioFileReadSumReal cmAudioFileReadSumDouble
|
||||||
|
#define cmAudioFileGetReal cmAudioFileGetDouble
|
||||||
|
#define cmAudioFileGetSumReal cmAudioFileGetSumDouble
|
||||||
|
#define cmAudioFileWriteReal cmAudioFileWriteDouble
|
||||||
|
#define cmAudioFileWriteFileReal cmAudioFileWriteFileDouble
|
||||||
|
|
||||||
|
#endif
|
||||||
|
///@}
|
||||||
|
|
||||||
|
|
||||||
|
/// \name Minimum, Maximum, Mean
|
||||||
|
///@{
|
||||||
|
/// Scan an entire audio file and return the minimum, maximum and mean sample value.
|
||||||
|
/// On error *minPtr, *maxPtr, and *meanPtr are set to -acSample_MAX, cmSample_MAX, and 0 respectively
|
||||||
|
cmRC_t cmAudioFileMinMaxMean( cmAudioFileH_t h, unsigned chIdx, cmSample_t* minPtr, cmSample_t* maxPtr, cmSample_t* meanPtr );
|
||||||
|
cmRC_t cmAudioFileMinMaxMeanFn( const cmChar_t* fn, unsigned chIdx, cmSample_t* minPtr, cmSample_t* maxPtr, cmSample_t* meanPtr, cmRpt_t* rpt );
|
||||||
|
///@}
|
||||||
|
|
||||||
|
/// Return the file name associated with a audio file handle.
|
||||||
|
const cmChar_t* cmAudioFileName( cmAudioFileH_t h );
|
||||||
|
|
||||||
|
/// Given an error code return the associated message.
|
||||||
|
const char* cmAudioFileErrorMsg( unsigned rc );
|
||||||
|
|
||||||
|
/// \name Get information about an audio file
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/// Return the cmAudioFileInfo_t record associated with a file.
|
||||||
|
cmRC_t cmAudioFileGetInfo( const cmChar_t* fn, cmAudioFileInfo_t* infoPtr, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
/// Print the cmAudioFileInfo_t to a file.
|
||||||
|
void cmAudioFilePrintInfo( const cmAudioFileInfo_t* infoPtr, cmRpt_t* );
|
||||||
|
|
||||||
|
/// Print the file header information and frmCnt sample values beginning at frame index frmIdx.
|
||||||
|
cmRC_t cmAudioFileReport( cmAudioFileH_t h, cmRpt_t* rpt, unsigned frmIdx, unsigned frmCnt );
|
||||||
|
|
||||||
|
/// Print the file header information and frmCnt sample values beginning at frame index frmIdx.
|
||||||
|
cmRC_t cmAudioFileReportFn( const cmChar_t* fn, unsigned frmIdx, unsigned frmCnt, cmRpt_t* rpt );
|
||||||
|
///@}
|
||||||
|
|
||||||
|
/// Testing and example routine for functions in cmAudioFile.h.
|
||||||
|
/// Also see cmProcTest.c cmAudioFileReadWriteTest()
|
||||||
|
void cmAudioFileTest( const cmChar_t* audioFn, const cmChar_t* outFn, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
561
cmAudioFileDev.c
Normal file
561
cmAudioFileDev.c
Normal file
@ -0,0 +1,561 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmAudioFile.h"
|
||||||
|
#include "cmThread.h"
|
||||||
|
#include "cmAudioPort.h"
|
||||||
|
#include "cmAudioFileDev.h"
|
||||||
|
|
||||||
|
#include <unistd.h> // usleep()
|
||||||
|
|
||||||
|
#ifdef OS_OSX
|
||||||
|
#include "osx/clock_gettime_stub.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OS_LINUX
|
||||||
|
#include <time.h> // clock_gettime()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
cmAfdH_t cmAfdNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
#define cmAfd_Billion (1000000000)
|
||||||
|
#define cmAfd_Million (1000000)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err; // error object
|
||||||
|
cmApCallbackPtr_t callbackPtr; // client callback function
|
||||||
|
void* cbDataPtr; // argument to be passed with the client callback
|
||||||
|
|
||||||
|
unsigned devIdx;
|
||||||
|
cmChar_t* label;
|
||||||
|
cmChar_t* oFn;
|
||||||
|
unsigned oBits;
|
||||||
|
unsigned oChCnt;
|
||||||
|
|
||||||
|
cmAudioFileH_t iAfH; // audio input file handle
|
||||||
|
cmAudioFileH_t oAfH; // audio output file handle
|
||||||
|
cmThreadH_t tH; // thread handle
|
||||||
|
|
||||||
|
double srate; // file device sample rate
|
||||||
|
unsigned framesPerCycle; // count of samples sent/recv'd from the client on each callback
|
||||||
|
|
||||||
|
cmApAudioPacket_t iPkt; // audio packet used sent to the client via callbackPtr.
|
||||||
|
cmApAudioPacket_t oPkt; //
|
||||||
|
cmApSample_t** iChArray; // audio buffer channel arrays used with cmAudioFile
|
||||||
|
cmApSample_t** oChArray; //
|
||||||
|
|
||||||
|
bool runFl; // set to true as long as the thread should continue looping
|
||||||
|
bool rewindFl; // set to true when the input file should rewind
|
||||||
|
unsigned readErrCnt; // count of read errors from the input file
|
||||||
|
bool eofFl; // set to true when the input file reaches the EOF
|
||||||
|
unsigned writeErrCnt; // count of write errors from the output file
|
||||||
|
|
||||||
|
long nanosPerCycle; // nano-seconds per cycle
|
||||||
|
struct timespec baseTime;
|
||||||
|
struct timespec nextTime; // next execution time
|
||||||
|
|
||||||
|
unsigned cycleCnt; // count of cycles completed
|
||||||
|
|
||||||
|
} cmAfd_t;
|
||||||
|
|
||||||
|
cmAfd_t* _cmAfdHandleToPtr( cmAfdH_t h )
|
||||||
|
{
|
||||||
|
cmAfd_t* p = (cmAfd_t*)h.h;
|
||||||
|
assert(p != NULL );
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void _cmAudioFileDevExec( cmAfd_t* p )
|
||||||
|
{
|
||||||
|
unsigned iPktCnt = 0;
|
||||||
|
unsigned oPktCnt = p->oPkt.chCnt!=0;
|
||||||
|
|
||||||
|
// if the input device is enabled
|
||||||
|
if( p->iPkt.chCnt )
|
||||||
|
{
|
||||||
|
unsigned actualFrmCnt = p->framesPerCycle;
|
||||||
|
|
||||||
|
// if the input file has reached EOF - zero the input buffer
|
||||||
|
if( p->eofFl )
|
||||||
|
memset(p->iPkt.audioBytesPtr,0,p->framesPerCycle*sizeof(cmApSample_t));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// otherwise fill the input buffer from the input file
|
||||||
|
if( cmAudioFileReadSample(p->iAfH, p->framesPerCycle, p->iPkt.begChIdx, p->iPkt.chCnt, p->iChArray, &actualFrmCnt) != kOkAfRC )
|
||||||
|
++p->readErrCnt;
|
||||||
|
|
||||||
|
// if the input file reachged EOF the set p->eofFl
|
||||||
|
if( (actualFrmCnt < p->framesPerCycle) && cmAudioFileIsEOF(p->iAfH) )
|
||||||
|
p->eofFl = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
iPktCnt = actualFrmCnt>0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback to the client to provde incoming samples and receive outgoing samples
|
||||||
|
p->callbackPtr(iPktCnt ? &p->iPkt : NULL, iPktCnt, oPktCnt ? &p->oPkt : NULL, oPktCnt );
|
||||||
|
|
||||||
|
// if the output device is enabled
|
||||||
|
if( p->oPkt.chCnt )
|
||||||
|
{
|
||||||
|
// write the output samples
|
||||||
|
if( cmAudioFileWriteSample( p->oAfH, p->framesPerCycle, p->oPkt.chCnt, p->oChArray ) != kOkAfRC )
|
||||||
|
++p->writeErrCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
++p->cycleCnt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// incrment p->nextTime to the next execution time
|
||||||
|
void _cmAfdIncrNextTime( cmAfd_t* p )
|
||||||
|
{
|
||||||
|
long nsec = p->nextTime.tv_nsec + p->nanosPerCycle;
|
||||||
|
|
||||||
|
if( nsec < cmAfd_Billion )
|
||||||
|
p->nextTime.tv_nsec = nsec;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p->nextTime.tv_sec += 1;
|
||||||
|
p->nextTime.tv_nsec = nsec - cmAfd_Billion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calc the time between t1 and t0 - t1 is assummed to come after t0 in order to produce a positive result
|
||||||
|
long _cmAfdDiffMicros( const struct timespec* t0, const struct timespec* t1 )
|
||||||
|
{
|
||||||
|
long u0 = t0->tv_sec * cmAfd_Million;
|
||||||
|
long u1 = t1->tv_sec * cmAfd_Million;
|
||||||
|
|
||||||
|
u0 += t0->tv_nsec / 1000;
|
||||||
|
u1 += t1->tv_nsec / 1000;
|
||||||
|
|
||||||
|
return u1 - u0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// thread callback function
|
||||||
|
bool _cmAudioDevThreadFunc(void* param)
|
||||||
|
{
|
||||||
|
cmAfd_t* p = (cmAfd_t*)param;
|
||||||
|
struct timespec t0;
|
||||||
|
|
||||||
|
// if this is the first time this callback has been called after a call to cmAudioFileDevStart().
|
||||||
|
if( p->cycleCnt == 0 )
|
||||||
|
{
|
||||||
|
// get the baseTime - all other times will be relative to this time
|
||||||
|
clock_gettime(CLOCK_REALTIME,&p->baseTime);
|
||||||
|
p->nextTime = p->baseTime;
|
||||||
|
p->nextTime.tv_sec = 0;
|
||||||
|
_cmAfdIncrNextTime(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the thread has not been requested to stop
|
||||||
|
if( p->runFl )
|
||||||
|
{
|
||||||
|
// get the current time as an offset from baseTime.
|
||||||
|
clock_gettime(CLOCK_REALTIME,&t0);
|
||||||
|
t0.tv_sec -= p->baseTime.tv_sec;
|
||||||
|
|
||||||
|
// get length of time to next exec point
|
||||||
|
long dusec = _cmAfdDiffMicros(&t0, &p->nextTime);
|
||||||
|
|
||||||
|
// if the execution time has not yet arrived
|
||||||
|
if( dusec > 0 )
|
||||||
|
{
|
||||||
|
usleep(dusec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the thread is still running
|
||||||
|
if( p->runFl )
|
||||||
|
{
|
||||||
|
// read/callback/write
|
||||||
|
_cmAudioFileDevExec(p);
|
||||||
|
|
||||||
|
// calc the next exec time
|
||||||
|
_cmAfdIncrNextTime(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p->runFl;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfdRC_t cmAudioFileDevInitialize(
|
||||||
|
cmAfdH_t* hp,
|
||||||
|
const cmChar_t* label,
|
||||||
|
unsigned devIdx,
|
||||||
|
const cmChar_t* iFn,
|
||||||
|
const cmChar_t* oFn,
|
||||||
|
unsigned oBits,
|
||||||
|
unsigned oChCnt,
|
||||||
|
cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmAfdRC_t rc;
|
||||||
|
cmRC_t afRC;
|
||||||
|
|
||||||
|
if((rc = cmAudioFileDevFinalize(hp)) != kOkAfdRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// allocate the object
|
||||||
|
cmAfd_t* p = cmMemAllocZ(cmAfd_t,1);
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
cmErrSetup(&p->err,rpt,"AudioFileDevice");
|
||||||
|
|
||||||
|
// create the input audio file handle
|
||||||
|
if( iFn != NULL )
|
||||||
|
{
|
||||||
|
cmAudioFileInfo_t afInfo;
|
||||||
|
|
||||||
|
// open the input file
|
||||||
|
if(cmAudioFileIsValid( p->iAfH = cmAudioFileNewOpen(iFn,&afInfo,&afRC,rpt)) == false )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"The audio input file '%s' could not be opened.", iFn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
p->iPkt.devIdx = devIdx;
|
||||||
|
p->iPkt.begChIdx = 0;
|
||||||
|
p->iPkt.chCnt = afInfo.chCnt; // setting iPkt.chCnt to a non-zero value marks the input file as active
|
||||||
|
p->iPkt.audioFramesCnt = 0;
|
||||||
|
p->iPkt.bitsPerSample = afInfo.bits;
|
||||||
|
p->iPkt.flags = kFloatApFl;
|
||||||
|
p->iPkt.audioBytesPtr = NULL;
|
||||||
|
p->iChArray = cmMemResizeZ( cmApSample_t*, p->iChArray, afInfo.chCnt );
|
||||||
|
p->readErrCnt = 0;
|
||||||
|
p->eofFl = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the output audio file handle
|
||||||
|
if(cmAudioFileIsValid( p->oAfH = cmAudioFileNewOpen(NULL,NULL,NULL,rpt)) == false )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"The audio output file object allocation failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the driver thread
|
||||||
|
if( cmThreadCreate(&p->tH, _cmAudioDevThreadFunc, p, rpt ) != kOkThRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kThreadFailAfdRC,"The internal thread could not be created.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->runFl = true;
|
||||||
|
p->devIdx = devIdx;
|
||||||
|
p->label = cmMemAllocStr(label);
|
||||||
|
p->oFn = cmMemAllocStr(oFn);
|
||||||
|
p->oBits = oBits;
|
||||||
|
p->oChCnt = oChCnt;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( rc != kOkAfdRC )
|
||||||
|
cmAudioFileDevFinalize(hp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfdRC_t cmAudioFileDevFinalize( cmAfdH_t* hp )
|
||||||
|
{
|
||||||
|
if( hp == NULL || cmAudioFileDevIsValid(*hp) == false )
|
||||||
|
return kOkAfdRC;
|
||||||
|
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(*hp);
|
||||||
|
|
||||||
|
p->runFl = false;
|
||||||
|
|
||||||
|
if( cmThreadIsValid(p->tH) )
|
||||||
|
cmThreadDestroy(&p->tH);
|
||||||
|
|
||||||
|
cmAudioFileDelete(&p->iAfH);
|
||||||
|
cmAudioFileDelete(&p->oAfH);
|
||||||
|
cmMemPtrFree(&p->label);
|
||||||
|
cmMemPtrFree(&p->oFn);
|
||||||
|
cmMemPtrFree(&p->iPkt.audioBytesPtr);
|
||||||
|
cmMemPtrFree(&p->oPkt.audioBytesPtr);
|
||||||
|
cmMemPtrFree(&p->iChArray);
|
||||||
|
cmMemPtrFree(&p->oChArray);
|
||||||
|
cmMemPtrFree(&p);
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
return kOkAfdRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmAudioFileDevIsValid( cmAfdH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
|
||||||
|
cmAfdRC_t cmAudioFileDevSetup(
|
||||||
|
cmAfdH_t h,
|
||||||
|
double srate,
|
||||||
|
unsigned framesPerCycle,
|
||||||
|
cmApCallbackPtr_t callbackPtr,
|
||||||
|
void* cbDataPtr )
|
||||||
|
{
|
||||||
|
cmAfdRC_t rc = kOkAfdRC;
|
||||||
|
bool restartFl = false;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if( cmAudioFileDevIsStarted(h) )
|
||||||
|
{
|
||||||
|
if((rc = cmAudioFileDevStop(h)) != kOkAfdRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
restartFl = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// close the existing input file
|
||||||
|
if(cmAudioFileClose(&p->iAfH) != kOkAfRC )
|
||||||
|
rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file close failed on input audio file.");
|
||||||
|
|
||||||
|
p->iPkt.chCnt = 0; // mark the input file as inactive
|
||||||
|
*/
|
||||||
|
|
||||||
|
if( cmAudioFileIsValid( p->iAfH ) )
|
||||||
|
if( cmAudioFileSeek( p->iAfH, 0 ) != kOkAfRC )
|
||||||
|
rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file device rewind failed.");
|
||||||
|
|
||||||
|
|
||||||
|
// close the existing output file
|
||||||
|
if(cmAudioFileClose(&p->oAfH) != kOkAfRC )
|
||||||
|
rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file close failed on output audio file.");
|
||||||
|
|
||||||
|
p->oPkt.chCnt = 0; // mark the output file as inactive
|
||||||
|
|
||||||
|
// if an output audio file was given ...
|
||||||
|
if( p->oFn != NULL )
|
||||||
|
{
|
||||||
|
// ... then open it
|
||||||
|
if( cmAudioFileCreate( p->oAfH, p->oFn, srate, p->oBits, p->oChCnt ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kAudioFileFailAfdRC, "The audio output file '%s' could not be created.",p->oFn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApSample_t* bp = (cmApSample_t*)p->oPkt.audioBytesPtr;
|
||||||
|
|
||||||
|
p->oPkt.devIdx = p->devIdx;
|
||||||
|
p->oPkt.begChIdx = 0;
|
||||||
|
p->oPkt.chCnt = p->oChCnt;
|
||||||
|
p->oPkt.audioFramesCnt = framesPerCycle;
|
||||||
|
p->oPkt.bitsPerSample = p->oBits;
|
||||||
|
p->oPkt.flags = kFloatApFl;
|
||||||
|
p->oPkt.audioBytesPtr = bp = cmMemResizeZ( cmApSample_t, bp, framesPerCycle*p->oChCnt );
|
||||||
|
p->oChArray = cmMemResizeZ( cmApSample_t*, p->oChArray, p->oChCnt );
|
||||||
|
|
||||||
|
for(i=0; i<p->oChCnt; ++i)
|
||||||
|
p->oChArray[i] = bp + (i*framesPerCycle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmAudioFileIsValid( p->iAfH) )
|
||||||
|
{
|
||||||
|
cmApSample_t* bp = (cmApSample_t*)p->iPkt.audioBytesPtr;
|
||||||
|
|
||||||
|
p->iPkt.audioFramesCnt = framesPerCycle;
|
||||||
|
p->iPkt.audioBytesPtr = bp = cmMemResizeZ( cmApSample_t, bp, framesPerCycle*p->iPkt.chCnt ); ;
|
||||||
|
for(i=0; i<p->iPkt.chCnt; ++i)
|
||||||
|
p->iChArray[i] = bp + (i*framesPerCycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
p->callbackPtr = callbackPtr;
|
||||||
|
p->cbDataPtr = cbDataPtr;
|
||||||
|
p->framesPerCycle = framesPerCycle;
|
||||||
|
p->srate = srate;
|
||||||
|
p->cycleCnt = 0;
|
||||||
|
p->nanosPerCycle = floor((double)framesPerCycle / srate * cmAfd_Billion );
|
||||||
|
|
||||||
|
if( restartFl )
|
||||||
|
{
|
||||||
|
if((rc = cmAudioFileDevStart(h)) != kOkAfdRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kRestartFailAfdRC,"The audio file device could not be restarted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* cmAudioFileDevLabel( cmAfdH_t h )
|
||||||
|
{
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
return p->label;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmAudioFileDevChannelCount( cmAfdH_t h, bool inputFl )
|
||||||
|
{
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
return inputFl ? p->iPkt.chCnt : p->oPkt.chCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
double cmAudioFileDevSampleRate( cmAfdH_t h )
|
||||||
|
{
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
return p->srate;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmAudioFileDevFramesPerCycle( cmAfdH_t h, bool inputFl )
|
||||||
|
{
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
return inputFl ? p->iPkt.audioFramesCnt : p->oPkt.audioFramesCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmAfdRC_t cmAudioFileDevRewind( cmAfdH_t h )
|
||||||
|
{
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
p->rewindFl = true;
|
||||||
|
return kOkAfdRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfdRC_t cmAudioFileDevStart( cmAfdH_t h )
|
||||||
|
{
|
||||||
|
cmAfdRC_t rc = kOkAfdRC;
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
|
||||||
|
p->cycleCnt = 0;
|
||||||
|
|
||||||
|
if( cmThreadPause( p->tH, 0 ) != kOkThRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kThreadFailAfdRC,"Thread start failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("Start\n",stderr);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfdRC_t cmAudioFileDevStop( cmAfdH_t h )
|
||||||
|
{
|
||||||
|
cmAfdRC_t rc = kOkAfdRC;
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
|
||||||
|
if( cmThreadPause( p->tH, kPauseThFl | kWaitThFl ) != kOkThRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&p->err,kThreadFailAfdRC,"Thread stop failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("Stop\n",stderr);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmAudioFileDevIsStarted( cmAfdH_t h )
|
||||||
|
{
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
return cmThreadState(p->tH) == kRunningThId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmAudioFileDevReport( cmAfdH_t h, cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmAfd_t* p = _cmAfdHandleToPtr(h);
|
||||||
|
cmRptPrintf(rpt,"label:%s thr state:%i srate:%f\n",p->label,cmThreadState(p->tH),p->srate);
|
||||||
|
cmRptPrintf(rpt, "in chs:%i %s\n",p->iPkt.chCnt,cmAudioFileName(p->iAfH));
|
||||||
|
cmRptPrintf(rpt, "out chs:%i %s\n",p->oPkt.chCnt,p->oFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// device callback function used with cmAudioFileDevTest() note that this assumes
|
||||||
|
// that the packet buffer contain non-interleaved data.
|
||||||
|
void _cmAfdCallback(
|
||||||
|
cmApAudioPacket_t* inPktArray,
|
||||||
|
unsigned inPktCnt,
|
||||||
|
cmApAudioPacket_t* outPktArray,
|
||||||
|
unsigned outPktCnt )
|
||||||
|
{
|
||||||
|
cmApAudioPacket_t* ip = inPktArray;
|
||||||
|
cmApAudioPacket_t* op = outPktArray;
|
||||||
|
unsigned opi = 0;
|
||||||
|
unsigned ipi = 0;
|
||||||
|
unsigned oci = 0;
|
||||||
|
unsigned ici = 0;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
if( ici == ip->chCnt)
|
||||||
|
{
|
||||||
|
ici = 0;
|
||||||
|
if( ++ipi >= inPktCnt )
|
||||||
|
break;
|
||||||
|
|
||||||
|
ip = inPktArray + ipi;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( oci == op->chCnt )
|
||||||
|
{
|
||||||
|
oci = 0;
|
||||||
|
if( ++opi >= outPktCnt )
|
||||||
|
break;
|
||||||
|
|
||||||
|
ip = outPktArray + opi;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( ip->audioFramesCnt == op->audioFramesCnt );
|
||||||
|
assert( cmIsFlag(ip->flags,kInterleavedApFl)==false && cmIsFlag(ip->flags,kInterleavedApFl)==false );
|
||||||
|
|
||||||
|
cmApSample_t* ibp = ((cmApSample_t*)ip->audioBytesPtr) + (ip->audioFramesCnt*ici);
|
||||||
|
cmApSample_t* obp = ((cmApSample_t*)op->audioBytesPtr) + (op->audioFramesCnt*oci);
|
||||||
|
|
||||||
|
memcpy(obp,ibp,ip->audioFramesCnt*sizeof(cmApSample_t));
|
||||||
|
|
||||||
|
++ici;
|
||||||
|
++oci;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmAudioFileDevTest( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmAfdH_t afdH = cmAfdNullHandle;
|
||||||
|
double srate = 44100;
|
||||||
|
unsigned framesPerCycle = 512;
|
||||||
|
void* cbDataPtr = NULL;
|
||||||
|
unsigned devIdx = 0;
|
||||||
|
const cmChar_t* iFn = "/home/kevin/media/audio/McGill-1/1 Audio Track.aiff";
|
||||||
|
const cmChar_t* oFn = "/home/kevin/temp/afd0.aif";
|
||||||
|
unsigned oBits = 16;
|
||||||
|
unsigned oChCnt = 2;
|
||||||
|
|
||||||
|
if( cmAudioFileDevInitialize(&afdH,"file",devIdx,iFn,oFn,oBits,oChCnt,rpt) != kOkAfdRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
if( cmAudioFileDevSetup(afdH,srate,framesPerCycle,_cmAfdCallback,cbDataPtr) != kOkAfdRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
char c;
|
||||||
|
fputs("q=quit 1=start 0=stop\n",stderr);
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
|
while((c=getchar()) != 'q')
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case '1': cmAudioFileDevStart(afdH); break;
|
||||||
|
case '0': cmAudioFileDevStop(afdH); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
fflush(stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
cmAudioFileDevFinalize(&afdH);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
74
cmAudioFileDev.h
Normal file
74
cmAudioFileDev.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#ifndef cmAudioFileDev_h
|
||||||
|
#define cmAudioFileDev_h
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAfdRC = cmOkRC,
|
||||||
|
kAudioFileFailAfdRC,
|
||||||
|
kThreadFailAfdRC,
|
||||||
|
kRestartFailAfdRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef unsigned cmAfdRC_t;
|
||||||
|
typedef cmHandle_t cmAfdH_t;
|
||||||
|
|
||||||
|
extern cmAfdH_t cmAfdNullHandle;
|
||||||
|
|
||||||
|
/// Initialize an audio file device.
|
||||||
|
/// Called by cmApPortInitialize().
|
||||||
|
cmAfdRC_t cmAudioFileDevInitialize(
|
||||||
|
cmAfdH_t* hp,
|
||||||
|
const cmChar_t* label,
|
||||||
|
unsigned devIdx,
|
||||||
|
const cmChar_t* iFn,
|
||||||
|
const cmChar_t* oFn,
|
||||||
|
unsigned oBits,
|
||||||
|
unsigned oChCnt,
|
||||||
|
cmRpt_t* rpt );
|
||||||
|
|
||||||
|
/// Finalize an audio file device.
|
||||||
|
/// Called by cmApPortFinalize().
|
||||||
|
cmAfdRC_t cmAudioFileDevFinalize( cmAfdH_t* hp );
|
||||||
|
|
||||||
|
/// Return true if 'h' represents a successfully initialized audio file device.
|
||||||
|
bool cmAudioFileDevIsValid( cmAfdH_t h );
|
||||||
|
|
||||||
|
|
||||||
|
/// Setup the device. This function must be called prior to cmAudioFileDevStart().
|
||||||
|
cmAfdRC_t cmAudioFileDevSetup(
|
||||||
|
cmAfdH_t h,
|
||||||
|
double srate,
|
||||||
|
unsigned framesPerCycle,
|
||||||
|
cmApCallbackPtr_t callbackPtr,
|
||||||
|
void* cbDataPtr );
|
||||||
|
|
||||||
|
/// Return a device label.
|
||||||
|
const char* cmAudioFileDevLabel( cmAfdH_t h );
|
||||||
|
|
||||||
|
/// Return a channel count.
|
||||||
|
unsigned cmAudioFileDevChannelCount( cmAfdH_t h, bool inputFl );
|
||||||
|
|
||||||
|
/// Return the sample rate.
|
||||||
|
double cmAudioFileDevSampleRate( cmAfdH_t h );
|
||||||
|
|
||||||
|
/// Return frames per cycle.
|
||||||
|
unsigned cmAudioFileDevFramesPerCycle( cmAfdH_t h, bool inputFl );
|
||||||
|
|
||||||
|
/// Rewind the input file.
|
||||||
|
cmAfdRC_t cmAudioFileDevRewind( cmAfdH_t h );
|
||||||
|
|
||||||
|
/// Start the file device playing/recording.
|
||||||
|
cmAfdRC_t cmAudioFileDevStart( cmAfdH_t h );
|
||||||
|
|
||||||
|
/// Stop the file device playing/recording.
|
||||||
|
cmAfdRC_t cmAudioFileDevStop( cmAfdH_t h );
|
||||||
|
|
||||||
|
/// Return true if the file device is currently started.
|
||||||
|
bool cmAudioFileDevIsStarted( cmAfdH_t h );
|
||||||
|
|
||||||
|
///
|
||||||
|
void cmAudioFileDevReport( cmAfdH_t h, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
void cmAudioFileDevTest( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
#endif
|
483
cmAudioFileMgr.c
Normal file
483
cmAudioFileMgr.c
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmAudioFile.h"
|
||||||
|
#include "cmVectOpsTemplateMain.h"
|
||||||
|
|
||||||
|
#include "cmAudioFileMgr.h"
|
||||||
|
|
||||||
|
struct cmAfm_str;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmSample_t* minV; // minV[summN]
|
||||||
|
cmSample_t* maxV; // maxV[summN]
|
||||||
|
unsigned summN; // lenght of minV[] and maxV[]
|
||||||
|
} cmAfmSummary_t;
|
||||||
|
|
||||||
|
typedef struct cmAfmFile_str
|
||||||
|
{
|
||||||
|
unsigned id;
|
||||||
|
cmAudioFileH_t afH;
|
||||||
|
cmAudioFileInfo_t afInfo;
|
||||||
|
unsigned smpPerSummPt;
|
||||||
|
|
||||||
|
cmAfmSummary_t* summArray; // summArray[ afInfo.chCnt ]
|
||||||
|
cmSample_t* summMem; // memory used by summArray[] vectors
|
||||||
|
|
||||||
|
struct cmAfm_str* p;
|
||||||
|
struct cmAfmFile_str* next;
|
||||||
|
struct cmAfmFile_str* prev;
|
||||||
|
} cmAfmFile_t;
|
||||||
|
|
||||||
|
typedef struct cmAfm_str
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmAfmFile_t* list;
|
||||||
|
} cmAfm_t;
|
||||||
|
|
||||||
|
|
||||||
|
cmAfmH_t cmAfmNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
cmAfmFileH_t cmAfmFileNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
cmAfm_t* _cmAfmHandleToPtr( cmAfmH_t h )
|
||||||
|
{
|
||||||
|
cmAfm_t* p = (cmAfm_t*)h.h;
|
||||||
|
assert(p!=NULL);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfmFile_t* _cmAfmFileHandleToPtr( cmAfmFileH_t fh )
|
||||||
|
{
|
||||||
|
cmAfmFile_t* fp = (cmAfmFile_t*)fh.h;
|
||||||
|
assert(fp!=NULL);
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmAfmRC_t _cmAfmFileClose( cmAfmFile_t* fp )
|
||||||
|
{
|
||||||
|
cmAfmRC_t rc = kOkAfmRC;
|
||||||
|
|
||||||
|
if( cmAudioFileIsValid( fp->afH ) )
|
||||||
|
if( cmAudioFileDelete( &fp->afH) != kOkAfRC )
|
||||||
|
return cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file close failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
|
||||||
|
|
||||||
|
if( fp->next != NULL )
|
||||||
|
fp->next->prev = fp->prev;
|
||||||
|
|
||||||
|
if( fp->prev != NULL )
|
||||||
|
fp->prev->next = fp->next;
|
||||||
|
|
||||||
|
if( fp->p->list == fp )
|
||||||
|
fp->p->list = fp->next;
|
||||||
|
|
||||||
|
|
||||||
|
cmMemFree(fp->summArray);
|
||||||
|
cmMemFree(fp->summMem);
|
||||||
|
cmMemFree(fp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfmRC_t cmAfmFileOpen( cmAfmH_t h, cmAfmFileH_t* fhp, const cmChar_t* audioFn, unsigned id, cmAudioFileInfo_t* afInfo )
|
||||||
|
{
|
||||||
|
cmAfmRC_t rc;
|
||||||
|
cmRC_t afRC;
|
||||||
|
|
||||||
|
if((rc = cmAfmFileClose(fhp)) != kOkAfmRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmAfmFile_t* fp = cmMemAllocZ(cmAfmFile_t,1);
|
||||||
|
fp->p = _cmAfmHandleToPtr(h);
|
||||||
|
|
||||||
|
// open the audio file
|
||||||
|
if( cmAudioFileIsValid(fp->afH = cmAudioFileNewOpen(audioFn, &fp->afInfo, &afRC, fp->p->err.rpt )) == false )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"The audio file '%s' could not be opened.",cmStringNullGuard(audioFn));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepend the new file to the mgr's file list
|
||||||
|
if( fp->p->list != NULL )
|
||||||
|
fp->p->list->prev = fp;
|
||||||
|
|
||||||
|
fp->next = fp->p->list;
|
||||||
|
fp->p->list = fp;
|
||||||
|
|
||||||
|
fp->id = id;
|
||||||
|
|
||||||
|
fhp->h = fp;
|
||||||
|
|
||||||
|
if( afInfo != NULL )
|
||||||
|
*afInfo = fp->afInfo;
|
||||||
|
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( rc != kOkAfmRC )
|
||||||
|
_cmAfmFileClose(fp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfmRC_t cmAfmFileClose( cmAfmFileH_t* fhp )
|
||||||
|
{
|
||||||
|
cmAfmRC_t rc = kOkAfmRC;
|
||||||
|
if( fhp==NULL || cmAfmFileIsValid(*fhp)==false)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmAfmFile_t* fp = _cmAfmFileHandleToPtr( *fhp );
|
||||||
|
if((rc = _cmAfmFileClose(fp)) != kOkAfmRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
fhp->h = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmAfmFileIsValid( cmAfmFileH_t fh )
|
||||||
|
{ return fh.h != NULL; }
|
||||||
|
|
||||||
|
|
||||||
|
unsigned cmAfmFileId( cmAfmFileH_t fh )
|
||||||
|
{
|
||||||
|
cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
|
||||||
|
return fp->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAudioFileH_t cmAfmFileHandle( cmAfmFileH_t fh )
|
||||||
|
{
|
||||||
|
cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
|
||||||
|
return fp->afH;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmAudioFileInfo_t* cmAfmFileInfo( cmAfmFileH_t fh )
|
||||||
|
{
|
||||||
|
cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
|
||||||
|
return &fp->afInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfmRC_t cmAfmFileSummarize( cmAfmFileH_t fh, unsigned smpPerSummPt )
|
||||||
|
{
|
||||||
|
cmAfmFile_t* fp = _cmAfmFileHandleToPtr(fh);
|
||||||
|
cmAfmRC_t rc = kOkAfmRC;
|
||||||
|
unsigned chCnt = fp->afInfo.chCnt;
|
||||||
|
|
||||||
|
// summary points per channel per vector
|
||||||
|
unsigned summN = (unsigned)ceil((double)fp->afInfo.frameCnt / smpPerSummPt );
|
||||||
|
|
||||||
|
// total summary points in all channels and vectors
|
||||||
|
unsigned n = chCnt*2*summN;
|
||||||
|
|
||||||
|
// Calc the number of summary points per audio file read
|
||||||
|
unsigned ptsPerRd = cmMax(1,cmMax(smpPerSummPt,8192) / smpPerSummPt);
|
||||||
|
|
||||||
|
// Calc the number samples per audio file read as an integer multiple of ptsPerRd.
|
||||||
|
unsigned frmCnt = ptsPerRd * smpPerSummPt;
|
||||||
|
|
||||||
|
unsigned actualFrmCnt = 0;
|
||||||
|
cmSample_t* chBuf[ chCnt ];
|
||||||
|
cmSample_t buf[ frmCnt * chCnt ];
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// allocate the summary record array
|
||||||
|
if( fp->summArray == NULL )
|
||||||
|
fp->summArray = cmMemAllocZ( cmAfmSummary_t, chCnt );
|
||||||
|
|
||||||
|
// allocate the summary vector memory for all channels
|
||||||
|
fp->summMem = cmMemResizeZ( cmSample_t, fp->summMem, n);
|
||||||
|
fp->smpPerSummPt = smpPerSummPt;
|
||||||
|
|
||||||
|
// setup the summary record array and audio file read buffer
|
||||||
|
for(i=0; i<chCnt; ++i)
|
||||||
|
{
|
||||||
|
// assign memory to the summary vectors
|
||||||
|
fp->summArray[i].minV = fp->summMem + i * summN * 2;
|
||||||
|
fp->summArray[i].maxV = fp->summArray[i].minV + summN;
|
||||||
|
fp->summArray[i].summN = summN;
|
||||||
|
|
||||||
|
// setup the audio file reading channel buffer
|
||||||
|
chBuf[i] = buf + (i*frmCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the entire file and calculate the summary vectors
|
||||||
|
i = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
unsigned chIdx = 0;
|
||||||
|
unsigned j,k;
|
||||||
|
|
||||||
|
// read the next frmCnt samples from the
|
||||||
|
if( cmAudioFileReadSample(fp->afH, frmCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each summary point
|
||||||
|
for(k=0; k<actualFrmCnt && i<summN; k+=smpPerSummPt,++i)
|
||||||
|
{
|
||||||
|
// cnt of samples in this summary report
|
||||||
|
unsigned m = cmMin(smpPerSummPt,actualFrmCnt-k);
|
||||||
|
|
||||||
|
// for each channel
|
||||||
|
for(j=0; j<chCnt; ++j)
|
||||||
|
{
|
||||||
|
fp->summArray[j].minV[i] = cmVOS_Min(chBuf[j]+k,m,1);
|
||||||
|
fp->summArray[j].maxV[i] = cmVOS_Max(chBuf[j]+k,m,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}while( i<summN && actualFrmCnt==frmCnt );
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downsample the summary data to produce the output.
|
||||||
|
// There must be 1 or more summary points per output point.
|
||||||
|
cmAfmRC_t _cmAfmFileGetDownSummary(
|
||||||
|
cmAfmFile_t* fp,
|
||||||
|
unsigned chIdx,
|
||||||
|
unsigned begSmpIdx,
|
||||||
|
unsigned smpCnt,
|
||||||
|
cmSample_t* minV,
|
||||||
|
cmSample_t* maxV,
|
||||||
|
unsigned outCnt )
|
||||||
|
{
|
||||||
|
assert( smpCnt >= outCnt );
|
||||||
|
|
||||||
|
double smpPerOut = (double)smpCnt/outCnt;
|
||||||
|
double summPerOut = smpPerOut/fp->smpPerSummPt;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for(i=0; i<outCnt; ++i)
|
||||||
|
{
|
||||||
|
double fsbi = (begSmpIdx + (i*smpPerOut)) / fp->smpPerSummPt; // starting summary pt index
|
||||||
|
double fsei = fsbi + summPerOut; // endiing summary pt index
|
||||||
|
unsigned si = (unsigned)floor(fsbi);
|
||||||
|
unsigned sn = (unsigned)floor(fsei - fsbi + 1);
|
||||||
|
|
||||||
|
if( si > fp->summArray[chIdx].summN )
|
||||||
|
{
|
||||||
|
minV[i] = 0;
|
||||||
|
maxV[i] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( si + sn > fp->summArray[chIdx].summN )
|
||||||
|
sn = fp->summArray[chIdx].summN - si;
|
||||||
|
|
||||||
|
if( sn == 0 )
|
||||||
|
{
|
||||||
|
minV[i] = 0;
|
||||||
|
maxV[i] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
minV[i] = cmVOS_Min(fp->summArray[chIdx].minV+si,sn,1);
|
||||||
|
maxV[i] = cmVOS_Max(fp->summArray[chIdx].maxV+si,sn,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkAfmRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downsample the audio data to produce the output.
|
||||||
|
cmAfmRC_t _cmAfmFileGetDownAudio(
|
||||||
|
cmAfmFile_t* fp,
|
||||||
|
unsigned chIdx,
|
||||||
|
unsigned begSmpIdx,
|
||||||
|
unsigned smpCnt,
|
||||||
|
cmSample_t* minV,
|
||||||
|
cmSample_t* maxV,
|
||||||
|
unsigned outCnt )
|
||||||
|
{
|
||||||
|
assert( smpCnt >= outCnt );
|
||||||
|
|
||||||
|
cmAfmRC_t rc = kOkAfmRC;
|
||||||
|
unsigned actualFrmCnt = 0;
|
||||||
|
unsigned chCnt = 1;
|
||||||
|
unsigned i;
|
||||||
|
cmSample_t buf[ smpCnt ];
|
||||||
|
cmSample_t* chBuf[] = { buf };
|
||||||
|
|
||||||
|
// seek to the read location
|
||||||
|
if( cmAudioFileSeek( fp->afH, begSmpIdx ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file seek failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read 'smpCnt' samples into chBuf[][]
|
||||||
|
if( cmAudioFileReadSample(fp->afH, smpCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s' durnig upsample.",cmStringNullGuard(cmAudioFileName(fp->afH)));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double smpPerOut = (double)smpCnt/outCnt;
|
||||||
|
|
||||||
|
for(i=0; i<outCnt; ++i)
|
||||||
|
{
|
||||||
|
double fsbi = i*smpPerOut;
|
||||||
|
double fsei = fsbi + smpPerOut;
|
||||||
|
unsigned si = (unsigned)floor(fsbi);
|
||||||
|
unsigned sn = (unsigned)floor(fsei - fsbi + 1);
|
||||||
|
|
||||||
|
if( si > smpCnt )
|
||||||
|
{
|
||||||
|
minV[i] = 0;
|
||||||
|
maxV[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( si + sn > smpCnt )
|
||||||
|
sn = smpCnt - si;
|
||||||
|
|
||||||
|
minV[i] = cmVOS_Min(chBuf[chIdx]+si,sn,1);
|
||||||
|
maxV[i] = cmVOS_Max(chBuf[chIdx]+si,sn,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If there is one or less summary points per output
|
||||||
|
cmAfmRC_t _cmAfmFileGetUpSummary(
|
||||||
|
cmAfmFile_t* fp,
|
||||||
|
unsigned chIdx,
|
||||||
|
unsigned begSmpIdx,
|
||||||
|
unsigned smpCnt,
|
||||||
|
cmSample_t* minV,
|
||||||
|
cmSample_t* maxV,
|
||||||
|
unsigned outCnt )
|
||||||
|
{
|
||||||
|
assert( outCnt >= smpCnt );
|
||||||
|
cmAfmRC_t rc = kOkAfmRC;
|
||||||
|
unsigned actualFrmCnt = 0;
|
||||||
|
unsigned chCnt = 1;
|
||||||
|
unsigned i;
|
||||||
|
cmSample_t buf[ smpCnt ];
|
||||||
|
cmSample_t* chBuf[] = { buf };
|
||||||
|
|
||||||
|
if( cmAudioFileSeek( fp->afH, begSmpIdx ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file seek failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmAudioFileReadSample(fp->afH, smpCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s' durnig upsample.",cmStringNullGuard(cmAudioFileName(fp->afH)));
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for(i=0; i<outCnt; ++i)
|
||||||
|
{
|
||||||
|
unsigned si = cmMin(smpCnt-1, (unsigned)floor(i * smpCnt / outCnt));
|
||||||
|
cmSample_t v = buf[si];
|
||||||
|
minV[i] = v;
|
||||||
|
maxV[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmAfmRC_t cmAfmFileGetSummary( cmAfmFileH_t fh, unsigned chIdx, unsigned begSmpIdx, unsigned smpCnt, cmSample_t* minV, cmSample_t* maxV, unsigned outCnt )
|
||||||
|
{
|
||||||
|
cmAfmRC_t rc = kOkAfmRC;
|
||||||
|
cmAfmFile_t* fp = _cmAfmFileHandleToPtr(fh);
|
||||||
|
double maxHiResDurSecs = 20.0;
|
||||||
|
|
||||||
|
if( smpCnt <= outCnt )
|
||||||
|
rc = _cmAfmFileGetUpSummary( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( smpCnt/fp->afInfo.srate < maxHiResDurSecs )
|
||||||
|
rc = _cmAfmFileGetDownAudio( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
|
||||||
|
else
|
||||||
|
rc = _cmAfmFileGetDownSummary( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Audio File Manager
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
cmAfmRC_t _cmAfmDestroy( cmAfm_t* p )
|
||||||
|
{
|
||||||
|
cmAfmRC_t rc = kOkAfmRC;
|
||||||
|
|
||||||
|
while( p->list != NULL )
|
||||||
|
{
|
||||||
|
if((rc = _cmAfmFileClose(p->list)) != kOkAfmRC )
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemFree(p);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfmRC_t cmAfmCreate( cmCtx_t* ctx, cmAfmH_t* hp )
|
||||||
|
{
|
||||||
|
cmAfmRC_t rc;
|
||||||
|
if((rc = cmAfmDestroy(hp)) != kOkAfmRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmAfm_t* p = cmMemAllocZ(cmAfm_t,1);
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"Audio File Mgr");
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmAfmRC_t cmAfmDestroy( cmAfmH_t* hp )
|
||||||
|
{
|
||||||
|
cmAfmRC_t rc = kOkAfmRC;
|
||||||
|
|
||||||
|
if( hp==NULL || cmAfmIsValid(*hp)==false)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmAfm_t* p = _cmAfmHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if((rc = _cmAfmDestroy(p)) != kOkAfmRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmAfmIsValid( cmAfmH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
cmAfmFileH_t cmAfmIdToHandle( cmAfmH_t h, unsigned fileId )
|
||||||
|
{
|
||||||
|
cmAfm_t* p = _cmAfmHandleToPtr(h);
|
||||||
|
cmAfmFile_t* fp = p->list;
|
||||||
|
cmAfmFileH_t fh = cmAfmFileNullHandle;
|
||||||
|
|
||||||
|
for(; fp!=NULL; fp=fp->next)
|
||||||
|
if( fp->id == fileId )
|
||||||
|
{
|
||||||
|
fh.h = fp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fh;
|
||||||
|
}
|
67
cmAudioFileMgr.h
Normal file
67
cmAudioFileMgr.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#ifndef cmAudioFileMgr_h
|
||||||
|
#define cmAudioFileMgr_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAfmRC = cmOkRC,
|
||||||
|
kAudioFileFailAfmRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmHandle_t cmAfmH_t;
|
||||||
|
typedef cmHandle_t cmAfmFileH_t;
|
||||||
|
typedef cmRC_t cmAfmRC_t;
|
||||||
|
|
||||||
|
extern cmAfmH_t cmAfmNullHandle;
|
||||||
|
extern cmAfmFileH_t cmAfmFileNullHandle;
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Audio Files
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
cmAfmRC_t cmAfmFileOpen( cmAfmH_t h, cmAfmFileH_t* fhp, const cmChar_t* audioFn, unsigned fileId, cmAudioFileInfo_t* afInfo );
|
||||||
|
cmAfmRC_t cmAfmFileClose( cmAfmFileH_t* fhp );
|
||||||
|
bool cmAfmFileIsValid( cmAfmFileH_t fh );
|
||||||
|
|
||||||
|
// Return the application supplied file id associated with this file.
|
||||||
|
// This value was set by the 'fileId' argument to cmAfmFileOpen().
|
||||||
|
unsigned cmAfmFileId( cmAfmFileH_t fh );
|
||||||
|
|
||||||
|
// Return the file handle associated with this file.
|
||||||
|
cmAudioFileH_t cmAfmFileHandle( cmAfmFileH_t fh );
|
||||||
|
|
||||||
|
// Return a pointer to the information record associated with this file.
|
||||||
|
const cmAudioFileInfo_t* cmAfmFileInfo( cmAfmFileH_t fh );
|
||||||
|
|
||||||
|
// Summarize min and max values of the downSampled audio file.
|
||||||
|
// The summary is kept in an internal cache which is used to
|
||||||
|
// optimize the time required to complete later calls to cmAfmFileGetSummary().
|
||||||
|
// 'downSampleFactor' is the count of samples per summary point.
|
||||||
|
cmAfmRC_t cmAfmFileSummarize( cmAfmFileH_t fh, unsigned downSampleFactor );
|
||||||
|
|
||||||
|
// Return a summary of the samples in the range audio file range
|
||||||
|
// begSmpIdx:begSmpIdx+smpCnt-1 reduced or expanded to 'outCnt' values
|
||||||
|
// in minV[outCnt] and maxV[outCnt].
|
||||||
|
// If 'outCnt' is equal to 'smpCnt' then the actual sample values are returned.
|
||||||
|
cmAfmRC_t cmAfmFileGetSummary( cmAfmFileH_t fh, unsigned chIdx, unsigned begSmpIdx, unsigned smpCnt, cmSample_t* minV, cmSample_t* maxV, unsigned outCnt );
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Audio File Manager
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
cmAfmRC_t cmAfmCreate( cmCtx_t* ctx, cmAfmH_t* hp );
|
||||||
|
cmAfmRC_t cmAfmDestroy( cmAfmH_t* hp );
|
||||||
|
bool cmAfmIsValid( cmAfmH_t h );
|
||||||
|
cmAfmFileH_t cmAfmIdToHandle( cmAfmH_t h, unsigned fileId );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
355
cmAudioNrtDev.c
Normal file
355
cmAudioNrtDev.c
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.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 && i<idx; ++i)
|
||||||
|
dp = dp->link;
|
||||||
|
|
||||||
|
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; i<frmCnt; ++i, sp+=chCnt )
|
||||||
|
{
|
||||||
|
unsigned limit = periodSmp;
|
||||||
|
double gain = loLin;
|
||||||
|
|
||||||
|
cp->phase += 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(; i<chCnt; ++i)
|
||||||
|
cmApNrtGenImpulseCh(dp,i,buf,chCnt,frmCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmApNrtGenSamples( cmApNrtDev_t* dp, cmSample_t* buf, unsigned chCnt, unsigned frmCnt )
|
||||||
|
{
|
||||||
|
memset(buf,0,chCnt*frmCnt*sizeof(cmSample_t));
|
||||||
|
|
||||||
|
cmApNrtGenImpulse(dp,buf,chCnt,frmCnt);
|
||||||
|
|
||||||
|
dp->curTimeSmp += frmCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return 'false' to terminate otherwise return 'true'.
|
||||||
|
bool cmApNrtThreadFunc(void* param)
|
||||||
|
{
|
||||||
|
cmApNrtDev_t* dp = (cmApNrtDev_t*)param;
|
||||||
|
|
||||||
|
usleep( 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);
|
||||||
|
}
|
65
cmAudioNrtDev.h
Normal file
65
cmAudioNrtDev.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef cmAudioNrtDev_h
|
||||||
|
#define cmAudioNrtDev_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cmApRC_t cmApNrtAllocate( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
cmApRC_t cmApNrtFree();
|
||||||
|
|
||||||
|
cmApRC_t cmApNrtCreateDevice(
|
||||||
|
const cmChar_t* label,
|
||||||
|
double srate,
|
||||||
|
unsigned iChCnt,
|
||||||
|
unsigned oChCnt,
|
||||||
|
unsigned cbPeriodMs );
|
||||||
|
|
||||||
|
|
||||||
|
/// Setup the audio port management object for this machine.
|
||||||
|
cmApRC_t cmApNrtInitialize( cmRpt_t* rpt, unsigned baseApDevIdx );
|
||||||
|
|
||||||
|
/// Stop all audio devices and release any resources held
|
||||||
|
/// by the audio port management object.
|
||||||
|
cmApRC_t cmApNrtFinalize();
|
||||||
|
|
||||||
|
/// Return the count of audio devices attached to this machine.
|
||||||
|
unsigned cmApNrtDeviceCount();
|
||||||
|
|
||||||
|
/// Get a textual description of the device at index 'devIdx'.
|
||||||
|
const char* cmApNrtDeviceLabel( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Get the count of audio input or output channesl on device at index 'devIdx'.
|
||||||
|
unsigned cmApNrtDeviceChannelCount( unsigned devIdx, bool inputFl );
|
||||||
|
|
||||||
|
/// Get the current sample rate of a device. Note that if the device has both
|
||||||
|
/// input and output capability then the sample rate is the same for both.
|
||||||
|
double cmApNrtDeviceSampleRate( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Get the count of samples per callback for the input or output for this device.
|
||||||
|
unsigned cmApNrtDeviceFramesPerCycle( unsigned devIdx, bool inputFl );
|
||||||
|
|
||||||
|
/// Configure a device.
|
||||||
|
cmApRC_t cmApNrtDeviceSetup(
|
||||||
|
unsigned devIdx,
|
||||||
|
double srate,
|
||||||
|
unsigned framesPerCycle,
|
||||||
|
cmApCallbackPtr_t callbackPtr,
|
||||||
|
void* userCbPtr );
|
||||||
|
|
||||||
|
/// Start a device. Note that the callback may be made prior to this function returning.
|
||||||
|
cmApRC_t cmApNrtDeviceStart( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Stop a device.
|
||||||
|
cmApRC_t cmApNrtDeviceStop( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Return true if the device is currently started.
|
||||||
|
bool cmApNrtDeviceIsStarted( unsigned devIdx );
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
799
cmAudioPort.c
Normal file
799
cmAudioPort.c
Normal file
@ -0,0 +1,799 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.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 )
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
unsigned di;
|
||||||
|
cmApRC_t rc;
|
||||||
|
|
||||||
|
if( devIdx == cmInvalidIdx )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
|
||||||
|
return cmStringNullGuard(NULL);
|
||||||
|
|
||||||
|
return dp->deviceLabel(di);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmApDeviceChannelCount( unsigned devIdx, bool inputFl )
|
||||||
|
{
|
||||||
|
cmApDriver_t* dp;
|
||||||
|
unsigned di;
|
||||||
|
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;
|
||||||
|
unsigned di;
|
||||||
|
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;
|
||||||
|
unsigned di;
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// 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]
|
||||||
|
|
||||||
|
|
||||||
|
|
148
cmAudioPort.h
Normal file
148
cmAudioPort.h
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/// \file cmAudioPort.h
|
||||||
|
/// \brief Cross platform audio I/O interface.
|
||||||
|
///
|
||||||
|
/// This interface provides data declarations for platform dependent
|
||||||
|
/// audio I/O functions. The implementation for the functions are
|
||||||
|
/// in platform specific modules. See cmAudioPortOsx.c and cmAudioPortAlsa.c.
|
||||||
|
///
|
||||||
|
/// ALSA Notes:
|
||||||
|
/// Assign capture device to line or mic input:
|
||||||
|
/// amixer -c 0 cset iface=MIXER,name='Input Source',index=0 Mic
|
||||||
|
/// amixer -c 0 cset iface=MIXER,name='Input Source',index=0 Line
|
||||||
|
///
|
||||||
|
/// -c 0 select the first card
|
||||||
|
/// -iface=MIXER the cset is targetting the MIXER component
|
||||||
|
/// -name='Input Source',index=0 the control to set is the first 'Input Source'
|
||||||
|
/// Note that the 'Capture' control sets the input gain.
|
||||||
|
///
|
||||||
|
/// See alsamixer for a GUI to accomplish the same thing.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Usage example and testing code:
|
||||||
|
/// See cmApPortTest() and cmAudioSysTest().
|
||||||
|
/// \snippet cmAudioPort.c cmAudioPortExample
|
||||||
|
///
|
||||||
|
#ifndef cmAudioPort_h
|
||||||
|
#define cmAudioPort_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef unsigned cmApRC_t; ///< Audio port interface result code.
|
||||||
|
typedef float cmApSample_t; ///< Audio sample type.
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkApRC =0,
|
||||||
|
kSysErrApRC,
|
||||||
|
kInvalidDevIdApRC,
|
||||||
|
kAudioPortFileFailApRC,
|
||||||
|
kThreadFailApRC
|
||||||
|
};
|
||||||
|
|
||||||
|
// cmApAudioPacket_t flags
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInterleavedApFl = 0x01, ///< The audio samples are interleaved.
|
||||||
|
kFloatApFl = 0x02 ///< The audio samples are single precision floating point values.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Audio packet record used by the cmApAudioPacket_t callback.
|
||||||
|
/// Audio ports send and receive audio using this data structure.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned devIdx; ///< device associated with packet
|
||||||
|
unsigned begChIdx; ///< first device channel
|
||||||
|
unsigned chCnt; ///< count of channels
|
||||||
|
unsigned audioFramesCnt; ///< samples per channel (see note below)
|
||||||
|
unsigned bitsPerSample; ///< bits per sample word
|
||||||
|
unsigned flags; ///< kInterleavedApFl | kFloatApFl
|
||||||
|
void* audioBytesPtr; ///< pointer to sample data
|
||||||
|
void* userCbPtr; ///< user defined value passed in cmApDeviceSetup()
|
||||||
|
} cmApAudioPacket_t;
|
||||||
|
|
||||||
|
|
||||||
|
/// Audio port callback signature.
|
||||||
|
/// inPktArray[inPktCnt] are full packets of audio coming from the ADC to the application.
|
||||||
|
/// outPktArray[outPktCnt] are empty packets of audio which will be filled by the application
|
||||||
|
/// and then sent to the DAC.
|
||||||
|
///
|
||||||
|
/// The value of audioFrameCnt gives the number of samples per channel which are available
|
||||||
|
/// in the packet data buffer 'audioBytesPtr'. The callback function may decrease this number in
|
||||||
|
/// output packets if the number of samples available is less than the size of the buffer.
|
||||||
|
/// It is the responsibility of the calling audio port to notice this change and pass the new,
|
||||||
|
/// decreased number of samples to the hardware.
|
||||||
|
///
|
||||||
|
/// In general it should be assmed that this call is made from a system thread which is not
|
||||||
|
/// the same as the application thread.
|
||||||
|
/// The usual thread safety precautions should therefore be taken if this function implementation
|
||||||
|
/// interacts with data structures also handled by the application. The audio buffer class (\see cmApBuf.h)
|
||||||
|
/// is designed to provide a safe and efficient way to communicate between
|
||||||
|
/// the audio thread and the application.
|
||||||
|
typedef void (*cmApCallbackPtr_t)( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt );
|
||||||
|
|
||||||
|
/// Setup the audio port management object for this machine.
|
||||||
|
cmApRC_t cmApInitialize( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
/// Stop all audio devices and release any resources held
|
||||||
|
/// by the audio port management object.
|
||||||
|
cmApRC_t cmApFinalize();
|
||||||
|
|
||||||
|
/// Return the count of audio devices attached to this machine.
|
||||||
|
unsigned cmApDeviceCount();
|
||||||
|
|
||||||
|
/// Get a textual description of the device at index 'devIdx'.
|
||||||
|
const char* cmApDeviceLabel( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Get the count of audio input or output channesl on device at index 'devIdx'.
|
||||||
|
unsigned cmApDeviceChannelCount( unsigned devIdx, bool inputFl );
|
||||||
|
|
||||||
|
/// Get the current sample rate of a device. Note that if the device has both
|
||||||
|
/// input and output capability then the sample rate is the same for both.
|
||||||
|
double cmApDeviceSampleRate( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Get the count of samples per callback for the input or output for this device.
|
||||||
|
unsigned cmApDeviceFramesPerCycle( unsigned devIdx, bool inputFl );
|
||||||
|
|
||||||
|
/// Configure a device.
|
||||||
|
/// All devices must be setup before they are started.
|
||||||
|
/// framesPerCycle is the requested number of samples per audio callback. The
|
||||||
|
/// actual number of samples made from a callback may be smaller. See the note
|
||||||
|
/// regarding this in cmApAudioPacket_t.
|
||||||
|
/// If the device cannot support the requested configuration then the function
|
||||||
|
/// will return an error code.
|
||||||
|
/// If the device is started when this function is called then it will be
|
||||||
|
/// automatically stopped and then restarted following the reconfiguration.
|
||||||
|
/// If the reconfiguration fails then the device may not be restared.
|
||||||
|
cmApRC_t cmApDeviceSetup(
|
||||||
|
unsigned devIdx,
|
||||||
|
double srate,
|
||||||
|
unsigned framesPerCycle,
|
||||||
|
cmApCallbackPtr_t callbackPtr,
|
||||||
|
void* userCbPtr );
|
||||||
|
|
||||||
|
/// Start a device. Note that the callback may be made prior to this function returning.
|
||||||
|
cmApRC_t cmApDeviceStart( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Stop a device.
|
||||||
|
cmApRC_t cmApDeviceStop( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Return true if the device is currently started.
|
||||||
|
bool cmApDeviceIsStarted( unsigned devIdx );
|
||||||
|
|
||||||
|
/// Print a report of all the current audio device configurations.
|
||||||
|
void cmApReport( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
/// Test the audio port by synthesizing a sine signal or passing audio through
|
||||||
|
/// from the input to the output. This is also a good example of how to
|
||||||
|
/// use all of the functions in the interface.
|
||||||
|
/// Set runFl to false to print a report without starting any audio devices.
|
||||||
|
/// See cmAudiotPortTest.c for usage example for this function.
|
||||||
|
int cmApPortTest(bool runFl, cmRpt_t* rpt, int argc, const char* argv[] );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
280
cmAudioPortFile.c
Normal file
280
cmAudioPortFile.c
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmAudioPort.h"
|
||||||
|
#include "cmAudioPortFile.h"
|
||||||
|
#include "cmAudioFileDev.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmAfdH_t devH;
|
||||||
|
} cmApDev_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmApDev_t* devArray;
|
||||||
|
unsigned devCnt;
|
||||||
|
} cmApf_t;
|
||||||
|
|
||||||
|
cmApf_t* _cmApf = NULL;
|
||||||
|
|
||||||
|
cmApRC_t cmApFileInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
|
||||||
|
{
|
||||||
|
cmApRC_t rc;
|
||||||
|
if((rc = cmApFileFinalize()) != kOkApRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
_cmApf = cmMemAllocZ(cmApf_t,1);
|
||||||
|
|
||||||
|
cmErrSetup(&_cmApf->err,rpt,"Audio Port File");
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApRC_t cmApFileFinalize()
|
||||||
|
{
|
||||||
|
cmApRC_t rc = kOkApRC;
|
||||||
|
|
||||||
|
if( _cmApf == NULL )
|
||||||
|
return kOkApRC;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; i<_cmApf->devCnt; ++i)
|
||||||
|
{
|
||||||
|
cmApRC_t rc0;
|
||||||
|
if((rc0 = cmApFileDeviceDestroy(i)) != kOkApRC )
|
||||||
|
rc = rc0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemPtrFree(&_cmApf->devArray);
|
||||||
|
_cmApf->devCnt = 0;
|
||||||
|
|
||||||
|
cmMemPtrFree(&_cmApf);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmApFileDeviceCreate(
|
||||||
|
const cmChar_t* devLabel,
|
||||||
|
const cmChar_t* iFn,
|
||||||
|
const cmChar_t* oFn,
|
||||||
|
unsigned oBits,
|
||||||
|
unsigned oChCnt )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// find an available device slot
|
||||||
|
for(i=0; i<_cmApf->devCnt; ++i)
|
||||||
|
if( cmAudioFileDevIsValid( _cmApf->devArray[i].devH ) == false )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// if no device slot is availd ...
|
||||||
|
if( i == _cmApf->devCnt )
|
||||||
|
{
|
||||||
|
// ... create a new one
|
||||||
|
_cmApf->devArray = cmMemResizePZ(cmApDev_t, _cmApf->devArray, _cmApf->devCnt+1);
|
||||||
|
++_cmApf->devCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the file device
|
||||||
|
if( cmAudioFileDevInitialize( &_cmApf->devArray[i].devH, devLabel, i, iFn, oFn, oBits, oChCnt, _cmApf->err.rpt ) != kOkAfdRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device initialization failed.");
|
||||||
|
i = cmInvalidIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApRC_t cmApFileDeviceDestroy( unsigned devIdx )
|
||||||
|
{
|
||||||
|
if( cmAudioFileDevFinalize( &_cmApf->devArray[devIdx].devH ) != kOkAfdRC )
|
||||||
|
return cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device finalize failed.");
|
||||||
|
|
||||||
|
return kOkApRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmApFileDeviceCount()
|
||||||
|
{ return _cmApf->devCnt; }
|
||||||
|
|
||||||
|
const char* cmApFileDeviceLabel( unsigned devIdx )
|
||||||
|
{
|
||||||
|
assert( devIdx < cmApFileDeviceCount());
|
||||||
|
return cmAudioFileDevLabel( _cmApf->devArray[devIdx].devH );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmApFileDeviceChannelCount( unsigned devIdx, bool inputFl )
|
||||||
|
{
|
||||||
|
assert( devIdx < cmApFileDeviceCount());
|
||||||
|
return cmAudioFileDevChannelCount( _cmApf->devArray[devIdx].devH, inputFl );
|
||||||
|
}
|
||||||
|
|
||||||
|
double cmApFileDeviceSampleRate( unsigned devIdx )
|
||||||
|
{
|
||||||
|
assert( devIdx < cmApFileDeviceCount());
|
||||||
|
return cmAudioFileDevSampleRate( _cmApf->devArray[devIdx].devH );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmApFileDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
|
||||||
|
{
|
||||||
|
assert( devIdx < cmApFileDeviceCount());
|
||||||
|
return cmAudioFileDevFramesPerCycle( _cmApf->devArray[devIdx].devH, inputFl );
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApRC_t cmApFileDeviceSetup(
|
||||||
|
unsigned devIdx,
|
||||||
|
double srate,
|
||||||
|
unsigned framesPerCycle,
|
||||||
|
cmApCallbackPtr_t callbackPtr,
|
||||||
|
void* userCbPtr )
|
||||||
|
{
|
||||||
|
assert( devIdx < cmApFileDeviceCount());
|
||||||
|
|
||||||
|
if( cmAudioFileDevSetup( _cmApf->devArray[devIdx].devH,srate,framesPerCycle,callbackPtr,userCbPtr) != kOkAfdRC )
|
||||||
|
return cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device setup failed.");
|
||||||
|
|
||||||
|
return kOkApRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApRC_t cmApFileDeviceStart( unsigned devIdx )
|
||||||
|
{
|
||||||
|
assert( devIdx < cmApFileDeviceCount());
|
||||||
|
|
||||||
|
if( cmAudioFileDevStart( _cmApf->devArray[devIdx].devH ) != kOkAfdRC )
|
||||||
|
return cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device setup failed.");
|
||||||
|
|
||||||
|
return kOkApRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmApRC_t cmApFileDeviceStop( unsigned devIdx )
|
||||||
|
{
|
||||||
|
assert( devIdx < cmApFileDeviceCount());
|
||||||
|
|
||||||
|
if( cmAudioFileDevStop( _cmApf->devArray[devIdx].devH ) != kOkAfdRC )
|
||||||
|
return cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device setup failed.");
|
||||||
|
|
||||||
|
return kOkApRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmApFileDeviceIsStarted( unsigned devIdx )
|
||||||
|
{
|
||||||
|
assert( devIdx < cmApFileDeviceCount());
|
||||||
|
return cmAudioFileDevIsStarted( _cmApf->devArray[devIdx].devH );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmApFileReport( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; _cmApf->devCnt; ++i)
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"%i: ",i);
|
||||||
|
cmAudioFileDevReport( _cmApf->devArray[i].devH, rpt );
|
||||||
|
cmRptPrintf(rpt,"\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// device callback function used with cmAudioPortFileTest() note that this assumes
|
||||||
|
// that the packet buffer contain non-interleaved data.
|
||||||
|
void _cmApFileTestCb(
|
||||||
|
cmApAudioPacket_t* inPktArray,
|
||||||
|
unsigned inPktCnt,
|
||||||
|
cmApAudioPacket_t* outPktArray,
|
||||||
|
unsigned outPktCnt )
|
||||||
|
{
|
||||||
|
cmApAudioPacket_t* ip = inPktArray;
|
||||||
|
cmApAudioPacket_t* op = outPktArray;
|
||||||
|
unsigned opi = 0;
|
||||||
|
unsigned ipi = 0;
|
||||||
|
unsigned oci = 0;
|
||||||
|
unsigned ici = 0;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
if( ici == ip->chCnt)
|
||||||
|
{
|
||||||
|
ici = 0;
|
||||||
|
if( ++ipi >= inPktCnt )
|
||||||
|
break;
|
||||||
|
|
||||||
|
ip = inPktArray + ipi;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( oci == op->chCnt )
|
||||||
|
{
|
||||||
|
oci = 0;
|
||||||
|
if( ++opi >= outPktCnt )
|
||||||
|
break;
|
||||||
|
|
||||||
|
ip = outPktArray + opi;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( ip->audioFramesCnt == op->audioFramesCnt );
|
||||||
|
assert( cmIsFlag(ip->flags,kInterleavedApFl)==false && cmIsFlag(ip->flags,kInterleavedApFl)==false );
|
||||||
|
|
||||||
|
cmApSample_t* ibp = ((cmApSample_t*)ip->audioBytesPtr) + (ip->audioFramesCnt*ici);
|
||||||
|
cmApSample_t* obp = ((cmApSample_t*)op->audioBytesPtr) + (op->audioFramesCnt*oci);
|
||||||
|
|
||||||
|
memcpy(obp,ibp,ip->audioFramesCnt*sizeof(cmApSample_t));
|
||||||
|
|
||||||
|
++ici;
|
||||||
|
++oci;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmApFileTest( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
unsigned dev0Idx;
|
||||||
|
|
||||||
|
const cmChar_t* promptStr = "apf> q=quit 1=start 0=stop\n";
|
||||||
|
const cmChar_t* label0 = "file0";
|
||||||
|
const cmChar_t* i0Fn = "/home/kevin/media/audio/McGill-1/1 Audio Track.aiff";
|
||||||
|
const cmChar_t* o0Fn = "/home/kevin/temp/afd1.aif";
|
||||||
|
unsigned o0Bits = 16;
|
||||||
|
unsigned o0ChCnt = 2;
|
||||||
|
double srate = 44100;
|
||||||
|
unsigned framesPerCycle = 512;
|
||||||
|
|
||||||
|
// initialize audio port file API
|
||||||
|
if( cmApFileInitialize(rpt,0) != kOkApRC )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// create an audio port file
|
||||||
|
if((dev0Idx = cmApFileDeviceCreate(label0,i0Fn,o0Fn,o0Bits,o0ChCnt)) == cmInvalidIdx )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
// configure an audio port file
|
||||||
|
if( cmApFileDeviceSetup( dev0Idx, srate, framesPerCycle, _cmApFileTestCb, NULL ) != kOkAfdRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
char c;
|
||||||
|
fputs(promptStr,stderr);
|
||||||
|
fflush(stdin);
|
||||||
|
|
||||||
|
while((c=getchar()) != 'q')
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case '0': cmApFileDeviceStart(dev0Idx); break;
|
||||||
|
case '1': cmApFileDeviceStop(dev0Idx); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs(promptStr,stderr);
|
||||||
|
fflush(stdin);
|
||||||
|
c = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
//cmApFileDeviceDestroy(dev0Idx);
|
||||||
|
|
||||||
|
cmApFileFinalize();
|
||||||
|
|
||||||
|
}
|
47
cmAudioPortFile.h
Normal file
47
cmAudioPortFile.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef cmAudioPortFile_h
|
||||||
|
#define cmAudioPortFile_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
cmApRC_t cmApFileInitialize( cmRpt_t* rpt, unsigned baseApDevIdx );
|
||||||
|
cmApRC_t cmApFileFinalize();
|
||||||
|
bool cmApFileIsValid();
|
||||||
|
|
||||||
|
unsigned cmApFileDeviceCreate(
|
||||||
|
const cmChar_t* devLabel,
|
||||||
|
const cmChar_t* iFn,
|
||||||
|
const cmChar_t* oFn,
|
||||||
|
unsigned oBits,
|
||||||
|
unsigned oChCnt );
|
||||||
|
|
||||||
|
cmApRC_t cmApFileDeviceDestroy( unsigned devIdx );
|
||||||
|
|
||||||
|
unsigned cmApFileDeviceCount();
|
||||||
|
const char* cmApFileDeviceLabel( unsigned devIdx );
|
||||||
|
unsigned cmApFileDeviceChannelCount( unsigned devIdx, bool inputFl );
|
||||||
|
double cmApFileDeviceSampleRate( unsigned devIdx );
|
||||||
|
unsigned cmApFileDeviceFramesPerCycle( unsigned devIdx, bool inputFl );
|
||||||
|
|
||||||
|
cmApRC_t cmApFileDeviceSetup(
|
||||||
|
unsigned devIdx,
|
||||||
|
double srate,
|
||||||
|
unsigned framesPerCycle,
|
||||||
|
cmApCallbackPtr_t callbackPtr,
|
||||||
|
void* userCbPtr );
|
||||||
|
|
||||||
|
|
||||||
|
cmApRC_t cmApFileDeviceStart( unsigned devIdx );
|
||||||
|
cmApRC_t cmApFileDeviceStop( unsigned devIdx );
|
||||||
|
bool cmApFileDeviceIsStarted( unsigned devIdx );
|
||||||
|
void cmApFileReport( cmRpt_t* rpt );
|
||||||
|
void cmApFileTest( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
1415
cmAudioSys.c
Normal file
1415
cmAudioSys.c
Normal file
File diff suppressed because it is too large
Load Diff
298
cmAudioSys.h
Normal file
298
cmAudioSys.h
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
/// \file cmAudioSys.h
|
||||||
|
/// \brief Implements a real-time audio processing engine.
|
||||||
|
///
|
||||||
|
/// The audio system is composed a collection of independent sub-systems.
|
||||||
|
/// Each sub-system maintains a thread which runs asynchrounsly
|
||||||
|
/// from the application, the MIDI devices, and the audio devices.
|
||||||
|
/// To faciliate communication between these components each sub-system maintains
|
||||||
|
/// two thread-safe data buffers one for control information and a second
|
||||||
|
/// for audio data.
|
||||||
|
///
|
||||||
|
/// The audio devices are the primary driver for the system.
|
||||||
|
/// Callbacks from the audio devices (See #cmApCallbackPtr_t)
|
||||||
|
/// inserts incoming audio samples into the audio
|
||||||
|
/// record buffers and extracts samples from the playback buffer.
|
||||||
|
/// When sufficient incoming samples and outgoing empty buffer space exists
|
||||||
|
/// a sub-system thread is waken up by the callback. This triggers a DSP audio
|
||||||
|
/// processing cycle which empties/fills the audio buffers. During a DSP
|
||||||
|
/// processing cycle control messages from the application and MIDI are blocked and
|
||||||
|
/// buffered. Upon completetion of the DSP cycle a control message
|
||||||
|
/// transfer cycles occurs - buffered incoming messages are passed to
|
||||||
|
/// the DSP system and messages originating in the DSP system are
|
||||||
|
/// buffered by the audio system for later pickup by the application
|
||||||
|
/// or MIDI system.
|
||||||
|
///
|
||||||
|
/// Note that control messages that arrive when the DSP cycle is not
|
||||||
|
/// occurring can pass directly through to the DSP system.
|
||||||
|
///
|
||||||
|
/// The DSP system sends messages back to the host by calling
|
||||||
|
/// cmAsDspToHostFunc_t provided by cmAudioSysCtx_t. These
|
||||||
|
/// calls are always made from within an audio system call to
|
||||||
|
/// audio or control update within cmAsCallback_t. cmAsDspToHostFunc_t
|
||||||
|
/// simply stores the message in a message buffer. The host picks
|
||||||
|
/// up the message at some later time when it notices that messages
|
||||||
|
/// are waiting via polling cmAudioSysIsMsgWaiting().
|
||||||
|
///
|
||||||
|
/// Implementation: \n
|
||||||
|
/// The audio sub-systems work by maintaining an internal thread
|
||||||
|
/// which blocks on a mutex condition variable.
|
||||||
|
/// While the thread is blocked the mutex is unlocked allowing messages
|
||||||
|
/// to pass directly through to the DSP procedure via cmAsCallback().
|
||||||
|
///
|
||||||
|
/// Periodic calls from running audio devices update the audio buffer.
|
||||||
|
/// When the audio buffer has input samples waiting and output space
|
||||||
|
/// available the condition variable is signaled, the mutex is
|
||||||
|
/// then automatically locked by the system, and the DSP execution
|
||||||
|
/// procedure is called via cmAsCallback().
|
||||||
|
///
|
||||||
|
/// Messages arriving while the mutex is locked are queued and
|
||||||
|
/// delivered to the DSP procedure at the end of the DSP execution
|
||||||
|
/// procedure.
|
||||||
|
///
|
||||||
|
/// Usage example and testing code:
|
||||||
|
/// See cmAudioSysTest().
|
||||||
|
/// \snippet cmAudioSys.c cmAudioSysTest
|
||||||
|
|
||||||
|
#ifndef cmAudioSys_h
|
||||||
|
#define cmAudioSys_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Audio system result codes
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkAsRC = cmOkRC,
|
||||||
|
kThreadErrAsRC,
|
||||||
|
kMutexErrAsRC,
|
||||||
|
kTsQueueErrAsRC,
|
||||||
|
kMsgEnqueueFailAsRC,
|
||||||
|
kAudioDevSetupErrAsRC,
|
||||||
|
kAudioBufSetupErrAsRC,
|
||||||
|
kAudioDevStartFailAsRC,
|
||||||
|
kAudioDevStopFailAsRC,
|
||||||
|
kBufTooSmallAsRC,
|
||||||
|
kNoMsgWaitingAsRC,
|
||||||
|
kMidiSysFailAsRC,
|
||||||
|
kMsgSerializeFailAsRC,
|
||||||
|
kStateBufFailAsRC,
|
||||||
|
kInvalidArgAsRC,
|
||||||
|
kNotInitAsRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmHandle_t cmAudioSysH_t; ///< Audio system handle type
|
||||||
|
typedef unsigned cmAsRC_t; ///< Audio system result code
|
||||||
|
|
||||||
|
struct cmAudioSysCtx_str;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// DSP system callback function.
|
||||||
|
///
|
||||||
|
/// This is the sole point of entry into the DSP system while the audio system is running.
|
||||||
|
///
|
||||||
|
/// ctxPtr is pointer to a cmAudioSysCtx_t record.
|
||||||
|
///
|
||||||
|
/// This function is called under two circumstances:
|
||||||
|
///
|
||||||
|
/// 1) To notify the DSP system that the audio input/output buffers need to be serviced.
|
||||||
|
/// This is a perioidic request which the DSP system uses as its execution trigger.
|
||||||
|
/// The msgByteCnt argument is set to zero to indicate this type of call.
|
||||||
|
///
|
||||||
|
/// 2) To pass messages from the host application to the DSP system.
|
||||||
|
/// The DSP system is asyncronous with the host because it executes in the audio system thread
|
||||||
|
/// rather than the host thread. The cmAudioSysDeliverMsg() function synchronizes incoming
|
||||||
|
/// messages with the internal audio system thread to prevent thread collisions.
|
||||||
|
///
|
||||||
|
/// Notes:
|
||||||
|
/// This callback is always made with the internal audio system mutex locked.
|
||||||
|
///
|
||||||
|
/// The signal time covered by the callback is from
|
||||||
|
/// ctx->begSmpIdx to ctx->begSmpIdx+cfg->dspFramesPerCycle.
|
||||||
|
///
|
||||||
|
/// The return value is currently not used.
|
||||||
|
typedef cmRC_t (*cmAsCallback_t)(void* ctxPtr, unsigned msgByteCnt, const void* msgDataPtr );
|
||||||
|
|
||||||
|
|
||||||
|
/// Audio device sub-sytem configuration record
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmRpt_t* rpt; ///< system console object
|
||||||
|
unsigned inDevIdx; ///< input audio device
|
||||||
|
unsigned outDevIdx; ///< output audio device
|
||||||
|
bool syncInputFl; ///< true/false sync the DSP update callbacks with audio input/output
|
||||||
|
unsigned msgQueueByteCnt; ///< Size of the internal msg queue used to buffer msgs arriving via cmAudioSysDeliverMsg().
|
||||||
|
unsigned devFramesPerCycle; ///< (512) Audio device samples per channel per device update buffer.
|
||||||
|
unsigned dspFramesPerCycle; ///< (64) Audio samples per channel per DSP cycle.
|
||||||
|
unsigned audioBufCnt; ///< (3) Audio device buffers.
|
||||||
|
double srate; ///< Audio sample rate.
|
||||||
|
} cmAudioSysArgs_t;
|
||||||
|
|
||||||
|
/// Audio sub-system configuration record.
|
||||||
|
/// This record is provided by the host to configure the audio system
|
||||||
|
/// via cmAudioSystemAllocate() or cmAudioSystemInitialize().
|
||||||
|
typedef struct cmAudioSysSubSys_str
|
||||||
|
{
|
||||||
|
cmAudioSysArgs_t args; ///< Audio device configuration
|
||||||
|
cmAsCallback_t cbFunc; ///< DSP system entry point function.
|
||||||
|
void* cbDataPtr; ///< Host provided data for the DSP system callback.
|
||||||
|
} cmAudioSysSubSys_t;
|
||||||
|
|
||||||
|
|
||||||
|
/// Signature of a callback function provided by the audio system to receive messages
|
||||||
|
/// from the DSP system for later dispatch to the host application.
|
||||||
|
/// This declaration is used by the DSP system implementation and the audio system.
|
||||||
|
/// Note that this function is intended to convey one message broken into multiple parts.
|
||||||
|
/// See cmTsQueueEnqueueSegMsg() for the equivalent interface.
|
||||||
|
typedef cmAsRC_t (*cmAsDspToHostFunc_t)(struct cmAudioSysCtx_str* p, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt);
|
||||||
|
|
||||||
|
/// Informational record passed with each call to the DSP callback function cmAsCallback_t
|
||||||
|
typedef struct cmAudioSysCtx_str
|
||||||
|
{
|
||||||
|
void* reserved; ///< used internally by the system
|
||||||
|
|
||||||
|
bool audioRateFl;
|
||||||
|
|
||||||
|
unsigned srcNetNodeId; ///<
|
||||||
|
unsigned asSubIdx; ///< index of the sub-system this DSP process is serving
|
||||||
|
|
||||||
|
cmAudioSysSubSys_t* ss; ///< ptr to a copy of the cfg recd used to initialize the audio system
|
||||||
|
unsigned begSmpIdx; ///< gives signal time as a sample count
|
||||||
|
|
||||||
|
cmAsDspToHostFunc_t dspToHostFunc; ///< Callback used by the DSP process to send messages to the host
|
||||||
|
///< via the audio system. Returns a cmAsRC_t result code.
|
||||||
|
|
||||||
|
///< output (playback) buffers
|
||||||
|
cmSample_t** oChArray; ///< each ele is a ptr to buffer with cfg.dspFramesPerCycle samples
|
||||||
|
unsigned oChCnt; ///< count of output channels (ele's in oChArray[])
|
||||||
|
|
||||||
|
///< input (recording) buffers
|
||||||
|
cmSample_t** iChArray; ///< each ele is a ptr to buffer with cfg.dspFramesPerCycle samples
|
||||||
|
unsigned iChCnt; ///< count of input channels (ele's in iChArray[])
|
||||||
|
|
||||||
|
} cmAudioSysCtx_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const cmChar_t* devLabel;
|
||||||
|
const cmChar_t* inAudioFn;
|
||||||
|
const cmChar_t* outAudioFn;
|
||||||
|
unsigned oBits;
|
||||||
|
unsigned oChCnt;
|
||||||
|
} cmAudioSysFilePort_t;
|
||||||
|
|
||||||
|
|
||||||
|
/// Audio system configuration record used by cmAudioSysAllocate().
|
||||||
|
typedef struct cmAudioSysCfg_str
|
||||||
|
{
|
||||||
|
cmAudioSysSubSys_t* ssArray; ///< sub-system cfg record array
|
||||||
|
unsigned ssCnt; ///< count of sub-systems
|
||||||
|
cmAudioSysFilePort_t* afpArray; ///< audio port file cfg recd array
|
||||||
|
unsigned afpCnt; ///< audio port file cnt
|
||||||
|
unsigned meterMs; ///< Meter sample period in milliseconds
|
||||||
|
void* clientCbData; ///< User arg. for clientCbFunc().
|
||||||
|
cmTsQueueCb_t clientCbFunc; ///< Called by cmAudioSysReceiveMsg() to deliver internally generated msg's to the host.
|
||||||
|
/// Set to NULL if msg's will be directly returned by buffers passed to cmAudioSysReceiveMsg().
|
||||||
|
cmUdpNetH_t netH;
|
||||||
|
} cmAudioSysCfg_t;
|
||||||
|
|
||||||
|
extern cmAudioSysH_t cmAudioSysNullHandle;
|
||||||
|
|
||||||
|
/// Allocate and initialize an audio system as a collection of 'cfgCnt' sub-systems.
|
||||||
|
/// Notes:
|
||||||
|
/// The audio ports system must be initalized (via cmApInitialize()) prior to calling cmAudioSysAllocate().
|
||||||
|
/// The MIDI port system must be initialized (via cmMpInitialize()) prior to calling cmAudioSysAllocate().
|
||||||
|
/// Furthermore cmApFinalize() and cmMpFinalize() cannot be called prior to cmAudioSysFree().
|
||||||
|
/// See cmAudioSystemTest() for a complete example.
|
||||||
|
cmAsRC_t cmAudioSysAllocate( cmAudioSysH_t* hp, cmRpt_t* rpt, const cmAudioSysCfg_t* cfg );
|
||||||
|
|
||||||
|
/// Finalize and release any resources held by the audio system.
|
||||||
|
cmAsRC_t cmAudioSysFree( cmAudioSysH_t* hp );
|
||||||
|
|
||||||
|
/// Returns true if 'h' is a handle which was successfully allocated by cmAudioSysAllocate().
|
||||||
|
bool cmAudioSysHandleIsValid( cmAudioSysH_t h );
|
||||||
|
|
||||||
|
/// Reinitialize a previously allocated audio system. This function
|
||||||
|
/// begins with a call to cmAudioSysFinalize().
|
||||||
|
/// Use cmAudioSysEnable(h,true) to begin processing audio following this call.
|
||||||
|
cmAsRC_t cmAudioSysInitialize( cmAudioSysH_t h, const cmAudioSysCfg_t* cfg );
|
||||||
|
|
||||||
|
/// Complements cmAudioSysInitialize(). In general there is no need to call this function
|
||||||
|
/// since calls to cmAudioSysInitialize() and cmAudioSysFree() automaticatically call it.
|
||||||
|
cmAsRC_t cmAudioSysFinalize( cmAudioSysH_t h );
|
||||||
|
|
||||||
|
/// Returns true if the audio system has been successfully initialized.
|
||||||
|
bool cmAudioSysIsInitialized( cmAudioSysH_t );
|
||||||
|
|
||||||
|
/// Returns true if the audio system is enabled.
|
||||||
|
bool cmAudioSysIsEnabled( cmAudioSysH_t h );
|
||||||
|
|
||||||
|
/// Enable/disable the audio system. Enabling the starts audio stream
|
||||||
|
/// in/out of the system.
|
||||||
|
cmAsRC_t cmAudioSysEnable( cmAudioSysH_t h, bool enableFl );
|
||||||
|
|
||||||
|
/// \name Host to DSP delivery functions
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Deliver a message from the host application to the DSP process. (host -> DSP);
|
||||||
|
/// The message is formed as a concatenation of the bytes in each of the segments
|
||||||
|
/// pointed to by 'msgDataPtrArrary[segCnt][msgByteCntArray[segCnt]'.
|
||||||
|
/// This is the canonical msg delivery function in so far as the other host->DSP
|
||||||
|
/// msg delivery function are written in terms of this function.
|
||||||
|
/// The first 4 bytes in the first segment must contain the index of the audio sub-system
|
||||||
|
/// which is to receive the message.
|
||||||
|
cmAsRC_t cmAudioSysDeliverSegMsg( cmAudioSysH_t h, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt, unsigned srcNetNodeId );
|
||||||
|
|
||||||
|
/// Deliver a single message from the host to the DSP system.
|
||||||
|
cmAsRC_t cmAudioSysDeliverMsg( cmAudioSysH_t h, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId );
|
||||||
|
|
||||||
|
/// Deliver a single message from the host to the DSP system.
|
||||||
|
/// Prior to delivery the 'id' is prepended to the message.
|
||||||
|
cmAsRC_t cmAudioSysDeliverIdMsg( cmAudioSysH_t h, unsigned asSubIdx, unsigned id, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId );
|
||||||
|
///@}
|
||||||
|
|
||||||
|
/// \name DSP to Host message functions
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Is a msg from the DSP waiting to be picked up by the host? (host <- DSP)
|
||||||
|
/// 0 = no msgs are waiting or the msg queue is locked by the DSP process.
|
||||||
|
/// >0 = the size of the buffer required to hold the next msg returned via
|
||||||
|
/// cmAudioSysReceiveMsg().
|
||||||
|
unsigned cmAudioSysIsMsgWaiting( cmAudioSysH_t h );
|
||||||
|
|
||||||
|
/// Copy the next available msg sent from the DSP process to the host into the host supplied msg buffer
|
||||||
|
/// pointed to by 'msgBufPtr'. Set 'msgDataPtr' to NULL to receive msg by callback from cmAudioSysCfg_t.clientCbFunc.
|
||||||
|
/// Returns kBufTooSmallAsRC if msgDataPtr[msgByteCnt] is too small to hold the msg.
|
||||||
|
/// Returns kNoMsgWaitingAsRC if no messages are waiting for delivery or the msg queue is locked by the DSP process.
|
||||||
|
/// Returns kOkAsRC if a msg was delivered.
|
||||||
|
/// Call cmAudioSysIsMsgWaiting() prior to calling this function to get
|
||||||
|
/// the size of the data buffer required to hold the next message.
|
||||||
|
cmAsRC_t cmAudioSysReceiveMsg( cmAudioSysH_t h, void* msgDataPtr, unsigned msgByteCnt );
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
|
/// Fill an audio system status record.
|
||||||
|
void cmAudioSysStatus( cmAudioSysH_t h, unsigned asSubIdx, cmAudioSysStatus_t* statusPtr );
|
||||||
|
|
||||||
|
/// Enable cmAudioSysStatus_t notifications to be sent periodically to the host.
|
||||||
|
/// Set asSubIdx to cmInvalidIdx to enable/disable all sub-systems.
|
||||||
|
/// The notifications occur approximately every cmAudioSysCfg_t.meterMs milliseconds.
|
||||||
|
void cmAudioSysStatusNotifyEnable( cmAudioSysH_t, unsigned asSubIdx, bool enableFl );
|
||||||
|
|
||||||
|
/// Return a pointer the context record associated with a sub-system
|
||||||
|
cmAudioSysCtx_t* cmAudioSysContext( cmAudioSysH_t h, unsigned asSubIdx );
|
||||||
|
|
||||||
|
/// Return the count of audio sub-systems.
|
||||||
|
/// This is the same as the count of cfg recds passed to cmAudioSystemInitialize().
|
||||||
|
unsigned cmAudioSysSubSystemCount( cmAudioSysH_t h );
|
||||||
|
|
||||||
|
/// Audio system test and example function.
|
||||||
|
void cmAudioSysTest( cmRpt_t* rpt, int argc, const char* argv[] );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
87
cmComplexTypes.h
Normal file
87
cmComplexTypes.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#ifndef cmComplexTypes_h
|
||||||
|
#define cmComplexTypes_h
|
||||||
|
|
||||||
|
#include <complex.h>
|
||||||
|
#include <fftw3.h>
|
||||||
|
|
||||||
|
#if CM_FLOAT_SMP == 1
|
||||||
|
|
||||||
|
#define cmCabsS cabsf
|
||||||
|
#define cmCatanS catanf
|
||||||
|
#define cmCrealS crealf
|
||||||
|
#define cmCimagS cimagf
|
||||||
|
#define cmCargS cargf
|
||||||
|
|
||||||
|
#define cmFftPlanAllocS fftwf_plan_dft_r2c_1d
|
||||||
|
#define cmFft1dPlanAllocS fftwf_plan_dft_1d
|
||||||
|
#define cmIFftPlanAllocS fftwf_plan_dft_c2r_1d
|
||||||
|
#define cmFftPlanFreeS fftwf_destroy_plan
|
||||||
|
#define cmFftMallocS fftwf_malloc
|
||||||
|
#define cmFftFreeMemS fftwf_free
|
||||||
|
#define cmFftExecuteS fftwf_execute
|
||||||
|
|
||||||
|
typedef fftwf_plan cmFftPlanS_t;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define cmCabsS cabs
|
||||||
|
#define cmCatanS catan
|
||||||
|
#define cmCrealS creal
|
||||||
|
#define cmCimagS cimag
|
||||||
|
#define cmCargS carg
|
||||||
|
|
||||||
|
#define cmFftPlanAllocS fftw_plan_dft_r2c_1d
|
||||||
|
#define cmFft1dPlanAllocS fftw_plan_dft_1d
|
||||||
|
#define cmIFftPlanAllocS fftw_plan_dft_c2r_1d
|
||||||
|
#define cmFftPlanFreeS fftw_destroy_plan
|
||||||
|
#define cmFftMallocS fftw_malloc
|
||||||
|
#define cmFftFreeMemS fftw_free
|
||||||
|
#define cmFftExecuteS fftw_execute
|
||||||
|
|
||||||
|
typedef fftw_plan cmFftPlanS_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
|
||||||
|
#if CM_FLOAT_REAL == 1
|
||||||
|
|
||||||
|
#define cmCabsR cabsf
|
||||||
|
#define cmCatanR catanf
|
||||||
|
#define cmCrealR crealf
|
||||||
|
#define cmCimagR cimagf
|
||||||
|
#define cmCargR cargf
|
||||||
|
|
||||||
|
#define cmFftPlanAllocR fftwf_plan_dft_r2c_1d
|
||||||
|
#define cmFft1dPlanAllocR fftwf_plan_dft_1d
|
||||||
|
#define cmIFftPlanAllocR fftwf_plan_dft_c2r_1d
|
||||||
|
#define cmFftPlanFreeR fftwf_destroy_plan
|
||||||
|
#define cmFftMallocR fftwf_malloc
|
||||||
|
#define cmFftFreeMemR fftwf_free
|
||||||
|
#define cmFftExecuteR fftwf_execute
|
||||||
|
|
||||||
|
typedef fftwf_plan cmFftPlanR_t;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define cmCabsR cabs
|
||||||
|
#define cmCatanR catan
|
||||||
|
#define cmCrealR creal
|
||||||
|
#define cmCimagR cimag
|
||||||
|
#define cmCargR carg
|
||||||
|
|
||||||
|
#define cmFftPlanAllocR fftw_plan_dft_r2c_1d
|
||||||
|
#define cmFft1dPlanAllocR fftw_plan_dft_1d
|
||||||
|
#define cmIFftPlanAllocR fftw_plan_dft_c2r_1d
|
||||||
|
#define cmFftPlanFreeR fftw_destroy_plan
|
||||||
|
#define cmFftMallocR fftw_malloc
|
||||||
|
#define cmFftFreeMemR fftw_free
|
||||||
|
#define cmFftExecuteR fftw_execute
|
||||||
|
|
||||||
|
typedef fftw_plan cmFftPlanR_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
149
cmCsv.h
Normal file
149
cmCsv.h
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#ifndef cmCsv_h
|
||||||
|
#define cmCsv_h
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkCsvRC = 0,
|
||||||
|
kMemAllocErrCsvRC,
|
||||||
|
kLexErrCsvRC,
|
||||||
|
kSymTblErrCsvRC,
|
||||||
|
kSyntaxErrCsvRC,
|
||||||
|
kFileOpenErrCsvRC,
|
||||||
|
kFileCreateErrCsvRC,
|
||||||
|
kFileReadErrCsvRC,
|
||||||
|
kFileSeekErrCsvRC,
|
||||||
|
kFileCloseErrCsvRC,
|
||||||
|
kDataCvtErrCsvRC,
|
||||||
|
kCellNotFoundCsvRC,
|
||||||
|
kDuplicateLexCsvId
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef unsigned cmCsvRC_t;
|
||||||
|
typedef cmHandle_t cmCsvH_t;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kIntCsvTFl = 0x01,
|
||||||
|
kHexCsvTFl = 0x02,
|
||||||
|
kRealCsvTFl = 0x04,
|
||||||
|
kIdentCsvTFl = 0x08,
|
||||||
|
kStrCsvTFl = 0x10,
|
||||||
|
kUdefCsvTFl = 0x20,
|
||||||
|
|
||||||
|
kNumberTMask = kIntCsvTFl | kHexCsvTFl | kRealCsvTFl,
|
||||||
|
kTextTMask = kIdentCsvTFl | kStrCsvTFl,
|
||||||
|
kTypeTMask = kNumberTMask | kTextTMask
|
||||||
|
};
|
||||||
|
|
||||||
|
// Each non-blank CSV cell is represented by a cmCsvCell_t record.
|
||||||
|
// All the non-blank cells in a given row are organized as a linked
|
||||||
|
// list throught 'rowPtr'.
|
||||||
|
typedef struct cmCsvCell_str
|
||||||
|
{
|
||||||
|
unsigned row; // CSV row number
|
||||||
|
unsigned col; // CSV column number
|
||||||
|
struct cmCsvCell_str* rowPtr; // links together cells in this row
|
||||||
|
|
||||||
|
unsigned symId; // symbol id for this cell
|
||||||
|
unsigned flags; // cell flags (see kXXXCsvTFl)
|
||||||
|
unsigned lexTId;
|
||||||
|
|
||||||
|
} cmCsvCell_t;
|
||||||
|
|
||||||
|
extern cmCsvH_t cmCsvNullHandle;
|
||||||
|
|
||||||
|
cmCsvRC_t cmCsvInitialize( cmCsvH_t *hp, cmCtx_t* ctx );
|
||||||
|
cmCsvRC_t cmCsvFinalize( cmCsvH_t *hp );
|
||||||
|
|
||||||
|
cmCsvRC_t cmCsvInitializeFromFile( cmCsvH_t *hp, const char* fn, unsigned maxRowCnt, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
bool cmCsvIsValid( cmCsvH_t h);
|
||||||
|
cmCsvRC_t cmCsvLastRC( cmCsvH_t h);
|
||||||
|
void cmCsvClearRC( cmCsvH_t h);
|
||||||
|
|
||||||
|
// Register token matchers. See cmLexRegisterToken and cmLexRegisterMatcher
|
||||||
|
// for details.
|
||||||
|
cmCsvRC_t cmCsvLexRegisterToken( cmCsvH_t h, unsigned id, const cmChar_t* token );
|
||||||
|
cmCsvRC_t cmCsvLexRegisterMatcher( cmCsvH_t h, unsigned id, cmLexUserMatcherPtr_t funcPtr );
|
||||||
|
|
||||||
|
// Return the next available lexer token id above the token id's used internally
|
||||||
|
// by the object. This value is fixed after cmCsvInitialize()
|
||||||
|
// and does not change for the life of the CSV object. The application is
|
||||||
|
// therefore free to choose any lexer id values equal to or above the
|
||||||
|
// returned value.
|
||||||
|
unsigned cmCsvLexNextAvailId( cmCsvH_t h );
|
||||||
|
|
||||||
|
// Set 'maxRowCnt' to 0 if there is no row limit on the file.
|
||||||
|
cmCsvRC_t cmCsvParse( cmCsvH_t h, const char* buf, unsigned bufCharCnt, unsigned maxRowCnt );
|
||||||
|
cmCsvRC_t cmCsvParseFile( cmCsvH_t h, const char* fn, unsigned maxRowCnt );
|
||||||
|
|
||||||
|
unsigned cmCsvRowCount( cmCsvH_t h );
|
||||||
|
|
||||||
|
// Return a pointer to a given cell.
|
||||||
|
cmCsvCell_t* cmCsvCellPtr( cmCsvH_t h, unsigned row, unsigned col );
|
||||||
|
|
||||||
|
// Return a pointer to the first cell in a given row
|
||||||
|
cmCsvCell_t* cmCsvRowPtr( cmCsvH_t h, unsigned row );
|
||||||
|
|
||||||
|
|
||||||
|
// Convert a cell symbold id to a value.
|
||||||
|
const char* cmCsvCellSymText( cmCsvH_t h, unsigned symId );
|
||||||
|
cmCsvRC_t cmCsvCellSymInt( cmCsvH_t h, unsigned symId, int* vp );
|
||||||
|
cmCsvRC_t cmCsvCellSymUInt( cmCsvH_t h, unsigned symId, unsigned* vp );
|
||||||
|
cmCsvRC_t cmCsvCellSymFloat( cmCsvH_t h, unsigned symId, float* vp );
|
||||||
|
cmCsvRC_t cmCsvCellSymDouble( cmCsvH_t h, unsigned symId, double* vp );
|
||||||
|
|
||||||
|
// Return the value associated with a cell.
|
||||||
|
const char* cmCsvCellText( cmCsvH_t h, unsigned row, unsigned col ); // Returns NULL on error.
|
||||||
|
int cmCsvCellInt( cmCsvH_t h, unsigned row, unsigned col ); // Returns INT_MAX on error.
|
||||||
|
unsigned cmCsvCellUInt( cmCsvH_t h, unsigned row, unsigned col ); // Returns UINT_MAX on error.
|
||||||
|
float cmCsvCellFloat( cmCsvH_t h, unsigned row, unsigned col ); // Returns FLT_MAX on error.
|
||||||
|
double cmCsvCellDouble(cmCsvH_t h, unsigned row, unsigned col ); // Returns DBL_MAX on error.
|
||||||
|
|
||||||
|
// Insert a value into the internal symbol table.
|
||||||
|
unsigned cmCsvInsertSymText( cmCsvH_t h, const char* text );
|
||||||
|
unsigned cmCsvInsertSymInt( cmCsvH_t h, int v );
|
||||||
|
unsigned cmCsvInsertSymUInt( cmCsvH_t h, unsigned v );
|
||||||
|
unsigned cmCsvInsertSymFloat( cmCsvH_t h, float v );
|
||||||
|
unsigned cmCsvInsertSymDouble( cmCsvH_t h, double v );
|
||||||
|
|
||||||
|
// Set the value associated with a cell.
|
||||||
|
cmCsvRC_t cmCsvSetCellText( cmCsvH_t h, unsigned row, unsigned col, const char* text );
|
||||||
|
cmCsvRC_t cmCsvSetCellInt( cmCsvH_t h, unsigned row, unsigned col, int v );
|
||||||
|
cmCsvRC_t cmCsvSetCellUInt( cmCsvH_t h, unsigned row, unsigned col, unsigned v );
|
||||||
|
cmCsvRC_t cmCsvSetCellFloat( cmCsvH_t h, unsigned row, unsigned col, float v );
|
||||||
|
cmCsvRC_t cmCsvSetCellDouble( cmCsvH_t h, unsigned row, unsigned col, double v );
|
||||||
|
|
||||||
|
// Insert a new row and column 0 cell just above the row assigned to 'row'.
|
||||||
|
// lexTId is an arbitrary id used by the application to set the value of
|
||||||
|
// cmCsvCell_t.lexTId in the new cell. There are no constraints on its value.
|
||||||
|
//cmCsvRC_t cmCsvInsertRowBefore( cmCsvH_t h, unsigned row, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId );
|
||||||
|
|
||||||
|
cmCsvRC_t cmCsvAppendRow( cmCsvH_t h, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId );
|
||||||
|
|
||||||
|
// Insert a new cell to the right of leftCellPtr.
|
||||||
|
// lexTId is an arbitrary id used by the application to set the value of
|
||||||
|
// cmCsvCell_t.lexTId in the new cell. There are no constraints on its value.
|
||||||
|
cmCsvRC_t cmCsvInsertColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId );
|
||||||
|
|
||||||
|
cmCsvRC_t cmCsvInsertTextColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, const char* val, unsigned lexTId );
|
||||||
|
cmCsvRC_t cmCsvInsertIntColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, int val, unsigned lexTId );
|
||||||
|
cmCsvRC_t cmCsvInsertUIntColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned val, unsigned lexTId );
|
||||||
|
cmCsvRC_t cmCsvInsertFloatColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, float val, unsigned lexTId );
|
||||||
|
cmCsvRC_t cmCsvInsertDoubleColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, double val, unsigned lexTId );
|
||||||
|
|
||||||
|
// Write the CSV object out to a file.
|
||||||
|
cmCsvRC_t cmCsvWrite( cmCsvH_t h, const char* fn );
|
||||||
|
|
||||||
|
cmCsvRC_t cmCsvPrint( cmCsvH_t h, unsigned rowCnt );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
22
cmCtx.c
Normal file
22
cmCtx.c
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
|
||||||
|
void cmCtxSetup(
|
||||||
|
cmCtx_t* ctx,
|
||||||
|
const cmChar_t* title,
|
||||||
|
cmRptPrintFunc_t prtFunc,
|
||||||
|
cmRptPrintFunc_t errFunc,
|
||||||
|
void* cbPtr,
|
||||||
|
unsigned guardByteCnt,
|
||||||
|
unsigned alignByteCnt,
|
||||||
|
unsigned mmFlags )
|
||||||
|
{
|
||||||
|
cmRptSetup(&ctx->rpt,prtFunc,errFunc,cbPtr);
|
||||||
|
cmErrSetup(&ctx->err,&ctx->rpt,title);
|
||||||
|
ctx->guardByteCnt = guardByteCnt;
|
||||||
|
ctx->alignByteCnt = alignByteCnt;
|
||||||
|
ctx->mmFlags = mmFlags;
|
||||||
|
}
|
53
cmCtx.h
Normal file
53
cmCtx.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
//{
|
||||||
|
//(
|
||||||
|
// cmCtx_t is used to hold application supplied cmRpt_t, cmErr_t and
|
||||||
|
// other global values for easy distribution throughtout a cm based application.
|
||||||
|
//
|
||||||
|
// Most the cm components need at least an application supplied cmRpt_t function
|
||||||
|
// to initialize their own internal cmErr_t error class. Likewise classes which
|
||||||
|
// use a cmLHeapH_t based internal heap manager require application wide memory
|
||||||
|
// manager configuration information. The cmCtx_t packages this information and
|
||||||
|
// allows it to be easily distributed. The applicaton and its constituent objects
|
||||||
|
// then need only maintain and pass pointers to a single cmCtx_t object to have access to
|
||||||
|
// this all the global program information.
|
||||||
|
//)
|
||||||
|
|
||||||
|
#ifndef cmCtx_h
|
||||||
|
#define cmCtx_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//(
|
||||||
|
|
||||||
|
// cmCtx_t data type.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmRpt_t rpt; //< Application supplied global reporter. This reporter is also use by \ref err.
|
||||||
|
cmErr_t err; //< Application error reporter which can be used to report errors prior to the client object being initialized to the point where it can use it's own cmErr_t.
|
||||||
|
unsigned guardByteCnt; //< Guard byte count in use by \ref cmMallocDebug.h .
|
||||||
|
unsigned alignByteCnt; //< Align byte count used by the \ref cmMallocDebug.h
|
||||||
|
unsigned mmFlags; //< Initialization flags used by \ref cmMallocDebug.h.
|
||||||
|
void* userDefPtr; //< Application defined pointer.
|
||||||
|
} cmCtx_t;
|
||||||
|
|
||||||
|
// cmCtx_t initialization function.
|
||||||
|
void cmCtxSetup(
|
||||||
|
cmCtx_t* ctx, //< The cmCtx_t to initialize.
|
||||||
|
const cmChar_t* title, //< The cmCtx_t error label. See cmErrSetup().
|
||||||
|
cmRptPrintFunc_t prtFunc, //< The printFunc() to assign to the cmCtx_t.rpt.
|
||||||
|
cmRptPrintFunc_t errFunc, //< The errFunc() to assign to cmCtx_t.rpt.
|
||||||
|
void* cbPtr, //< Callback data to use with prtFunc() and errFunc().
|
||||||
|
unsigned guardByteCnt,//< Guard byte count used to configure \ref cmMallocDebug.h
|
||||||
|
unsigned alignByteCnt,//< Align byte count used to configure \ref cmMallocDebug.h
|
||||||
|
unsigned mmFlags //< Initialization flags used to configure \ref cmMallocDebug.h
|
||||||
|
);
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
54
cmDocMain.h
Normal file
54
cmDocMain.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*! \mainpage cm Manual
|
||||||
|
|
||||||
|
To modify this page edit cmDocMain.h
|
||||||
|
|
||||||
|
\section building Building
|
||||||
|
\subsection debug_mode Debug/Release Compile Mode
|
||||||
|
By default the project builds in debug mode. To build in release mode define NDEBUG
|
||||||
|
on the compiler command line. The existence of NDEBUG is tested in cmGlobal.h and
|
||||||
|
the value of the preprocessor variable #cmDEBUG_FL is set to 0 if NDEBUG was defined
|
||||||
|
and 1 otherwise. Code which depends on the debug/release mode then tests the value of
|
||||||
|
#cmDEBUG_FL.
|
||||||
|
|
||||||
|
|
||||||
|
The cm library is a set of C routines for working audio signals.
|
||||||
|
|
||||||
|
\section foundation Foundation
|
||||||
|
\subsection mem Memory Management
|
||||||
|
\subsection output Output and Error Reporting
|
||||||
|
\subsection files File Management
|
||||||
|
\subsection cfg Program Configuration and Data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\subsection step1 Step 1: Opening the box
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
|
||||||
|
\defgroup base Base
|
||||||
|
@{
|
||||||
|
|
||||||
|
@}
|
||||||
|
|
||||||
|
\defgroup rt Real-time
|
||||||
|
@{
|
||||||
|
@}
|
||||||
|
|
||||||
|
\defgroup audio Audio
|
||||||
|
@{
|
||||||
|
|
||||||
|
@}
|
||||||
|
|
||||||
|
\defgroup dsp Signal Processing
|
||||||
|
@{
|
||||||
|
@}
|
||||||
|
|
||||||
|
\defgroup gr Graphics
|
||||||
|
@{
|
||||||
|
@}
|
||||||
|
|
||||||
|
*/
|
137
cmErr.c
Normal file
137
cmErr.c
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
|
||||||
|
void cmErrSetup( cmErr_t* err, cmRpt_t* rpt, const cmChar_t* label )
|
||||||
|
{
|
||||||
|
err->rpt = rpt;
|
||||||
|
err->label = label;
|
||||||
|
err->rc = cmOkRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmErrClone( cmErr_t* dstErr, const cmErr_t* srcErr )
|
||||||
|
{ memcpy(dstErr,srcErr,sizeof(*dstErr)); }
|
||||||
|
|
||||||
|
void _cmErrVMsg(cmErr_t* err, bool warnFl, cmRC_t rc, const cmChar_t* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
if( err->rpt == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const cmChar_t* hdrFmt = warnFl ? "%s warning: " : "%s error: ";
|
||||||
|
const cmChar_t* codeFmt = " (RC:%i)";
|
||||||
|
|
||||||
|
int n0 = snprintf( NULL,0,hdrFmt,cmStringNullGuard(err->label));
|
||||||
|
int n1 = vsnprintf(NULL,0,fmt,vl);
|
||||||
|
int n2 = snprintf( NULL,0,codeFmt,rc);
|
||||||
|
int n = n0+n1+n2+1;
|
||||||
|
cmChar_t s[n];
|
||||||
|
n0 = snprintf(s,n,hdrFmt,cmStringNullGuard(err->label));
|
||||||
|
n0 += vsnprintf(s+n0,n-n0,fmt,vl);
|
||||||
|
n0 += snprintf(s+n0,n-n0,codeFmt,rc);
|
||||||
|
assert(n0 <= n );
|
||||||
|
cmRptErrorf(err->rpt,"%s\n",s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmErrMsg( cmErr_t* err, bool warnFl, cmRC_t rc, const cmChar_t* fmt, ... )
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,fmt);
|
||||||
|
_cmErrVMsg(err,warnFl,rc,fmt,vl);
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmErrVMsg(cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
cmErrSetRC(err,rc);
|
||||||
|
_cmErrVMsg(err,false,rc,fmt,vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmRC_t cmErrMsg( cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, ... )
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,fmt);
|
||||||
|
rc = cmErrVMsg(err,rc,fmt,vl);
|
||||||
|
va_end(vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _cmErrSysVMsg(cmErr_t* err, bool warnFl, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
const char* sysFmt = "\n System Error: (code:%i) %s.";
|
||||||
|
int n0 = snprintf(NULL,0,sysFmt,sysErrCode,strerror(sysErrCode));
|
||||||
|
int n1 = vsnprintf(NULL,0,fmt,vl);
|
||||||
|
int n = n0 + n1 + 1;
|
||||||
|
cmChar_t s[n0+n1+1];
|
||||||
|
|
||||||
|
n0 = snprintf(s,n,sysFmt,sysErrCode,strerror(sysErrCode));
|
||||||
|
n0 += vsnprintf(s+n0,n-n0,fmt,vl);
|
||||||
|
assert( n0 <= n );
|
||||||
|
_cmErrMsg(err,warnFl,rc,s);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmErrVSysMsg(cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
cmErrSetRC(err,rc);
|
||||||
|
_cmErrSysVMsg(err,false,rc,sysErrCode,fmt,vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmErrSysMsg( cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, ... )
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,fmt);
|
||||||
|
rc = cmErrVSysMsg(err,rc,sysErrCode,fmt,vl);
|
||||||
|
va_end(vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmErrWarnVMsg(cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
_cmErrVMsg(err,true,rc,fmt,vl);
|
||||||
|
err->warnRC = rc;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmErrWarnMsg( cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, ... )
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,fmt);
|
||||||
|
rc = cmErrWarnVMsg(err,rc,fmt,vl);
|
||||||
|
va_end(vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmErrWarnVSysMsg(cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
_cmErrSysVMsg(err,true,rc,sysErrCode,fmt,vl);
|
||||||
|
err->warnRC = rc;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmRC_t cmErrWarnSysMsg( cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, ... )
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,fmt);
|
||||||
|
rc = cmErrWarnVSysMsg(err,rc,sysErrCode,fmt,vl);
|
||||||
|
va_end(vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmRC_t cmErrLastRC( cmErr_t* err )
|
||||||
|
{ return err->rc; }
|
||||||
|
|
||||||
|
cmRC_t cmErrSetRC( cmErr_t* err, cmRC_t rc )
|
||||||
|
{
|
||||||
|
cmRC_t retVal = err->rc;
|
||||||
|
err->rc = rc;
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmErrClearRC( cmErr_t* err )
|
||||||
|
{ return cmErrSetRC(err,cmOkRC); }
|
84
cmErr.h
Normal file
84
cmErr.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//{
|
||||||
|
//(
|
||||||
|
// This class is used to format error messages and track the last error generated.
|
||||||
|
//
|
||||||
|
// Most of the cmHandle_t based classes use cmErr_t to format error messages with a
|
||||||
|
// title, maintain the last result code which indicated an error, and to hold
|
||||||
|
// a cmRpt_t object to manage application supplied text printing callbacks.
|
||||||
|
//
|
||||||
|
//)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef cmErr_h
|
||||||
|
#define cmErr_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//(
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmRpt_t* rpt; //< Pointer to a cmRpt_t object which is used to direct error messages to an application supplied console.
|
||||||
|
const cmChar_t* label; //< This field contains a pointer to a text label used to form the error message title.
|
||||||
|
cmRC_t rc; //< This is the last result code passed via one of the cmErrXXXMsg() functions.
|
||||||
|
cmRC_t warnRC; //< Last warning RC
|
||||||
|
} cmErr_t;
|
||||||
|
|
||||||
|
// Setup a cmErr_t record.
|
||||||
|
//
|
||||||
|
// Note that rpt and staticLabelStr must point to client supplied objects
|
||||||
|
// whose lifetime is at least that of this cmErr_t object.
|
||||||
|
void cmErrSetup( cmErr_t* err, cmRpt_t* rpt, const cmChar_t* staticLabelStr );
|
||||||
|
|
||||||
|
// Duplicate a cmErr_t record.
|
||||||
|
void cmErrClone( cmErr_t* dstErr, const cmErr_t* srcErr );
|
||||||
|
|
||||||
|
// Error Reporting functions:
|
||||||
|
// Functions to signal an error. The rc argument is generally specific to the
|
||||||
|
// client class using the error. See the kXXXRC enumerations in the handle based
|
||||||
|
// classes for examples of result codes.
|
||||||
|
cmRC_t cmErrMsg( cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, ... );
|
||||||
|
cmRC_t cmErrVMsg(cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, va_list vl );
|
||||||
|
|
||||||
|
|
||||||
|
// Report Errors which contain accompanying system error codes.
|
||||||
|
// Use these functions when a system error (e.g. Unix errno) gives additional information
|
||||||
|
// about the source of the error.
|
||||||
|
cmRC_t cmErrSysMsg( cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, ... );
|
||||||
|
cmRC_t cmErrVSysMsg(cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl );
|
||||||
|
|
||||||
|
// Warning Reporting functions:
|
||||||
|
// Errors generally result in a task aborting. Warnings are informative but the task is
|
||||||
|
// expected to continue.
|
||||||
|
// Functions to signal a warning. The rc argument is generally specific to the
|
||||||
|
// client class using the error. See the kXXXRC enumerations in the handle based
|
||||||
|
// classes for examples of result codes.
|
||||||
|
cmRC_t cmErrWarnMsg( cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, ... );
|
||||||
|
cmRC_t cmErrWarnVMsg(cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, va_list vl );
|
||||||
|
|
||||||
|
|
||||||
|
// Report warnings which contain accompanying system error codes.
|
||||||
|
// Use these functions when a system error (e.g. Unix errno) gives additional information
|
||||||
|
// about the source of the error.
|
||||||
|
cmRC_t cmErrWarnSysMsg( cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, ... );
|
||||||
|
cmRC_t cmErrWarnVSysMsg(cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl );
|
||||||
|
|
||||||
|
// Return the last recorded RC.
|
||||||
|
cmRC_t cmErrLastRC( cmErr_t* err );
|
||||||
|
|
||||||
|
// Return the last recorded RC and set it to a new value.
|
||||||
|
cmRC_t cmErrSetRC( cmErr_t* err, cmRC_t rc );
|
||||||
|
|
||||||
|
// Return the last recorded RC and set it to cmOkRC.
|
||||||
|
cmRC_t cmErrClearRC( cmErr_t* err );
|
||||||
|
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
2455
cmFeatFile.c
Normal file
2455
cmFeatFile.c
Normal file
File diff suppressed because it is too large
Load Diff
279
cmFeatFile.h
Normal file
279
cmFeatFile.h
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
/// \file cmFeatFile.h
|
||||||
|
/// \brief Audio file acoustic feature analyzer and accompanying file reader.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef cmFeatFile_h
|
||||||
|
#define cmFeatFile_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Result codes for all functions in cmFeatFile.h
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkFtRC = cmOkRC,
|
||||||
|
kCfgParseFailFtRC,
|
||||||
|
kFileSysFailFtRC,
|
||||||
|
kJsonFailFtRC,
|
||||||
|
kDspProcFailFtRC,
|
||||||
|
kDirCreateFailFtRC,
|
||||||
|
kFileNotFoundFtRC,
|
||||||
|
kAudioFileOpenFailFtRC,
|
||||||
|
kFrameFileFailFtRC,
|
||||||
|
kChIdxInvalidFtRC,
|
||||||
|
kParamRangeFtRC,
|
||||||
|
kParamErrorFtRC,
|
||||||
|
kFrameWriteFailFtRC,
|
||||||
|
kEofFtRC,
|
||||||
|
kPlviewFailFtRC,
|
||||||
|
kSerialFailFtRC,
|
||||||
|
kInvalidFeatIdFtRC,
|
||||||
|
kFileFailFtRC,
|
||||||
|
kInvalidFrmIdxFtRC
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Feature Id's
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidFtId, ///< 0
|
||||||
|
kAmplFtId, ///< 1 Fourier transform amplitude
|
||||||
|
kDbAmplFtId, ///< 2 Fourier transform decibel
|
||||||
|
kPowFtId, ///< 3 Fourier transform power
|
||||||
|
kDbPowFtId, ///< 4 Fourier transform power decibel
|
||||||
|
kPhaseFtId, ///< 5 Fourier transform phase (not unwrapped)
|
||||||
|
kBfccFtId, ///< 6 Bark Frequency Cepstral Coeffcients
|
||||||
|
kMfccFtId, ///< 7 Mel Frequency Cepstral Coefficients
|
||||||
|
kCepsFtId, ///< 8 Cepstral Coefficients
|
||||||
|
kConstQFtId, ///< 9 Constant-Q transform
|
||||||
|
kLogConstQFtId, ///< 10 Log Constant-Q transform
|
||||||
|
kRmsFtId, ///< 11 Root means square of the audio signal
|
||||||
|
kDbRmsFtId, ///< 12 RMS in decibels
|
||||||
|
|
||||||
|
kD1AmplFtId, ///< 13 1st order difference over time of the Fourier transform amplitude
|
||||||
|
kD1DbAmplFtId, ///< 14 1st order difference over time of the Fourier transform decibel
|
||||||
|
kD1PowFtId, ///< 15 1st order difference over time of the Fourier transform power
|
||||||
|
kD1DbPowFtId, ///< 16 1st order difference over time of the Fourier transform power decibel
|
||||||
|
kD1PhaseFtId, ///< 17 1st order difference over time of the Fourier transform phase (not unwrapped)
|
||||||
|
kD1BfccFtId, ///< 18 1st order difference over time of the Bark Frequency Cepstral Coeffcients
|
||||||
|
kD1MfccFtId, ///< 19 1st order difference over time of the Mel Frequency Cepstral Coefficients
|
||||||
|
kD1CepsFtId, ///< 20 1st order difference over time of the Cepstral Coefficients
|
||||||
|
kD1ConstQFtId, ///< 21 1st order difference over time of the Constant-Q transform
|
||||||
|
kD1LogConstQFtId, ///< 22 1st order difference over time of the Log Constant-Q transform
|
||||||
|
kD1RmsFtId, ///< 23 1st order difference over time of the Root means square of the audio signal
|
||||||
|
kD1DbRmsFtId, ///< 24 1st order difference over time of the RMS in decibels
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// User defined feature parameters
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned id; ///< feature id
|
||||||
|
unsigned cnt; ///< length of feature vector
|
||||||
|
bool normFl; ///< normalize this feature
|
||||||
|
bool enableFl; ///< true if this feature is enabled
|
||||||
|
} cmFtAttr_t;
|
||||||
|
|
||||||
|
|
||||||
|
/// Skip input audio range record
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned smpIdx; ///< Index of first sample to skip
|
||||||
|
unsigned smpCnt; ///< Count of successive samples to skip.
|
||||||
|
} cmFtSkip_t;
|
||||||
|
|
||||||
|
|
||||||
|
/// Analysis parameters
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char* audioFn; ///< Audio file name.
|
||||||
|
const char* featFn; ///< Feature file name.
|
||||||
|
unsigned chIdx; ///< Audio file channel index
|
||||||
|
cmReal_t wndMs; ///< Length of the analysis window in milliseconds.
|
||||||
|
unsigned hopFact; ///< Analysis window overlap factor 1 = 1:1 2=2:1 ...
|
||||||
|
bool normAudioFl; ///< Normalize the audio over the length of the audio file
|
||||||
|
cmMidiByte_t constQMinPitch; ///< Used to determine the base const-q octave.
|
||||||
|
cmMidiByte_t constQMaxPitch; ///< Used to determine the maximum const-q frequency of interest.
|
||||||
|
unsigned constQBinsPerOctave; ///< Bands per const-q octave.
|
||||||
|
unsigned onsetMedFiltWndSmpCnt; ///< Complex onset median filter
|
||||||
|
cmReal_t onsetThreshold; ///< Complex onset threshold
|
||||||
|
cmReal_t minDb; ///< Fourier Transform magnitude values below minDb are set to minDb.
|
||||||
|
cmReal_t floorThreshDb; ///< Frames with an RMS below this value will be skipped
|
||||||
|
cmFtSkip_t* skipArray; ///< skipArray[skipCnt] user defined sample skip ranges
|
||||||
|
unsigned skipCnt; ///< Count of records in skipArray[].
|
||||||
|
cmFtAttr_t* attrArray; ///< attrArray[attrCnt] user defined parameter array
|
||||||
|
unsigned attrCnt; ///< Count of records in attrArray[].
|
||||||
|
} cmFtParam_t;
|
||||||
|
|
||||||
|
|
||||||
|
/// Feature summary information
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned id; ///< feature id (same as associated cmFtAttr.id)
|
||||||
|
unsigned cnt; ///< length of each feature vector (same as associated cmFtAttr.cnt)
|
||||||
|
|
||||||
|
/// The raw feature summary values are calculated prior to normalization.
|
||||||
|
cmReal_t* rawMinV; ///< Vector of min value over time for each feature element.
|
||||||
|
cmReal_t* rawMaxV; ///< Vector of max value over time for each feature element.
|
||||||
|
cmReal_t* rawAvgV; ///< Vector of avg value over time for each feature element.
|
||||||
|
cmReal_t* rawSdvV; ///< Vector of standard deviation values over time for each feature element.
|
||||||
|
cmReal_t rawMin; ///< Min value of all values for this feature. Equivalent to min(rawMinV).
|
||||||
|
cmReal_t rawMax; ///< Max value of all values for this feature. Equivalent to max(rawMaxV).
|
||||||
|
|
||||||
|
/// normalized feature summary values
|
||||||
|
cmReal_t* normMinV; ///< Vector of min value over time for each feature element.
|
||||||
|
cmReal_t* normMaxV; ///< Vector of max value over time for each feature element.
|
||||||
|
cmReal_t* normAvgV; ///< Vector of avg value over time for each feature element.
|
||||||
|
cmReal_t* normSdvV; ///< Vector of standard deviation values over time for each feature element.
|
||||||
|
cmReal_t normMin; ///< Min value of all values for this feature. Equivalent to min(normMinV).
|
||||||
|
cmReal_t normMax; ///< Max value of all values for this feature. Equivalent to max(rawMaxV).
|
||||||
|
|
||||||
|
} cmFtSumm_t;
|
||||||
|
|
||||||
|
/// Feature file info record
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned frmCnt; ///< count of frames in the file
|
||||||
|
cmReal_t srate; ///< audio sample rate
|
||||||
|
unsigned smpCnt; ///< audio sample count
|
||||||
|
unsigned fftSmpCnt; ///< FFT window length (always power of 2)
|
||||||
|
unsigned hopSmpCnt; ///< audio sample hop count
|
||||||
|
unsigned binCnt; ///< FFT bin count (always fftSmpCnt/2 + 1)
|
||||||
|
unsigned skipFrmCnt; ///< count of frames skipped based on user skip array
|
||||||
|
unsigned floorFrmCnt; ///< count of frames skipped because below floorThreshDb
|
||||||
|
cmFtParam_t param; ///< analysis parameter record used to form this feature file
|
||||||
|
cmFtSumm_t* summArray; ///< summArray[ param.attrCnt ] feature summary information
|
||||||
|
} cmFtInfo_t;
|
||||||
|
|
||||||
|
/// Data structure returned by cmFtReaderAdvance().
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned smpIdx; ///< The audio signal sample index this frames information is based on.
|
||||||
|
unsigned frmIdx; ///< The frame index relative to other frames in this feature file.
|
||||||
|
} cmFtFrameDesc_t;
|
||||||
|
|
||||||
|
typedef cmHandle_t cmFtH_t; ///< Analyzer handle
|
||||||
|
typedef cmHandle_t cmFtFileH_t; ///< Feature file handle.
|
||||||
|
typedef unsigned cmFtRC_t; ///< Result code type used by all functions in cmFeatFile.h.
|
||||||
|
|
||||||
|
extern cmFtH_t cmFtNullHandle; ///< A NULL handle useful for indicating an uninitialized analyzer.
|
||||||
|
extern cmFtFileH_t cmFtFileNullHandle; ///< A NULL handle useful for indicating an uninitialized feature file.
|
||||||
|
|
||||||
|
|
||||||
|
/// Given a feature type id return the associated label.
|
||||||
|
const char* cmFtFeatIdToLabel( unsigned featId );
|
||||||
|
|
||||||
|
/// Given a feature type label return the associated id.
|
||||||
|
unsigned cmFtFeatLabelToId( const char* label );
|
||||||
|
|
||||||
|
/// \name Feature Analyzer Related functions
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/// Initialize the feature analyzer. The memory manager and file system must
|
||||||
|
/// be initialized (cmMdInitialize(), cmFsInitialize()) prior to calling this function.
|
||||||
|
cmFtRC_t cmFtInitialize( cmFtH_t* hp, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
/// Finalize a feature analyzer.
|
||||||
|
cmFtRC_t cmFtFinalize( cmFtH_t* h );
|
||||||
|
|
||||||
|
/// Return true if the handle represents an initialized feature analyzer.
|
||||||
|
bool cmFtIsValid( cmFtH_t h );
|
||||||
|
|
||||||
|
/// Parse a JSON file containing a set of analysis parameters.
|
||||||
|
cmFtRC_t cmFtParse( cmFtH_t h, const char* cfgFn );
|
||||||
|
|
||||||
|
/// Run the analyzer.
|
||||||
|
cmFtRC_t cmFtAnalyze( cmFtH_t h );
|
||||||
|
|
||||||
|
/// If cmFtAnalyze() is being run in a seperate thread this function
|
||||||
|
/// can be used to access the analyzers progress.
|
||||||
|
const char* cmFtAnalyzeProgress( cmFtH_t h, unsigned* passPtr, cmReal_t* percentPtr );
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
/// \name Feature File Related Functions
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/// Open a feature file.
|
||||||
|
/// Note that inforPtrPtr is optional and will be ignored if it is set to NULL.
|
||||||
|
cmFtRC_t cmFtReaderOpen( cmFtH_t h, cmFtFileH_t* hp, const char* featFn, const cmFtInfo_t** infoPtrPtr );
|
||||||
|
|
||||||
|
/// Close a feature file.
|
||||||
|
cmFtRC_t cmFtReaderClose( cmFtFileH_t* hp );
|
||||||
|
|
||||||
|
/// Return true if the handle reprents an open feature file.
|
||||||
|
bool cmFtReaderIsValid( cmFtFileH_t h );
|
||||||
|
|
||||||
|
/// Return the count of features types this file contains.
|
||||||
|
unsigned cmFtReaderFeatCount( cmFtFileH_t h );
|
||||||
|
|
||||||
|
/// Return the feature type id associated with the specified index.
|
||||||
|
unsigned cmFtReaderFeatId( cmFtFileH_t h, unsigned index );
|
||||||
|
|
||||||
|
/// Reset the current file location to the first frame but do not load it.
|
||||||
|
/// The next call to cmFtReadAdvance() will load the next frame.
|
||||||
|
cmFtRC_t cmFtReaderRewind( cmFtFileH_t h );
|
||||||
|
|
||||||
|
/// Make frmIdx the current file location.
|
||||||
|
cmFtRC_t cmFtReaderSeek( cmFtFileH_t h, unsigned frmIdx );
|
||||||
|
|
||||||
|
/// Load the current frame, advance the current file position, and return
|
||||||
|
/// a pointer to a cmFtFrameDesc_t record for the loaded frame.
|
||||||
|
/// Returns kEofFtRC upon reaching end of file.
|
||||||
|
/// The frameDescPtr is optional.
|
||||||
|
cmFtRC_t cmFtReaderAdvance( cmFtFileH_t h, cmFtFrameDesc_t* frameDescPtr );
|
||||||
|
|
||||||
|
/// Returns a pointer to a data matrix in the feature identified by featId in the current feature frame.
|
||||||
|
cmReal_t* cmFtReaderData( cmFtFileH_t h, unsigned featId, unsigned* cntPtr );
|
||||||
|
|
||||||
|
/// Copy the contents of a given set of frames into buf[frmCnt*elePerFrmCnt].
|
||||||
|
cmFtRC_t cmFtReaderCopy( cmFtFileH_t h, unsigned featId, unsigned frmIdx, cmReal_t* buf, unsigned frmCnt, unsigned elePerFrmCnt, unsigned* outEleCntPtr );
|
||||||
|
|
||||||
|
/// Data structure used to specify multiple features for use by cmFtReaderMultiSetup().
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned featId; ///< Feature id of feature to include in the feature vector
|
||||||
|
unsigned cnt; ///< Set to count of feat ele's for this feat. Error if greater than avail. Set to -1 to use all avail ele's.
|
||||||
|
/// returned with actual count used
|
||||||
|
|
||||||
|
unsigned id0; ///< Ignored on input. Used internally by cmFtReaderXXX()
|
||||||
|
unsigned id1; ///< Ignored on input. Used internally by cmFtReaderXXX()
|
||||||
|
} cmFtMulti_t;
|
||||||
|
|
||||||
|
/// Setup an array of cmFtMulti_t records. The cmFtMulti_t array
|
||||||
|
/// used by cmFtReaderMulitData() must be initialized by this function.
|
||||||
|
cmFtRC_t cmFtReaderMultiSetup( cmFtFileH_t h, cmFtMulti_t* multiArray, unsigned multiCnt, unsigned* featVectEleCntPtr );
|
||||||
|
|
||||||
|
/// Fill outV[outN] with a consecutive data from the features specified in the cmFtMulti_t array.
|
||||||
|
/// Use cmFtReaderMultiSetup() to configure the cmFtMulti_t array prior to calling this function.
|
||||||
|
cmFtRC_t cmFtReaderMultiData( cmFtFileH_t h, const cmFtMulti_t* multiArray, unsigned multiCnt, cmReal_t* outV, unsigned outN );
|
||||||
|
|
||||||
|
/// Report summary information for the specified feature.
|
||||||
|
cmFtRC_t cmFtReaderReport( cmFtFileH_t h, unsigned featId );
|
||||||
|
|
||||||
|
/// Identical to cmFtReaderReport() except the feature file is identified from a file name rather than an open cmFtFileH_t.
|
||||||
|
cmFtRC_t cmFtReaderReportFn( cmFtH_t h, const cmChar_t* fn, unsigned featId );
|
||||||
|
|
||||||
|
/// Report feature data for the specified set of feature frames.
|
||||||
|
cmFtRC_t cmFtReaderReportFeature( cmFtFileH_t h, unsigned featId, unsigned frmIdx, unsigned frmCnt );
|
||||||
|
|
||||||
|
/// Write a feature into a binary file.
|
||||||
|
/// Set 'frmCnt' to the cmInvalidCnt to include all frames past frmIdx.
|
||||||
|
/// The first three unsigned values in the output file
|
||||||
|
/// contain the row count, maximum column count, and the count of bytes in each data element (4=float,8=double).
|
||||||
|
/// Each row of the file begins with the count of elements in the row and is followed by a data array.
|
||||||
|
cmFtRC_t cmFtReaderToBinary( cmFtFileH_t h, unsigned featId, unsigned frmIdx, unsigned frmCnt, const cmChar_t* outFn );
|
||||||
|
|
||||||
|
/// Identical to cmFtReaderToBinary() except it takes a feature file name instead of a file handle.
|
||||||
|
cmFtRC_t cmFtReaderToBinaryFn( cmFtH_t h, const cmChar_t* fn, unsigned featId, unsigned frmIdx, unsigned frmCnt, const cmChar_t* outFn );
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
555
cmFile.c
Normal file
555
cmFile.c
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmFile.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include <sys/stat.h>
|
||||||
|
cmFileH_t cmFileNullHandle = { NULL };
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
FILE* fp;
|
||||||
|
cmErr_t err;
|
||||||
|
cmChar_t* fnStr;
|
||||||
|
} cmFile_t;
|
||||||
|
|
||||||
|
cmFile_t* _cmFileHandleToPtr( cmFileH_t h )
|
||||||
|
{
|
||||||
|
cmFile_t* p = (cmFile_t*)h.h;
|
||||||
|
assert(p != NULL);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t _cmFileError( cmFile_t* p, cmFileRC_t rc, int errNumb, const cmChar_t* msg )
|
||||||
|
{
|
||||||
|
if(errNumb == 0)
|
||||||
|
rc = cmErrMsg(&p->err,rc,"%s on file '%s'",msg,p->fnStr);
|
||||||
|
else
|
||||||
|
rc = cmErrMsg(&p->err,rc,"%s on file '%s'\nSystem Msg:%s",msg,p->fnStr,strerror(errNumb));
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileOpen( cmFileH_t* hp, const cmChar_t* fn, enum cmFileOpenFlags_t flags, cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
char mode[] = "/0/0/0";
|
||||||
|
cmFile_t* p = NULL;
|
||||||
|
cmErr_t err;
|
||||||
|
cmFileRC_t rc;
|
||||||
|
|
||||||
|
if((rc = cmFileClose(hp)) != kOkFileRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmErrSetup(&err,rpt,"File");
|
||||||
|
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
if( cmIsFlag(flags,kReadFileFl) )
|
||||||
|
mode[0]='r';
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kWriteFileFl) )
|
||||||
|
mode[0]='w';
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kAppendFileFl) )
|
||||||
|
mode[0]='a';
|
||||||
|
else
|
||||||
|
cmErrMsg(&err,kInvalidFlagFileRC,"File open flags must contain 'kReadFileFl','kWriteFileFl', or 'kAppendFileFl'.");
|
||||||
|
|
||||||
|
if( cmIsFlag(flags,kUpdateFileFl) )
|
||||||
|
mode[1]='+';
|
||||||
|
|
||||||
|
if( fn == NULL )
|
||||||
|
return cmErrMsg(&err,kObjAllocFailFileRC,"File object allocation failed due to empty file name.");
|
||||||
|
|
||||||
|
unsigned byteCnt = sizeof(cmFile_t) + strlen(fn) + 1;
|
||||||
|
|
||||||
|
if((p = (cmFile_t*)cmMemMallocZ(byteCnt)) == NULL )
|
||||||
|
return cmErrMsg(&err,kObjAllocFailFileRC,"File object allocation failed for file '%s'.",cmStringNullGuard(fn));
|
||||||
|
|
||||||
|
cmErrClone(&p->err,&err);
|
||||||
|
|
||||||
|
p->fnStr = (cmChar_t*)(p+1);
|
||||||
|
strcpy(p->fnStr,fn);
|
||||||
|
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if((p->fp = fopen(fn,mode)) == NULL )
|
||||||
|
{
|
||||||
|
cmFileRC_t rc = _cmFileError(p,kOpenFailFileRC,errno,"File open failed");
|
||||||
|
cmMemFree(p);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileClose( cmFileH_t* hp )
|
||||||
|
{
|
||||||
|
if( cmFileIsValid(*hp) == false )
|
||||||
|
return kOkFileRC;
|
||||||
|
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(*hp);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if( p->fp != NULL )
|
||||||
|
if( fclose(p->fp) != 0 )
|
||||||
|
return _cmFileError(p,kCloseFailFileRC,errno,"File close failed");
|
||||||
|
|
||||||
|
cmMemFree(p);
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmFileIsValid( cmFileH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileRead( cmFileH_t h, void* buf, unsigned bufByteCnt )
|
||||||
|
{
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if( fread(buf,bufByteCnt,1,p->fp) != 1 )
|
||||||
|
return _cmFileError(p,kReadFailFileRC,errno,"File read failed");
|
||||||
|
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWrite( cmFileH_t h, const void* buf, unsigned bufByteCnt )
|
||||||
|
{
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if( fwrite(buf,bufByteCnt,1,p->fp) != 1 )
|
||||||
|
return _cmFileError(p,kWriteFailFileRC,errno,"File write failed");
|
||||||
|
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileSeek( cmFileH_t h, enum cmFileSeekFlags_t flags, int offsByteCnt )
|
||||||
|
{
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
unsigned fileflags = 0;
|
||||||
|
|
||||||
|
if( cmIsFlag(flags,kBeginFileFl) )
|
||||||
|
fileflags = SEEK_SET;
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kCurFileFl) )
|
||||||
|
fileflags = SEEK_CUR;
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kEndFileFl) )
|
||||||
|
fileflags = SEEK_END;
|
||||||
|
else
|
||||||
|
return cmErrMsg(&p->err,kInvalidFlagFileRC,"Invalid file seek flag on '%s'.",p->fnStr);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if( fseek(p->fp,offsByteCnt,fileflags) != 0 )
|
||||||
|
return _cmFileError(p,kSeekFailFileRC,errno,"File seek failed");
|
||||||
|
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileTell( cmFileH_t h, long* offsPtr )
|
||||||
|
{
|
||||||
|
assert( offsPtr != NULL );
|
||||||
|
*offsPtr = -1;
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
errno = 0;
|
||||||
|
if((*offsPtr = ftell(p->fp)) == -1)
|
||||||
|
return _cmFileError(p,kTellFailFileRC,errno,"File tell failed");
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool cmFileEof( cmFileH_t h )
|
||||||
|
{ return feof( _cmFileHandleToPtr(h)->fp ) != 0; }
|
||||||
|
|
||||||
|
|
||||||
|
unsigned cmFileByteCount( cmFileH_t h )
|
||||||
|
{
|
||||||
|
struct stat sr;
|
||||||
|
int f;
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
const cmChar_t errMsg[] = "File byte count request failed.";
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
if((f = fileno(p->fp)) == -1)
|
||||||
|
{
|
||||||
|
_cmFileError(p,kHandleInvalidFileRC,errno,errMsg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fstat(f,&sr) == -1)
|
||||||
|
{
|
||||||
|
_cmFileError(p,kStatFailFileRC,errno,errMsg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sr.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmChar_t* cmFileName( cmFileH_t h )
|
||||||
|
{
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
return p->fnStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileFnWrite( const cmChar_t* fn, cmRpt_t* rpt, const void* buf, unsigned bufByteCnt )
|
||||||
|
{
|
||||||
|
cmFileH_t h = cmFileNullHandle;
|
||||||
|
cmFileRC_t rc;
|
||||||
|
|
||||||
|
if((rc = cmFileOpen(&h,fn,kWriteFileFl,rpt)) != kOkFileRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
rc = cmFileWrite(h,buf,bufByteCnt);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
cmFileClose(&h);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmChar_t* _cmFileToBuf( cmFileH_t h, unsigned nn, unsigned* bufByteCntPtr )
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
unsigned n = cmFileByteCount(h);
|
||||||
|
cmChar_t* buf = NULL;
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
|
||||||
|
|
||||||
|
// if the file size calculation is ok
|
||||||
|
if( errno != 0 )
|
||||||
|
{
|
||||||
|
_cmFileError(p,kBufAllocFailFileRC,errno,"Invalid file buffer length.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate the read target buffer
|
||||||
|
if((buf = cmMemAlloc(cmChar_t,n+nn)) == NULL)
|
||||||
|
{
|
||||||
|
_cmFileError(p,kBufAllocFailFileRC,0,"Read buffer allocation failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the file
|
||||||
|
if( cmFileRead(h,buf,n) != kOkFileRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
// zero memory after the file data
|
||||||
|
memset(buf+n,0,nn);
|
||||||
|
|
||||||
|
if( bufByteCntPtr != NULL )
|
||||||
|
*bufByteCntPtr = n;
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( bufByteCntPtr != NULL )
|
||||||
|
*bufByteCntPtr = 0;
|
||||||
|
|
||||||
|
cmMemFree(buf);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmChar_t* _cmFileFnToBuf( const cmChar_t* fn, cmRpt_t* rpt, unsigned nn, unsigned* bufByteCntPtr )
|
||||||
|
{
|
||||||
|
cmFileH_t h = cmFileNullHandle;
|
||||||
|
cmChar_t* buf = NULL;
|
||||||
|
|
||||||
|
if( cmFileOpen(&h,fn,kReadFileFl | kBinaryFileFl,rpt) != kOkFileRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
buf = _cmFileToBuf(h,nn,bufByteCntPtr);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
cmFileClose(&h);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmChar_t* cmFileToBuf( cmFileH_t h, unsigned* bufByteCntPtr )
|
||||||
|
{ return _cmFileToBuf(h,0,bufByteCntPtr); }
|
||||||
|
|
||||||
|
cmChar_t* cmFileFnToBuf( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr )
|
||||||
|
{ return _cmFileFnToBuf(fn,rpt,0,bufByteCntPtr); }
|
||||||
|
|
||||||
|
cmChar_t* cmFileToStr( cmFileH_t h, unsigned* bufByteCntPtr )
|
||||||
|
{ return _cmFileToBuf(h,1,bufByteCntPtr); }
|
||||||
|
|
||||||
|
cmChar_t* cmFileFnToStr( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr )
|
||||||
|
{ return _cmFileFnToBuf(fn,rpt,1,bufByteCntPtr); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileLineCount( cmFileH_t h, unsigned* lineCntPtr )
|
||||||
|
{
|
||||||
|
cmFileRC_t rc = kOkFileRC;
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
unsigned lineCnt = 0;
|
||||||
|
long offs;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
|
||||||
|
assert( lineCntPtr != NULL );
|
||||||
|
*lineCntPtr = 0;
|
||||||
|
|
||||||
|
if((rc = cmFileTell(h,&offs)) != kOkFileRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
c = fgetc(p->fp);
|
||||||
|
|
||||||
|
if( c == EOF )
|
||||||
|
{
|
||||||
|
if( errno )
|
||||||
|
rc =_cmFileError(p,kReadFailFileRC,errno,"File read char failed");
|
||||||
|
else
|
||||||
|
++lineCnt; // add one in case the last line isn't terminated with a '\n'.
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if an end-of-line was encountered
|
||||||
|
if( c == '\n' )
|
||||||
|
++lineCnt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = cmFileSeek(h,kBeginFileFl,offs)) != kOkFileRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
*lineCntPtr = lineCnt;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t _cmFileGetLine( cmFile_t* p, cmChar_t* buf, unsigned* bufByteCntPtr )
|
||||||
|
{
|
||||||
|
// fgets() reads up to n-1 bytes into buf[]
|
||||||
|
if( fgets(buf,*bufByteCntPtr,p->fp) == NULL )
|
||||||
|
{
|
||||||
|
// an read error or EOF condition occurred
|
||||||
|
*bufByteCntPtr = 0;
|
||||||
|
|
||||||
|
if( !feof(p->fp ) )
|
||||||
|
return _cmFileError(p,kReadFailFileRC,errno,"File read line failed");
|
||||||
|
|
||||||
|
return kReadFailFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileGetLine( cmFileH_t h, cmChar_t* buf, unsigned* bufByteCntPtr )
|
||||||
|
{
|
||||||
|
assert( bufByteCntPtr != NULL );
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
unsigned tn = 128;
|
||||||
|
cmChar_t t[ tn ];
|
||||||
|
unsigned on = *bufByteCntPtr;
|
||||||
|
long offs;
|
||||||
|
cmFileRC_t rc;
|
||||||
|
|
||||||
|
// store the current file offset
|
||||||
|
if((rc = cmFileTell(h,&offs)) != kOkFileRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// if no buffer was given then use t[]
|
||||||
|
if( buf == NULL || *bufByteCntPtr == 0 )
|
||||||
|
{
|
||||||
|
*bufByteCntPtr = tn;
|
||||||
|
buf = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill the buffer from the current line
|
||||||
|
if((rc = _cmFileGetLine(p,buf,bufByteCntPtr)) != kOkFileRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// get length of the string in the buffer
|
||||||
|
// (this is one less than the count of bytes written to the buffer)
|
||||||
|
unsigned n = strlen(buf);
|
||||||
|
|
||||||
|
// if the provided buffer was large enough to read the entire string
|
||||||
|
if( on > n+1 )
|
||||||
|
{
|
||||||
|
//*bufByteCntPtr = n+1;
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// the provided buffer was not large enough
|
||||||
|
//
|
||||||
|
|
||||||
|
// m tracks the length of the string
|
||||||
|
unsigned m = n;
|
||||||
|
|
||||||
|
while( n+1 == *bufByteCntPtr )
|
||||||
|
{
|
||||||
|
// fill the buffer from the current line
|
||||||
|
if((rc = _cmFileGetLine(p,buf,bufByteCntPtr)) != kOkFileRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
n = strlen(buf);
|
||||||
|
m += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore the original file offset
|
||||||
|
if((rc = cmFileSeek(h,kBeginFileFl,offs)) != kOkFileRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// add 1 for /0, 1 for /n and 1 to detect buf-too-short
|
||||||
|
*bufByteCntPtr = m+3;
|
||||||
|
|
||||||
|
return kBufTooSmallFileRC;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileGetLineAuto( cmFileH_t h, cmChar_t** bufPtrPtr, unsigned* bufByteCntPtr )
|
||||||
|
{
|
||||||
|
cmFileRC_t rc = kOkFileRC;
|
||||||
|
bool fl = true;
|
||||||
|
cmChar_t* buf = *bufPtrPtr;
|
||||||
|
|
||||||
|
*bufPtrPtr = NULL;
|
||||||
|
|
||||||
|
while(fl)
|
||||||
|
{
|
||||||
|
fl = false;
|
||||||
|
|
||||||
|
switch( rc = cmFileGetLine(h,buf,bufByteCntPtr) )
|
||||||
|
{
|
||||||
|
case kOkFileRC:
|
||||||
|
{
|
||||||
|
*bufPtrPtr = buf;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kBufTooSmallFileRC:
|
||||||
|
buf = cmMemResizeZ(cmChar_t,buf,*bufByteCntPtr);
|
||||||
|
fl = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cmMemFree(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadChar( cmFileH_t h, char* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadUChar( cmFileH_t h, unsigned char* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadShort( cmFileH_t h, short* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadUShort( cmFileH_t h, unsigned short* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadLong( cmFileH_t h, long* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadULong( cmFileH_t h, unsigned long* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadInt( cmFileH_t h, int* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadUInt( cmFileH_t h, unsigned int* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadFloat( cmFileH_t h, float* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadDouble( cmFileH_t h, double* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileReadBool( cmFileH_t h, bool* buf, unsigned cnt )
|
||||||
|
{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteChar( cmFileH_t h, const char* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteUChar( cmFileH_t h, const unsigned char* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteShort( cmFileH_t h, const short* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteUShort( cmFileH_t h, const unsigned short* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteLong( cmFileH_t h, const long* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteULong( cmFileH_t h, const unsigned long* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteInt( cmFileH_t h, const int* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteUInt( cmFileH_t h, const unsigned int* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteFloat( cmFileH_t h, const float* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteDouble( cmFileH_t h, const double* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
cmFileRC_t cmFileWriteBool( cmFileH_t h, const bool* buf, unsigned cnt )
|
||||||
|
{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cmFileRC_t cmFilePrint( cmFileH_t h, const cmChar_t* text )
|
||||||
|
{
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if( fputs(text,p->fp) < 0 )
|
||||||
|
return _cmFileError(p,kPrintFailFileRC,errno,"File print failed");
|
||||||
|
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmFileRC_t cmFileVPrintf( cmFileH_t h, const cmChar_t* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
cmFile_t* p = _cmFileHandleToPtr(h);
|
||||||
|
|
||||||
|
if( vfprintf(p->fp,fmt,vl) < 0 )
|
||||||
|
return _cmFileError(p,kPrintFailFileRC,errno,"File print failed");
|
||||||
|
|
||||||
|
return kOkFileRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmFileRC_t cmFilePrintf( cmFileH_t h, const cmChar_t* fmt, ... )
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,fmt);
|
||||||
|
cmFileRC_t rc = cmFileVPrintf(h,fmt,vl);
|
||||||
|
va_end(vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
203
cmFile.h
Normal file
203
cmFile.h
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
//{
|
||||||
|
//(
|
||||||
|
// File abstraction class which slightly extends the C standard file handling routines
|
||||||
|
// to cm style error handling.
|
||||||
|
//)
|
||||||
|
//
|
||||||
|
#ifndef cmFile_h
|
||||||
|
#define cmFile_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//(
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkFileRC = cmOkRC,
|
||||||
|
kInvalidFlagFileRC,
|
||||||
|
kOpenFailFileRC,
|
||||||
|
kCloseFailFileRC,
|
||||||
|
kReadFailFileRC,
|
||||||
|
kWriteFailFileRC,
|
||||||
|
kSeekFailFileRC,
|
||||||
|
kTellFailFileRC,
|
||||||
|
kPrintFailFileRC,
|
||||||
|
kObjAllocFailFileRC,
|
||||||
|
kHandleInvalidFileRC,
|
||||||
|
kStatFailFileRC,
|
||||||
|
kBufAllocFailFileRC,
|
||||||
|
kBufTooSmallFileRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef unsigned cmFileRC_t;
|
||||||
|
typedef cmHandle_t cmFileH_t;
|
||||||
|
|
||||||
|
extern cmFileH_t cmFileNullHandle;
|
||||||
|
|
||||||
|
// Flags for use with cmFileOpen().
|
||||||
|
enum cmFileOpenFlags_t
|
||||||
|
{
|
||||||
|
kReadFileFl = 0x01, //< Open a file for reading
|
||||||
|
kWriteFileFl = 0x02, //< Create an empty file for writing
|
||||||
|
kAppendFileFl = 0x04, //< Open a file for writing at the end of the file.
|
||||||
|
kUpdateFileFl = 0x08, //< Open a file for reading and writing.
|
||||||
|
kBinaryFileFl = 0x10 //< Open a file for binary (not text) input/output.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Open or create a file.
|
||||||
|
// Equivalent to fopen().
|
||||||
|
// If *hp was not initalized by an earlier call to cmFileOpen() then it should
|
||||||
|
// be set to cmFileNullHandle prior to calling this function. If *hp is a valid handle
|
||||||
|
// then it is automatically finalized by an internal call to cmFileClose() prior to
|
||||||
|
// being re-iniitalized.
|
||||||
|
cmFileRC_t cmFileOpen(
|
||||||
|
cmFileH_t* hp, // Pointer to a client supplied cmFileHandle_t to recieve the handle for the new object.
|
||||||
|
const cmChar_t* fn, // The name of the file to open or create.
|
||||||
|
enum cmFileOpenFlags_t flags, // See cmFileOpenFlags_t
|
||||||
|
cmRpt_t* rpt // The cmRpt_t to use for error reporting
|
||||||
|
);
|
||||||
|
|
||||||
|
// Close a file opened with Equivalent to fclose().
|
||||||
|
cmFileRC_t cmFileClose( cmFileH_t* hp );
|
||||||
|
|
||||||
|
// Return true if the file handle is associated with an open file.
|
||||||
|
bool cmFileIsValid( cmFileH_t h );
|
||||||
|
|
||||||
|
// Read a block bytes from a file. Equivalent to fread().
|
||||||
|
cmFileRC_t cmFileRead( cmFileH_t h, void* buf, unsigned bufByteCnt );
|
||||||
|
|
||||||
|
// Write a block of bytes to a file. Equivalent to fwrite().
|
||||||
|
cmFileRC_t cmFileWrite( cmFileH_t h, const void* buf, unsigned bufByteCnt );
|
||||||
|
|
||||||
|
enum cmFileSeekFlags_t
|
||||||
|
{
|
||||||
|
kBeginFileFl = 0x01,
|
||||||
|
kCurFileFl = 0x02,
|
||||||
|
kEndFileFl = 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the file position indicator. Equivalent to fseek().
|
||||||
|
cmFileRC_t cmFileSeek( cmFileH_t h, enum cmFileSeekFlags_t flags, int offsByteCnt );
|
||||||
|
|
||||||
|
// Return the file position indicator. Equivalent to ftell().
|
||||||
|
cmFileRC_t cmFileTell( cmFileH_t h, long* offsPtr );
|
||||||
|
|
||||||
|
// Return true if the file position indicator is at the end of the file.
|
||||||
|
// Equivalent to feof().
|
||||||
|
bool cmFileEof( cmFileH_t h );
|
||||||
|
|
||||||
|
// Return the length of the file in bytes
|
||||||
|
unsigned cmFileByteCount( cmFileH_t h );
|
||||||
|
|
||||||
|
// Return the file name associated with a file handle.
|
||||||
|
const cmChar_t* cmFileName( cmFileH_t h );
|
||||||
|
|
||||||
|
// Write a buffer to a file.
|
||||||
|
cmFileRC_t cmFileFnWrite( const cmChar_t* fn, cmRpt_t* rpt, const void* buf, unsigned bufByteCnt );
|
||||||
|
|
||||||
|
// Allocate and fill a buffer from the file.
|
||||||
|
// Set *bufByteCntPtr to count of bytes read into the buffer.
|
||||||
|
// 'bufByteCntPtr' is optional - set it to NULL if it is not required by the caller.
|
||||||
|
// It is the callers responsibility to delete the returned buffer with a
|
||||||
|
// call to cmMemFree()
|
||||||
|
cmChar_t* cmFileToBuf( cmFileH_t h, unsigned* bufByteCntPtr );
|
||||||
|
|
||||||
|
// Same as cmFileToBuf() but accepts a file name argument.
|
||||||
|
// 'rpt' is the report object to use for error reporting.
|
||||||
|
cmChar_t* cmFileFnToBuf( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr );
|
||||||
|
|
||||||
|
// Allocate and fill a zero terminated string from a file.
|
||||||
|
// Set *bufByteCntPtr to count of bytes read into the buffer.=
|
||||||
|
// (the buffer memory size is one byte larger to account for the terminating zero)
|
||||||
|
// 'bufByteCntPtr' is optional - set it to NULL if it is not required by the caller.
|
||||||
|
// It is the callers responsibility to delete the returned buffer with a
|
||||||
|
// call to cmMemFree()
|
||||||
|
cmChar_t* cmFileToStr( cmFileH_t h, unsigned* bufByteCntPtr );
|
||||||
|
|
||||||
|
// Same as cmFileToBuf() but accepts a file name argument.
|
||||||
|
// 'rpt' is the report object to use for error reporting.
|
||||||
|
cmChar_t* cmFileFnToStr( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr );
|
||||||
|
|
||||||
|
// Return the count of lines in a file.
|
||||||
|
cmFileRC_t cmFileLineCount( cmFileH_t h, unsigned* lineCntPtr );
|
||||||
|
|
||||||
|
// Read the next line into buf[bufByteCnt].
|
||||||
|
// Consider using cmFileGetLineAuto() as an alternative to this function
|
||||||
|
// to avoid having to use a buffer with an explicit size.
|
||||||
|
//
|
||||||
|
// If buf is not long enough to hold the entire string then
|
||||||
|
//
|
||||||
|
// 1. The function returns kFileBufTooSmallRC
|
||||||
|
// 2. *bufByteCntPtr is set to the size of the required buffer.
|
||||||
|
// 3. The internal file position is left unchanged.
|
||||||
|
//
|
||||||
|
// If the buffer is long enough to hold the entire line then
|
||||||
|
// *bufByteCntPtr is left unchanged.
|
||||||
|
// See cmFileGetLineTest() in cmProcTest.c or cmFileGetLineAuto()
|
||||||
|
// in cmFile.c for examples of how to use this function to a
|
||||||
|
// achieve proper buffer sizing.
|
||||||
|
cmFileRC_t cmFileGetLine( cmFileH_t h, cmChar_t* buf, unsigned* bufByteCntPtr );
|
||||||
|
|
||||||
|
// A version of cmFileGetLine() which eliminates the need to handle buffer
|
||||||
|
// sizing.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// cmChar_t* buf = NULL;
|
||||||
|
// unsigned bufByteCnt = 0;
|
||||||
|
// while(cmFileGetLineAuto(h,&buf,&bufByteCnt)==kOkFileRC)
|
||||||
|
// proc(buf);
|
||||||
|
// cmMemPtrFree(buf);
|
||||||
|
//
|
||||||
|
// On the first call to this function *bufPtrPtr must be set to NULL and
|
||||||
|
// *bufByteCntPtr must be set to 0.
|
||||||
|
// Following the last call to this function call cmMemPtrFree(bufPtrptr)
|
||||||
|
// to be sure the line buffer is fully released. Note this step is not
|
||||||
|
// neccessary if the last call does not return kOkFileRC.
|
||||||
|
cmFileRC_t cmFileGetLineAuto( cmFileH_t h, cmChar_t** bufPtrPtr, unsigned* bufByteCntPtr );
|
||||||
|
|
||||||
|
// Binary Array Reading Functions
|
||||||
|
// Each of these functions reads a block of binary data from a file.
|
||||||
|
// The advantage to using these functions over cmFileRead() is only that they are type specific.
|
||||||
|
cmFileRC_t cmFileReadChar( cmFileH_t h, char* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadUChar( cmFileH_t h, unsigned char* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadShort( cmFileH_t h, short* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadUShort( cmFileH_t h, unsigned short* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadLong( cmFileH_t h, long* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadULong( cmFileH_t h, unsigned long* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadInt( cmFileH_t h, int* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadUInt( cmFileH_t h, unsigned int* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadFloat( cmFileH_t h, float* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadDouble( cmFileH_t h, double* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileReadBool( cmFileH_t h, bool* buf, unsigned cnt );
|
||||||
|
|
||||||
|
// Binary Array Writing Functions
|
||||||
|
// Each of these functions writes an array to a binary file.
|
||||||
|
// The advantage to using functions rather than cmFileWrite() is only that they are type specific.
|
||||||
|
cmFileRC_t cmFileWriteChar( cmFileH_t h, const char* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteUChar( cmFileH_t h, const unsigned char* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteShort( cmFileH_t h, const short* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteUShort( cmFileH_t h, const unsigned short* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteLong( cmFileH_t h, const long* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteULong( cmFileH_t h, const unsigned long* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteInt( cmFileH_t h, const int* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteUInt( cmFileH_t h, const unsigned int* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteFloat( cmFileH_t h, const float* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteDouble( cmFileH_t h, const double* buf, unsigned cnt );
|
||||||
|
cmFileRC_t cmFileWriteBool( cmFileH_t h, const bool* buf, unsigned cnt );
|
||||||
|
|
||||||
|
// Formatted Text Output Functions:
|
||||||
|
// Print formatted text to a file.
|
||||||
|
cmFileRC_t cmFilePrint( cmFileH_t h, const cmChar_t* text );
|
||||||
|
cmFileRC_t cmFilePrintf( cmFileH_t h, const cmChar_t* fmt, ... );
|
||||||
|
cmFileRC_t cmFileVPrintf( cmFileH_t h, const cmChar_t* fmt, va_list vl );
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
1285
cmFileSys.c
Normal file
1285
cmFileSys.c
Normal file
File diff suppressed because it is too large
Load Diff
231
cmFileSys.h
Normal file
231
cmFileSys.h
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
//{
|
||||||
|
//(
|
||||||
|
// A collection of file system utility functions.
|
||||||
|
//
|
||||||
|
// Note that cmFileSysInitialize() creates an internal cmLHeapH_t based
|
||||||
|
// heap manager from which it allocates memory for some returned objects.
|
||||||
|
// (e.g. cmFileSysMakeFn(), cmFileSysPathParts(), cmFileSysDirEntries())
|
||||||
|
// Where possible the client can explicitely free these objects via the
|
||||||
|
// provided functions. (e.g. cmFileSysFreeFn(), cmFileSysFreePathParts(), cmFileSysDirFreeEntries())
|
||||||
|
// However if these objects are not free they will be automatically deallocated
|
||||||
|
// when the internal heap is destroyed by cmFileSysFinalize().
|
||||||
|
//
|
||||||
|
//)
|
||||||
|
|
||||||
|
#ifndef cmFileSys_h
|
||||||
|
#define cmFileSys_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//(
|
||||||
|
// Result codes returned by cmFileSys.h functions
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkFsRC = cmOkRC,
|
||||||
|
kMemAllocErrFsRC,
|
||||||
|
kLHeapAllocErrFsRC,
|
||||||
|
kStatFailFsRC,
|
||||||
|
kAssertFailFsRC,
|
||||||
|
kOpenDirFailFsRC,
|
||||||
|
kFnTooLongFsRC,
|
||||||
|
kMkDirFailFsRC,
|
||||||
|
kSysErrFsRC,
|
||||||
|
kOsxFailFsRC,
|
||||||
|
kLinuxFailFsRC,
|
||||||
|
kInvalidDirFsRC
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmHandle_t cmFileSysH_t; //< Opaque handle type used by all cmFileSys.h functions
|
||||||
|
typedef unsigned cmFsRC_t; //< Result code used as the return type by many cmFileSys.h functions.
|
||||||
|
|
||||||
|
extern cmFileSysH_t cmFileSysNullHandle; // NULL handle to be used for setting cmFileSysH_t type handles to an explicit uninitialized value.
|
||||||
|
|
||||||
|
// Initialize a file system object.
|
||||||
|
// If *hp was not initalized by an earlier call to cmFileSysInitialize() then it should
|
||||||
|
// be set to cmFileSysNullHandle prior to calling this function. If *hp is a valid handle
|
||||||
|
// then it is automatically finalized by an internal call to cmFileSysFinalize() prior to
|
||||||
|
// being re-iniitalized.
|
||||||
|
// The appNameStr is used to determine the location of the preference and resource directories
|
||||||
|
// on some platforms
|
||||||
|
cmFsRC_t cmFileSysInitialize( cmFileSysH_t* hp, cmCtx_t* ctx, const cmChar_t* appNameStr );
|
||||||
|
|
||||||
|
// Finalize a file system object.
|
||||||
|
// Upon successful completion *hp is set to cmFileSysNullHandle.
|
||||||
|
cmFsRC_t cmFileSysFinalize( cmFileSysH_t* hp );
|
||||||
|
|
||||||
|
// Returns true if the file system handle is active and initialized.
|
||||||
|
bool cmFileSysIsValid( cmFileSysH_t h );
|
||||||
|
|
||||||
|
const cmChar_t* cmFileSysPrefsDir( cmFileSysH_t h ); //< Return the operating system dependent preference data directory for this application.
|
||||||
|
const cmChar_t* cmFileSysRsrcDir( cmFileSysH_t h ); //< Return the operating system dependent application resource directory for this application.
|
||||||
|
const cmChar_t* cmFileSysUserDir( cmFileSysH_t h ); //< Return the operating system dependent user directory for this application.
|
||||||
|
|
||||||
|
// Test the type of a file system object:
|
||||||
|
//
|
||||||
|
bool cmFileSysIsDir( cmFileSysH_t h, const cmChar_t* dirStr ); //< Return true if 'dirStr' refers to an existing directory.
|
||||||
|
bool cmFileSysIsFile( cmFileSysH_t h, const cmChar_t* fnStr ); //< Return true if 'fnStr' refers to an existing file.
|
||||||
|
bool cmFileSysIsLink( cmFileSysH_t h, const cmChar_t* fnStr ); //< Return true if 'fnStr' refers to a symbolic link.
|
||||||
|
|
||||||
|
// Create File Names:
|
||||||
|
//
|
||||||
|
// Create a file name by concatenating sub-strings.
|
||||||
|
//
|
||||||
|
// Variable arg's. entries are directories inserted between
|
||||||
|
// 'dirPrefixStr' and the file name.
|
||||||
|
// Terminate var arg's directory list with a NULL.
|
||||||
|
//
|
||||||
|
// The returned string is allocated in a local heap maintained by the cmFileSys object.
|
||||||
|
// The memory used by the string will exist until it is released with cmFileSysFreeFn()
|
||||||
|
// or the cmFileSys object is finalized.
|
||||||
|
const cmChar_t* cmFileSysMakeFn( cmFileSysH_t h, const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, ... );
|
||||||
|
|
||||||
|
// Same as cmFileSysMakeFn with but with a va_list argument to accept the var. args. parameters.
|
||||||
|
const cmChar_t* cmFileSysVMakeFn( cmFileSysH_t h, const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, va_list vl );
|
||||||
|
|
||||||
|
// Release the file name created through an earlier call to cmFileSysMakeFn().
|
||||||
|
void cmFileSysFreeFn( cmFileSysH_t h, const cmChar_t* fn );
|
||||||
|
|
||||||
|
// Create a directory - where the entire path already exists except for the
|
||||||
|
// final directory.
|
||||||
|
cmFsRC_t cmFileSysMkDir( cmFileSysH_t h, const cmChar_t* dir );
|
||||||
|
|
||||||
|
// Create a complete directory path - where any of the path segments may
|
||||||
|
// not already exist.
|
||||||
|
cmFsRC_t cmFileSysMkDirAll( cmFileSysH_t h, const cmChar_t* dir );
|
||||||
|
|
||||||
|
// Parse a path into its parts:
|
||||||
|
//
|
||||||
|
// Return record used by cmFileSysParts()
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const cmChar_t* dirStr;
|
||||||
|
const cmChar_t* fnStr;
|
||||||
|
const cmChar_t* extStr;
|
||||||
|
} cmFileSysPathPart_t;
|
||||||
|
|
||||||
|
// Given a file name decompose it into a directory string, file name string and file extension string.
|
||||||
|
// The cmFileSysPathPart_t record and the memory used by the strings that it references
|
||||||
|
// are allocated from a local heap maintained by the cmFileSys object. This memory will exist
|
||||||
|
// until it is released with cmFileSysFreePathParts() or the cmFileSysH_t handle is finalized.
|
||||||
|
cmFileSysPathPart_t* cmFileSysPathParts( cmFileSysH_t h, const cmChar_t* pathNameStr );
|
||||||
|
|
||||||
|
// Free the memory associated with a cmFileSysPathPart_t record returned from an eariler call to cmFileSysPathParts().
|
||||||
|
void cmFileSysFreePathParts( cmFileSysH_t h, cmFileSysPathPart_t* p );
|
||||||
|
|
||||||
|
// Return the parts of a directory path as an array of strings.
|
||||||
|
// The last element in the array is set to NULL to mark the end of the array.
|
||||||
|
// Note that all '/' separator characters are removed from the result with
|
||||||
|
// the exception of the first one - which denotes the root directory.
|
||||||
|
// The returned array is allocated from the file systems internal heap and will
|
||||||
|
// be automatically released when the file system is closed by cmFileSysDestroy().
|
||||||
|
// The caller may optionally release the array memory with a call to
|
||||||
|
// cmFileSysFreeDirParts().
|
||||||
|
cmChar_t** cmFileSysDirParts( cmFileSysH_t h, const cmChar_t* dirStr );
|
||||||
|
void cmFileSysFreeDirParts( cmFileSysH_t h, cmChar_t** dirStrArray );
|
||||||
|
|
||||||
|
// Return the count of elements in a directory parts array as returned by
|
||||||
|
// cmFileSysDirParts().
|
||||||
|
unsigned cmFileSysDirPartsCount(cmFileSysH_t h, cmChar_t** dirStrArray );
|
||||||
|
|
||||||
|
// Form a directory string from a NULL terminated array of strings.
|
||||||
|
// If the first element in the array is set to '/' then the
|
||||||
|
// resulting directory will be absolute rather than relative.
|
||||||
|
// The returned string is allocated from the file systems internal heap and will
|
||||||
|
// be automatically released when the file system is closed by cmFileSysDestroy().
|
||||||
|
// The caller may optionally release the array memory with a call to
|
||||||
|
// cmFileSysFreeDir().
|
||||||
|
cmChar_t* cmFileSysFormDir( cmFileSysH_t h, cmChar_t** dirStrArray, unsigned n );
|
||||||
|
void cmFileSysFreeDir( cmFileSysH_t h, const cmChar_t* dir );
|
||||||
|
|
||||||
|
// Walk a directory tree:
|
||||||
|
//
|
||||||
|
// Flags used by cmFileSysDirEntries 'includeFlags' parameter.
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kFileFsFl = 0x01, //< include all visible files
|
||||||
|
kDirFsFl = 0x02, //< include all visible directory
|
||||||
|
kInvisibleFsFl = 0x04, //< include file/dir name beginning with a '.'
|
||||||
|
kCurDirFsFl = 0x08, //< include '.' directory
|
||||||
|
kParentDirFsFl = 0x10, //< include '..' directory
|
||||||
|
|
||||||
|
kAllFsFl = 0x1f, //< all type flags
|
||||||
|
|
||||||
|
kFullPathFsFl = 0x40, //< return the full path in the 'name' field of cmFileSysDirEntry_t;
|
||||||
|
kRecurseFsFl = 0x80 //< recurse into directories
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// The return type for cmFileSysDirEntries().
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned flags; //< Entry type flags from kXXXFsFl.
|
||||||
|
const cmChar_t* name; //< Entry name or full path depending on kFullPathFsFl.
|
||||||
|
} cmFileSysDirEntry_t;
|
||||||
|
|
||||||
|
// Return the file and directory names contained in a given subdirectory.
|
||||||
|
//
|
||||||
|
// Set 'includeFlags' with the kXXXFsFl flags of the files to include in the returned
|
||||||
|
// directory entry array. The value pointed to by dirEntryCntPtr will be set to the
|
||||||
|
// number of records in the returned array.
|
||||||
|
cmFileSysDirEntry_t* cmFileSysDirEntries( cmFileSysH_t h, const cmChar_t* dirStr, unsigned includeFlags, unsigned* dirEntryCntPtr );
|
||||||
|
|
||||||
|
// Release the memory assoicated with a cmFileSysDirEntry_t array returned from an earlier call to cmFileSysDirEntries().
|
||||||
|
void cmFileSysDirFreeEntries( cmFileSysH_t h, cmFileSysDirEntry_t* p );
|
||||||
|
|
||||||
|
// Return the last error code generated by the file system.
|
||||||
|
cmFsRC_t cmFileSysErrorCode( cmFileSysH_t h );
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Global file system functions:
|
||||||
|
// These functions work using a global cmFileSysH created by cmFsInitialize().
|
||||||
|
// The functions are otherwise just wrappers for the same named function above.
|
||||||
|
|
||||||
|
cmFsRC_t cmFsInitialize( cmCtx_t* ctx, const cmChar_t* appNameStr );
|
||||||
|
cmFsRC_t cmFsFinalize();
|
||||||
|
|
||||||
|
const cmChar_t* cmFsPrefsDir();
|
||||||
|
const cmChar_t* cmFsRsrcDir();
|
||||||
|
const cmChar_t* cmFsUserDir();
|
||||||
|
|
||||||
|
bool cmFsIsDir( const cmChar_t* dirStr );
|
||||||
|
bool cmFsIsFile( const cmChar_t* fnStr );
|
||||||
|
bool cmFsIsLink( const cmChar_t* fnStr );
|
||||||
|
|
||||||
|
const cmChar_t* cmFsVMakeFn( const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, va_list vl );
|
||||||
|
const cmChar_t* cmFsMakeFn( const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, ... );
|
||||||
|
void cmFsFreeFn( const cmChar_t* fn );
|
||||||
|
|
||||||
|
cmFsRC_t cmFsMkDir( const cmChar_t* dir );
|
||||||
|
cmFsRC_t cmFsMkDirAll( const cmChar_t* dir );
|
||||||
|
|
||||||
|
cmFileSysPathPart_t* cmFsPathParts( const cmChar_t* pathNameStr );
|
||||||
|
void cmFsFreePathParts( cmFileSysPathPart_t* p );
|
||||||
|
|
||||||
|
|
||||||
|
cmChar_t** cmFsDirParts( const cmChar_t* dirStr );
|
||||||
|
void cmFsFreeDirParts( cmChar_t** dirStrArray );
|
||||||
|
unsigned cmFsDirPartsCount( cmChar_t** dirStrArray );
|
||||||
|
cmChar_t* cmFsFormDir( cmChar_t** dirStrArray, unsigned n );
|
||||||
|
void cmFsFreeDir( const cmChar_t* dir );
|
||||||
|
|
||||||
|
cmFileSysDirEntry_t* cmFsDirEntries( const cmChar_t* dirStr, unsigned includeFlags, unsigned* dirEntryCntPtr );
|
||||||
|
void cmFsDirFreeEntries( cmFileSysDirEntry_t* p );
|
||||||
|
|
||||||
|
cmFsRC_t cmFsErrorCode();
|
||||||
|
|
||||||
|
|
||||||
|
// Test and example function to demonstrate the use of the functions in cmFileSys.h
|
||||||
|
cmFsRC_t cmFileSysTest( cmCtx_t* ctx );
|
||||||
|
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
81
cmFloatTypes.h
Normal file
81
cmFloatTypes.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/// \file cmFloatTypes.h
|
||||||
|
/// \brief Declare the types cmReal_t and cmSample_t and define some useful limits.
|
||||||
|
///
|
||||||
|
/// For signal processing functions the cm library uses the types cmSample_t to indicate an audio
|
||||||
|
/// sample value and cmReal_t to specify a general purpose floating point value. The library
|
||||||
|
/// is designed in such a way that the actual type, float or double, for these two types may
|
||||||
|
/// be set at compilation time. Set the preprocessor variable CM_FLOAT_SMP to 1 to indicate
|
||||||
|
/// that cmSample_t will be of type 'float' otherwise it will be of type 'double'.
|
||||||
|
/// Set the preprocessor variable CM_FLOAT_REAL to 1 to indicate
|
||||||
|
/// that cmSample_t will be of type 'float' otherwise it will be of type 'double'.
|
||||||
|
/// By default cmSample_t is float nad cmReal_t is double.
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef cmFloatTypes_h
|
||||||
|
#define cmFloatTypes_h
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
#ifndef CM_FLOAT_SMP
|
||||||
|
#define CM_FLOAT_SMP 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CM_FLOAT_SMP == 1
|
||||||
|
|
||||||
|
typedef float cmSample_t; ///< cmSample_t is a float
|
||||||
|
typedef float _Complex cmComplexS_t;///< cmComplexS_t is single precision.
|
||||||
|
|
||||||
|
#define cmSample_EPSILON FLT_EPSILON ///< Minimum increment between 1.0 and the next greaterv value. (1E-5)
|
||||||
|
#define cmSample_MAX FLT_MAX ///< Maximum representable number (1E+37).
|
||||||
|
#define cmSample_MIN FLT_MIN ///< Minimum representable number (1E-37).
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef double cmSample_t; ///< cmSample_t is a double
|
||||||
|
typedef double _Complex cmComplexS_t; ///< cmComplexS_t is doulbe precision.
|
||||||
|
|
||||||
|
#define cmSample_EPSILON DBL_EPSILON ///< Minimum increment between 1.0 and the next greaterv value. (1E-9)
|
||||||
|
#define cmSample_MAX DBL_MAX ///< Maximum representable number (1E+37).
|
||||||
|
#define cmSample_MIN DBL_MIN ///< Minimum representable number (1E-37).
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef CM_FLOAT_REAL
|
||||||
|
#define CM_FLOAT_REAL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CM_FLOAT_REAL == 1
|
||||||
|
|
||||||
|
typedef float cmReal_t; ///< cmReal_t is a float
|
||||||
|
typedef float _Complex cmComplexR_t; ///< cmComplexR_t is single precision.
|
||||||
|
|
||||||
|
#define cmReal_EPSILON FLT_EPSILON ///< Minimum increment between 1.0 and the next greaterv value. (1E-5)
|
||||||
|
#define cmReal_MAX FLT_MAX ///< Maximum representable number (1E+37).
|
||||||
|
#define cmReal_MIN FLT_MIN ///< Minimum representable number (1E-37).
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef double cmReal_t; ///< cmReal_t is a double.
|
||||||
|
typedef double _Complex cmComplexR_t; ///< cmComplexR_t is double precision.
|
||||||
|
|
||||||
|
#define cmReal_EPSILON DBL_EPSILON ///< Minimum increment between 1.0 and the next greaterv value (1E-9).
|
||||||
|
#define cmReal_MAX DBL_MAX ///< Maximum representable number (1E+37).
|
||||||
|
#define cmReal_MIN DBL_MIN ///< Minimum representable number (1E-37).
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
2210
cmFrameFile.c
Normal file
2210
cmFrameFile.c
Normal file
File diff suppressed because it is too large
Load Diff
360
cmFrameFile.h
Normal file
360
cmFrameFile.h
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
#ifndef cmFrameFile_h
|
||||||
|
#define cmFrameFile_h
|
||||||
|
|
||||||
|
/*
|
||||||
|
file -> cmFfFile_t frame*
|
||||||
|
frame -> cmFfFrame_t mtx*
|
||||||
|
mtx -> cmFfMtx_t data*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidFrameTId = 0,
|
||||||
|
kInvalidStreamId = 0,
|
||||||
|
|
||||||
|
kTocFrameTId = -3,
|
||||||
|
kTocStreamId = -3,
|
||||||
|
|
||||||
|
kFileFfTId = 'FrmF',
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkFfRC = 0, // 0
|
||||||
|
kFileOpenFailFfRC, // 1
|
||||||
|
kFileReadFailFfRC, // 2
|
||||||
|
kFileWriteFailFfRC, // 3
|
||||||
|
kFileSeekFailFfRC, // 4
|
||||||
|
kFileCloseFailFfRC, // 5
|
||||||
|
kEofFfRC, // 6
|
||||||
|
kInvalidHandleFfRC, // 7
|
||||||
|
kMemAllocErrFfRC, // 8
|
||||||
|
kNotFrameFileFfRC, // 9
|
||||||
|
kUnknownErrFfRC, // 10
|
||||||
|
kNoMatchingFrameFfRC, // 11
|
||||||
|
kInvalidFileModeFfRC, // 12
|
||||||
|
kJsonFailFfRC, // 13
|
||||||
|
kInvalidFrameIdxFfRC, // 14
|
||||||
|
kDuplicateMtxIdFfRC, // 15
|
||||||
|
kFileTellFailFfRC, // 16
|
||||||
|
kLHeapFailFfRC, // 17
|
||||||
|
kTocFrameRdFailFfRC, // 18
|
||||||
|
kTocRecdNotFoundFfRC, // 19
|
||||||
|
kBufTooSmallFfRC // 20
|
||||||
|
};
|
||||||
|
|
||||||
|
// row data formats
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidFmtId, // 0
|
||||||
|
kUCharFmtId, // 1
|
||||||
|
kCharFmtId, // 2
|
||||||
|
kUShortFmtId, // 3
|
||||||
|
kShortFmtId, // 4
|
||||||
|
kULongFmtId, // 5
|
||||||
|
kLongFmtId, // 6
|
||||||
|
kUIntFmtId, // 7
|
||||||
|
kIntFmtId, // 8
|
||||||
|
kLLongFmtId, // 9
|
||||||
|
kULLongFmtId, // 10
|
||||||
|
kOff_tFmtId, // 11
|
||||||
|
kFloatFmtId, // 12
|
||||||
|
kDoubleFmtId, // 13
|
||||||
|
kStringZFmtId, // 14
|
||||||
|
kBlobFmtId, // 15
|
||||||
|
kJsonFmtId // 16
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidUId , // 0
|
||||||
|
kNoUnitsUId, // 1
|
||||||
|
kHzUId, // 2
|
||||||
|
kRadsUId, // 3 -pi to pi
|
||||||
|
kAmplUId, // 4 -1.0 to 1.0
|
||||||
|
kPowUId, // 5 amp^2/2
|
||||||
|
k10DbUId, // 6 10*log10(v)
|
||||||
|
k20DbUId, // 7 20*log10(v)
|
||||||
|
kCntUId, // 8 count of elements
|
||||||
|
kIdxUId, // 9 element index
|
||||||
|
kBfccUId, // 10
|
||||||
|
kMfccUId, // 11
|
||||||
|
kCepsUId, // 12
|
||||||
|
|
||||||
|
kD1UFl = 0x80000000 // this is a 1st difference
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kTocMId = -2,
|
||||||
|
|
||||||
|
|
||||||
|
kInvalidMId = 0,// 0
|
||||||
|
kAudioMId, // 1
|
||||||
|
kMagMId, // 2
|
||||||
|
kPhsMId, // 3
|
||||||
|
kFrqMId, // 4 measured bin frequecies in Hz
|
||||||
|
kTrkMId, // 5
|
||||||
|
kRmsMId, // 6
|
||||||
|
kBinCntMId, // 7 count of frequency domain bins in frame
|
||||||
|
kWndSmpCntMId, // 8 actual count of samples in FFT window (this is no (binCnt-1)*2)
|
||||||
|
kAudSmpCntMId, // 9 count of audio samples in frame
|
||||||
|
kBfccMId, // 10 vector of BFCC's
|
||||||
|
kBfccBandCntMId,// 11 count of coeff's BFCC vector in this frame
|
||||||
|
kMfccMId, // 12 vector of MFCC's
|
||||||
|
kCepsMId, // 13 vector of cepstral coefficients
|
||||||
|
kConstqMId, // 14 vector of constant
|
||||||
|
kDataMId // 15 blob of misc data
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmHandle_t cmFrameFileH_t;
|
||||||
|
typedef unsigned cmFfRC_t;
|
||||||
|
|
||||||
|
// mtx desc record
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned type;
|
||||||
|
unsigned fmtId;
|
||||||
|
unsigned unitsId;
|
||||||
|
unsigned rowCnt;
|
||||||
|
unsigned colCnt;
|
||||||
|
} cmFfMtx_t;
|
||||||
|
|
||||||
|
// frame desc record
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned type;
|
||||||
|
unsigned mtxCnt;
|
||||||
|
unsigned flags; // used internally to track time format (seconds or samples)
|
||||||
|
unsigned streamId;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
unsigned sampleIdx;
|
||||||
|
double seconds;
|
||||||
|
} time;
|
||||||
|
|
||||||
|
} cmFfFrame_t;
|
||||||
|
|
||||||
|
|
||||||
|
// file desc record
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmChar_t* filenameStr;
|
||||||
|
unsigned version;
|
||||||
|
unsigned frameCnt; // count of frames in all streams
|
||||||
|
double srate; // sample rate for all frames
|
||||||
|
} cmFfFile_t;
|
||||||
|
|
||||||
|
extern cmFrameFileH_t cmFrameFileNullHandle;
|
||||||
|
|
||||||
|
cmFfRC_t cmFrameFileCreate( cmFrameFileH_t* hPtr, const char* fn, double srate, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
// The fileDescPtrPtr is optional. Set to NULL to ignore.
|
||||||
|
cmFfRC_t cmFrameFileOpen( cmFrameFileH_t* hPtr, const char* fn, cmCtx_t* ctx, const cmFfFile_t** fileDescPtrPtr );
|
||||||
|
cmFfRC_t cmFrameFileClose( cmFrameFileH_t* hPtr );
|
||||||
|
bool cmFrameFileIsValid( cmFrameFileH_t h );
|
||||||
|
const cmFfFile_t* cmFrameFileDesc( cmFrameFileH_t h );
|
||||||
|
|
||||||
|
// Return the count of frames in the requested stream.
|
||||||
|
unsigned cmFrameFileFrameCount( cmFrameFileH_t h, unsigned streamId );
|
||||||
|
|
||||||
|
// Create a frame in a file created via cmFrameFileCreate().
|
||||||
|
// Set 'sampleIdx' to -1 if seconds is being used instead of samples.
|
||||||
|
cmFfRC_t cmFrameFileFrameCreate( cmFrameFileH_t h, unsigned frameType, unsigned streamId, unsigned sampleIdx, double secs );
|
||||||
|
|
||||||
|
// (W) Complete and write a frame created via an earilier call
|
||||||
|
// to cmFrameFileFrameCreate()
|
||||||
|
cmFfRC_t cmFrameFileFrameClose( cmFrameFileH_t h );
|
||||||
|
|
||||||
|
// (W) Fill a frame with matrix data. The frame must have been created
|
||||||
|
// via an earlier call to cmFrameFileCreate().
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxUChar( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned char* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxChar( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const char* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxUShort( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned short* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxShort( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const short* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxULong( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned long* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxLong( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const long* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxUInt( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxInt( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const int* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxULLong( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned long long* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxLLong( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const long long* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxOff_t( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const off_t* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxFloat( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const float* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxDouble( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const double* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxBlob( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const void* p, unsigned rn, unsigned cn );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxStringZ( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const char* p );
|
||||||
|
cmFfRC_t cmFrameFileWriteMtxJson( cmFrameFileH_t h, unsigned mtxType, cmJsonH_t jsH, const cmJsonNode_t* nodePtr );
|
||||||
|
|
||||||
|
// (R/W) Rewind to the first frame. Must call cmFrameFileFrameNext()
|
||||||
|
// following successful execution of this function to maintain correct
|
||||||
|
// alignment.
|
||||||
|
cmFfRC_t cmFrameFileRewind( cmFrameFileH_t h );
|
||||||
|
|
||||||
|
// (R/W) Seek to the frame at index 'frameIdx'. Must call cmFrameFileFrameNext()
|
||||||
|
// following successful execution of this function to maintain correct
|
||||||
|
// alignment.
|
||||||
|
cmFfRC_t cmFrameFileSeek( cmFrameFileH_t h, unsigned streamId, unsigned frameIdx );
|
||||||
|
|
||||||
|
// (R/W) Seek to the next frame in stream frameStreamId with type frameTypeId.
|
||||||
|
// Set frameTypeId to kInvalidFrameTId to return any frame type.
|
||||||
|
// Set frameStreamId to kInvalidStreamId to return any stream type.
|
||||||
|
// This function must be followed by a call to cmFrameFileFrameLoad()
|
||||||
|
// or cmFrameFileSkip().
|
||||||
|
cmFfRC_t cmFrameFileFrameNext( cmFrameFileH_t h, unsigned frameTypeId, unsigned streamId );
|
||||||
|
|
||||||
|
// (R/W) Load the matrix data associated with the current frame.
|
||||||
|
// This function can only be called after a successful call to
|
||||||
|
// cmFrameFileFrameNext().
|
||||||
|
// The frameDescPtrPtr is optional. Set to NULL to ignore.
|
||||||
|
cmFfRC_t cmFrameFileFrameLoad( cmFrameFileH_t h, const cmFfFrame_t** frameDescPtrPtr );
|
||||||
|
|
||||||
|
// (R/W) Skip over the matrix data associated with the current frame.
|
||||||
|
// This is an alternative to cmFrameFileLoad().
|
||||||
|
cmFfRC_t cmFrameFileFrameSkip( cmFrameFileH_t h );
|
||||||
|
|
||||||
|
// (R/W) Combines cmFrameFileFrameNext() and cmFrameFileFrameLoad()
|
||||||
|
// into a single call.
|
||||||
|
cmFfRC_t cmFrameFileFrameLoadNext( cmFrameFileH_t h, unsigned frameTypeId, unsigned streamId, const cmFfFrame_t** frameDescPtrPtr );
|
||||||
|
|
||||||
|
// (R/W) Write the current frame back to disk.
|
||||||
|
cmFfRC_t cmFrameFileFrameUpdate( cmFrameFileH_t h );
|
||||||
|
|
||||||
|
// Return the current frame description record.
|
||||||
|
const cmFfFrame_t* cmFrameFileFrameDesc( cmFrameFileH_t h );
|
||||||
|
|
||||||
|
// Currently loaded frame index.
|
||||||
|
unsigned cmFrameFileFrameLoadedIndex( cmFrameFileH_t h );
|
||||||
|
|
||||||
|
// Return the index of the frame with type 'mtxTypeId', units 'unitsId', and format 'fmtId'
|
||||||
|
// or cmInvalidIdx if the specified frame is not found.
|
||||||
|
// Set mtxTypeId to kInvalidMId to return any mtx type.
|
||||||
|
// Set unitsId to kInvalidUId to return a mtx with any units.
|
||||||
|
// Set fmtId to kInvalidFmtId to return a mtx with any fmt.
|
||||||
|
unsigned cmFrameFileMtxIndex( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, unsigned fmtId );
|
||||||
|
|
||||||
|
// Return a matrix description record.
|
||||||
|
const cmFfMtx_t* cmFrameFileMtxDesc( cmFrameFileH_t h, unsigned mtxIdx );
|
||||||
|
|
||||||
|
// Access matrix data.
|
||||||
|
//Set descPtr to NULL if the matrix desc is not needed.
|
||||||
|
unsigned char* cmFrameFileMtxIndexUChar( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
char* cmFrameFileMtxIndexChar( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
unsigned short* cmFrameFileMtxIndexUShort( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
short* cmFrameFileMtxIndexShort( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
unsigned long* cmFrameFileMtxIndexULong( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
long* cmFrameFileMtxIndexLong( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
unsigned* cmFrameFileMtxIndexUInt( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
int* cmFrameFileMtxIndexInt( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
unsigned long long* cmFrameFileMtxIndexULLong( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
long long* cmFrameFileMtxIndexLLong( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
off_t* cmFrameFileMtxIndexOff_t( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
float* cmFrameFileMtxIndexFloat( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
double* cmFrameFileMtxIndexDouble( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
char* cmFrameFileMtxIndexStringZ( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
void* cmFrameFileMtxIndexBlob( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
// (The caller is responsible for invoking cmJsonFinalize() to finalize the returned json handle.)
|
||||||
|
cmJsonH_t cmFrameFileMtxIndexJson( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
|
||||||
|
|
||||||
|
// Return a pointer to the data, and optionally the descPtr, for a matrix with the given
|
||||||
|
// type,units and format in the current frame.
|
||||||
|
// The following functions are implmented in terms of
|
||||||
|
// cmFrameFileMtxIndexXXX() and cmFrameFileMtxIndex().
|
||||||
|
unsigned char* cmFrameFileMtxUChar( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
char* cmFrameFileMtxChar( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
unsigned short* cmFrameFileMtxUShort( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
short* cmFrameFileMtxShort( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
unsigned long* cmFrameFileMtxULong( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
long* cmFrameFileMtxLong( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
unsigned* cmFrameFileMtxUInt( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
int* cmFrameFileMtxInt( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
unsigned long long* cmFrameFileMtxULLong( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
long long* cmFrameFileMtxLLong( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
off_t* cmFrameFileMtxOff_t( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
float* cmFrameFileMtxFloat( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
double* cmFrameFileMtxDouble( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
char* cmFrameFileMtxStringZ( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
void* cmFrameFileMtxBlob( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
// (The caller is responsible for invoking cmJsonFinalize() to finalize the returned json handle.)
|
||||||
|
cmJsonH_t cmFrameFileMtxJson( cmFrameFileH_t h, unsigned mtxTypeId, const cmFfMtx_t** descPtrPtr );
|
||||||
|
|
||||||
|
|
||||||
|
// Return the max row cnt, max col count, and total element count
|
||||||
|
// for all matrices which match the given stream/mtx/units/fmt
|
||||||
|
// combination. Note that if the returned ele count is less than
|
||||||
|
// maxRowCnt * maxColCnt * cmFrameFileFrameCount(streamId) then
|
||||||
|
// some matched matrices contain fewer than maxRowCnt/maxColCnt
|
||||||
|
// rows/columns.
|
||||||
|
cmFfRC_t cmFrameFileMtxSize( cmFrameFileH_t h, unsigned streamId, unsigned mtxType, unsigned unitsId, unsigned fmtId, unsigned* frameCntPtr, unsigned* rowCntPtr, unsigned* colCntPtr, unsigned* eleCntPtr );
|
||||||
|
|
||||||
|
// Load a buffer with all of the data which matches a given
|
||||||
|
// stream/mtx/unit/fmt combination in the given range of frames.
|
||||||
|
// 'frmIdx' specifies the frame index relative to the given stream id as opposed to
|
||||||
|
// and absolute frame index.
|
||||||
|
// Set frmCnt to -1 to include all frames following 'frmIdx'.
|
||||||
|
// *outCntPtr is set to the actual number of elements copied into the buffer
|
||||||
|
// The data is packed into the return buffer by copying columwise from the source.
|
||||||
|
// matrices to the buf[]. If all of the matrices are not of a fixed known size
|
||||||
|
// it may therefore be difficult to distinguish where one frames data ends and
|
||||||
|
// the next begins.
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadUChar( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, unsigned char* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadChar( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, char* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadUShort( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, unsigned short* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadShort( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, short* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadULong( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, unsigned long* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadLong( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, long* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadUInt( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, unsigned int* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadInt( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, int* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadULLong( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, unsigned long long* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadLLong( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, long long* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadOff_t( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, off_t* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadFloat( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, float* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadDouble( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, double* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadStringZ( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, char* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
cmFfRC_t cmFrameFileMtxLoadBlob( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt, void* buf, unsigned eleCnt, unsigned* outCntPtr );
|
||||||
|
|
||||||
|
cmFfRC_t cmFrameFileReport( cmFrameFileH_t h, bool summOnlyFl, cmRpt_t* rpt );
|
||||||
|
cmFfRC_t cmFrameFileNameReport( const char* fn, bool summOnlyFl, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
cmFfRC_t cmFrameFileTest( const char* fn, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
#if CM_FLOAT_SMP == 1
|
||||||
|
#define cmFrameFileMtxLoadSample cmFrameFileMtxLoadFloat
|
||||||
|
#define cmFrameFileWriteMtxSample cmFrameFileWriteMtxFloat
|
||||||
|
#define cmFrameFileMtxIndexSample cmFrameFileMtxIndexFloat
|
||||||
|
#define cmFrameFileMtxSample cmFrameFileMtxFloat
|
||||||
|
#define kSampleFmtId kFloatFmtId
|
||||||
|
#else
|
||||||
|
#define cmFrameFileMtxLoadSample cmFrameFileMtxLoadDouble
|
||||||
|
#define cmFrameFileWriteMtxSample cmFrameFileWriteMtxDouble
|
||||||
|
#define cmFrameFileMtxIndexSample cmFrameFileMtxIndexDouble
|
||||||
|
#define cmFrameFileMtxSample cmFrameFileMtxDouble
|
||||||
|
#define kSampleFmtId kDoubleFmtId
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CM_FLOAT_REAL == 1
|
||||||
|
#define cmFrameFileMtxLoadReal cmFrameFileMtxLoadFloat
|
||||||
|
#define cmFrameFileWriteMtxReal cmFrameFileWriteMtxFloat
|
||||||
|
#define cmFrameFileMtxIndexReal cmFrameFileMtxIndexFloat
|
||||||
|
#define cmFrameFileMtxReal cmFrameFileMtxFloat
|
||||||
|
#define kRealFmtId kFloatFmtId
|
||||||
|
#else
|
||||||
|
#define cmFrameFileMtxLoadReal cmFrameFileMtxLoadDouble
|
||||||
|
#define cmFrameFileWriteMtxReal cmFrameFileWriteMtxDouble
|
||||||
|
#define cmFrameFileMtxIndexReal cmFrameFileMtxIndexDouble
|
||||||
|
#define cmFrameFileMtxReal cmFrameFileMtxDouble
|
||||||
|
#define kRealFmtId kDoubleFmtId
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
2
cmGlobal.c
Normal file
2
cmGlobal.c
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
|
154
cmGlobal.h
Normal file
154
cmGlobal.h
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
//{
|
||||||
|
//(
|
||||||
|
// cmGlobal.h contains the global macros, header files, and
|
||||||
|
// typedefs availale to all other cm modules.
|
||||||
|
//
|
||||||
|
// All operating system dependencies should be resolved in this file via
|
||||||
|
// testing for OS_LINUX, OS_OSX, or OS_W32.
|
||||||
|
//)
|
||||||
|
|
||||||
|
#ifndef cmGlobal_h
|
||||||
|
#define cmGlobal_h
|
||||||
|
|
||||||
|
//(
|
||||||
|
#include "config.h" // created by 'configure'
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define CM_FLOAT_SMP 1 //< make cmSample_t = float in cmFloatTypes.h
|
||||||
|
#define CM_FLOAT_REAL 0 //< make cmReal_t = double in cmFloatTypes.h
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define cmDEBUG_FL 0 //< Define cmDEBUG_FL as 0 when building in release mode. See \ref debug_mode.
|
||||||
|
#else
|
||||||
|
#define cmDEBUG_FL 1 //< Define cmDEBUG_FL as 1 when building in debug mode. See \ref debug_mode.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Perform byte swapping on 16 bit values.
|
||||||
|
#define cmSwap16(x) \
|
||||||
|
(((((unsigned short)(x)) & 0x00ff) << 8) | ((((unsigned short)(x)) & 0xff00) >> 8))
|
||||||
|
|
||||||
|
#ifdef OS_LINUX
|
||||||
|
#include <byteswap.h> // gcc specific
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Perform byte swapping on 32 bit values on systems were <byteswap.h> is available.
|
||||||
|
#define cmSwap32(x) (bswap_32(x))
|
||||||
|
|
||||||
|
// Perform byte swapping on 64 bit values on systems were <byteswap.h> is available.
|
||||||
|
#define cmSwap64(x) (bswap_64(x))
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef OS_OSX
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Perform byte swapping on 32 bit values on systems were <byteswap.h> is not available.
|
||||||
|
#define cmSwap32(x) \
|
||||||
|
((((unsigned)((x) & 0x000000FF)) << 24) | \
|
||||||
|
(((unsigned)((x) & 0x0000FF00)) << 8) | \
|
||||||
|
(((unsigned)((x) & 0x00FF0000)) >> 8) | \
|
||||||
|
(((unsigned)((x) & 0xFF000000)) >> 24))
|
||||||
|
|
||||||
|
// Perform byte swapping on 64 bit values on systems were <byteswap.h> is not available.
|
||||||
|
#define cmSwap64(x) \
|
||||||
|
(((((unsigned long long)(x))<<56) & 0xFF00000000000000ULL) | \
|
||||||
|
((((unsigned long long)(x))<<40) & 0x00FF000000000000ULL) | \
|
||||||
|
((((unsigned long long)(x))<<24) & 0x0000FF0000000000ULL) | \
|
||||||
|
((((unsigned long long)(x))<< 8) & 0x000000FF00000000ULL) | \
|
||||||
|
((((unsigned long long)(x))>> 8) & 0x00000000FF000000ULL) | \
|
||||||
|
((((unsigned long long)(x))>>24) & 0x0000000000FF0000ULL) | \
|
||||||
|
((((unsigned long long)(x))>>40) & 0x000000000000FF00ULL) | \
|
||||||
|
((((unsigned long long)(x))>>56) & 0x00000000000000FFULL))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define cmAllFlags(f,m) (((f) & (m)) == (m)) //< Test if all of a group 'm' of binary flags in 'f' are set.
|
||||||
|
#define cmIsFlag(f,m) (((f) & (m)) ? true : false) //< Test if any one of a the bits in 'm' is also set in 'f'.
|
||||||
|
#define cmIsNotFlag(f,m) (cmIsFlag(f,m)==false) //< Test if none of the bits in 'm' are set in 'f'.
|
||||||
|
#define cmSetFlag(f,m) ((f) | (m)) //< Return 'f' with the bits in 'm' set.
|
||||||
|
#define cmClrFlag(f,m) ((f) & (~(m))) //< Return 'f' with the bits in 'm' cleared.
|
||||||
|
#define cmTogFlag(f,m) ((f)^(m)) //< Return 'f' with the bits in 'm' toggled.
|
||||||
|
#define cmEnaFlag(f,m,b) (b) ? cmSetFlag(f,m) : cmClrFlag(f,m) //< \brief Set or clear bits in 'f' based on bits in 'm' and the state of 'b'.
|
||||||
|
//<
|
||||||
|
//< If 'b' == 0 then return 'f' with the bits in 'm' cleared.
|
||||||
|
//< otherwise return 'f' with the bits in 'm' set.
|
||||||
|
|
||||||
|
|
||||||
|
#define cmMin(v0,v1) ((v0)<(v1) ? (v0) : (v1)) //< Return the minimum arg.
|
||||||
|
#define cmMax(v0,v1) ((v0)>(v1) ? (v0) : (v1)) //< Return the maximum arg.
|
||||||
|
|
||||||
|
|
||||||
|
#define cmStringNullGuard(p) ((p)==NULL?"<null>":(p)) //< If 'p'==NULL return the static string "<null>" otherwise return 'p'.
|
||||||
|
|
||||||
|
// Default return code indicating successful function completion.
|
||||||
|
#define cmOkRC (0)
|
||||||
|
|
||||||
|
// Default directory separator character for unix based systems.
|
||||||
|
#define cmPathSeparatorChar ("/")
|
||||||
|
|
||||||
|
#define cmInvalidIdx (0xffffffff) //< cm wide value indicating a invalid or NULL index.
|
||||||
|
#define cmInvalidId (cmInvalidIdx) //< cm wide value indicating an invalid or NULL numeric id.
|
||||||
|
#define cmInvalidCnt (cmInvalidIdx) //< cm wide value indicating a invalid or NULL count of items.
|
||||||
|
|
||||||
|
#define cmSTATIC_NULL_HANDLE {NULL} //< Default NULL value for cmHandle_t
|
||||||
|
|
||||||
|
// Generic data type for implementing opaque object handles.
|
||||||
|
/*
|
||||||
|
typedef struct cmHandle_str
|
||||||
|
{
|
||||||
|
void* h;
|
||||||
|
} cmHandle_t;
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define cmHandle_t struct { void* h; }
|
||||||
|
|
||||||
|
#define cmHandlesAreEqual( a, b ) ((a).h == (b).h) //< Test if two cmHandle_t values are equivalent.
|
||||||
|
#define cmHandlesAreNotEqual( a, b ) (!cmHandlesAreEqual(a,b)) //< Test if two cmHandle_t value are not equivalent.
|
||||||
|
|
||||||
|
// Data type commonly used as a function return value. Functions returning cmRC_t values
|
||||||
|
// return cmOkRC (0) to indicate successful completion or some other value to indicate
|
||||||
|
// some kind of exceptional conidtion. In general the condition indicates an unexpected condition
|
||||||
|
// such as resource exhaution, or a missing file.
|
||||||
|
typedef unsigned cmRC_t;
|
||||||
|
|
||||||
|
// A data type which indicates a system dependent error. This is generally an abstraction for an 'errno'
|
||||||
|
// like code.
|
||||||
|
typedef int cmSysErrCode_t; // same as errno
|
||||||
|
|
||||||
|
|
||||||
|
// cmChar_t is a data type used to indicate that a char is being used to hold human readable
|
||||||
|
// text. Eventually this type will be used to locate and handle unicode based strings.
|
||||||
|
typedef char cmChar_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef unsigned int cmUInt32_t; //< This typedef is used to indicate that the type must be an unsigned 32 bit integer.
|
||||||
|
typedef unsigned short cmUInt16_t; //< This typedef is used to indicate that hte type must be an unsigned 16 bit integer.
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endif
|
1121
cmGnuPlot.c
Normal file
1121
cmGnuPlot.c
Normal file
File diff suppressed because it is too large
Load Diff
102
cmGnuPlot.h
Normal file
102
cmGnuPlot.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#ifndef cmGnuPlot_h
|
||||||
|
#define cmGnuPlot_h
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkPlRC,
|
||||||
|
kSignalFailedPlRC,
|
||||||
|
kPipeFailedPlRC,
|
||||||
|
kForkFailedPlRC,
|
||||||
|
kExecFailedPlRC,
|
||||||
|
kPipeCloseFailedPlRC,
|
||||||
|
kPlotNotFoundPlRC,
|
||||||
|
kNoCurPlotPlRC,
|
||||||
|
kPlotDataFileFailPlRC,
|
||||||
|
kFopenFailedPlRC,
|
||||||
|
kFcloseFailedPlRC
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidPlotPtId = 0x000,
|
||||||
|
kPlusPlotPtId = 0x001,
|
||||||
|
kXPlotPtId = 0x002,
|
||||||
|
kAsteriskPlotPtId = 0x003,
|
||||||
|
kSquarePlotPtId = 0x004,
|
||||||
|
kFillSquarePlotPtId = 0x005,
|
||||||
|
kCirclePlotPtId = 0x006,
|
||||||
|
kFillCirclePlotPtId = 0x007,
|
||||||
|
kTriPlotPtId = 0x008,
|
||||||
|
kFillTriPlotPtId = 0x009,
|
||||||
|
kInvTriPlotPtId = 0x00a,
|
||||||
|
kInvFillTriPlotPtId = 0x00b,
|
||||||
|
kDiamondPlotPtId = 0x00c,
|
||||||
|
kFillDiamonPlotPtId = 0x00d,
|
||||||
|
kPlotPtMask = 0x00f,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidPlotLineId = 0x000, // -2 after translation
|
||||||
|
kSolidPlotLineId = 0x010, // -1 after translation
|
||||||
|
kDashPlotLineId = 0x020, // 0 after translation
|
||||||
|
kPlotLineMask = 0x0f0,
|
||||||
|
kPlotLineShift = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kImpulsePlotFl = 0x100
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Set terminal to NULL to use the default terminal.
|
||||||
|
cmRC_t cmPlotInitialize( const char* terminalStr );
|
||||||
|
|
||||||
|
// Combines initializaion and setup in a single call.
|
||||||
|
cmRC_t cmPlotInitialize2( const char* terminalStr, const char* title, unsigned rowCnt, unsigned colCnt );
|
||||||
|
|
||||||
|
cmRC_t cmPlotFinalize();
|
||||||
|
|
||||||
|
/// Setup the plot page
|
||||||
|
cmRC_t cmPlotSetup( const char* title, unsigned rowCnt, unsigned colCnt );
|
||||||
|
|
||||||
|
/// Select sub-plot to apply subsequent commands to
|
||||||
|
cmRC_t cmPlotSelectSubPlot( unsigned ri, unsigned ci );
|
||||||
|
|
||||||
|
/// Clear the current current subplot
|
||||||
|
cmRC_t cmPlotClear();
|
||||||
|
|
||||||
|
/// Set the labels on the current sub-plot
|
||||||
|
cmRC_t cmPlotSetLabels( const char* title, const char* xLabelStr, const char* yLabelStr, const char* zLabelStr );
|
||||||
|
|
||||||
|
/// Set the default ranges for the x, y and z axes. To leave the ranges at their current values set the min and max to -1.
|
||||||
|
/// The range values are used to form data sets when data is not explicitely given.
|
||||||
|
cmRC_t cmPlotSetRange( double xMin, double xMax, double yMin, double yMax, double zMin, double zMax );
|
||||||
|
|
||||||
|
/// If x or y is given as NULL then the values will be taken from the range settings xMin:xMax or yMin:yMax.
|
||||||
|
/// Use the gnuplot command:'test' to see the valid lineType and pointType values for a given terminal
|
||||||
|
/// Color string may be any of the predefined color labels: show palette colornames or and rgb value: e.g. #FF00FF
|
||||||
|
cmRC_t cmPlotLineF( const char* legendStr, const float* x, const float* y, const float* z, unsigned n, const char* colorStr, unsigned styleFlags );
|
||||||
|
cmRC_t cmPlotLineD( const char* legendStr, const double* x, const double* y, const double* z, unsigned n, const char* colorStr, unsigned styleFlags );
|
||||||
|
|
||||||
|
cmRC_t cmPlotLineMD( const double* x, const double* y, const double* z, unsigned rn, unsigned cn, unsigned styleFlags );
|
||||||
|
|
||||||
|
|
||||||
|
#if CM_FLOAT_SMP == 1
|
||||||
|
#define cmPlotLineS cmPlotLineF
|
||||||
|
#else
|
||||||
|
#define cmPlotLineS cmPlotLineD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CM_FLOAT_REAL == 1
|
||||||
|
#define cmPlotLineR cmPlotLineF
|
||||||
|
#else
|
||||||
|
#define cmPlotLineR cmPlotLineD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
cmRC_t cmPlotDraw();
|
||||||
|
cmRC_t cmPlotPrint( bool printDataFl );
|
||||||
|
cmRC_t cmPlotDrawAndPrint( bool printDataFl );
|
||||||
|
|
||||||
|
#endif
|
871
cmGr.h
Normal file
871
cmGr.h
Normal file
@ -0,0 +1,871 @@
|
|||||||
|
#ifndef cmGr_h
|
||||||
|
#define cmGr_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kAliceBlueGrId = 0xf0f8ff,
|
||||||
|
kAntiqueWhiteGrId = 0xfaebd7,
|
||||||
|
kAquaGrId = 0x00ffff,
|
||||||
|
kAquamarineGrId = 0x7fffd4,
|
||||||
|
kAzureGrId = 0xf0ffff,
|
||||||
|
kBeigeGrId = 0xf5f5dc,
|
||||||
|
kBisqueGrId = 0xffe4c4,
|
||||||
|
kBlackGrId = 0x000000,
|
||||||
|
kBlanchedAlmondGrId = 0xffebcd,
|
||||||
|
kBlueGrId = 0x0000ff,
|
||||||
|
kBlueVioletGrId = 0x8a2be2,
|
||||||
|
kBrownGrId = 0xa52a2a,
|
||||||
|
kBurlyWoodGrId = 0xdeb887,
|
||||||
|
kCadetBlueGrId = 0x5f9ea0,
|
||||||
|
kChartreuseGrId = 0x7fff00,
|
||||||
|
kChocolateGrId = 0xd2691e,
|
||||||
|
kCoralGrId = 0xff7f50,
|
||||||
|
kCornflowerBlueGrId = 0x6495ed,
|
||||||
|
kCornsilkGrId = 0xfff8dc,
|
||||||
|
kCrimsonGrId = 0xdc143c,
|
||||||
|
kCyanGrId = 0x00ffff,
|
||||||
|
kDarkBlueGrId = 0x00008b,
|
||||||
|
kDarkCyanGrId = 0x008b8b,
|
||||||
|
kDarkGoldenRodGrId = 0xb8860b,
|
||||||
|
kDarkGrayGrId = 0xa9a9a9,
|
||||||
|
kDarkGreyGrId = 0xa9a9a9,
|
||||||
|
kDarkGreenGrId = 0x006400,
|
||||||
|
kDarkKhakiGrId = 0xbdb76b,
|
||||||
|
kDarkMagentaGrId = 0x8b008b,
|
||||||
|
kDarkOliveGreenGrId = 0x556b2f,
|
||||||
|
kDarkorangeGrId = 0xff8c00,
|
||||||
|
kDarkOrchidGrId = 0x9932cc,
|
||||||
|
kDarkRedGrId = 0x8b0000,
|
||||||
|
kDarkSalmonGrId = 0xe9967a,
|
||||||
|
kDarkSeaGreenGrId = 0x8fbc8f,
|
||||||
|
kDarkSlateBlueGrId = 0x483d8b,
|
||||||
|
kDarkSlateGrayGrId = 0x2f4f4f,
|
||||||
|
kDarkSlateGreyGrId = 0x2f4f4f,
|
||||||
|
kDarkTurquoiseGrId = 0x00ced1,
|
||||||
|
kDarkVioletGrId = 0x9400d3,
|
||||||
|
kDeepPinkGrId = 0xff1493,
|
||||||
|
kDeepSkyBlueGrId = 0x00bfff,
|
||||||
|
kDimGrayGrId = 0x696969,
|
||||||
|
kDimGreyGrId = 0x696969,
|
||||||
|
kDodgerBlueGrId = 0x1e90ff,
|
||||||
|
kFireBrickGrId = 0xb22222,
|
||||||
|
kFloralWhiteGrId = 0xfffaf0,
|
||||||
|
kForestGreenGrId = 0x228b22,
|
||||||
|
kFuchsiaGrId = 0xff00ff,
|
||||||
|
kGainsboroGrId = 0xdcdcdc,
|
||||||
|
kGhostWhiteGrId = 0xf8f8ff,
|
||||||
|
kGoldGrId = 0xffd700,
|
||||||
|
kGoldenRodGrId = 0xdaa520,
|
||||||
|
kGrayGrId = 0x808080,
|
||||||
|
kGreyGrId = 0x808080,
|
||||||
|
kGreenGrId = 0x008000,
|
||||||
|
kGreenYellowGrId = 0xadff2f,
|
||||||
|
kHoneyDewGrId = 0xf0fff0,
|
||||||
|
kHotPinkGrId = 0xff69b4,
|
||||||
|
kIndianRedGrId = 0xcd5c5c,
|
||||||
|
kIndigoGrId = 0x4b0082,
|
||||||
|
kIvoryGrId = 0xfffff0,
|
||||||
|
kKhakiGrId = 0xf0e68c,
|
||||||
|
kLavenderGrId = 0xe6e6fa,
|
||||||
|
kLavenderBlushGrId = 0xfff0f5,
|
||||||
|
kLawnGreenGrId = 0x7cfc00,
|
||||||
|
kLemonChiffonGrId = 0xfffacd,
|
||||||
|
kLightBlueGrId = 0xadd8e6,
|
||||||
|
kLightCoralGrId = 0xf08080,
|
||||||
|
kLightCyanGrId = 0xe0ffff,
|
||||||
|
kLightGoldenRodYellowGrId = 0xfafad2,
|
||||||
|
kLightGrayGrId = 0xd3d3d3,
|
||||||
|
kLightGreyGrId = 0xd3d3d3,
|
||||||
|
kLightGreenGrId = 0x90ee90,
|
||||||
|
kLightPinkGrId = 0xffb6c1,
|
||||||
|
kLightSalmonGrId = 0xffa07a,
|
||||||
|
kLightSeaGreenGrId = 0x20b2aa,
|
||||||
|
kLightSkyBlueGrId = 0x87cefa,
|
||||||
|
kLightSlateGrayGrId = 0x778899,
|
||||||
|
kLightSlateGreyGrId = 0x778899,
|
||||||
|
kLightSteelBlueGrId = 0xb0c4de,
|
||||||
|
kLightYellowGrId = 0xffffe0,
|
||||||
|
kLimeGrId = 0x00ff00,
|
||||||
|
kLimeGreenGrId = 0x32cd32,
|
||||||
|
kLinenGrId = 0xfaf0e6,
|
||||||
|
kMagentaGrId = 0xff00ff,
|
||||||
|
kMaroonGrId = 0x800000,
|
||||||
|
kMediumAquaMarineGrId = 0x66cdaa,
|
||||||
|
kMediumBlueGrId = 0x0000cd,
|
||||||
|
kMediumOrchidGrId = 0xba55d3,
|
||||||
|
kMediumPurpleGrId = 0x9370d8,
|
||||||
|
kMediumSeaGreenGrId = 0x3cb371,
|
||||||
|
kMediumSlateBlueGrId = 0x7b68ee,
|
||||||
|
kMediumSpringGreenGrId = 0x00fa9a,
|
||||||
|
kMediumTurquoiseGrId = 0x48d1cc,
|
||||||
|
kMediumVioletRedGrId = 0xc71585,
|
||||||
|
kMidnightBlueGrId = 0x191970,
|
||||||
|
kMintCreamGrId = 0xf5fffa,
|
||||||
|
kMistyRoseGrId = 0xffe4e1,
|
||||||
|
kMoccasinGrId = 0xffe4b5,
|
||||||
|
kNavajoWhiteGrId = 0xffdead,
|
||||||
|
kNavyGrId = 0x000080,
|
||||||
|
kOldLaceGrId = 0xfdf5e6,
|
||||||
|
kOliveGrId = 0x808000,
|
||||||
|
kOliveDrabGrId = 0x6b8e23,
|
||||||
|
kOrangeGrId = 0xffa500,
|
||||||
|
kOrangeRedGrId = 0xff4500,
|
||||||
|
kOrchidGrId = 0xda70d6,
|
||||||
|
kPaleGoldenRodGrId = 0xeee8aa,
|
||||||
|
kPaleGreenGrId = 0x98fb98,
|
||||||
|
kPaleTurquoiseGrId = 0xafeeee,
|
||||||
|
kPaleVioletRedGrId = 0xd87093,
|
||||||
|
kPapayaWhipGrId = 0xffefd5,
|
||||||
|
kPeachPuffGrId = 0xffdab9,
|
||||||
|
kPeruGrId = 0xcd853f,
|
||||||
|
kPinkGrId = 0xffc0cb,
|
||||||
|
kPlumGrId = 0xdda0dd,
|
||||||
|
kPowderBlueGrId = 0xb0e0e6,
|
||||||
|
kPurpleGrId = 0x800080,
|
||||||
|
kRedGrId = 0xff0000,
|
||||||
|
kRosyBrownGrId = 0xbc8f8f,
|
||||||
|
kRoyalBlueGrId = 0x4169e1,
|
||||||
|
kSaddleBrownGrId = 0x8b4513,
|
||||||
|
kSalmonGrId = 0xfa8072,
|
||||||
|
kSandyBrownGrId = 0xf4a460,
|
||||||
|
kSeaGreenGrId = 0x2e8b57,
|
||||||
|
kSeaShellGrId = 0xfff5ee,
|
||||||
|
kSiennaGrId = 0xa0522d,
|
||||||
|
kSilverGrId = 0xc0c0c0,
|
||||||
|
kSkyBlueGrId = 0x87ceeb,
|
||||||
|
kSlateBlueGrId = 0x6a5acd,
|
||||||
|
kSlateGrayGrId = 0x708090,
|
||||||
|
kSlateGreyGrId = 0x708090,
|
||||||
|
kSnowGrId = 0xfffafa,
|
||||||
|
kSpringGreenGrId = 0x00ff7f,
|
||||||
|
kSteelBlueGrId = 0x4682b4,
|
||||||
|
kTanGrId = 0xd2b48c,
|
||||||
|
kTealGrId = 0x008080,
|
||||||
|
kThistleGrId = 0xd8bfd8,
|
||||||
|
kTomatoGrId = 0xff6347,
|
||||||
|
kTurquoiseGrId = 0x40e0d0,
|
||||||
|
kVioletGrId = 0xee82ee,
|
||||||
|
kWheatGrId = 0xf5deb3,
|
||||||
|
kWhiteGrId = 0xffffff,
|
||||||
|
kWhiteSmokeGrId = 0xf5f5f5,
|
||||||
|
kYellowGrId = 0xffff00,
|
||||||
|
kYellowGreenGrId = 0x9acd32
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kHomeGrId = 5, // 5
|
||||||
|
kPageUpGrId, // 6
|
||||||
|
kEndGrId, // 7
|
||||||
|
kBackSpaceGrId = 8, // 8
|
||||||
|
kTabGrId = 9, // 9
|
||||||
|
kPageDownGrId, // 10
|
||||||
|
kLeftGrId, // 11
|
||||||
|
kUpGrId, // 12
|
||||||
|
kEnterGrId = 13, // 13
|
||||||
|
kRightGrId, // 14
|
||||||
|
kDownGrId, // 15
|
||||||
|
kInsertGrId, // 16
|
||||||
|
kPrintGrId, // 17
|
||||||
|
kScrollLockGrId, // 18
|
||||||
|
kPauseGrId, // 19
|
||||||
|
kMenuGrId, // 20
|
||||||
|
kLShiftGrId, // 21
|
||||||
|
kRShiftGrId, // 22
|
||||||
|
kLCtrlGrId, // 23
|
||||||
|
kRCtrlGrId, // 24
|
||||||
|
kLAltGrId, // 25
|
||||||
|
kRAltGrId, // 26
|
||||||
|
kEscapeGrId = 27, // 27
|
||||||
|
kLSuperGrId, // 28
|
||||||
|
kRSuperGrId, // 29
|
||||||
|
kNumLockGrId, // 30
|
||||||
|
kCapsLockGrId, // 31
|
||||||
|
kSpaceGrId = 32, // 32 Min. printable ASCII
|
||||||
|
kExclMarkGrId, // 33
|
||||||
|
kDQuoteGrId, // 34
|
||||||
|
kPoundGrId, // 35
|
||||||
|
kDollarGrId, // 36
|
||||||
|
kPercentGrId, // 37
|
||||||
|
kAmpersandGrId, // 38
|
||||||
|
kApostropheGrId, // 39
|
||||||
|
kLParenGrId, // 40
|
||||||
|
kRParenGrId, // 41
|
||||||
|
kAsteriskGrId, // 42
|
||||||
|
kPlusGrId, // 43
|
||||||
|
kCommaGrId, // 44
|
||||||
|
kHyphenGrId, // 45
|
||||||
|
kPeriodGrId, // 46
|
||||||
|
kForwardSlashGrId, // 47
|
||||||
|
k0GrId, // 48
|
||||||
|
k1GrId, // 49
|
||||||
|
k2GrId, // 50
|
||||||
|
k3GrId, // 51
|
||||||
|
k4GrId, // 52
|
||||||
|
k5GrId, // 53
|
||||||
|
k6GrId, // 54
|
||||||
|
k7GrId, // 55
|
||||||
|
k8GrId, // 56
|
||||||
|
k9GrId, // 57
|
||||||
|
kColonGrId, // 58
|
||||||
|
kSemiColonGrId, // 59
|
||||||
|
kLesserGrId, // 60
|
||||||
|
kEqualGrId, // 61
|
||||||
|
kGreaterGrId, // 62
|
||||||
|
kQMarkGrId, // 63
|
||||||
|
kAtGrId, // 64
|
||||||
|
kA_GrId, // 65
|
||||||
|
kB_GrId, // 66
|
||||||
|
kC_GrId, // 67
|
||||||
|
kD_GrId, // 68
|
||||||
|
kE_GrId, // 69
|
||||||
|
kF_GrId, // 70
|
||||||
|
kG_GrId, // 71
|
||||||
|
kH_GrId, // 72
|
||||||
|
kI_GrId, // 73
|
||||||
|
kJ_GrId, // 74
|
||||||
|
kK_GrId, // 75
|
||||||
|
kL_GrId, // 76
|
||||||
|
kM_GrId, // 77
|
||||||
|
kN_GrId, // 78
|
||||||
|
kO_GrId, // 79
|
||||||
|
kP_GrId, // 80
|
||||||
|
kQ_GrId, // 81
|
||||||
|
kR_GrId, // 82
|
||||||
|
kS_GrId, // 83
|
||||||
|
kT_GrId, // 84
|
||||||
|
kU_GrId, // 85
|
||||||
|
kV_GrId, // 86
|
||||||
|
kW_GrId, // 87
|
||||||
|
kX_GrId, // 88
|
||||||
|
kY_GrId, // 89
|
||||||
|
kZ_GrId, // 90
|
||||||
|
kLBracketGrId, // 91
|
||||||
|
kBackSlashGrId, // 92
|
||||||
|
kRBracketGrId, // 93
|
||||||
|
kCaretGrId, // 94
|
||||||
|
kUnderScoreGrId, // 95
|
||||||
|
kAccentGrId, // 96
|
||||||
|
ka_GrId, // 97
|
||||||
|
kb_GrId, // 98
|
||||||
|
kc_GrId, // 99
|
||||||
|
kd_GrId, // 100
|
||||||
|
ke_GrId, // 101
|
||||||
|
kf_GrId, // 102
|
||||||
|
kg_GrId, // 103
|
||||||
|
kh_GrId, // 104
|
||||||
|
ki_GrId, // 105
|
||||||
|
kj_GrId, // 106
|
||||||
|
kk_GrId, // 107
|
||||||
|
kl_GrId, // 108
|
||||||
|
km_GrId, // 109
|
||||||
|
kn_GrId, // 110
|
||||||
|
ko_GrId, // 111
|
||||||
|
kp_GrId, // 112
|
||||||
|
kq_GrId, // 113
|
||||||
|
kr_GrId, // 114
|
||||||
|
ks_GrId, // 115
|
||||||
|
kt_GrId, // 116
|
||||||
|
ku_GrId, // 117
|
||||||
|
kv_GrId, // 118
|
||||||
|
kw_GrId, // 119
|
||||||
|
kx_GrId, // 120
|
||||||
|
ky_GrId, // 121
|
||||||
|
kz_GrId, // 122
|
||||||
|
kLBraceGrId, // 123
|
||||||
|
kPipeGrId, // 124
|
||||||
|
kRBraceGrId, // 125
|
||||||
|
kTildeGrId, // 126
|
||||||
|
kDeleteGrId, // 127
|
||||||
|
kNP_MultGrId, // 128
|
||||||
|
kNP_PlusGrId, // 129
|
||||||
|
kNP_MinusGrId, // 130
|
||||||
|
kNP_DecPtGrId, // 131
|
||||||
|
kNP_DivGrId, // 132
|
||||||
|
kNP_0GrId, // 133
|
||||||
|
kNP_1GrId, // 134
|
||||||
|
kNP_2GrId, // 135
|
||||||
|
kNP_3GrId, // 136
|
||||||
|
kNP_4GrId, // 137
|
||||||
|
kNP_5GrId, // 138
|
||||||
|
kNP_6GrId, // 139
|
||||||
|
kNP_7GrId, // 140
|
||||||
|
kNP_8GrId, // 141
|
||||||
|
kNP_9GrId, // 142
|
||||||
|
kNP_EqualGrId, // 143
|
||||||
|
kNP_EnterGrId, // 144
|
||||||
|
kFunc_1GrId, // 145
|
||||||
|
kFunc_2GrId, // 146
|
||||||
|
kFunc_3GrId, // 147
|
||||||
|
kFunc_4GrId, // 148
|
||||||
|
kFunc_5GrId, // 149
|
||||||
|
kFunc_6GrId, // 150
|
||||||
|
kFunc_7GrId, // 151
|
||||||
|
kFunc_8GrId, // 152
|
||||||
|
kFunc_9GrId, // 153
|
||||||
|
kFunc_10GrId, // 154
|
||||||
|
kFunc_11GrId, // 155
|
||||||
|
kFunc_12GrId, // 156
|
||||||
|
kBrightUpGrId, // 157
|
||||||
|
kBrightDnGrId, // 158
|
||||||
|
kAudio_PrevGrId, // 159
|
||||||
|
kAudio_PlayGrId, // 160
|
||||||
|
kAudio_NextGrId, // 161
|
||||||
|
kAudio_MuteGrId, // 162
|
||||||
|
kAudio_UpGrId, // 163
|
||||||
|
kAudio_DnGrId, // 164
|
||||||
|
kEjectGrId, // 165
|
||||||
|
kInvalidKeyCodeGrId
|
||||||
|
} cmGrKeyCodeId_t;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kMinAsciiGrId = kSpaceGrId,
|
||||||
|
kMaxAsciiGrId = kDeleteGrId
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkGrRC,
|
||||||
|
kLHeapFailGrRC,
|
||||||
|
kAppErrGrRC,
|
||||||
|
kRootObjCreateFailGrRC,
|
||||||
|
kInvalidCoordsGrRC,
|
||||||
|
kExtsErrGrRC
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kLeftGrFl = 0x01,
|
||||||
|
kTopGrFl = 0x02,
|
||||||
|
kRightGrFl = 0x04,
|
||||||
|
kBottomGrFl = 0x08,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kLeftGrIdx = 0, // min-x
|
||||||
|
kTopGrIdx = 1, // max-y
|
||||||
|
kRightGrIdx = 2, // max-x
|
||||||
|
kBottomGrIdx = 3, // min-y
|
||||||
|
kAxisGrCnt = 4
|
||||||
|
} cmGrAxisIdx_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmHandle_t cmGrH_t;
|
||||||
|
typedef cmHandle_t cmGrObjH_t;
|
||||||
|
typedef cmHandle_t cmGrDcH_t;
|
||||||
|
typedef unsigned cmGrRC_t;
|
||||||
|
|
||||||
|
extern cmGrH_t cmGrNullHandle;
|
||||||
|
extern cmGrObjH_t cmGrObjNullHandle;
|
||||||
|
|
||||||
|
typedef cmReal_t cmGrV_t;
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
|
||||||
|
// Calculate the width and height between two pixels.
|
||||||
|
// This implies that the first and last pixel are inside the valid range.
|
||||||
|
#define cmGrXtoW(x0,x1) (abs((x1)-(x0))+1)
|
||||||
|
#define cmGrWtoX(x0,w) (((x0)+(w))-1)
|
||||||
|
|
||||||
|
#define cmGrYtoH(y0,y1) (abs((y1)-(y0))+1)
|
||||||
|
#define cmGrHtoY(y0,h) (((y0)+(h))-1)
|
||||||
|
|
||||||
|
#define cmGrPIsXInRange(x,x0,w) ((x0)<=(x)&&(x)<=cmGrWtoX((x0),(w)))
|
||||||
|
#define cmGrPIsYInRange(y,y0,h) ((y0)<=(y)&&(y)<=cmGrHtoY((y0),(h)))
|
||||||
|
|
||||||
|
#define cmGrVIsXInRange(x,x0,w) ((x0)<=(x)&&(x)<=((x0)+(w)))
|
||||||
|
#define cmGrVIsYInRange(y,y0,h) ((y0)<=(y)&&(y)<=((y0)+(h)))
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} cmGrPPt_t;
|
||||||
|
|
||||||
|
#define cmGrPPtSet( p, xx, yy ) do{ (p)->x=(xx); (p)->y=(yy); }while(0)
|
||||||
|
#define cmGrPPtIsEqual(p0,p1) ((p0)->x==(p1)->x && (p0)->y==(p1)->y)
|
||||||
|
#define cmGrPPtPrint(lbl,p) printf("%s x=%i y=%i\n",(lbl),(p)->x,(p)->y)
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
} cmGrPSz_t;
|
||||||
|
|
||||||
|
#define cmGrPSzSet( s, ww, hh ) do{ (s)->w=(ww); (s)->h=(hh);}while(0)
|
||||||
|
#define cmGrPSzSetD( s, x0, y0, x1, y1 ) cmGrPSzSet(cmGrXtoW(x0,x1),cmGrYtoH(y0,y1))
|
||||||
|
|
||||||
|
#define cmGrPSzSetEmpty( s ) ((s)->w = (s)->h = 0)
|
||||||
|
#define cmGrPSzSetNull( s ) ((s)->w = (s)->h = -1)
|
||||||
|
#define cmGrPSzIsEmpty( s ) ((s)->w== 0 && (s)->h== 0)
|
||||||
|
#define cmGrPSzIsNull( s ) ((s)->w==-1 && (s)->h==-1)
|
||||||
|
#define cmGrPSzIsEqual(s0,s1) ((s0)->w==(s1)->w && (s0)->h==(s1)->h)
|
||||||
|
#define cmGrPSzPrint(lbl,s) printf("%s w=%i h=%i\n",(lbl),(s)->w,(s)->h)
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmGrPPt_t loc;
|
||||||
|
cmGrPSz_t sz;
|
||||||
|
} cmGrPExt_t;
|
||||||
|
|
||||||
|
#define cmGrPExtSet( e, x, y, w, h ) do{ cmGrPPtSet(&(e)->loc,(x),(y)); cmGrPSzSet(&(e)->sz,(w),(h)); }while(0)
|
||||||
|
#define cmGrPExtSetD(e, x0, y0, x1, y1) cmGrPExtSet(e,cmMin(x0,x1),cmMin(y0,y1),cmGrXtoW(x0,x1),cmGrYtoH(y0,y1))
|
||||||
|
|
||||||
|
#define cmGrPExtL(e) ((e)->loc.x)
|
||||||
|
#define cmGrPExtT(e) ((e)->loc.y)
|
||||||
|
#define cmGrPExtR(e) (cmGrWtoX((e)->loc.x,(e)->sz.w))
|
||||||
|
#define cmGrPExtB(e) (cmGrHtoY((e)->loc.y,(e)->sz.h))
|
||||||
|
#define cmGrPExtW(e) ((e)->sz.w)
|
||||||
|
#define cmGrPExtH(e) ((e)->sz.h)
|
||||||
|
|
||||||
|
#define cmGrPExtSetL(e,v) ((e)->loc.x = (v))
|
||||||
|
#define cmGrPExtSetT(e,v) ((e)->loc.y = (v))
|
||||||
|
#define cmGrPExtSetR(e,v) cmGrPExtSetW(e,cmGrXtoW((e)->loc.x,(v)))
|
||||||
|
#define cmGrPExtSetB(e,v) cmGrPExtSetH(e,cmGrYtoH((e)->loc.y,(v)))
|
||||||
|
#define cmGrPExtSetW(e,v) ((e)->sz.w = (v))
|
||||||
|
#define cmGrPExtSetH(e,v) ((e)->sz.h = (v))
|
||||||
|
|
||||||
|
#define cmGrPExtCtrX(e) ((e)->loc.x + (e)->sz.w / 2)
|
||||||
|
#define cmGrPExtCtrY(e) ((e)->loc.y + (e)->sz.h / 2)
|
||||||
|
#define cmGrPExtCtr(e,pt) do{ (pt)->x=cmGrPExtCtrX(e); (pt)->y=cmGrPExtCtrY(e); }while(0)
|
||||||
|
|
||||||
|
#define cmGrPExtSetEmpty( e ) do{ cmGrPSzSetEmpty(&(e)->sz); cmGrPPtSet(&(e)->loc,0,0); }while(0)
|
||||||
|
#define cmGrPExtSetNull( e ) do{ cmGrPSzSetNull( &(e)->sz); cmGrPPtSet(&(e)->loc,0,0); }while(0)
|
||||||
|
#define cmGrPExtIsEmpty( e ) cmGrPSzIsEmpty( &(e)->sz )
|
||||||
|
#define cmGrPExtIsNull( e ) cmGrPSzIsNull( &(e)->sz )
|
||||||
|
#define cmGrPExtIsNullOrEmpty(e) (cmGrPExtIsNull(e)||cmGrPExtIsEmpty(e))
|
||||||
|
#define cmGrPExtIsNotEmpty(e) (!cmGrPExtIsEmpty(e))
|
||||||
|
#define cmGrPExtIsNotNull(e) (!cmGrPExtIsNull(e))
|
||||||
|
#define cmGrPExtIsNotNullOrEmpty(e) (cmGrPExtIsNotNull(e)||cmGrPExtIsNoEmpty(e))
|
||||||
|
|
||||||
|
#define cmGrPExtIsEqual( e0, e1 ) (cmGrPPtIsEqual(&(e0)->loc,&(e1)->loc) && cmGrPSzIsEqual(&(e0)->sz, &(e1)->sz))
|
||||||
|
|
||||||
|
#define cmGrPExtIsXyInside( e, xx, yy) (cmGrPIsXInRange((xx),(e)->loc.x,(e)->sz.w) && cmGrPIsYInRange((yy), (e)->loc.y, (e)->sz.h) )
|
||||||
|
#define cmGrPExtIsPtInside( e, pt ) (cmGrPExtIsXyInside((e),(pt)->x,(pt)->y))
|
||||||
|
#define cmGrPExtIsExtInside(e0, e1) (cmGrPExtIsPtInside((e0),&((e1)->loc)) && cmGrPExtIsXyInside((e0), cmGrWtoX((e1)->loc.x,(e1)->sz.w), cmGrHtoY((e1)->loc.y,(e1)->sz.h)))
|
||||||
|
|
||||||
|
#define cmGrPExtExpand(e,l,t,r,b) do{(e)->loc.x+=(l); (e)->loc.y+=(t); (e)->sz.w+=(abs(l)+abs(r)); (e)->sz.h+=(abs(t)+abs(b));}while(0)
|
||||||
|
|
||||||
|
#define cmGrPExtRpt(e,rpt) cmRptPrintf(rpt,"x:%i y:%i w:%i h:%i",(e)->loc.x,(e)->loc.y,(e)->sz.w,(e)->sz.h)
|
||||||
|
#define cmGrPExtPrint(lbl,e) printf("%s %i %i %i %i\n",lbl,(e)->loc.x,(e)->loc.y,(e)->sz.w,(e)->sz.h)
|
||||||
|
|
||||||
|
void cmGrPExtIntersect( cmGrPExt_t* r, const cmGrPExt_t* e0, const cmGrPExt_t* e1 );
|
||||||
|
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmGrV_t x;
|
||||||
|
cmGrV_t y;
|
||||||
|
} cmGrVPt_t;
|
||||||
|
|
||||||
|
#define cmGrVPtSet( p, xx, yy ) do{ (p)->x=(xx); (p)->y=(yy); }while(0)
|
||||||
|
#define cmGrVPtIsEqual(p0,p1) ((p0)->x==(p1)->x && (p0)->y==(p1)->y)
|
||||||
|
#define cmGrVPtIsNotEqual(p0,p1) (!cmGrVPtIsEqual(p0,p1))
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmGrV_t w;
|
||||||
|
cmGrV_t h;
|
||||||
|
} cmGrVSz_t;
|
||||||
|
|
||||||
|
#define cmGrVSzSet( s, ww, hh ) do{ (s)->w=(ww); (s)->h=(hh);}while(0)
|
||||||
|
#define cmGrVSzSetD( s, x0, y0, x1, y1 ) cmGrVSzSet((x1)-(x0),(y1)-(y0))
|
||||||
|
|
||||||
|
#define cmGrVSzSetEmpty( s ) ((s)->w = (s)->h = 0)
|
||||||
|
#define cmGrVSzSetNull( s ) ((s)->w = (s)->h = -1)
|
||||||
|
#define cmGrVSzIsEmpty( s ) ((s)->w== 0 && (s)->h== 0)
|
||||||
|
#define cmGrVSzIsNull( s ) ((s)->w==-1 || (s)->h==-1)
|
||||||
|
#define cmGrVSzIsEqual(s0,s1) ((s0)->w==(s1)->w && (s0)->h==(s1)->h)
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmGrVPt_t loc;
|
||||||
|
cmGrVSz_t sz;
|
||||||
|
} cmGrVExt_t;
|
||||||
|
|
||||||
|
#define cmGrVExtIsNorm( e ) ((e)->sz.w>=0 && (e)->sz.h>=0)
|
||||||
|
#define cmGrVExtNorm( e ) do{ if( cmGrVExtIsNotNull(e) ){ if((e)->sz.w<0){(e)->loc.x += (e)->sz.w; (e)->sz.w*=-1;} if((e)->sz.h<0){(e)->loc.y += (e)->sz.h; (e)->sz.h*=-1;}} }while(0)
|
||||||
|
#define cmGrVExtSet( e, x, y, w, h ) do{ cmGrVPtSet(&(e)->loc,(x),(y)); cmGrVSzSet(&(e)->sz,(w),(h)); cmGrVExtNorm(e); }while(0)
|
||||||
|
#define cmGrVExtSetD(e, x0, y0, x1, y1) cmGrVExtSet((e),(x0),(y0),(x1)-(x0),(y1)-(y0))
|
||||||
|
|
||||||
|
//
|
||||||
|
// l,t minx,maxy
|
||||||
|
// r,b maxx,miny
|
||||||
|
//
|
||||||
|
#define cmGrVExtMinX(e) ((e)->loc.x)
|
||||||
|
#define cmGrVExtMinY(e) ((e)->loc.y)
|
||||||
|
#define cmGrVExtMaxX(e) ((e)->loc.x + (e)->sz.w)
|
||||||
|
#define cmGrVExtMaxY(e) ((e)->loc.y + (e)->sz.h)
|
||||||
|
#define cmGrVExtW(e) ((e)->sz.w)
|
||||||
|
#define cmGrVExtH(e) ((e)->sz.h)
|
||||||
|
|
||||||
|
#define cmGrVExtSetMinX(e,v) ((e)->loc.x = (v))
|
||||||
|
#define cmGrVExtSetMinY(e,v) ((e)->loc.y = (v))
|
||||||
|
|
||||||
|
// Beware: setting maxx and maxy depends on the current value of minx and miny.
|
||||||
|
// If both minx and maxx are being changed then be sure to set minx first.
|
||||||
|
// If both miny and maxy are being changed then be sure to set miny first.
|
||||||
|
#define cmGrVExtSetMaxX(e,v) ((e)->sz.w = (v) - cmGrVExtMinX(e))
|
||||||
|
#define cmGrVExtSetMaxY(e,v) ((e)->sz.h = (v) - cmGrVExtMinY(e))
|
||||||
|
#define cmGrVExtSetW(e,v) ((e)->sz.w = (v))
|
||||||
|
#define cmGrVExtSetH(e,v) ((e)->sz.h = (v))
|
||||||
|
|
||||||
|
#define cmGrVExtSetEmpty( e ) do{ cmGrVSzSetEmpty(&(e)->sz); cmGrVPtSet(&(e)->loc,0,0); }while(0)
|
||||||
|
#define cmGrVExtSetNull( e ) do{ cmGrVSzSetNull(&(e)->sz); cmGrVPtSet(&(e)->loc,0,0); }while(0)
|
||||||
|
#define cmGrVExtIsEmpty( e ) cmGrVSzIsEmpty(&(e)->sz)
|
||||||
|
#define cmGrVExtIsNull( e ) cmGrVSzIsNull( &(e)->sz)
|
||||||
|
#define cmGrVExtIsNullOrEmpty(e) (cmGrVExtIsNull(e)||cmGrVExtIsEmpty(e))
|
||||||
|
#define cmGrVExtIsNotEmpty(e) (!cmGrVExtIsEmpty(e))
|
||||||
|
#define cmGrVExtIsNotNull(e) (!cmGrVExtIsNull(e))
|
||||||
|
#define cmGrVExtIsNotNullOrEmpty(e) (cmGrVExtIsNotNull(e)||cmGrVExtIsNotEmpty(e))
|
||||||
|
#define cmGrVExtIsEqual( e0, e1 ) (cmGrVPtIsEqual(&(e0)->loc,&(e1)->loc) && cmGrVSzIsEqual(&(e0)->sz, &(e1)->sz))
|
||||||
|
|
||||||
|
|
||||||
|
#define cmGrVExtIsXyInside( e, xx, yy) (cmGrVIsXInRange((xx),(e)->loc.x,(e)->sz.w) && cmGrVIsYInRange((yy),(e)->loc.y,(e)->sz.h))
|
||||||
|
#define cmGrVExtIsPtInside( e, pt ) (cmGrVExtIsXyInside((e),(pt)->x,(pt)->y))
|
||||||
|
|
||||||
|
// e1 is inside e0
|
||||||
|
#define cmGrVExtIsExtInside(e0, e1) (cmGrVExtIsXyInside((e0),cmGrVExtMinX(e1),cmGrVExtMinY(e1)) && cmGrVExtIsXyInside((e0), cmGrVExtMaxX(e1), cmGrVExtMaxY(e1)))
|
||||||
|
|
||||||
|
#define cmGrVExtRpt(e,rpt) cmRptPrintf(rpt,"x:%f y:%f w:%f h:%f",(e)->loc.x,(e)->loc.y,(e)->sz.w,(e)->sz.h)
|
||||||
|
#define cmGrVExtPrint(lbl,e) printf("%s %f %f %f %f\n",lbl,(e)->loc.x,(e)->loc.y,(e)->sz.w,(e)->sz.h)
|
||||||
|
|
||||||
|
|
||||||
|
// Shift and expand e0 to contain e1. Return true if e0 actually changes.
|
||||||
|
bool cmGrVExtExpandToContain( cmGrVExt_t* e0, const cmGrVExt_t* e1 );
|
||||||
|
|
||||||
|
// Force e1 to be contained by e0 by shifting e1's location. This function
|
||||||
|
// will never change the width or height of e1. Return true if e1 is changed.
|
||||||
|
bool cmGrVExtContain( const cmGrVExt_t* e0, cmGrVExt_t* e1 );
|
||||||
|
|
||||||
|
// Return the intersection of 'e0' with 'e1' in 'r'.
|
||||||
|
void cmGrVExtIntersect( cmGrVExt_t* r, const cmGrVExt_t* e0, const cmGrVExt_t* e1 );
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
#define cmGrRgbToColor( r, g, b ) (((r) << 16) + ((g) << 8) + (b))
|
||||||
|
#define cmGrColorToR( c ) (((c) >> 16) & 0x000000ff)
|
||||||
|
#define cmGrColorToG( c ) (((c) >> 8) & 0x000000ff)
|
||||||
|
#define cmGrColorToB( c ) (((c) ) & 0x000000ff)
|
||||||
|
|
||||||
|
typedef unsigned cmGrColor_t;
|
||||||
|
enum { kGrDefaultColorMapIdx = 0, kGrDefaultColorMapId=0 };
|
||||||
|
|
||||||
|
unsigned cmGrColorMapCount( cmGrH_t grH );
|
||||||
|
unsigned cmGrColorMapId( cmGrH_t grH, unsigned mapIdx );
|
||||||
|
const cmChar_t* cmGrColorMapLabel( cmGrH_t grH, unsigned id );
|
||||||
|
unsigned cmGrColorMapRegister( cmGrH_t grH, cmChar_t* label, const cmGrColor_t* array, unsigned cnt );
|
||||||
|
cmGrColor_t* cmGrColorMap( cmGrH_t grH, unsigned mapId );
|
||||||
|
unsigned cmGrColorMapEleCount( cmGrH_t grH, unsigned mapId );
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmCtx_t* ctx; // application context
|
||||||
|
cmGrH_t grH; // graphics system handle to which this graphic object belongs
|
||||||
|
cmGrObjH_t objH; // this graphics object handle
|
||||||
|
void* cbArg; // user callback arg
|
||||||
|
|
||||||
|
cmGrPPt_t msDnPPt; // mouse down phys point
|
||||||
|
cmGrVPt_t msDnVPt; // mouse down virt point inside op->parent->wext
|
||||||
|
cmGrVSz_t msDnVOffs; // virtual offset from mouse down point to msDnObj->vext
|
||||||
|
cmGrObjH_t msDnObjH; // handle of object which recv'd mouse down
|
||||||
|
cmGrVPt_t msVPt; // cur ms virtual point
|
||||||
|
|
||||||
|
} cmGrObjFuncArgs_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmGrRC_t (*cmGrCreateObjCb_t)( cmGrObjFuncArgs_t* args );
|
||||||
|
typedef void (*cmGrDestroyObjCb_t)( cmGrObjFuncArgs_t* args );
|
||||||
|
typedef bool (*cmGrRenderObjCb_t)( cmGrObjFuncArgs_t* args, cmGrDcH_t dcH );
|
||||||
|
typedef int (*cmGrDistanceObjCb_t)( cmGrObjFuncArgs_t* args, int x, int y );
|
||||||
|
typedef bool (*cmGrEventObjCb_t)( cmGrObjFuncArgs_t* args, unsigned flags, unsigned key, int px, int py );
|
||||||
|
typedef void (*cmGrVExtObjCb_t)( cmGrObjFuncArgs_t* args, cmGrVExt_t* vext );
|
||||||
|
typedef bool (*cmGrIsInsideObjCb_t)( cmGrObjFuncArgs_t* args, int px, int py, cmGrV_t vx, cmGrV_t vy );
|
||||||
|
|
||||||
|
typedef struct cmGrObjFunc_str
|
||||||
|
{
|
||||||
|
// User defined constructor.
|
||||||
|
cmGrCreateObjCb_t createCbFunc;
|
||||||
|
void* createCbArg;
|
||||||
|
|
||||||
|
// User defined destructor.
|
||||||
|
cmGrDestroyObjCb_t destroyCbFunc;
|
||||||
|
void* destroyCbArg;
|
||||||
|
|
||||||
|
// Draw the object by calling back to the cmGrDrawXXX() functions
|
||||||
|
cmGrRenderObjCb_t renderCbFunc;
|
||||||
|
void* renderCbArg;
|
||||||
|
|
||||||
|
// Return the physical distance from a physical view location to the object.
|
||||||
|
// (NOT USED)
|
||||||
|
cmGrDistanceObjCb_t distanceCbFunc;
|
||||||
|
void* distanceCbArg;
|
||||||
|
|
||||||
|
// Handle an event. gx,gy are in the same coord's as args.objH.vext (they are inside args.objH.parent.wext).
|
||||||
|
// Return true if the event objects dirty flag should be set.
|
||||||
|
cmGrEventObjCb_t eventCbFunc;
|
||||||
|
void* eventCbArg;
|
||||||
|
|
||||||
|
// Return the objects location and size inside op->parent->wext
|
||||||
|
cmGrVExtObjCb_t vextCbFunc;
|
||||||
|
void* vextCbArg;
|
||||||
|
|
||||||
|
// Return true if the point is inside this obj. vx,vy is in the the same coord's as op->vext (i.e. vx,vy is inside op->parent->wext)
|
||||||
|
// The simple answer to this call is cmGrVExtIsXyInside( *vext, vx, vy ).
|
||||||
|
// Called to determine which object is under the mouse.
|
||||||
|
cmGrIsInsideObjCb_t isInsideCbFunc;
|
||||||
|
void* isInsideCbArg;
|
||||||
|
} cmGrObjFunc_t;
|
||||||
|
|
||||||
|
|
||||||
|
// Create a graphic object. This function calls the user defined (*create)() function.
|
||||||
|
cmGrRC_t cmGrObjCreate( cmGrH_t h, cmGrObjH_t* hp, cmGrObjH_t parentH, cmGrObjFunc_t* f, unsigned id, unsigned flags, const cmGrVExt_t* wext );
|
||||||
|
|
||||||
|
// Destroy a graphic object and all of it's children.
|
||||||
|
// This function calls the user defined (*destroy)() function.
|
||||||
|
cmGrRC_t cmGrObjDestroy( cmGrH_t h, cmGrObjH_t* hp );
|
||||||
|
|
||||||
|
// Return true if 'oh' is a valid handle.
|
||||||
|
cmGrRC_t cmGrObjIsValid( cmGrH_t h, cmGrObjH_t oh );
|
||||||
|
|
||||||
|
// Return the user id associated with this object.
|
||||||
|
unsigned cmGrObjId( cmGrObjH_t oh );
|
||||||
|
void cmGrObjSetId( cmGrObjH_t oh, unsigned id );
|
||||||
|
|
||||||
|
// Return the handle to the parent object.
|
||||||
|
cmGrObjH_t cmGrObjParent( cmGrObjH_t oh );
|
||||||
|
|
||||||
|
// An object world coord's are used to place child objects.
|
||||||
|
cmGrRC_t cmGrObjSetWorldExt( cmGrH_t h, cmGrObjH_t oh, const cmGrVExt_t* vext );
|
||||||
|
void cmGrObjWorldExt( cmGrObjH_t oh, cmGrVExt_t* vext );
|
||||||
|
|
||||||
|
cmGrRC_t cmGrObjSetWorldLimitExt( cmGrH_t h, cmGrObjH_t oh, const cmGrVExt_t* vext, unsigned limitFlags );
|
||||||
|
void cmGrObjWorldLimitExt( cmGrObjH_t oh, cmGrVExt_t* vext, unsigned* limitFlags );
|
||||||
|
|
||||||
|
void cmGrObjSetCreateCb( cmGrObjH_t oh, cmGrCreateObjCb_t cbFunc, void* cbArg );
|
||||||
|
void cmGrObjSetDestroyCb( cmGrObjH_t oh, cmGrDestroyObjCb_t cbFunc, void* cbArg );
|
||||||
|
void cmGrObjSetRenderCb( cmGrObjH_t oh, cmGrRenderObjCb_t cbFunc, void* cbArg );
|
||||||
|
void cmGrObjSetDistanceCb( cmGrObjH_t oh, cmGrDistanceObjCb_t cbFunc, void* cbArg );
|
||||||
|
void cmGrObjSetEventCb( cmGrObjH_t oh, cmGrEventObjCb_t cbFunc, void* cbArg );
|
||||||
|
void cmGrObjSetVExtCb( cmGrObjH_t oh, cmGrVExtObjCb_t cbFunc, void* cbArg );
|
||||||
|
void cmGrObjSetIsInsideCb( cmGrObjH_t oh, cmGrIsInsideObjCb_t cbFunc, void* cbArg );
|
||||||
|
|
||||||
|
cmGrCreateObjCb_t cmGrObjCreateCbFunc( cmGrObjH_t oh );
|
||||||
|
cmGrDestroyObjCb_t cmGrObjDestroyCbFunc( cmGrObjH_t oh );
|
||||||
|
cmGrRenderObjCb_t cmGrObjRenderCbFunc( cmGrObjH_t oh );
|
||||||
|
cmGrDistanceObjCb_t cmGrObjDistanceCbFunc( cmGrObjH_t oh );
|
||||||
|
cmGrEventObjCb_t cmGrObjEventCbFunc( cmGrObjH_t oh );
|
||||||
|
cmGrVExtObjCb_t cmGrObjVExtCbFunc( cmGrObjH_t oh );
|
||||||
|
cmGrIsInsideObjCb_t cmGrObjIsInsideCbFunc( cmGrObjH_t oh );
|
||||||
|
|
||||||
|
void* cmGrObjCreateCbArg( cmGrObjH_t oh );
|
||||||
|
void* cmGrObjDestroyCbArg( cmGrObjH_t oh );
|
||||||
|
void* cmGrObjRenderCbArg( cmGrObjH_t oh );
|
||||||
|
void* cmGrObjDistanceCbArg( cmGrObjH_t oh );
|
||||||
|
void* cmGrObjEventCbArg( cmGrObjH_t oh );
|
||||||
|
void* cmGrObjVExtCbArg( cmGrObjH_t oh );
|
||||||
|
void* cmGrObjIsInsideCbArg( cmGrObjH_t oh );
|
||||||
|
|
||||||
|
|
||||||
|
// Same as call to user defined (*vect)().
|
||||||
|
void cmGrObjLocalVExt( cmGrH_t h, cmGrObjH_t oh, cmGrVExt_t* vext );
|
||||||
|
|
||||||
|
// Given an objects id return it's handle.
|
||||||
|
cmGrObjH_t cmGrObjIdToHandle( cmGrH_t h, unsigned id );
|
||||||
|
|
||||||
|
// Move 'aoH' such that it is drawn above 'boH' in the z-order.
|
||||||
|
// This means that 'boH' will be drawn before 'aoH'.
|
||||||
|
void cmGrObjDrawAbove( cmGrObjH_t boH, cmGrObjH_t aoH );
|
||||||
|
|
||||||
|
void cmGrObjReport( cmGrH_t h, cmGrObjH_t oh, cmRpt_t* rpt );
|
||||||
|
void cmGrObjReportR( cmGrH_t h, cmGrObjH_t oh, cmRpt_t* rpt ); // print children
|
||||||
|
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
// Drawing Functions - called by objects to draw themselves
|
||||||
|
|
||||||
|
int cmGrX_VtoP( cmGrH_t hh, cmGrObjH_t oh, cmGrV_t y );
|
||||||
|
int cmGrY_VtoP( cmGrH_t hh, cmGrObjH_t oh, cmGrV_t x );
|
||||||
|
|
||||||
|
void cmGrXY_VtoP( cmGrH_t hh, cmGrObjH_t oh, cmGrV_t x, cmGrV_t y, cmGrPPt_t* rp );
|
||||||
|
void cmGrXYWH_VtoP( cmGrH_t hh, cmGrObjH_t oh, cmGrV_t x, cmGrV_t y, cmGrV_t w, cmGrV_t h, cmGrPExt_t* pext );
|
||||||
|
void cmGrVExt_VtoP( cmGrH_t hh, cmGrObjH_t oh, const cmGrVExt_t* vext, cmGrPExt_t* pext );
|
||||||
|
|
||||||
|
void cmGrXY_PtoV( cmGrH_t hh, cmGrObjH_t oh, int x, int y, cmGrVPt_t* rp );
|
||||||
|
void cmGrXYWH_PtoV( cmGrH_t hh, cmGrObjH_t oh, int x, int y, int w, int h, cmGrVExt_t* vext );
|
||||||
|
void cmGrPExt_PtoV( cmGrH_t hh, cmGrObjH_t oh, const cmGrPExt_t* pext, cmGrVExt_t* vext );
|
||||||
|
|
||||||
|
void cmGrDrawVLine( cmGrH_t hh, cmGrDcH_t dcH, cmGrObjH_t oh, cmGrV_t x0, cmGrV_t y0, cmGrV_t x1, cmGrV_t y1 );
|
||||||
|
void cmGrDrawVRect( cmGrH_t hh, cmGrDcH_t dcH, cmGrObjH_t oh, cmGrV_t x, cmGrV_t y, cmGrV_t w, cmGrV_t h );
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
|
||||||
|
// Callback identifiers
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kCreateCbGrId,
|
||||||
|
kDestroyCbGrId,
|
||||||
|
kLocalPtCbGrId,
|
||||||
|
kGlobalPtCbGrId,
|
||||||
|
kPhysExtCbGrId,
|
||||||
|
kViewExtCbGrId,
|
||||||
|
kSelectExtCbGrId,
|
||||||
|
kFocusCbGrId,
|
||||||
|
kKeyUpCbGrId,
|
||||||
|
kKeyDnCbGrId
|
||||||
|
} cmGrCbId_t;
|
||||||
|
|
||||||
|
// Callback function associated with this canvas.
|
||||||
|
typedef void (*cmGrCbFunc_t)( void* arg, cmGrH_t grH, cmGrCbId_t id, unsigned evtFlags, cmGrKeyCodeId_t keycode );
|
||||||
|
|
||||||
|
// Configuration Flags
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kExpandViewGrFl = 0x01, // expand the view to show new objects
|
||||||
|
kSelectHorzGrFl = 0x02, // select along x-axis only
|
||||||
|
kSelectVertGrFl = 0x04 // select along y-axis only
|
||||||
|
};
|
||||||
|
|
||||||
|
// 'wext' is optional.
|
||||||
|
// 'id' is an arbitrary user definable identifier - although it is used
|
||||||
|
// as the view index by cmGrPage().
|
||||||
|
cmGrRC_t cmGrCreate(
|
||||||
|
cmCtx_t* ctx,
|
||||||
|
cmGrH_t* hp,
|
||||||
|
unsigned id,
|
||||||
|
unsigned cfgFlags,
|
||||||
|
cmGrCbFunc_t cbFunc,
|
||||||
|
void* cbArg,
|
||||||
|
const cmGrVExt_t* wext ); // Optional internal world extents for this object
|
||||||
|
|
||||||
|
// Destroy this canvas.
|
||||||
|
cmGrRC_t cmGrDestroy( cmGrH_t* hp );
|
||||||
|
|
||||||
|
// Remove all objects from the root object and restore the canvas to it's default state.
|
||||||
|
cmGrRC_t cmGrClear( cmGrH_t h );
|
||||||
|
|
||||||
|
// Get the root object handle
|
||||||
|
cmGrObjH_t cmGrRootObjH( cmGrH_t h );
|
||||||
|
|
||||||
|
// Get and set the configuration flags (e.g. kExpandViewGrFl | kSelectHorzGrFl | kSelectVertHorzGrFl )
|
||||||
|
unsigned cmGrCfgFlags( cmGrH_t h );
|
||||||
|
void cmGrSetCfgFlags( cmGrH_t h, unsigned cfgFlags );
|
||||||
|
|
||||||
|
// Draw the objects on the canvas.
|
||||||
|
cmGrRC_t cmGrDraw( cmGrH_t h, cmGrDcH_t dcH );
|
||||||
|
|
||||||
|
// event flags
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kMsDownGrFl = 0x0001,
|
||||||
|
kMsUpGrFl = 0x0002,
|
||||||
|
kMsMoveGrFl = 0x0004,
|
||||||
|
kMsWheelGrFl= 0x0008,
|
||||||
|
kMsDragGrFl = 0x0010,
|
||||||
|
kMsClickGrFl= 0x0020,
|
||||||
|
kKeyDnGrFl = 0x0040,
|
||||||
|
kKeyUpGrFl = 0x0080,
|
||||||
|
|
||||||
|
kMsEvtMask = 0x02f,
|
||||||
|
kEvtMask = 0x00ff,
|
||||||
|
|
||||||
|
kMsLBtnGrFl = 0x0100,
|
||||||
|
kMsCBtnGrFl = 0x0200,
|
||||||
|
kMsRBtnGrFl = 0x0400,
|
||||||
|
|
||||||
|
kShiftKeyGrFl = 0x0800,
|
||||||
|
kAltKeyGrFl = 0x1000,
|
||||||
|
kCtlKeyGrFl = 0x2000,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Receive a UI event.
|
||||||
|
bool cmGrEvent( cmGrH_t h, unsigned flags, cmGrKeyCodeId_t key, int x, int y );
|
||||||
|
|
||||||
|
// Return true if 'h' is valid.
|
||||||
|
bool cmGrIsValid( cmGrH_t h );
|
||||||
|
|
||||||
|
// Return the user defined 'id' set in cmGrCreate()
|
||||||
|
unsigned cmGrId( cmGrH_t h );
|
||||||
|
|
||||||
|
// Return the last mouse location in root object coordinates.
|
||||||
|
const cmGrVPt_t* cmGrGlobalPt( cmGrH_t h );
|
||||||
|
|
||||||
|
// Return the last mouse location in coordinates of the object the mouse was over.
|
||||||
|
const cmGrVPt_t* cmGrLocalPt( cmGrH_t h );
|
||||||
|
|
||||||
|
|
||||||
|
// The new view extents must fit inside the world extents.
|
||||||
|
// Return true if the view extents actually changed.
|
||||||
|
bool cmGrSetViewExtents( cmGrH_t hh, cmGrV_t minx, cmGrV_t miny, cmGrV_t maxx, cmGrV_t maxy );
|
||||||
|
bool cmGrSetViewExtentsE(cmGrH_t h, const cmGrVExt_t* ext );
|
||||||
|
void cmGrViewExtents( cmGrH_t h, cmGrVExt_t* exts );
|
||||||
|
|
||||||
|
// View Location
|
||||||
|
// Return true if the phys extents actually changed.
|
||||||
|
bool cmGrSetPhysExtents( cmGrH_t hh, int x, int y, int w, int h );
|
||||||
|
bool cmGrSetPhysExtentsE(cmGrH_t h, const cmGrPExt_t* ext );
|
||||||
|
void cmGrPhysExtents( cmGrH_t h, cmGrPExt_t* exts );
|
||||||
|
|
||||||
|
// Return some scroll bar values for this canvas.
|
||||||
|
// tot=world pixels, vis=vis pixels, max=max scroll pos pos=cur scroll pos
|
||||||
|
// All return values are optional.
|
||||||
|
void cmGrScrollExtents( cmGrH_t h, cmGrPSz_t* tot, cmGrPSz_t* vis, cmGrPSz_t* max, cmGrPPt_t* pos );
|
||||||
|
|
||||||
|
// Return true if the view location actually changed.
|
||||||
|
bool cmGrSetScrollH( cmGrH_t h, int x );
|
||||||
|
int cmGrScrollH( cmGrH_t h );
|
||||||
|
bool cmGrSetScrollV( cmGrH_t h, int y );
|
||||||
|
int cmGrScrollV( cmGrH_t h );
|
||||||
|
|
||||||
|
// Get the current selection extents.
|
||||||
|
// If the selection extents are not valid then the function returns false
|
||||||
|
// and sets the return extents to their null state.
|
||||||
|
bool cmGrSelectExtents( cmGrH_t h, cmGrVExt_t* vext, cmGrPExt_t* pext );
|
||||||
|
|
||||||
|
// Both pts are optional
|
||||||
|
void cmGrSetSelectPoints(cmGrH_t h, const cmGrVPt_t* pt0, const cmGrVPt_t* pt1 );
|
||||||
|
void cmGrSelectPoints( cmGrH_t h, cmGrVPt_t* pt0, cmGrVPt_t* pt1 );
|
||||||
|
|
||||||
|
enum { kZoomInGrFl=0x01, kXAxisGrFl=0x02, kYAxisGrFl=0x04, kSelectGrFl=0x08, kShowAllGrFl=0x10 };
|
||||||
|
|
||||||
|
// 1) If kSelectGrFl is not set then the center 1/3 of the current view
|
||||||
|
// becomes the new view.
|
||||||
|
// 2) If kSelectGrFl is set then the selection area becomes the view.
|
||||||
|
// 3) If kSelectGrFl is set but no selection area exists then
|
||||||
|
// option 1) is selected used and using the selection point as center.
|
||||||
|
void cmGrZoom( cmGrH_t h, unsigned flags );
|
||||||
|
|
||||||
|
// Synchronize the 'syncGrH' horz. and/or verical, world,view,select extents to
|
||||||
|
// this gr's extents. Changes to this gr's extents will be automatically
|
||||||
|
// applied to 'syncGrH'.
|
||||||
|
// If 'syncGrH' was used in a previous call to this function then flags will
|
||||||
|
// modify the previously set flags value.
|
||||||
|
// Clear the kHorzSyncFl and kVertSyncFl to disable the synchronization.
|
||||||
|
// Set flags to 0 to prevent future sync calls.
|
||||||
|
enum { kWorldSyncGrFl=0x01, kViewSyncGrFl=0x02, kSelectSyncGrFl=0x04, kHorzSyncGrFl=0x08, kVertSyncGrFl=0x10 };
|
||||||
|
void cmGrSetSync( cmGrH_t h, cmGrH_t syncGrH, unsigned flags );
|
||||||
|
|
||||||
|
|
||||||
|
void cmGrReport( cmGrH_t h, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
530
cmGrDevCtx.c
Normal file
530
cmGrDevCtx.c
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmGr.h"
|
||||||
|
#include "cmGrDevCtx.h"
|
||||||
|
|
||||||
|
|
||||||
|
cmGrDcH_t cmGrDcNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
// cmGrDcRecd is used to store the state of the
|
||||||
|
// device context on the cmGrDC_t stack.
|
||||||
|
typedef struct cmGrDcRecd_str
|
||||||
|
{
|
||||||
|
cmGrColor_t color;
|
||||||
|
unsigned fontId;
|
||||||
|
unsigned fontStyle;
|
||||||
|
unsigned fontSize;
|
||||||
|
unsigned penWidth;
|
||||||
|
unsigned penStyle;
|
||||||
|
|
||||||
|
struct cmGrDcRecd_str* next;
|
||||||
|
struct cmGrDcRecd_str* prev;
|
||||||
|
} cmGrDcRecd_t;
|
||||||
|
|
||||||
|
typedef struct cmGrDc_str
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmGrDev_t* dd; // device driver used by this context
|
||||||
|
void* ddArg; // user assigned device driver callback arg
|
||||||
|
cmGrDcRecd_t* list; // First recd on the stack (not the top).
|
||||||
|
cmGrDcRecd_t* cur; // Top recd on the stack.
|
||||||
|
cmGrPExt_t pext; // x,y is offset added to all drawing coordinates
|
||||||
|
// w,h is size of drawing area
|
||||||
|
} cmGrDc_t;
|
||||||
|
|
||||||
|
// Note: recd's prior to p->cur are available.
|
||||||
|
// Recd's after p->cur are on the stack.
|
||||||
|
|
||||||
|
cmGrDc_t* _cmGrDcHandleToPtr( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = (cmGrDc_t*)h.h;
|
||||||
|
assert( p != NULL );
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmGrDcRecdPrint( const cmChar_t* label, const cmGrDcRecd_t* r )
|
||||||
|
{
|
||||||
|
printf("%s r:%i g:%i b:%i fid:%i fs:0x%x fsz:%i pw:%i ps:0x%x\n",
|
||||||
|
cmStringNullGuard(label),
|
||||||
|
cmGrColorToR(r->color),cmGrColorToG(r->color),cmGrColorToB(r->color),
|
||||||
|
r->fontId,r->fontStyle,r->fontSize,r->penWidth,r->penStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a duplicate of the current record (if it exists)
|
||||||
|
// and insert it prior to the current record.
|
||||||
|
// make the new record current.
|
||||||
|
void _cmGrDcPush( cmGrDc_t* p )
|
||||||
|
{
|
||||||
|
if( p->cur == NULL )
|
||||||
|
{
|
||||||
|
assert( p->list == NULL );
|
||||||
|
cmGrDcRecd_t* r = cmMemAllocZ( cmGrDcRecd_t, 1);
|
||||||
|
p->dd->get_color( p->ddArg, &r->color );
|
||||||
|
r->fontId = p->dd->get_font_family(p->ddArg );
|
||||||
|
r->fontSize = p->dd->get_font_size( p->ddArg );
|
||||||
|
r->fontStyle = p->dd->get_font_style( p->ddArg );
|
||||||
|
r->penWidth = p->dd->get_pen_width( p->ddArg );
|
||||||
|
r->penStyle = p->dd->get_pen_style( p->ddArg );
|
||||||
|
p->list = r;
|
||||||
|
p->cur = r;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmGrDcRecd_t* r = p->cur->prev;
|
||||||
|
|
||||||
|
// if no prev recd exists ...
|
||||||
|
if( r == NULL )
|
||||||
|
{
|
||||||
|
// .... then allocate one
|
||||||
|
r = cmMemAllocZ( cmGrDcRecd_t, 1 );
|
||||||
|
*r = *p->cur;
|
||||||
|
p->cur->prev = r;
|
||||||
|
r->next = p->cur;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ... otherwise use the prev one
|
||||||
|
cmGrDcRecd_t* nrp = r->next;
|
||||||
|
cmGrDcRecd_t* prp = r->prev;
|
||||||
|
*r = *p->cur;
|
||||||
|
r->next = nrp;
|
||||||
|
r->prev = prp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make the new recd the cur recd
|
||||||
|
p->cur = r;
|
||||||
|
|
||||||
|
// if the new recd is the first on the list
|
||||||
|
// then update the list begin pointer
|
||||||
|
if( p->cur->prev == NULL )
|
||||||
|
p->list = p->cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
//_cmGrDcRecdPrint("push:", p->cur );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmGrDcRC_t _cmGrDcPop(cmGrDc_t* p )
|
||||||
|
{
|
||||||
|
if( p->cur==NULL || p->cur->next == NULL )
|
||||||
|
return cmErrMsg(&p->err,kStackFaultGrDcRC,"Cannot pop the last context record off the stack.");
|
||||||
|
|
||||||
|
p->cur = p->cur->next;
|
||||||
|
|
||||||
|
p->dd->set_color( p->ddArg, p->cur->color );
|
||||||
|
p->dd->set_font_family( p->ddArg, p->cur->fontId );
|
||||||
|
p->dd->set_font_size( p->ddArg, p->cur->fontSize );
|
||||||
|
p->dd->set_font_style( p->ddArg, p->cur->fontStyle );
|
||||||
|
p->dd->set_pen_width( p->ddArg, p->cur->penWidth );
|
||||||
|
p->dd->set_pen_style( p->ddArg, p->cur->penStyle );
|
||||||
|
|
||||||
|
//_cmGrDcRecdPrint("pop:", p->cur );
|
||||||
|
|
||||||
|
return kOkGrDcRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmGrDcRC_t _cmGrDcDestroy( cmGrDc_t* p )
|
||||||
|
{
|
||||||
|
cmGrDcRecd_t* rp = p->list;
|
||||||
|
while( rp!=NULL )
|
||||||
|
{
|
||||||
|
cmGrDcRecd_t* tp = rp->next;
|
||||||
|
cmMemFree( rp );
|
||||||
|
rp = tp;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->dd->destroy(p->ddArg);
|
||||||
|
|
||||||
|
|
||||||
|
cmMemFree(p);
|
||||||
|
|
||||||
|
return kOkGrDcRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmGrDcRC_t cmGrDevCtxCreate( cmCtx_t* ctx, cmGrDcH_t* hp, cmGrDev_t* dd, void* ddArg, int x, int y, int w, int h )
|
||||||
|
{
|
||||||
|
cmGrDcRC_t rc;
|
||||||
|
if((rc = cmGrDevCtxDestroy(hp)) != kOkGrDcRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
cmGrDc_t* p = cmMemAllocZ(cmGrDc_t,1);
|
||||||
|
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"cmGrDevCtx");
|
||||||
|
|
||||||
|
p->dd = dd;
|
||||||
|
p->ddArg = ddArg;
|
||||||
|
|
||||||
|
cmGrPExtSet(&p->pext,x,y,w,h);
|
||||||
|
|
||||||
|
if( dd->create(ddArg,w,h) == false )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kDevDrvFailGrDcRC,"Device driver create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cmGrDcPush(p); // create the default context
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if(rc != kOkGrDcRC )
|
||||||
|
_cmGrDcDestroy(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmGrDcRC_t cmGrDevCtxDestroy( cmGrDcH_t* hp )
|
||||||
|
{
|
||||||
|
cmGrDcRC_t rc;
|
||||||
|
|
||||||
|
if( hp==NULL || cmGrDevCtxIsValid(*hp)==false )
|
||||||
|
return kOkGrDcRC;
|
||||||
|
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if((rc = _cmGrDcDestroy(p)) != kOkGrDcRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmGrDevCtxIsValid( cmGrDcH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
cmGrDcRC_t cmGrDevCtxResize( cmGrDcH_t h, int x, int y, int ww, int hh )
|
||||||
|
{
|
||||||
|
cmGrDcRC_t rc = kOkGrDcRC;
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
|
||||||
|
// store the current drawing context state
|
||||||
|
_cmGrDcPush(p);
|
||||||
|
|
||||||
|
if( p->dd->create(p->ddArg,ww,hh) == false )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kDevDrvFailGrDcRC,"Device driver create failed on resize.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmGrPExtSet(&p->pext,-x,-y,ww,hh);
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
// force the current state to be reapplied to the new drawing context
|
||||||
|
_cmGrDcPop(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDevCtxSize( cmGrDcH_t h, cmGrPExt_t* pext )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
*pext = p->pext;
|
||||||
|
pext->loc.x *= -1;
|
||||||
|
pext->loc.y *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDevCtxBeginDraw( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->begin_draw( p->ddArg );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDevCtxEndDraw( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->end_draw( p->ddArg );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDevCtxDraw( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw( p->ddArg, -p->pext.loc.x, -p->pext.loc.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmGrDcPushCtx( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
_cmGrDcPush(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcPopCtx( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
_cmGrDcPop(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned cmGrDcColor( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
return p->cur->color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcSetColorRgb( cmGrDcH_t h, unsigned char r, unsigned char g, unsigned char b )
|
||||||
|
{
|
||||||
|
cmGrDcSetColor(h,cmGrRgbToColor(r,g,b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcSetColor( cmGrDcH_t h, cmGrColor_t color )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->set_color( p->ddArg, color );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmGrDcFontFamily( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
return p->cur->fontId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcSetFontFamily( cmGrDcH_t h, unsigned fontId )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->cur->fontId = fontId;
|
||||||
|
p->dd->set_font_family( p->ddArg, fontId );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmGrDcFontStyle( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
return p->cur->fontStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcSetFontStyle( cmGrDcH_t h, unsigned style )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->cur->fontStyle = style;
|
||||||
|
p->dd->set_font_style( p->ddArg, style );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmGrDcFontSize( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
return p->cur->fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcSetFontSize( cmGrDcH_t h, unsigned size )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->cur->fontSize = size;
|
||||||
|
p->dd->set_font_size( p->ddArg, size );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmGrDcPenWidth( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
return p->cur->penWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcSetPenWidth( cmGrDcH_t h, unsigned width )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->cur->penWidth = width;
|
||||||
|
p->dd->set_pen_width( p->ddArg, width );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmGrDcPenStyle( cmGrDcH_t h )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
return p->cur->penStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcSetPenStyle( cmGrDcH_t h, unsigned style )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->cur->penStyle = style;
|
||||||
|
p->dd->set_pen_style( p->ddArg, style );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawLine( cmGrDcH_t h, int x0, int y0, int x1, int y1 )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw_line( p->ddArg, x0+p->pext.loc.x, y0+p->pext.loc.y, x1+p->pext.loc.x, y1+p->pext.loc.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawRect( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw_rect( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawRectPExt( cmGrDcH_t h, const cmGrPExt_t* pext )
|
||||||
|
{ cmGrDcDrawRect( h, cmGrPExtL(pext), cmGrPExtT(pext), cmGrPExtW(pext), cmGrPExtH(pext) ); }
|
||||||
|
|
||||||
|
void cmGrDcFillRect( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->fill_rect( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawEllipse( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw_ellipse( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcFillEllipse( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->fill_ellipse( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawDiamond( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw_diamond( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcFillDiamond( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->fill_diamond( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawTriangle( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh, unsigned dirFlag )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw_triangle( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh, dirFlag );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcFillTriangle( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh, unsigned dirFlag )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->fill_triangle( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh, dirFlag );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cmGrDcMeasure( cmGrDcH_t h, const cmChar_t* text, cmGrPSz_t* sz )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
if( text == NULL )
|
||||||
|
cmGrPSzSet(sz,0,0);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned ww,hh;
|
||||||
|
p->dd->measure_text( p->ddArg, text, &ww, &hh );
|
||||||
|
sz->w = ww;
|
||||||
|
sz->h = hh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawText( cmGrDcH_t h, const cmChar_t* text, int x, int y )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw_text( p->ddArg, text, x+p->pext.loc.x, y+p->pext.loc.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawTextRot( cmGrDcH_t h, const cmChar_t* text, int x, int y, int angle )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw_text_rot( p->ddArg, text, x+p->pext.loc.x, y+p->pext.loc.y, angle );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmGrDcReadImage( cmGrDcH_t h, unsigned char* a, const cmGrPExt_t* pext )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->read_image( p->ddArg, a, pext->loc.x+p->pext.loc.x, pext->loc.y+p->pext.loc.y, pext->sz.w, pext->sz.h );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawImage( cmGrDcH_t h, const unsigned char* a, const cmGrPExt_t* pext )
|
||||||
|
{
|
||||||
|
cmGrDc_t* p = _cmGrDcHandleToPtr(h);
|
||||||
|
p->dd->draw_image( p->ddArg, a, pext->loc.x+p->pext.loc.x, pext->loc.y+p->pext.loc.y, pext->sz.w, pext->sz.h );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmGrDcSetFont( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style )
|
||||||
|
{
|
||||||
|
cmGrDcSetFontFamily(h,fontId);
|
||||||
|
cmGrDcSetFontSize( h,size);
|
||||||
|
cmGrDcSetFontStyle( h,style);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcFontSetAndMeasure(cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, cmGrPSz_t* sz )
|
||||||
|
{
|
||||||
|
cmGrDcSetFont(h,fontId,size,style);
|
||||||
|
cmGrDcMeasure(h,text,sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawTextJustify( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, const cmGrPExt_t* pext, unsigned flags )
|
||||||
|
{
|
||||||
|
int x = cmGrPExtCtrX(pext);
|
||||||
|
int y = cmGrPExtCtrY(pext);
|
||||||
|
|
||||||
|
if( cmIsFlag(flags,kNorthJsGrFl) )
|
||||||
|
y = cmGrPExtT(pext);
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kSouthJsGrFl) )
|
||||||
|
y = cmGrPExtB(pext);
|
||||||
|
|
||||||
|
if( cmIsFlag(flags,kEastJsGrFl) )
|
||||||
|
x = cmGrPExtR(pext);
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kWestJsGrFl) )
|
||||||
|
x = cmGrPExtL(pext);
|
||||||
|
|
||||||
|
cmGrDcDrawTextJustifyPt(h,fontId,size,style,text,flags,x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawTextJustifyPt( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, unsigned flags, int xx, int yy )
|
||||||
|
{
|
||||||
|
cmGrPSz_t sz;
|
||||||
|
cmGrDcFontSetAndMeasure(h, fontId, size, style, text, &sz );
|
||||||
|
|
||||||
|
int x,y;
|
||||||
|
if( cmIsFlag(flags,kRightJsGrFl) )
|
||||||
|
x = xx;
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kLeftJsGrFl) )
|
||||||
|
x = xx - sz.w;
|
||||||
|
else
|
||||||
|
x = xx - sz.w/2;
|
||||||
|
|
||||||
|
if( cmIsFlag(flags,kBottomJsGrFl) )
|
||||||
|
y = yy;
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kTopJsGrFl) )
|
||||||
|
y = yy + sz.h;
|
||||||
|
else
|
||||||
|
y = yy + sz.h/2;
|
||||||
|
|
||||||
|
cmGrDcDrawText(h, text, x+.5, y+.5 );
|
||||||
|
|
||||||
|
//cmGrPExt_t pext;
|
||||||
|
//cmGrDcDrawTextJustifyRect(h, fontId, size, style, text, flags, xx, yy, &pext );
|
||||||
|
//cmGrDcDrawRectPExt(h,&pext);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGrDcDrawTextJustifyRect( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, unsigned flags, int xx, int yy, cmGrPExt_t* pext )
|
||||||
|
{
|
||||||
|
cmGrPSz_t sz;
|
||||||
|
cmGrDcFontSetAndMeasure(h, fontId, size, style, text, &sz );
|
||||||
|
|
||||||
|
int x,y;
|
||||||
|
if( cmIsFlag(flags,kRightJsGrFl) )
|
||||||
|
x = xx;
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kLeftJsGrFl) )
|
||||||
|
x = xx - sz.w;
|
||||||
|
else
|
||||||
|
x = xx - sz.w/2;
|
||||||
|
|
||||||
|
if( cmIsFlag(flags,kBottomJsGrFl) )
|
||||||
|
y = yy;
|
||||||
|
else
|
||||||
|
if( cmIsFlag(flags,kTopJsGrFl) )
|
||||||
|
y = yy + sz.h;
|
||||||
|
else
|
||||||
|
y = yy + sz.h/2;
|
||||||
|
|
||||||
|
cmGrPExtSet( pext, x, y-sz.h, sz.w+1, sz.h );
|
||||||
|
}
|
196
cmGrDevCtx.h
Normal file
196
cmGrDevCtx.h
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
#ifndef cmGrDevCtx_h
|
||||||
|
#define cmGrDevCtx_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkGrDcRC = cmOkRC,
|
||||||
|
kStackFaultGrDcRC,
|
||||||
|
kDevDrvFailGrDcRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmGrDcRC_t;
|
||||||
|
|
||||||
|
extern cmGrDcH_t cmGrDcNullHandle;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kSolidLsGrFl = 0x01,
|
||||||
|
kDashLsGrFl = 0x02,
|
||||||
|
kDotLsGrFl = 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kHelveticaFfGrId,
|
||||||
|
kTimesFfGrId,
|
||||||
|
kCourierFfGrId,
|
||||||
|
|
||||||
|
kFontFfCnt
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kNormalFsGrFl = 0x00,
|
||||||
|
kBoldFsGrFl = 0x01,
|
||||||
|
kItalicFsGrFl = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct cmGrDev_str
|
||||||
|
{
|
||||||
|
// return true on success
|
||||||
|
bool (*create)( void* arg, unsigned w, unsigned h );
|
||||||
|
void (*destroy)( void* arg );
|
||||||
|
|
||||||
|
void (*begin_draw)( void* arg );
|
||||||
|
void (*end_draw)( void* arg );
|
||||||
|
void (*draw)( void* arg, int x, int y );
|
||||||
|
|
||||||
|
|
||||||
|
void (*set_color)( void* arg, const cmGrColor_t c );
|
||||||
|
void (*get_color)( void* arg, cmGrColor_t* c );
|
||||||
|
|
||||||
|
// Return false if the 'font' label is not recognized.
|
||||||
|
void (*set_font_family)(void* arg, unsigned fontId );
|
||||||
|
unsigned (*get_font_family)(void* arg );
|
||||||
|
|
||||||
|
void (*set_font_style)( void* arg, unsigned styleFLags );
|
||||||
|
unsigned (*get_font_style)( void* arg );
|
||||||
|
|
||||||
|
void (*set_font_size)( void* arg, unsigned size );
|
||||||
|
unsigned (*get_font_size)( void* arg );
|
||||||
|
|
||||||
|
void (*set_pen_style)( void* arg, unsigned styleFlags );
|
||||||
|
unsigned (*get_pen_style)( void* arg );
|
||||||
|
|
||||||
|
void (*set_pen_width)( void* arg, unsigned w );
|
||||||
|
unsigned (*get_pen_width)( void* arg );
|
||||||
|
|
||||||
|
void (*draw_line)( void* arg, int x, int y, int x1, int y1 );
|
||||||
|
void (*draw_rect)( void* arg, int x, int y, unsigned w, unsigned h );
|
||||||
|
void (*fill_rect)( void* arg, int x, int y, unsigned w, unsigned h );
|
||||||
|
|
||||||
|
// Draw an ellipse, diamond or triangle inside the rectangle formed by l,t,w,h.
|
||||||
|
void (*draw_ellipse)( void* arg, int x, int y, unsigned w, unsigned h );
|
||||||
|
void (*fill_ellipse)( void* arg, int x, int y, unsigned w, unsigned h );
|
||||||
|
void (*draw_diamond)( void* arg, int x, int y, unsigned w, unsigned h );
|
||||||
|
void (*fill_diamond)( void* arg, int x, int y, unsigned w, unsigned h );
|
||||||
|
void (*draw_triangle)( void* arg, int x, int y, unsigned w, unsigned h, unsigned dirFlag );
|
||||||
|
void (*fill_triangle)( void* arg, int x, int y, unsigned w, unsigned h, unsigned dirFlag );
|
||||||
|
|
||||||
|
// x,y identifies the left,lower text edge
|
||||||
|
void (*draw_text)( void* arg, const char* text, int x, int y );
|
||||||
|
void (*draw_text_rot)(void* arg, const char* text, int x, int y, int angle );
|
||||||
|
void (*measure_text)( void* arg, const char* text, unsigned* w, unsigned* h );
|
||||||
|
|
||||||
|
// Fill p[w*h*3] with RGB data.
|
||||||
|
void (*read_image)( void* arg, unsigned char* p, int x, int y, unsigned w, unsigned h );
|
||||||
|
void (*draw_image)( void* arg, const unsigned char* p, int x, int y, unsigned w, unsigned h );
|
||||||
|
|
||||||
|
} cmGrDev_t;
|
||||||
|
|
||||||
|
cmGrDcRC_t cmGrDevCtxCreate( cmCtx_t* ctx, cmGrDcH_t* hp, cmGrDev_t* dd, void* ddArg, int x, int y, int w, int h );
|
||||||
|
cmGrDcRC_t cmGrDevCtxDestroy( cmGrDcH_t* hp );
|
||||||
|
bool cmGrDevCtxIsValid( cmGrDcH_t h );
|
||||||
|
|
||||||
|
cmGrDcRC_t cmGrDevCtxResize( cmGrDcH_t h, int x, int y, int ww, int hh );
|
||||||
|
void cmGrDevCtxSize( cmGrDcH_t h, cmGrPExt_t* pext );
|
||||||
|
|
||||||
|
void cmGrDevCtxBeginDraw( cmGrDcH_t h );
|
||||||
|
void cmGrDevCtxEndDraw( cmGrDcH_t h );
|
||||||
|
void cmGrDevCtxDraw( cmGrDcH_t h );
|
||||||
|
|
||||||
|
|
||||||
|
void cmGrDcPushCtx( cmGrDcH_t h );
|
||||||
|
void cmGrDcPopCtx( cmGrDcH_t h );
|
||||||
|
|
||||||
|
unsigned cmGrDcColor( cmGrDcH_t h );
|
||||||
|
void cmGrDcSetColorRgb( cmGrDcH_t h, unsigned char r, unsigned char g, unsigned char b );
|
||||||
|
void cmGrDcSetColor( cmGrDcH_t h, cmGrColor_t color );
|
||||||
|
|
||||||
|
unsigned cmGrDcFontFamily( cmGrDcH_t h );
|
||||||
|
void cmGrDcSetFontFamily( cmGrDcH_t h, unsigned fontId );
|
||||||
|
|
||||||
|
unsigned cmGrDcFontStyle( cmGrDcH_t h );
|
||||||
|
void cmGrDcSetFontStyle( cmGrDcH_t h, unsigned style );
|
||||||
|
|
||||||
|
unsigned cmGrDcFontSize( cmGrDcH_t h );
|
||||||
|
void cmGrDcSetFontSize( cmGrDcH_t h, unsigned size );
|
||||||
|
|
||||||
|
unsigned cmGrDcPenWidth( cmGrDcH_t h );
|
||||||
|
void cmGrDcSetPenWidth( cmGrDcH_t h, unsigned width );
|
||||||
|
|
||||||
|
unsigned cmGrDcPenStyle( cmGrDcH_t h );
|
||||||
|
void cmGrDcSetPenStyle( cmGrDcH_t h, unsigned style );
|
||||||
|
|
||||||
|
void cmGrDcDrawLine( cmGrDcH_t h, int x, int y, int x1, int y1 );
|
||||||
|
// x,y is the upper,left.
|
||||||
|
void cmGrDcDrawRect( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh );
|
||||||
|
void cmGrDcDrawRectPExt( cmGrDcH_t h, const cmGrPExt_t* pext );
|
||||||
|
|
||||||
|
void cmGrDcFillRect( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh );
|
||||||
|
void cmGrDcDrawEllipse( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh );
|
||||||
|
void cmGrDcFillEllipse( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh );
|
||||||
|
|
||||||
|
void cmGrDcDrawDiamond( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh );
|
||||||
|
void cmGrDcFillDiamond( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh );
|
||||||
|
|
||||||
|
// Set 'dirFlag' to kTopGrFl,kBottomGrFl,kRightGrFl,kLeftGrFl to indicate
|
||||||
|
// the direction the triangle is pointeed.
|
||||||
|
void cmGrDcDrawTriangle( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh, unsigned dirFlag );
|
||||||
|
void cmGrDcFillTriangle( cmGrDcH_t h, int x, int y, unsigned ww, unsigned hh, unsigned dirFlag );
|
||||||
|
|
||||||
|
void cmGrDcMeasure( cmGrDcH_t h, const cmChar_t* text, cmGrPSz_t* sz );
|
||||||
|
void cmGrDcDrawText( cmGrDcH_t h, const cmChar_t* text, int x, int y );
|
||||||
|
void cmGrDcDrawTextRot( cmGrDcH_t h, const cmChar_t* text, int x, int y, int angle );
|
||||||
|
|
||||||
|
void cmGrDcReadImage( cmGrDcH_t h, unsigned char* p, const cmGrPExt_t* pext );
|
||||||
|
void cmGrDcDrawImage( cmGrDcH_t h, const unsigned char* p, const cmGrPExt_t* pext );
|
||||||
|
|
||||||
|
//
|
||||||
|
// Composite Functions
|
||||||
|
//
|
||||||
|
|
||||||
|
void cmGrDcSetFont( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style );
|
||||||
|
void cmGrDcFontSetAndMeasure(cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, cmGrPSz_t* sz );
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kLeftJsGrFl = 0x001,
|
||||||
|
kRightJsGrFl = 0x002,
|
||||||
|
kTopJsGrFl = 0x004,
|
||||||
|
kBottomJsGrFl = 0x008,
|
||||||
|
kHorzCtrJsGrFl = 0x010,
|
||||||
|
kVertCtrJsGrFl = 0x020,
|
||||||
|
|
||||||
|
kNorthJsGrFl = 0x040,
|
||||||
|
kEastJsGrFl = 0x080,
|
||||||
|
kSouthJsGrFl = 0x100,
|
||||||
|
kWestJsGrFl = 0x200
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use compass (NSEW) flags to select the draw point. Defaults to center for both dir's.
|
||||||
|
// Use TBLF flags to select the text justification relative to the point.
|
||||||
|
// In effect the TBLF flags select the corner of the text to place at the location of
|
||||||
|
// the point selected by the NSEW flags.
|
||||||
|
// If neither NS flag is set then the vertical point is set to the vertical center.
|
||||||
|
// If neither EW flags is set then the horizontal point is set to the horzontal center.
|
||||||
|
void cmGrDcDrawTextJustify( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, const cmGrPExt_t* pext, unsigned flags );
|
||||||
|
|
||||||
|
// Use LBLF to set the justification - the text corner to match to the given point.
|
||||||
|
// If neither TL flag is given then the point is matched to the vertical center of the text.
|
||||||
|
// If neither RL flag is given then the point is matched to the horizontal center of the text.
|
||||||
|
void cmGrDcDrawTextJustifyPt( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, unsigned flags, int x, int y );
|
||||||
|
|
||||||
|
// Return the rectangle around the text but do not display the text.
|
||||||
|
void cmGrDcDrawTextJustifyRect( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, unsigned flags, int x, int y, cmGrPExt_t* pext );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1471
cmGrPage.c
Normal file
1471
cmGrPage.c
Normal file
File diff suppressed because it is too large
Load Diff
164
cmGrPage.h
Normal file
164
cmGrPage.h
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#ifndef cmGrPage_h
|
||||||
|
#define cmGrPage_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kHashMarkGrFl = 0x10,
|
||||||
|
kHashLabelGrFl= 0x20
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmHandle_t cmGrPgH_t;
|
||||||
|
typedef cmHandle_t cmGrVwH_t;
|
||||||
|
typedef cmHandle_t cmGrAxH_t;
|
||||||
|
|
||||||
|
extern cmGrPgH_t cmGrPgNullHandle;
|
||||||
|
extern cmGrVwH_t cmGrVwNullHandle;
|
||||||
|
extern cmGrAxH_t cmGrAxNullHandle;
|
||||||
|
|
||||||
|
// Create a cmGrPage object.
|
||||||
|
cmGrRC_t cmGrPageCreate( cmCtx_t* ctx, cmGrPgH_t* hp, cmGrCbFunc_t cbFunc, void* cbArg );
|
||||||
|
|
||||||
|
// Destroy and release the resources assoc'd with a cmGrPage object;
|
||||||
|
cmGrRC_t cmGrPageDestroy( cmGrPgH_t* hp );
|
||||||
|
|
||||||
|
// Return true if the cmGrPage object handle is valid
|
||||||
|
bool cmGrPageIsValid( cmGrPgH_t h );
|
||||||
|
|
||||||
|
// Remove all objects from the page.
|
||||||
|
cmGrRC_t cmGrPageClear( cmGrPgH_t h );
|
||||||
|
|
||||||
|
// Intialize the count of rows and columns and setup the default layout.
|
||||||
|
cmGrRC_t cmGrPageInit( cmGrPgH_t h, const cmGrPExt_t* r, unsigned rn, unsigned cn, cmGrDcH_t dcH );
|
||||||
|
|
||||||
|
// Update the position of the views on the page.
|
||||||
|
cmGrRC_t cmGrPageResize( cmGrPgH_t h, const cmGrPExt_t* r, cmGrDcH_t dcH );
|
||||||
|
|
||||||
|
// Return the current page size and loc'n as set by cmGrPageInit() or cmGrPageResize().
|
||||||
|
void cmGrPageRect( cmGrPgH_t h, cmGrPExt_t* r );
|
||||||
|
|
||||||
|
// Return the count of plot views contained by this page. (rn*cn)
|
||||||
|
unsigned cmGrPageViewCount( cmGrPgH_t h );
|
||||||
|
|
||||||
|
// Enable or disable the focus for a given view.
|
||||||
|
// Note that the focused view is the view which is the target of controller
|
||||||
|
// buttons and scrollbars. This does not refer to the focused object.
|
||||||
|
// Set 'enableFl' if the view is receiving the focus.
|
||||||
|
// Clear 'enableFl' if the view is losing focus.
|
||||||
|
void cmGrPageViewFocus( cmGrPgH_t h, unsigned vwIdx, bool enableFl );
|
||||||
|
|
||||||
|
// Return the view which currently has the focus or cmGrVwNullHandle if
|
||||||
|
// no view has the focus.
|
||||||
|
cmGrVwH_t cmGrPageFocusedView( cmGrPgH_t h );
|
||||||
|
|
||||||
|
//
|
||||||
|
void cmGrPageLayout( cmGrPgH_t h, cmGrDcH_t dcH );
|
||||||
|
|
||||||
|
// Draw the page.
|
||||||
|
void cmGrPageDraw( cmGrPgH_t h, cmGrDcH_t dcH );
|
||||||
|
|
||||||
|
typedef void (*cmGrLabelFunc_t)( void* arg, cmChar_t* label, unsigned labelCharCnt, cmGrV_t value );
|
||||||
|
// Returns id of the new page label function.
|
||||||
|
unsigned cmGrPageLabelFuncRegister( cmGrPgH_t h, cmGrLabelFunc_t func, void* arg, const cmChar_t* label );
|
||||||
|
unsigned cmGrPageLabelFuncCount( cmGrPgH_t h );
|
||||||
|
unsigned cmGrPageLabelFuncIndexToId( cmGrPgH_t h, unsigned index );
|
||||||
|
unsigned cmGrPageLabelFuncLabelToId( cmGrPgH_t h, const cmChar_t* label );
|
||||||
|
cmGrLabelFunc_t cmGrPageLabelFunc( cmGrPgH_t h, unsigned id );
|
||||||
|
const cmChar_t* cmGrPageLabelFuncLabel( cmGrPgH_t h, unsigned id );
|
||||||
|
void* cmGrPageLabelFuncArg( cmGrPgH_t h, unsigned id );
|
||||||
|
|
||||||
|
|
||||||
|
// Get a view handle from the view index.
|
||||||
|
cmGrVwH_t cmGrPageViewHandle( cmGrPgH_t h, unsigned vwIdx );
|
||||||
|
|
||||||
|
// Get a view handle from to cmGrH_t.
|
||||||
|
cmGrVwH_t cmGrPageGrHandleToView( cmGrPgH_t h, cmGrH_t grH );
|
||||||
|
|
||||||
|
bool cmGrViewIsValid( cmGrVwH_t h );
|
||||||
|
|
||||||
|
// Initialize a plot view. title,xLabel, and yLabel are optional.
|
||||||
|
cmGrRC_t cmGrViewInit( cmGrVwH_t h, cmGrH_t grH, const cmChar_t* vwTitle, const cmChar_t* xLabel, const cmChar_t* yLabel );
|
||||||
|
|
||||||
|
// Remove all objects from the view.
|
||||||
|
cmGrRC_t cmGrViewClear( cmGrVwH_t h );
|
||||||
|
|
||||||
|
// Get the plot views physical extents. This function will return the
|
||||||
|
// current view location/size only after a call to cmGrPageDraw().
|
||||||
|
// See the implementation note at the top of this file.
|
||||||
|
cmGrRC_t cmGrViewPExt( cmGrVwH_t h, cmGrPExt_t* pext );
|
||||||
|
|
||||||
|
bool cmGrViewHasFocus( cmGrVwH_t h );
|
||||||
|
|
||||||
|
// Get the cmGrH_t associated with a view.
|
||||||
|
cmGrH_t cmGrViewGrHandle( cmGrVwH_t h );
|
||||||
|
|
||||||
|
// kExpandViewGrFl | kSelectHorzGrFl | kSelectVertGrFl
|
||||||
|
void cmGrViewSetCfg( cmGrVwH_t h, unsigned cfgFlags );
|
||||||
|
unsigned cmGrViewCfg( cmGrVwH_t h );
|
||||||
|
void cmGrViewSetTitle( cmGrVwH_t h, const cmChar_t* title );
|
||||||
|
const cmChar_t* cmGrViewTitle( cmGrVwH_t h );
|
||||||
|
void cmGrViewSetFontFamily( cmGrVwH_t h, unsigned id );
|
||||||
|
unsigned cmGrViewFontFamily( cmGrVwH_t h );
|
||||||
|
void cmGrViewSetFontStyle( cmGrVwH_t h, unsigned flags );
|
||||||
|
unsigned cmGrViewFontStyle( cmGrVwH_t h );
|
||||||
|
void cmGrViewSetFontSize( cmGrVwH_t h, unsigned size );
|
||||||
|
unsigned cmGrViewFontSize( cmGrVwH_t h );
|
||||||
|
|
||||||
|
// Assign a translation function to be used with cmGrViewValue().
|
||||||
|
// cmLeftGrIdx or cmGrRightGrIdx is used to assign y axis translation functions.
|
||||||
|
// cmTopGrIdx or cmGrBottomGrIdx is used to assign x axis translation functions.
|
||||||
|
// 'pgLabelFuncId' must be a valid page label function id as returned from cmGrPageLabelFuncRegister().
|
||||||
|
// or cmGrPageLabelFuncIndexToId().
|
||||||
|
void cmGrViewSetLabelFunc( cmGrVwH_t h, cmGrAxisIdx_t axisId, unsigned pgLabelFuncId );
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kLocalX_VwId,
|
||||||
|
kLocalY_VwId,
|
||||||
|
kGlobalX_VwId,
|
||||||
|
kGlobalY_VwId,
|
||||||
|
kSelX0_VwId,
|
||||||
|
kSelY0_VwId,
|
||||||
|
kSelX1_VwId,
|
||||||
|
kSelY1_VwId,
|
||||||
|
kSelW_VwId,
|
||||||
|
kSelH_VwId
|
||||||
|
} cmGrViewValueId_t;
|
||||||
|
const cmChar_t* cmGrViewValue( cmGrVwH_t h, cmGrViewValueId_t id, cmChar_t* buf, unsigned bufCharCnt );
|
||||||
|
|
||||||
|
// Get an axis handle.
|
||||||
|
cmGrAxH_t cmGrViewAxisHandle( cmGrVwH_t h, cmGrAxisIdx_t axisIdx );
|
||||||
|
|
||||||
|
bool cmGrAxisIsValid( cmGrAxH_t h );
|
||||||
|
// kHashMarkGrFl | kHashLabelGrFl
|
||||||
|
void cmGrAxisSetCfg( cmGrAxH_t h, unsigned cfgFlags );
|
||||||
|
unsigned cmGrAxisCfg( cmGrAxH_t h );
|
||||||
|
void cmGrAxisSetTitle( cmGrAxH_t h, const cmChar_t* title );
|
||||||
|
const cmChar_t* cmGrAxisTitle( cmGrAxH_t h );
|
||||||
|
void cmGrAxisTitleSetFontFamily( cmGrAxH_t h, unsigned id );
|
||||||
|
unsigned cmGrAxisTitleFontFamily( cmGrAxH_t h );
|
||||||
|
void cmGrAxisTitleSetFontStyle( cmGrAxH_t h, unsigned flags );
|
||||||
|
unsigned cmGrAxisTitleFontStyle( cmGrAxH_t h );
|
||||||
|
void cmGrAxisTitleSetFontSize( cmGrAxH_t h, unsigned size );
|
||||||
|
unsigned cmGrAxisTitleFontSize( cmGrAxH_t h );
|
||||||
|
|
||||||
|
void cmGrAxisLabelSetFontFamily( cmGrAxH_t h, unsigned id );
|
||||||
|
unsigned cmGrAxisLabelFontFamily( cmGrAxH_t h );
|
||||||
|
void cmGrAxisLabelSetFontStyle( cmGrAxH_t h, unsigned flags );
|
||||||
|
unsigned cmGrAxisLabelFontStyle( cmGrAxH_t h );
|
||||||
|
void cmGrAxisLabelSetFontSize( cmGrAxH_t h, unsigned size );
|
||||||
|
unsigned cmGrAxisLabelFontSize( cmGrAxH_t h );
|
||||||
|
|
||||||
|
// Assign a translation function for the value on this axis.
|
||||||
|
// 'pgLabelFuncId' must be a valid page label function id as returned from cmGrPageLabelFuncRegister().
|
||||||
|
// or cmGrPageLabelFuncIndexToId().
|
||||||
|
void cmGrAxisSetLabelFunc( cmGrAxH_t h, unsigned pgLabelFuncId );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1223
cmGrPlot.c
Normal file
1223
cmGrPlot.c
Normal file
File diff suppressed because it is too large
Load Diff
260
cmGrPlot.h
Normal file
260
cmGrPlot.h
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
#ifndef cmGrTimeLine_h
|
||||||
|
#define cmGrTimeLine_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkGrPlRC,
|
||||||
|
kObjNotFoundGrPlRC,
|
||||||
|
kGrFailGrPlRC,
|
||||||
|
kRsrcFailGrPlRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kInvalidPlId,
|
||||||
|
kRectGrPlId,
|
||||||
|
kHLineGrPlId,
|
||||||
|
kVLineGrPlId,
|
||||||
|
kLineGrPlId,
|
||||||
|
kEllipseGrPlId,
|
||||||
|
kDiamondGrPlId,
|
||||||
|
kUTriGrPlId,
|
||||||
|
kDTriGrPlId,
|
||||||
|
kLTriGrPlId,
|
||||||
|
kRTriGrPlId,
|
||||||
|
kStarGrPlId,
|
||||||
|
kCrossGrPlId,
|
||||||
|
kPlusGrPlId,
|
||||||
|
} cmGrPlObjTypeId_t;
|
||||||
|
|
||||||
|
// object cfg flags
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kSymbolGrPlFl = 0x0001, // This object is a symbol
|
||||||
|
kNoSelectGrPlFl = 0x0002, // The clicking with the mouse will not select this object
|
||||||
|
kNoDragGrPlFl = 0x0004, // Dragging with the mouse will not move this object
|
||||||
|
kNoFocusGrPlFl = 0x0008, // This object cannot receive focus.
|
||||||
|
kNoDrawGrPlFl = 0x0010, // Do not draw this object (is is invisible and disabled)
|
||||||
|
kNoFillGrPlFl = 0x0020, // Do not draw the fill area of this object
|
||||||
|
kNoBorderGrPlFl = 0x0040, // Do not draw the border of this object
|
||||||
|
kNoLabelGrPlFl = 0x0080, // Do not draw the label for this object
|
||||||
|
};
|
||||||
|
|
||||||
|
// object state flags
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kVisibleGrPlFl = 0x0001,
|
||||||
|
kEnabledGrPlFl = 0x0002, // Enabled obj's must be visible.
|
||||||
|
kSelectGrPlFl = 0x0004, //
|
||||||
|
kFocusGrPlFl = 0x0008 // Focused obj's are also selected.
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kFocusPlGrId,
|
||||||
|
kSelectPlGrId,
|
||||||
|
kEnablePlGrId,
|
||||||
|
kDisablePlGrId,
|
||||||
|
|
||||||
|
kMaxPlGrId
|
||||||
|
} cmGrPlStateId_t;
|
||||||
|
|
||||||
|
enum // TODO: change these into cmGrPlotH_t user settable variables
|
||||||
|
{
|
||||||
|
kDefaultSymW = 9,
|
||||||
|
kDefaultSymH = 9
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmHandle_t cmGrPlH_t;
|
||||||
|
typedef cmHandle_t cmGrPlObjH_t;
|
||||||
|
typedef cmRC_t cmGrPlRC_t;
|
||||||
|
|
||||||
|
// Plot object callback id's
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
kCreatedCbSelGrPlId,
|
||||||
|
kDestroyedCbSelGrPlId,
|
||||||
|
kPreEventCbSelGrPlId,
|
||||||
|
kEventCbSelGrPlId,
|
||||||
|
kStateChangeGrPlId, // See Note with cmGrPlotCbFunc_t below.
|
||||||
|
} cmGrPlCbSelId_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmCtx_t* ctx; // Global program context.
|
||||||
|
void* cbArg; // User pointer set in cmGrPlotObjSetCb().
|
||||||
|
cmGrPlCbSelId_t selId; // Callback selector id. See cmGrPlCbSeId_t.
|
||||||
|
cmGrPlObjH_t objH; // The plot object handle.
|
||||||
|
unsigned eventFlags; // Event flags from canvas callback (See cmGrEvent() flags)
|
||||||
|
cmGrKeyCodeId_t eventKey; // Event keys (See the cmGrEvent() keys)
|
||||||
|
int eventX; // Mouse X,Y location when event was generated (Same as cmGrEvent())
|
||||||
|
int eventY; //
|
||||||
|
unsigned deltaFlags; // Event caused an object state change (See kXXXGrPlFl flags)
|
||||||
|
} cmGrPlotCbArg_t;
|
||||||
|
|
||||||
|
|
||||||
|
// Return 'false' from kPreEventCbGrPlSelId events to prevent default processing.
|
||||||
|
// Note:
|
||||||
|
// When this callback is made with the 'kStateChangeGrPlId' the state of
|
||||||
|
// the object has not yet been changed. This may be confusing because if
|
||||||
|
// the state of the object is queried inside the callback it will have the
|
||||||
|
// pre-change state - but this state will be automatically toggled when the
|
||||||
|
// callback returns 'true'.
|
||||||
|
typedef bool (*cmGrPlotCbFunc_t)( cmGrPlotCbArg_t* arg );
|
||||||
|
|
||||||
|
extern cmGrPlH_t cmGrPlNullHandle;
|
||||||
|
extern cmGrPlObjH_t cmGrPlObjNullHandle;
|
||||||
|
|
||||||
|
// Notes:
|
||||||
|
// 1) Set kSymbolGrPlFl to create a symbol.
|
||||||
|
// 2) If kSymbolGrPlFl is set then w and h are taken as the physical size
|
||||||
|
// of the symbol. Set w and h to 0 to use the default symbols size
|
||||||
|
// kDefaultSymW, kDefaultSymH
|
||||||
|
|
||||||
|
cmGrPlRC_t cmGrPlotObjCreate(
|
||||||
|
cmGrPlH_t plH, // Owner Plot Object Manager. See cmGrPlotCreate().
|
||||||
|
cmGrH_t grH, // The canvas this object will be drawn on.
|
||||||
|
cmGrPlObjH_t* ohp, // Pointer to the new objects handle (optional)
|
||||||
|
unsigned id, // User defined identifier.
|
||||||
|
cmGrPlObjH_t parentObjH, // Containing parent object.
|
||||||
|
cmGrPlObjH_t xAnchorObjH, // x is taken as an offset from this obj's x coord (optional).
|
||||||
|
cmGrPlObjH_t yAnchorObjH, // y is taken as an offset from this obj's y coord (optional).
|
||||||
|
cmGrPlObjTypeId_t typeId, // See cmGrPlObjTypeId_t
|
||||||
|
unsigned cfgFlags, //
|
||||||
|
cmReal_t x, // Coord's within the parent's world coord system.
|
||||||
|
cmReal_t y, //
|
||||||
|
cmReal_t w, //
|
||||||
|
cmReal_t h, //
|
||||||
|
const cmChar_t* label, // Object text string (optional)
|
||||||
|
const cmGrVExt_t* wext ); // This objects internal world extents (optional)
|
||||||
|
|
||||||
|
cmGrPlRC_t cmGrPlotObjDestroy( cmGrPlObjH_t* ohp );
|
||||||
|
bool cmGrPlotObjIsValid( cmGrPlObjH_t oh );
|
||||||
|
|
||||||
|
cmGrPlH_t cmGrPlotObjMgrHandle( cmGrPlObjH_t oh );
|
||||||
|
cmGrObjH_t cmGrPlotObjHandle( cmGrPlObjH_t oh );
|
||||||
|
|
||||||
|
void cmGrPlotObjSetId( cmGrPlObjH_t oh, unsigned id );
|
||||||
|
unsigned cmGrPlotObjId( cmGrPlObjH_t oh );
|
||||||
|
|
||||||
|
void cmGrPlotObjSetUserPtr( cmGrPlObjH_t oh, void* userPtr );
|
||||||
|
void* cmGrPlotObjUserPtr( cmGrPlObjH_t oh );
|
||||||
|
|
||||||
|
void cmGrPlotObjSetLabel( cmGrPlObjH_t oh, const cmChar_t* label );
|
||||||
|
const cmChar_t* cmGrPlotObjLabel( cmGrPlObjH_t oh );
|
||||||
|
// Set flags to kXXXJsGrFl values. See cmGrDrawTextJustify for their meaning.
|
||||||
|
// 'color' is optional
|
||||||
|
void cmGrPlotObjSetLabelAttr( cmGrPlObjH_t oh, unsigned flags, int angle, const cmGrColor_t color );
|
||||||
|
unsigned cmGrPlotObjLabelFlags( cmGrPlObjH_t oh );
|
||||||
|
int cmGrPlotObjLabelAngle( cmGrPlObjH_t oh );
|
||||||
|
const cmGrColor_t cmGrPlotObjLabelColor( cmGrPlObjH_t oh );
|
||||||
|
|
||||||
|
|
||||||
|
void cmGrPlotObjSetStateFlags( cmGrPlObjH_t oh, unsigned flags );
|
||||||
|
unsigned cmGrPlotObjStateFlags( cmGrPlObjH_t oh );
|
||||||
|
|
||||||
|
void cmGrPlotObjSetCfgFlags( cmGrPlObjH_t oh, unsigned flags );
|
||||||
|
void cmGrPlotObjClrCfgFlags( cmGrPlObjH_t oh, unsigned flags );
|
||||||
|
void cmGrPlotObjTogCfgFlags( cmGrPlObjH_t oh, unsigned flags );
|
||||||
|
unsigned cmGrPlotObjCfgFlags( cmGrPlObjH_t oh );
|
||||||
|
|
||||||
|
cmGrPlRC_t cmGrPlotObjSetPhysExt( cmGrPlObjH_t oh, int loffs, int toffs, int roffs, int boffs );
|
||||||
|
void cmGrPlotObjPhysExt( cmGrPlObjH_t oh, int* loffs, int* toffs, int* roffs, int* boffs );
|
||||||
|
|
||||||
|
void cmGrPlotObjVExt( cmGrPlObjH_t oh, cmGrVExt_t* vext );
|
||||||
|
|
||||||
|
void cmGrPlotObjSetFontFamily( cmGrPlObjH_t h, unsigned id );
|
||||||
|
unsigned cmGrPlotObjFontFamily( cmGrPlObjH_t h );
|
||||||
|
void cmGrPlotObjSetFontSize( cmGrPlObjH_t h, unsigned size );
|
||||||
|
unsigned cmGrPlotObjFontSize( cmGrPlObjH_t h );
|
||||||
|
void cmGrPlotObjSetFontStyle( cmGrPlObjH_t h, unsigned flags );
|
||||||
|
unsigned cmGrPlotObjFontStyle( cmGrPlObjH_t h );
|
||||||
|
void cmGrPlotObjSetFont( cmGrPlObjH_t h, unsigned id, unsigned size, unsigned style );
|
||||||
|
|
||||||
|
void cmGrPlotObjSetLineColor( cmGrPlObjH_t h, cmGrPlStateId_t id, const cmGrColor_t c );
|
||||||
|
const cmGrColor_t cmGrPlotObjLineColor( cmGrPlObjH_t h, cmGrPlStateId_t id );
|
||||||
|
const cmGrColor_t cmGrPlotObjCurLineColor( cmGrPlObjH_t h );
|
||||||
|
|
||||||
|
void cmGrPlotObjSetFillColor( cmGrPlObjH_t h, cmGrPlStateId_t id, const cmGrColor_t c );
|
||||||
|
const cmGrColor_t cmGrPlotObjFillColor( cmGrPlObjH_t h, cmGrPlStateId_t id );
|
||||||
|
const cmGrColor_t cmGrPlotObjCurFillColor( cmGrPlObjH_t h );
|
||||||
|
|
||||||
|
void cmGrPlotObjSetCb( cmGrPlObjH_t h, cmGrPlotCbFunc_t func, void* arg );
|
||||||
|
cmGrPlotCbFunc_t cmGrPlotObjCbFunc( cmGrPlObjH_t h );
|
||||||
|
void* cmGrPlotObjCbArg( cmGrPlObjH_t h );
|
||||||
|
|
||||||
|
|
||||||
|
// Draw aH above bH in the z-order.
|
||||||
|
void cmGrPlotObjDrawAbove( cmGrPlObjH_t bH, cmGrPlObjH_t aH );
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Plot Object Manager Functions
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
cmGrPlRC_t cmGrPlotCreate( cmCtx_t* ctx, cmGrPlH_t* hp );
|
||||||
|
cmGrPlRC_t cmGrPlotDestroy( cmGrPlH_t* hp );
|
||||||
|
bool cmGrPlotIsValid( cmGrPlH_t h );
|
||||||
|
cmGrPlRC_t cmGrPlotClear( cmGrPlH_t h ); // destroy all objects
|
||||||
|
cmErr_t* cmGrPlotErr( cmGrPlH_t h );
|
||||||
|
cmRpt_t* cmGrPlotRpt( cmGrPlH_t h );
|
||||||
|
unsigned cmGrPlotObjectCount( cmGrPlH_t h );
|
||||||
|
cmGrPlObjH_t cmGrPlotObjectIndexToHandle( cmGrPlH_t h, unsigned index );
|
||||||
|
cmGrPlObjH_t cmGrPlotObjectIdToHandle( cmGrPlH_t h, unsigned id );
|
||||||
|
|
||||||
|
// Pass a keyboard event to the plot system.
|
||||||
|
void cmGrPlotKeyEvent( cmGrPlH_t h, cmGrH_t grH, unsigned eventFlags, cmGrKeyCodeId_t keycode );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Plot Object Attributes:
|
||||||
|
|
||||||
|
Location: x,y
|
||||||
|
Size: w,h
|
||||||
|
|
||||||
|
Shape:
|
||||||
|
rectangle:
|
||||||
|
ellipse:
|
||||||
|
line:
|
||||||
|
hline:
|
||||||
|
vline:
|
||||||
|
symbol:
|
||||||
|
|
||||||
|
Parent: Defines the world coordinate system in which this object is drawn.
|
||||||
|
Children are always fully contained by their parent and may not be dragged
|
||||||
|
outside of their parent.
|
||||||
|
|
||||||
|
Label:
|
||||||
|
Border Color: One per state (enabled,selected,focused)
|
||||||
|
Fill Color: One per state (enabled,selected,focused)
|
||||||
|
State:
|
||||||
|
Visible: Draw this object.
|
||||||
|
Enabled: Disabled objects cannot be selected,focused, or dragged.
|
||||||
|
Selected: Multiple objects may be selected.
|
||||||
|
Focused: Only one object may be focused.
|
||||||
|
|
||||||
|
Physical Offsets: Physical offsets which expand the size of the object.
|
||||||
|
Font Family:
|
||||||
|
Font Style:
|
||||||
|
Font Size:
|
||||||
|
Cfg Flags:
|
||||||
|
No Drag: Do not allow dragging.
|
||||||
|
No Select: Do not allow selection.
|
||||||
|
No Focus: Do not allow focusing.
|
||||||
|
No Draw: Do not draw this object (automatically disabled the object)
|
||||||
|
No Fill: Do not draw fill color.
|
||||||
|
No Border: Do not draw border.
|
||||||
|
No Label: Do not draw label.
|
||||||
|
|
||||||
|
*/
|
180
cmGrPlotAudio.c
Normal file
180
cmGrPlotAudio.c
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmGr.h"
|
||||||
|
#include "cmGrDevCtx.h"
|
||||||
|
#include "cmGrPlot.h"
|
||||||
|
|
||||||
|
#include "cmAudioFile.h"
|
||||||
|
#include "cmAudioFileMgr.h"
|
||||||
|
#include "cmGrPlotAudio.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmGrPlObjH_t oH;
|
||||||
|
cmAfmFileH_t afH;
|
||||||
|
unsigned chIdx;
|
||||||
|
|
||||||
|
cmGrRenderObjCb_t renderCbFunc;
|
||||||
|
void* renderCbArg;
|
||||||
|
|
||||||
|
cmGrDestroyObjCb_t destroyCbFunc;
|
||||||
|
void* destroyCbArg;
|
||||||
|
|
||||||
|
cmGrIsInsideObjCb_t isInsideCbFunc;
|
||||||
|
void* isInsideCbArg;
|
||||||
|
|
||||||
|
void* mem;
|
||||||
|
cmGrPExt_t pext;
|
||||||
|
unsigned pixN;
|
||||||
|
cmSample_t* fMinV;
|
||||||
|
cmSample_t* fMaxV;
|
||||||
|
int* iMinV;
|
||||||
|
int* iMaxV;
|
||||||
|
|
||||||
|
} cmGrPlObjAf_t;
|
||||||
|
|
||||||
|
cmGrPlRC_t _cmGrPlObjAfCalcImage( cmGrPlObjAf_t* op, cmGrH_t grH )
|
||||||
|
{
|
||||||
|
cmGrPlRC_t rc = kOkGrPlRC;
|
||||||
|
cmGrObjH_t grObjH = cmGrPlotObjHandle(op->oH);
|
||||||
|
cmGrVExt_t vwExt,objExt,drExt;
|
||||||
|
|
||||||
|
// get the intersection of the view and this audio object
|
||||||
|
cmGrViewExtents( grH, &vwExt );
|
||||||
|
cmGrPlotObjVExt( op->oH, &objExt );
|
||||||
|
cmGrVExtIntersect(&drExt,&vwExt,&objExt);
|
||||||
|
|
||||||
|
// if the audio object is visible
|
||||||
|
if( cmGrVExtIsNotNullOrEmpty(&drExt) )
|
||||||
|
{
|
||||||
|
// get the extents of the visible portion of the audio object
|
||||||
|
cmGrVExt_VtoP( grH, cmGrPlotObjHandle(op->oH), &drExt, &op->pext);
|
||||||
|
|
||||||
|
// store the count of horizontal pixels
|
||||||
|
op->pixN = op->pext.sz.w;
|
||||||
|
|
||||||
|
// allocate a cache to hold the image data
|
||||||
|
unsigned byteCnt = op->pixN * 2 * sizeof(int) + op->pixN * 2 * sizeof(cmSample_t);
|
||||||
|
op->mem = cmMemResize(char,op->mem,byteCnt);
|
||||||
|
op->fMinV = (cmSample_t*)op->mem;
|
||||||
|
op->fMaxV = op->fMinV + op->pixN;
|
||||||
|
op->iMinV = (int*)(op->fMaxV + op->pixN);
|
||||||
|
op->iMaxV = op->iMinV + op->pixN;
|
||||||
|
assert( op->iMaxV + op->pixN == op->mem + byteCnt );
|
||||||
|
|
||||||
|
// locate the offset into the file of the first sample to be displayed
|
||||||
|
unsigned si = 0;
|
||||||
|
if( drExt.loc.x > objExt.loc.x )
|
||||||
|
si = drExt.loc.x - objExt.loc.x;
|
||||||
|
|
||||||
|
|
||||||
|
// get the floating point audio summary signal
|
||||||
|
if( cmAfmFileGetSummary( op->afH, op->chIdx, si, drExt.sz.w, op->fMinV, op->fMaxV, op->pixN ) != kOkAfmRC )
|
||||||
|
{
|
||||||
|
const cmChar_t* afn = cmAudioFileName( cmAfmFileHandle(op->afH));
|
||||||
|
rc = cmErrMsg( cmGrPlotErr( cmGrPlotObjMgrHandle(op->oH) ), kRsrcFailGrPlRC, "Audio file summary read failure on '%s'.",afn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
// convert the summary to pixels values
|
||||||
|
for(i=0; i<op->pixN; ++i)
|
||||||
|
{
|
||||||
|
// Note the reversal of min and max during the conversion.
|
||||||
|
op->iMaxV[i] = cmGrY_VtoP( grH, grObjH, op->fMinV[i] );
|
||||||
|
op->iMinV[i] = cmGrY_VtoP( grH, grObjH, op->fMaxV[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool _cmGrPlObjAfRender( cmGrObjFuncArgs_t* args, cmGrDcH_t dcH )
|
||||||
|
{
|
||||||
|
cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;
|
||||||
|
|
||||||
|
if( _cmGrPlObjAfCalcImage(op, args->grH ) == kOkGrPlRC )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
cmGrPExt_t pext;
|
||||||
|
cmGrPhysExtents( args->grH, &pext);
|
||||||
|
|
||||||
|
cmGrDcSetColor(dcH, cmGrPlotObjCurLineColor(op->oH));
|
||||||
|
|
||||||
|
// draw a horz line at y=0
|
||||||
|
int y0 = cmGrY_VtoP( args->grH, cmGrPlotObjHandle(op->oH), 0.0 );
|
||||||
|
cmGrDcDrawLine(dcH, cmGrPExtL(&op->pext), y0, cmGrPExtR(&op->pext) , y0 );
|
||||||
|
|
||||||
|
// draw a vertical line for each
|
||||||
|
for(i=0; i<op->pixN; ++i)
|
||||||
|
cmGrDcDrawLine(dcH, op->pext.loc.x+i, op->iMinV[i], op->pext.loc.x+i, op->iMaxV[i] );
|
||||||
|
|
||||||
|
|
||||||
|
// draw a rectangle around the entire audio clip
|
||||||
|
cmGrDcDrawRect(dcH, op->pext.loc.x, cmGrPExtT(&pext), op->pext.sz.w, cmGrPExtB(&pext) );
|
||||||
|
|
||||||
|
// draw the file label
|
||||||
|
cmGrDcDrawTextJustify( dcH, cmGrPlotObjFontFamily(op->oH), cmGrPlotObjFontSize(op->oH), cmGrPlotObjFontStyle(op->oH), cmGrPlotObjLabel(op->oH), &op->pext, kHorzCtrJsGrFl | kTopJsGrFl );
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _cmGrPlObjAfIsInside( cmGrObjFuncArgs_t* args, int px, int py, cmGrV_t vx, cmGrV_t vy )
|
||||||
|
{
|
||||||
|
cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;
|
||||||
|
|
||||||
|
if( cmGrPExtIsXyInside( &op->pext, px, py ) )
|
||||||
|
{
|
||||||
|
px -= op->pext.loc.x;
|
||||||
|
if( 0 <= px && px < op->pixN )
|
||||||
|
return op->iMinV[px] <= py && py <= op->iMaxV[px];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmGrPlObjAfDestroy( cmGrObjFuncArgs_t* args )
|
||||||
|
{
|
||||||
|
cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;
|
||||||
|
args->cbArg = op->destroyCbArg;
|
||||||
|
op->destroyCbFunc(args);
|
||||||
|
cmMemFree(op->mem);
|
||||||
|
cmMemFree(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmGrPlRC_t cmGrPlotAudioFileObjCreate(
|
||||||
|
cmGrPlObjH_t oH,
|
||||||
|
cmAfmFileH_t afH,
|
||||||
|
unsigned audioChIdx )
|
||||||
|
{
|
||||||
|
cmGrPlObjAf_t* op = cmMemAllocZ(cmGrPlObjAf_t,1);
|
||||||
|
op->oH = oH;
|
||||||
|
op->afH = afH;
|
||||||
|
op->chIdx = audioChIdx;
|
||||||
|
|
||||||
|
cmGrObjH_t grObjH = cmGrPlotObjHandle(op->oH);
|
||||||
|
|
||||||
|
op->renderCbFunc = cmGrObjRenderCbFunc(grObjH);
|
||||||
|
op->renderCbArg = cmGrObjRenderCbArg(grObjH);
|
||||||
|
cmGrObjSetRenderCb( grObjH, _cmGrPlObjAfRender, op );
|
||||||
|
|
||||||
|
op->destroyCbFunc = cmGrObjDestroyCbFunc(grObjH);
|
||||||
|
op->destroyCbArg = cmGrObjDestroyCbArg(grObjH);
|
||||||
|
cmGrObjSetDestroyCb( grObjH, _cmGrPlObjAfDestroy, op );
|
||||||
|
|
||||||
|
op->isInsideCbFunc = cmGrObjIsInsideCbFunc(grObjH);
|
||||||
|
op->isInsideCbArg = cmGrObjIsInsideCbArg(grObjH);
|
||||||
|
cmGrObjSetIsInsideCb( grObjH, _cmGrPlObjAfIsInside, op );
|
||||||
|
|
||||||
|
cmGrPlotObjSetUserPtr(oH,op);
|
||||||
|
|
||||||
|
return kOkGrPlRC;
|
||||||
|
}
|
19
cmGrPlotAudio.h
Normal file
19
cmGrPlotAudio.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef cmGrPlotAudio_h
|
||||||
|
#define cmGrPlotAudio_h
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
cmGrPlRC_t cmGrPlotAudioFileObjCreate(
|
||||||
|
cmGrPlObjH_t oH,
|
||||||
|
cmAfmFileH_t afH,
|
||||||
|
unsigned audioChIdx );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
504
cmJson.h
Normal file
504
cmJson.h
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
#ifndef cmJson_h
|
||||||
|
#define cmJson_h
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
//{
|
||||||
|
//(
|
||||||
|
//
|
||||||
|
// Limitations:
|
||||||
|
//
|
||||||
|
// 1. Accpets two digit hex sequences with
|
||||||
|
// the \\u escape command. JSON specifies 4 digits.
|
||||||
|
//
|
||||||
|
// 2. The scientific notation for real numbers is limited to
|
||||||
|
// exponent prefixes: e,E,e-,E-. The prefixes e+ and E+ are
|
||||||
|
// not recognized by cmLex.
|
||||||
|
//
|
||||||
|
// Extensions:
|
||||||
|
//
|
||||||
|
// 1. Will accept C style identifiers where JSON demands
|
||||||
|
// quoted strings.
|
||||||
|
//
|
||||||
|
// 2. Will accept C style hex notation (0xddd) for integer values.
|
||||||
|
//
|
||||||
|
//)
|
||||||
|
|
||||||
|
//(
|
||||||
|
|
||||||
|
// JSON data type flags
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidTId = 0x0000,
|
||||||
|
kObjectTId = 0x0001, // children are pairs
|
||||||
|
kPairTId = 0x0002, // children are string : value pairs
|
||||||
|
kArrayTId = 0x0004, // children may be of any type
|
||||||
|
kStringTId = 0x0008, // terminal
|
||||||
|
kNullTId = 0x0040, // terminal
|
||||||
|
kIntTId = 0x0010, // terminal
|
||||||
|
kRealTId = 0x0020, // terminal
|
||||||
|
kTrueTId = 0x0080, // terminal
|
||||||
|
kFalseTId = 0x0100, // terminal
|
||||||
|
|
||||||
|
kMaskTId = 0x01ff,
|
||||||
|
|
||||||
|
kOptArgJsFl = 0x0800, // only used by cmJsonVMemberValues()
|
||||||
|
kTempJsFl = 0x1000, // used internally
|
||||||
|
|
||||||
|
kNumericTId = kIntTId | kRealTId | kTrueTId | kFalseTId,
|
||||||
|
kBoolTId = kTrueTId | kFalseTId
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkJsRC,
|
||||||
|
kMemAllocErrJsRC,
|
||||||
|
kLexErrJsRC,
|
||||||
|
kSyntaxErrJsRC,
|
||||||
|
kFileOpenErrJsRC,
|
||||||
|
kFileCreateErrJsRC,
|
||||||
|
kFileReadErrJsRC,
|
||||||
|
kFileSeekErrJsRC,
|
||||||
|
kFileCloseErrJsRC,
|
||||||
|
kInvalidHexEscapeJsRC,
|
||||||
|
kSerialErrJsRC,
|
||||||
|
kNodeNotFoundJsRC,
|
||||||
|
kNodeCannotCvtJsRC,
|
||||||
|
kCannotRemoveLabelJsRC,
|
||||||
|
kInvalidNodeTypeJsRC,
|
||||||
|
kValidateFailJsRC,
|
||||||
|
kCsvErrJsRC,
|
||||||
|
kBufTooSmallJsRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef unsigned cmJsRC_t;
|
||||||
|
|
||||||
|
typedef cmHandle_t cmJsonH_t;
|
||||||
|
|
||||||
|
// JSON tree node
|
||||||
|
typedef struct cmJsonNode_str
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned typeId; // id of this node
|
||||||
|
struct cmJsonNode_str* siblingPtr; // next ele in array or member list
|
||||||
|
struct cmJsonNode_str* ownerPtr; // parent node ptr
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
// childPtr usage:
|
||||||
|
// object: first pair
|
||||||
|
// array: first element
|
||||||
|
// pair: string
|
||||||
|
struct cmJsonNode_str* childPtr;
|
||||||
|
int intVal; // valid if typeId == kIntTId
|
||||||
|
double realVal; // valid if typeId == kRealTId
|
||||||
|
char* stringVal; // valid if typeId == kStringTId
|
||||||
|
bool boolVal; // valid if typeId == kTrueTId || kFalseTId
|
||||||
|
} u;
|
||||||
|
|
||||||
|
} cmJsonNode_t;
|
||||||
|
|
||||||
|
extern cmJsonH_t cmJsonNullHandle;
|
||||||
|
|
||||||
|
// Initialize a json parser/tree object
|
||||||
|
cmJsRC_t cmJsonInitialize( cmJsonH_t* hp, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
// Equivalent to cmJsonInitialize() followed by cmJsonParseFile()
|
||||||
|
cmJsRC_t cmJsonInitializeFromFile( cmJsonH_t* hp, const char* fn, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
// Equivalent to cmJsonInitialize() followed by cmJsonParse(h,buf,cnt,NULL).
|
||||||
|
cmJsRC_t cmJsonInitializeFromBuf( cmJsonH_t* hp, cmCtx_t* ctx, const char* buf, unsigned bufByteCnt );
|
||||||
|
|
||||||
|
// Release all the resources held by the tree.
|
||||||
|
cmJsRC_t cmJsonFinalize( cmJsonH_t* hp );
|
||||||
|
|
||||||
|
// Returns true if 'h' is a valid cmJsonH_t handle.
|
||||||
|
bool cmJsonIsValid( cmJsonH_t h );
|
||||||
|
|
||||||
|
// Build the internal tree by parsing a text buffer.
|
||||||
|
// altRootPtr is an optional alternate root ptr which can be used
|
||||||
|
// append to an existing tree. Set to altRootPtr to
|
||||||
|
// NULL to append the tree to the internal root.
|
||||||
|
// If altRootPtr is given it must point ot either an array or
|
||||||
|
// object node.
|
||||||
|
cmJsRC_t cmJsonParse( cmJsonH_t h, const char* buf, unsigned bufCharCnt, cmJsonNode_t* altRootPtr );
|
||||||
|
|
||||||
|
// Fills a text buffer from a file and calls cmJsonParse().
|
||||||
|
cmJsRC_t cmJsonParseFile( cmJsonH_t h, const char* fn, cmJsonNode_t* altRootPtr );
|
||||||
|
|
||||||
|
// Return the root node of the internal tree.
|
||||||
|
cmJsonNode_t* cmJsonRoot( cmJsonH_t h );
|
||||||
|
|
||||||
|
// Return the tree to the post initialize state by clearing the internal tree.
|
||||||
|
cmJsRC_t cmJsonClearTree( cmJsonH_t h );
|
||||||
|
|
||||||
|
// Node type predicates.
|
||||||
|
bool cmJsonIsObject( const cmJsonNode_t* np );
|
||||||
|
bool cmJsonIsArray( const cmJsonNode_t* np );
|
||||||
|
bool cmJsonIsPair( const cmJsonNode_t* np );
|
||||||
|
bool cmJsonIsString( const cmJsonNode_t* np );
|
||||||
|
bool cmJsonIsInt( const cmJsonNode_t* np );
|
||||||
|
bool cmJsonIsReal( const cmJsonNode_t* np );
|
||||||
|
bool cmJsonIsBool( const cmJsonNode_t* np );
|
||||||
|
|
||||||
|
|
||||||
|
// Return the count of child nodes of 'np'.
|
||||||
|
// Note that only object,array, and pair nodes have children.
|
||||||
|
unsigned cmJsonChildCount( const cmJsonNode_t* np );
|
||||||
|
|
||||||
|
// Return the node at 'index' from an element array.
|
||||||
|
// 'np must point to an array element.
|
||||||
|
cmJsonNode_t* cmJsonArrayElement( cmJsonNode_t* np, unsigned index );
|
||||||
|
const cmJsonNode_t* cmJsonArrayElementC( const cmJsonNode_t* np, unsigned index );
|
||||||
|
|
||||||
|
// Return the child value node of a pair with a label node equal to 'label'.
|
||||||
|
// Set 'root' to NULL to begin the search at the internal tree root node.
|
||||||
|
// Set 'typeIdMask' with all type flags to match.
|
||||||
|
// If 'typeIdMask' is equal to kInvalidTId then all types will match.
|
||||||
|
cmJsonNode_t* cmJsonFindValue( cmJsonH_t h, const char* label, const cmJsonNode_t* root, unsigned typeIdMask );
|
||||||
|
|
||||||
|
// Return the value node of a pair at the end of an object path.
|
||||||
|
// 'path' is a '/' seperated list of object names where the final
|
||||||
|
// object specifies the pair label for the value to return.
|
||||||
|
const cmJsonNode_t* cmJsonFindPathValueC( cmJsonH_t h, const char* path, const cmJsonNode_t* root, unsigned typeIdMask );
|
||||||
|
cmJsonNode_t* cmJsonFindPathValue( cmJsonH_t h, const char* path, const cmJsonNode_t* root, unsigned typeIdMask );
|
||||||
|
|
||||||
|
// Return the node value. If 'np' does not point to the same type as
|
||||||
|
// specified in '*retPtr' then the value is converted if possible.
|
||||||
|
// If the value cannot be converted function returns a 'kNodeCannotCvtJsRC'
|
||||||
|
// error
|
||||||
|
cmJsRC_t cmJsonUIntValue( const cmJsonNode_t* np, unsigned* retPtr );
|
||||||
|
cmJsRC_t cmJsonIntValue( const cmJsonNode_t* np, int* retPtr );
|
||||||
|
cmJsRC_t cmJsonRealValue( const cmJsonNode_t* np, double* retPtr );
|
||||||
|
cmJsRC_t cmJsonBoolValue( const cmJsonNode_t* np, bool* retPtr );
|
||||||
|
cmJsRC_t cmJsonStringValue( const cmJsonNode_t* np, const char ** retPtrPtr );
|
||||||
|
cmJsRC_t cmJsonPairNode( const cmJsonNode_t* vp, cmJsonNode_t ** retPtrPtr );
|
||||||
|
cmJsRC_t cmJsonArrayNode( const cmJsonNode_t* vp, cmJsonNode_t ** retPtrPtr );
|
||||||
|
cmJsRC_t cmJsonObjectNode( const cmJsonNode_t* vp, cmJsonNode_t ** retPtrPtr );
|
||||||
|
|
||||||
|
|
||||||
|
// Return the label from a pair object.
|
||||||
|
const char* cmJsonPairLabel( const cmJsonNode_t* pairPtr );
|
||||||
|
unsigned cmJsonPairTypeId( const cmJsonNode_t* pairPtr );
|
||||||
|
cmJsonNode_t* cmJsonPairValue( cmJsonNode_t* pairPtr );
|
||||||
|
|
||||||
|
// Return values associated with the member values in the object
|
||||||
|
// pointed to by object objectNodePtr.
|
||||||
|
cmJsRC_t cmJsonUIntMember( const cmJsonNode_t* objectNodePtr, const char* label, unsigned* retPtr );
|
||||||
|
cmJsRC_t cmJsonIntMember( const cmJsonNode_t* objectNodePtr, const char* label, int* retPtr );
|
||||||
|
cmJsRC_t cmJsonRealMember( const cmJsonNode_t* objectNodePtr, const char* label, double* retPtr );
|
||||||
|
cmJsRC_t cmJsonBoolMember( const cmJsonNode_t* objectNodePtr, const char* label, bool* retPtr );
|
||||||
|
cmJsRC_t cmJsonStringMember( const cmJsonNode_t* objectNodePtr, const char* label, const char** retPtrPtr );
|
||||||
|
|
||||||
|
// Returns array or object nodes.
|
||||||
|
cmJsRC_t cmJsonNodeMember( const cmJsonNode_t* objectNodePtr, const char* label, cmJsonNode_t** nodePtrPtr );
|
||||||
|
|
||||||
|
// Returns the value of the member pair named by 'label' or NULL if the
|
||||||
|
// named pair does not exist.
|
||||||
|
cmJsonNode_t* cmJsonNodeMemberValue( const cmJsonNode_t* np, const char* label );
|
||||||
|
|
||||||
|
// Return values for specified pairs from an object node.
|
||||||
|
//
|
||||||
|
// The var args syntax is: <label>,<typeId>,<valuePtr>.
|
||||||
|
// This functionis implemented in terms of cmJsonXXXMember().
|
||||||
|
//
|
||||||
|
// Add kOptArgJsFl to <typeId> if the member may not exist in
|
||||||
|
// the object - otherwise the function will fail with a
|
||||||
|
// kNodeNotFoundJsRC error and errLabelPtr will have the name of the missing pair.
|
||||||
|
//
|
||||||
|
// Terminate the var args list with NULL.
|
||||||
|
//
|
||||||
|
// Object,Array, and Pair members are returned as node pointers.
|
||||||
|
//
|
||||||
|
// Since kBoolTId does not exist use kTrueTId or kFalseTId to
|
||||||
|
// return bool values.
|
||||||
|
|
||||||
|
cmJsRC_t cmJsonVMemberValues(const cmJsonNode_t* objectNodePtr, const char** errLabelPtrPtr, va_list vl );
|
||||||
|
cmJsRC_t cmJsonMemberValues( const cmJsonNode_t* objectNodePtr, const char** errLabelPtrPtr, ... );
|
||||||
|
|
||||||
|
// If objectNodePtr is set to NULL then the tree root is used as the base for the search.
|
||||||
|
// pathPrefix may be set to NULL.
|
||||||
|
cmJsRC_t cmJsonPathToValueNode( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, const cmJsonNode_t** nodePtrPtr );
|
||||||
|
cmJsRC_t cmJsonPathToBool( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, bool* retValPtr );
|
||||||
|
cmJsRC_t cmJsonPathToInt( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, int* retValPtr );
|
||||||
|
cmJsRC_t cmJsonPathToUInt( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, unsigned* retValPtr );
|
||||||
|
cmJsRC_t cmJsonPathToReal( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, double* retValPtr );
|
||||||
|
cmJsRC_t cmJsonPathToString( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, const char** retValPtr );
|
||||||
|
cmJsRC_t cmJsonPathToPair( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, cmJsonNode_t** retValPtr );
|
||||||
|
cmJsRC_t cmJsonPathToArray( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, cmJsonNode_t** retValPtr );
|
||||||
|
cmJsRC_t cmJsonPathToObject( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, cmJsonNode_t** retValPtr );
|
||||||
|
|
||||||
|
|
||||||
|
// Same as cmJsonMemberValues() except labels may be paths to a given variable.
|
||||||
|
// These paths are equivalent to those used in cmJsonFindPathValue()
|
||||||
|
// If objectNodePtr is NULL then the JSON tree root is used as the
|
||||||
|
// base reference object.
|
||||||
|
// If pathPrefix is non-NULL then it is appended to each of the
|
||||||
|
// individual paths.
|
||||||
|
cmJsRC_t cmJsonVPathValues(cmJsonH_t h, const char* pathPrefix, const cmJsonNode_t* objectNodePtr, const char** errLabelPtrPtr, va_list vl );
|
||||||
|
cmJsRC_t cmJsonPathValues( cmJsonH_t h, const char* pathPrefix, const cmJsonNode_t* objectNodePtr, const char** errLabelPtrPtr, ... );
|
||||||
|
|
||||||
|
|
||||||
|
// Set 'typeId' to the type of the new node.
|
||||||
|
// Use 'intVal' for the value of int nodes.
|
||||||
|
// Use 'realVal' for the value of real nodes.
|
||||||
|
// Use 'stringVal' for the label of pairs and the value of string nodes.
|
||||||
|
// 'retNodePtrPtr' is optional
|
||||||
|
cmJsRC_t cmJsonCreate( cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned typeId, const char* stringVal, int intVal, double realVal, cmJsonNode_t** newNodePtrPtr );
|
||||||
|
|
||||||
|
// Insert new nodes in the tree. If the tree is empty then the first
|
||||||
|
// inserted node will become the root (this must be an object or array node.).
|
||||||
|
cmJsonNode_t* cmJsonCreateObject( cmJsonH_t h, cmJsonNode_t* parentPtr );
|
||||||
|
cmJsonNode_t* cmJsonCreateArray( cmJsonH_t h, cmJsonNode_t* parentPtr );
|
||||||
|
cmJsonNode_t* cmJsonCreatePair( cmJsonH_t h, cmJsonNode_t* parentPtr, const char* label );
|
||||||
|
cmJsRC_t cmJsonCreateString( cmJsonH_t h, cmJsonNode_t* parentPtr, const char* stringValue );
|
||||||
|
cmJsRC_t cmJsonCreateStringN(cmJsonH_t h, cmJsonNode_t* parentPtr, const char* stringValue, unsigned stringCharCnt );
|
||||||
|
cmJsRC_t cmJsonCreateInt( cmJsonH_t h, cmJsonNode_t* parentPtr, int value );
|
||||||
|
cmJsRC_t cmJsonCreateReal( cmJsonH_t h, cmJsonNode_t* parentPtr, double value );
|
||||||
|
cmJsRC_t cmJsonCreateBool( cmJsonH_t h, cmJsonNode_t* parentPtr, bool value );
|
||||||
|
cmJsRC_t cmJsonCreateNull( cmJsonH_t h, cmJsonNode_t* parentPtr );
|
||||||
|
|
||||||
|
cmJsRC_t cmJsonCreateStringArray( cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned n, const char** values );
|
||||||
|
cmJsRC_t cmJsonCreateIntArray( cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned n, const int* values );
|
||||||
|
cmJsRC_t cmJsonCreateRealArray( cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned n, const double* values );
|
||||||
|
cmJsRC_t cmJsonCreateBoolArray( cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned n, const bool* values );
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Tree creation helper functiosn
|
||||||
|
//
|
||||||
|
|
||||||
|
cmJsRC_t cmJsonSetInt( cmJsonH_t h, cmJsonNode_t* np, int ival );
|
||||||
|
cmJsRC_t cmJsonSetReal( cmJsonH_t h, cmJsonNode_t * np, double rval );
|
||||||
|
cmJsRC_t cmJsonSetBool( cmJsonH_t h, cmJsonNode_t * np, bool bval );
|
||||||
|
cmJsRC_t cmJsonSetString( cmJsonH_t h, cmJsonNode_t* np, const char* sval );
|
||||||
|
|
||||||
|
// Insert a pair with a value indicated by 'typeId'.
|
||||||
|
// 'stringVal','intVal' and 'realVal' are used as in cmJsonCreate().
|
||||||
|
// Return a pointer to the new pair.
|
||||||
|
cmJsonNode_t* cmJsonInsertPair( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned typeId, const char* stringVal, int intVal, double realVal );
|
||||||
|
|
||||||
|
// Create a pair node and the associated label and value nodes and insert the pair in a parent object.
|
||||||
|
// The object,array and pair creation functions return pointers to the pair value node.
|
||||||
|
// These are helper functions that are implemented in terms of cmJsonCreateXXX() function.
|
||||||
|
cmJsonNode_t* cmJsonInsertPairObject( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label );
|
||||||
|
cmJsonNode_t* cmJsonInsertPairArray( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label );
|
||||||
|
cmJsonNode_t* cmJsonInsertPairPair( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, const char* pairLabel );
|
||||||
|
cmJsRC_t cmJsonInsertPairInt( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, int intVal );
|
||||||
|
cmJsRC_t cmJsonInsertPairReal( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, double realVal );
|
||||||
|
cmJsRC_t cmJsonInsertPairString( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, const char* string );
|
||||||
|
cmJsRC_t cmJsonInsertPairStringN(cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, const char* string, unsigned stringCharCnt );
|
||||||
|
cmJsRC_t cmJsonInsertPairBool( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, bool boolVal );
|
||||||
|
cmJsRC_t cmJsonInsertPairNull( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label );
|
||||||
|
|
||||||
|
cmJsRC_t cmJsonInsertPairIntArray( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const int* values );
|
||||||
|
cmJsRC_t cmJsonInsertPairRealArray( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const double* values );
|
||||||
|
cmJsRC_t cmJsonInsertPairStringArray( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const char** values );
|
||||||
|
cmJsRC_t cmJsonInsertPairBoolArray( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const bool* values );
|
||||||
|
|
||||||
|
// Returns pair pointer
|
||||||
|
cmJsonNode_t* cmJsonInsertPairIntArray2( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const int* values );
|
||||||
|
cmJsonNode_t* cmJsonInsertPairRealArray2( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const double* values );
|
||||||
|
cmJsonNode_t* cmJsonInsertPairStringArray2( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const char** values );
|
||||||
|
cmJsonNode_t* cmJsonInsertPairBoolArray2( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const bool* values );
|
||||||
|
|
||||||
|
// Insert a pair (same as cmJsonInsertPair()) or if a pair
|
||||||
|
// with a matching label/type already exists then replace the
|
||||||
|
// existing value with a the new value.
|
||||||
|
//
|
||||||
|
// Set matchTypeMask with the type id's of all pair value types
|
||||||
|
// which sould be considered a match. If matchTypeMask is set to
|
||||||
|
// kInvalidTId then all value types will match.
|
||||||
|
//
|
||||||
|
// Return a pointer to the new or existing pair.
|
||||||
|
//
|
||||||
|
// When newTypeId == kObjectTId or kArrayTId then 'nv' may optionally be set to an object or array
|
||||||
|
// to be set as the new value node for the selected pair. If 'nv' is NULL then an empty array or
|
||||||
|
// object is created as the pair value node.
|
||||||
|
//
|
||||||
|
// When newTypeId == kPairTId then we are inserting/replacing a pair as the value of the selected pair.
|
||||||
|
// In this case sv must be set to the new pair label. 'nv' may be optionally
|
||||||
|
// set to a new pair value node. If 'nv' is NULL then the the new pair will have a value of type kNullTId
|
||||||
|
cmJsonNode_t* cmJsonInsertOrReplacePair( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, unsigned newTypeId, const char* sv, int iv, double dv, cmJsonNode_t* nv );
|
||||||
|
|
||||||
|
// Returns pointer to object node.
|
||||||
|
cmJsonNode_t* cmJsonInsertOrReplacePairObject( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, cmJsonNode_t* newObjNodePtr );
|
||||||
|
// Returns pointer to array node.
|
||||||
|
cmJsonNode_t* cmJsonInsertOrReplacePairArray( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, cmJsonNode_t* newArrayNodePtr );
|
||||||
|
// Returns pointer to child pair node
|
||||||
|
cmJsonNode_t* cmJsonInsertOrReplacePairPair( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, const char* newPairLabel, cmJsonNode_t* newPairValNodePtr );
|
||||||
|
cmJsRC_t cmJsonInsertOrReplacePairInt( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, int intVal );
|
||||||
|
cmJsRC_t cmJsonInsertOrReplacePairReal( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, double realVal );
|
||||||
|
cmJsRC_t cmJsonInsertOrReplacePairString( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, const char* string );
|
||||||
|
cmJsRC_t cmJsonInsertOrReplacePairBool( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, bool boolVal );
|
||||||
|
cmJsRC_t cmJsonInsertOrReplacePairNull( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask );
|
||||||
|
|
||||||
|
// Same as the above cmJsonInsertOrReplaceXXX() functions except
|
||||||
|
// the function fails if a matching pair is not found.
|
||||||
|
//
|
||||||
|
// Replace a pair with the same name/type and return a pointer to the
|
||||||
|
// effected pair. If a pair with the same name and type are not
|
||||||
|
// found then no change is made and the function returns NULL.
|
||||||
|
// For newTypeId=kObjectTId,kArrayTId,kPairTId the replaced pair node is blank,
|
||||||
|
// in other words it will have no child nodes.
|
||||||
|
cmJsonNode_t* cmJsonReplacePair( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, unsigned newTypeId, const char* sv, int iv, double dv, cmJsonNode_t* nv );
|
||||||
|
|
||||||
|
// Returns pointer to object node.
|
||||||
|
cmJsonNode_t* cmJsonReplacePairObject( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, cmJsonNode_t* newObjNodePtr );
|
||||||
|
// Return pointer to array node.
|
||||||
|
cmJsonNode_t* cmJsonReplacePairArray( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, cmJsonNode_t* newArrayNodePtr );
|
||||||
|
// Returns pointer to child pair node.
|
||||||
|
cmJsonNode_t* cmJsonReplacePairPair( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, const char* newPairLabel, cmJsonNode_t* newPairValueNodePtr );
|
||||||
|
cmJsRC_t cmJsonReplacePairInt( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, int intVal );
|
||||||
|
cmJsRC_t cmJsonReplacePairReal( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, double realVal );
|
||||||
|
cmJsRC_t cmJsonReplacePairString( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, const char* string );
|
||||||
|
cmJsRC_t cmJsonReplacePairBool( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, bool boolVal );
|
||||||
|
cmJsRC_t cmJsonReplacePairNull( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask );
|
||||||
|
|
||||||
|
|
||||||
|
// Insert multiple pairs in a parent object. Terminate the pair sets with NULL.
|
||||||
|
// Note that pair,int,real,bool, and string pairs are specified with 3 args: <label>,<typeId>,<value>
|
||||||
|
// all others are specified with 2 args: <label>,<typeId>.
|
||||||
|
// The last argument in this function must always be NULL.
|
||||||
|
cmJsRC_t cmJsonVInsertPairs( cmJsonH_t h, cmJsonNode_t* objectNodePtr, va_list vl );
|
||||||
|
cmJsRC_t cmJsonInsertPairs( cmJsonH_t h, cmJsonNode_t* objectNodePtr, ... );
|
||||||
|
|
||||||
|
|
||||||
|
// Create an object node, fill it with the specified pairs, and return a pointer to
|
||||||
|
// the new object node.
|
||||||
|
// This function uses same var args syntax as cmJsonInsertPairs()
|
||||||
|
cmJsonNode_t* cmJsonVCreateFilledObject( cmJsonH_t h, cmJsonNode_t* parentPtr, va_list vl );
|
||||||
|
cmJsonNode_t* cmJsonCreateFilledObject( cmJsonH_t h, cmJsonNode_t* parentPtr, ... );
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Remove a node from the tree by unlinking it from its
|
||||||
|
// parent and siblings.
|
||||||
|
// Set the freeFl to true if the node memory should also
|
||||||
|
// be released.
|
||||||
|
//
|
||||||
|
// If 'freeFl' is false then np->ownerPtr and
|
||||||
|
// np->siblingPtr remain valid when the function returns.
|
||||||
|
// Even with the valid pointer however be careful
|
||||||
|
// not to use the node in a way that it depends
|
||||||
|
// on existing in the tree since the parent and
|
||||||
|
// siblings will no longer know about it.
|
||||||
|
//
|
||||||
|
// If np is the root then the internal tree will be
|
||||||
|
// cleared (i.e. cmJsonRoot(h) == NULL).
|
||||||
|
//
|
||||||
|
// This function will fail if 'np' points to the label value
|
||||||
|
// of a pair node. Pair labels cannot be removed because this
|
||||||
|
// would result in an invalid tree (all pairs must have two
|
||||||
|
// child nodes where the first node is a string value). If 'np'
|
||||||
|
// points to a pair value node then the value node is replaced
|
||||||
|
// with a null node to maintain a valid pair structure.
|
||||||
|
cmJsRC_t cmJsonRemoveNode( cmJsonH_t h, cmJsonNode_t* np, bool freeFl );
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// Duplicate the subtree pointed to by 'np' and attach it as a child
|
||||||
|
// of the node pointed to by 'parentPtr'. This function performs a
|
||||||
|
// deep copy of the subtree pointed to by np and returns the pointer
|
||||||
|
// to the duplicated subtree.
|
||||||
|
//
|
||||||
|
// 'parentPtr' must be a legal parent for the sub-tree or NULL to not
|
||||||
|
// attach the duplicate tree to any parent.
|
||||||
|
//
|
||||||
|
// The returned value is a pointer to the new subtree.
|
||||||
|
//
|
||||||
|
// If an error occurs the return value is NULL and cmJsonErrorCode()
|
||||||
|
// can be used to obtain the code associated with the error.
|
||||||
|
cmJsonNode_t* cmJsonDuplicateNode( cmJsonH_t h, const cmJsonNode_t* np, cmJsonNode_t* parentPtr );
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Copy any pairs not found in the destintaion object from the
|
||||||
|
// source object.
|
||||||
|
// If an error occurs during merging the destination object is
|
||||||
|
// returned unmodified. The most likely cause of an error is a
|
||||||
|
// destination pair with the same name but different type
|
||||||
|
// than a source pair.
|
||||||
|
cmJsRC_t cmJsonMergeObjectNodes( cmJsonH_t h, cmJsonNode_t* destObjNodePtr, const cmJsonNode_t* srcObjNodePtr );
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// Validate the tree.
|
||||||
|
cmJsRC_t cmJsonValidateTree( cmJsonH_t h );
|
||||||
|
|
||||||
|
// Validate the tree beginning with np. Note that this function does
|
||||||
|
// not print an error on failure but simply returns kValidateFailJsRC.
|
||||||
|
cmJsRC_t cmJsonValidate( const cmJsonNode_t* np );
|
||||||
|
|
||||||
|
// Get the count of bytes required to serialize the tree rooted at 'np'.
|
||||||
|
unsigned cmJsonSerialByteCount( const cmJsonNode_t* np );
|
||||||
|
|
||||||
|
// Serialize the tree rooted at 'np' into the buffer buf[bufByteCnt].
|
||||||
|
cmJsRC_t cmJsonSerialize( const cmJsonNode_t* np, void* buf, unsigned bufByteCnt );
|
||||||
|
|
||||||
|
// Serialize the subtree indicated by 'np' or the entire tree
|
||||||
|
// if 'np' is NULL. The buffer created by this call will exist
|
||||||
|
// for the life of 'h' or until the next call to cmJsonSerialize().
|
||||||
|
// This function is implemented in terms of cmJsonSerialByteCount()
|
||||||
|
// and cmJsonSerializeTree().
|
||||||
|
cmJsRC_t cmJsonSerializeTree( cmJsonH_t h, const cmJsonNode_t* np, void** bufPtrPtr, unsigned* bufByteCntPtr);
|
||||||
|
|
||||||
|
// Recreate the objects previously serialzed via cmJsonSerialize().
|
||||||
|
// The tree held in the buffer will be reconstructed as a child of
|
||||||
|
// altRootPtr (if it is non-NULL) or the internal root.
|
||||||
|
// If altRootPtr is given then it must point to an array
|
||||||
|
// or object node.
|
||||||
|
cmJsRC_t cmJsonDeserialize( cmJsonH_t h, const void* bufPtr, cmJsonNode_t* altRootPtr );
|
||||||
|
|
||||||
|
// Return a string/int/real/null/bool node as a string value.
|
||||||
|
cmJsRC_t cmJsonLeafToString( const cmJsonNode_t* np, cmChar_t* buf, unsigned bufCharCnt );
|
||||||
|
|
||||||
|
// Given a CSV file convert it to an array of JSON objects.
|
||||||
|
// The first line of the CSV file must contain a comma seperated lists of types.
|
||||||
|
// The type labels must be from the set: 'int','real','string','true','false'.
|
||||||
|
// Note that either 'true' or 'false' can be use for boolean columns.
|
||||||
|
// The seocnd line contains the field names as comma separated quoted strings.
|
||||||
|
// For example "column1","column2","column3"
|
||||||
|
// The data is presented as comma separated fields.
|
||||||
|
// If parentNodePtr is NULL then the array will be created unattached to
|
||||||
|
// the tree.
|
||||||
|
// if arrayNodePtrPtr is non-NULL then the array node ptr will be returned.
|
||||||
|
cmJsRC_t cmJsonFromCSV( cmJsonH_t h, const char* iFn, cmJsonNode_t* parentPtr, cmJsonNode_t** arrayNodePtrPtr );
|
||||||
|
|
||||||
|
// Write a CSV file from an array of objects.
|
||||||
|
// arrayNodePtr must point to an array of objects.
|
||||||
|
cmJsRC_t cmJsonToCSV( cmJsonH_t h, const char* oFn, const cmJsonNode_t* arrayNodePtr );
|
||||||
|
|
||||||
|
// Print the subtree using 'np as the root.
|
||||||
|
void cmJsonPrintTree( const cmJsonNode_t* np, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
// Print the subtree using 'np' as the root to a file.
|
||||||
|
// 'np' is optional and defaults to cmJsonRoot().
|
||||||
|
cmJsRC_t cmJsonWrite( cmJsonH_t h, const cmJsonNode_t* np, const cmChar_t* fn );
|
||||||
|
|
||||||
|
// Return the code of the last error generated. This is useful for the
|
||||||
|
// the cmJsonCreateXXX() functions which do not return error codes but
|
||||||
|
// may still fail.
|
||||||
|
cmJsRC_t cmJsonErrorCode( cmJsonH_t h );
|
||||||
|
void cmJsonClearErrorCode( cmJsonH_t h );
|
||||||
|
|
||||||
|
// Validate the tree and print all the nodes.
|
||||||
|
cmJsRC_t cmJsonReport( cmJsonH_t h );
|
||||||
|
|
||||||
|
// Testing stub.
|
||||||
|
cmJsRC_t cmJsonTest( const char* fn, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
235
cmKeyboard.c
Normal file
235
cmKeyboard.c
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmKeyboard.h"
|
||||||
|
#include <termios.h>
|
||||||
|
//#include <stropts.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
//#include <linux/kd.h>
|
||||||
|
//#include <linux/keyboard.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
struct termios new_settings;
|
||||||
|
struct termios stored_settings;
|
||||||
|
|
||||||
|
void set_keypress(void)
|
||||||
|
{
|
||||||
|
struct termios new_settings;
|
||||||
|
|
||||||
|
tcgetattr(0,&stored_settings);
|
||||||
|
new_settings = stored_settings;
|
||||||
|
new_settings.c_lflag &= (~ICANON);
|
||||||
|
new_settings.c_lflag &= (~ECHO);
|
||||||
|
new_settings.c_cc[VTIME] = 0;
|
||||||
|
|
||||||
|
//int i;
|
||||||
|
//for(i=0; i<NCCS; ++i)
|
||||||
|
// printf("%i ",new_settings.c_cc[i]);
|
||||||
|
//printf("\n");
|
||||||
|
|
||||||
|
|
||||||
|
tcgetattr(0,&stored_settings);
|
||||||
|
|
||||||
|
new_settings.c_cc[VMIN] = 1;
|
||||||
|
tcsetattr(0,TCSANOW,&new_settings);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_keypress(void)
|
||||||
|
{
|
||||||
|
tcsetattr(0,TCSANOW,&stored_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CM_KB_TBL_CNT (10)
|
||||||
|
|
||||||
|
unsigned _cmKbTbl[][CM_KB_TBL_CNT] =
|
||||||
|
{
|
||||||
|
|
||||||
|
// alt ctl code
|
||||||
|
{ 3, 27, 91, 68, 0, 0, 0, 0, 0, kLeftArrowKId },
|
||||||
|
{ 3, 27, 91, 67, 0, 0, 0, 0, 0, kRightArrowKId },
|
||||||
|
{ 3, 27, 91, 65, 0, 0, 0, 0, 0, kUpArrowKId },
|
||||||
|
{ 3, 27, 91, 66, 0, 0, 0, 0, 0, kDownArrowKId },
|
||||||
|
{ 3, 27, 79, 72, 0, 0, 0, 0, 0, kHomeKId },
|
||||||
|
{ 3, 27, 79, 70, 0, 0, 0, 0, 0, kEndKId },
|
||||||
|
{ 4, 27, 91, 53, 126, 0, 0, 0, 0, kPgUpKId },
|
||||||
|
{ 4, 27, 91, 54, 126, 0, 0, 0, 0, kPgDownKId },
|
||||||
|
{ 4, 27, 91, 50, 126, 0, 0, 0, 0, kInsertKId },
|
||||||
|
{ 4, 27, 91, 51, 126, 0, 0, 0, 0, kDeleteKId },
|
||||||
|
{ 6, 27, 91, 49, 59, 53, 68, 0, 1, kLeftArrowKId },
|
||||||
|
{ 6, 27, 91, 49, 59, 53, 67, 0, 1, kRightArrowKId },
|
||||||
|
{ 6, 27, 91, 49, 59, 53, 65, 0, 1, kUpArrowKId },
|
||||||
|
{ 6, 27, 91, 49, 59, 53, 66, 0, 1, kDownArrowKId },
|
||||||
|
{ 6, 27, 91, 53, 59, 53, 126, 0, 1, kPgUpKId },
|
||||||
|
{ 6, 27, 91, 54, 59, 53, 126, 0, 1, kPgDownKId },
|
||||||
|
{ 4, 27, 91, 51, 59, 53, 126, 0, 1, kDeleteKId },
|
||||||
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, kInvalidKId }
|
||||||
|
};
|
||||||
|
|
||||||
|
void cmKeyPress( cmKbRecd* p )
|
||||||
|
{
|
||||||
|
const int bufN = 16;
|
||||||
|
char buf[bufN];
|
||||||
|
int n,j, k;
|
||||||
|
int rc;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if( p != NULL )
|
||||||
|
{
|
||||||
|
p->code = kInvalidKId;
|
||||||
|
p->ch = 0;
|
||||||
|
p->ctlFl = false;
|
||||||
|
p->altFl = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_keypress();
|
||||||
|
|
||||||
|
// block for the first character
|
||||||
|
if((rc = read(0, &c, 1 )) == 1)
|
||||||
|
buf[0]=c;
|
||||||
|
|
||||||
|
// loop in non-blocking for successive characters
|
||||||
|
new_settings.c_cc[VMIN] = 0;
|
||||||
|
tcsetattr(0,TCSANOW,&new_settings);
|
||||||
|
|
||||||
|
for(n=1; n<bufN; ++n)
|
||||||
|
if(read(0,&c,1) == 1 )
|
||||||
|
buf[n] = c;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
new_settings.c_cc[VMIN] = 1;
|
||||||
|
tcsetattr(0,TCSANOW,&new_settings);
|
||||||
|
|
||||||
|
/*
|
||||||
|
for(j=0; j<n; ++j)
|
||||||
|
printf("{%c (%i)} ",buf[j],buf[j]);
|
||||||
|
printf(" :%i\f\n",n);
|
||||||
|
fflush(stdout);
|
||||||
|
*/
|
||||||
|
|
||||||
|
if( p != NULL )
|
||||||
|
{
|
||||||
|
// translate the keypress
|
||||||
|
if( n == 1)
|
||||||
|
{
|
||||||
|
p->code = kAsciiKId;
|
||||||
|
p->ch = buf[0];
|
||||||
|
p->ctlFl = buf[0] <= 31;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(j=0; _cmKbTbl[j][0] != 0; ++j)
|
||||||
|
if( _cmKbTbl[j][0] == n )
|
||||||
|
{
|
||||||
|
for(k=1; k<=n; ++k)
|
||||||
|
if( _cmKbTbl[j][k] != buf[k-1] )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// if the key was found
|
||||||
|
if( k==n+1 )
|
||||||
|
{
|
||||||
|
p->code = _cmKbTbl[j][ CM_KB_TBL_CNT - 1 ];
|
||||||
|
p->ctlFl = _cmKbTbl[j][ CM_KB_TBL_CNT - 2 ];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reset_keypress();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmKbTest()
|
||||||
|
{
|
||||||
|
set_keypress();
|
||||||
|
|
||||||
|
int c = 0;
|
||||||
|
int r;
|
||||||
|
while( c != 'q' )
|
||||||
|
{
|
||||||
|
|
||||||
|
printf("0>"); fflush(stdout);
|
||||||
|
r = read(0, &c, 1 );
|
||||||
|
printf("0: %c (%i)\r\n",(char)c,c);
|
||||||
|
|
||||||
|
|
||||||
|
new_settings.c_cc[VMIN] = 0;
|
||||||
|
tcsetattr(0,TCSANOW,&new_settings);
|
||||||
|
|
||||||
|
if( r == 1 && c == 27 )
|
||||||
|
{
|
||||||
|
r = read(0, &c, 1 );
|
||||||
|
printf("1: %c (%i)\n",(char)c,c);
|
||||||
|
|
||||||
|
if( r == 1 && c == '[' )
|
||||||
|
{
|
||||||
|
r = read(0, &c, 1 );
|
||||||
|
printf("2: %c (%i)\n",(char)c,c);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
new_settings.c_cc[VMIN] = 1;
|
||||||
|
tcsetattr(0,TCSANOW,&new_settings);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_keypress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: this technique does not work because the
|
||||||
|
void testKb2()
|
||||||
|
{
|
||||||
|
set_keypress();
|
||||||
|
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
int c=0;
|
||||||
|
|
||||||
|
|
||||||
|
while( c != 'q' )
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
printf(">");
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Watch stdin (fd 0) to see when it has input. */
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(0, &rfds);
|
||||||
|
|
||||||
|
// don't wait
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
retval = select(1, &rfds, NULL, NULL, i==0 ? NULL : &tv);
|
||||||
|
// Don't rely on the value of tv now - it may have been overwritten by select
|
||||||
|
|
||||||
|
// if an error occurred
|
||||||
|
if (retval == -1)
|
||||||
|
perror("select()");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if data is waiting
|
||||||
|
if (retval)
|
||||||
|
{
|
||||||
|
c = getchar();
|
||||||
|
printf("%i %c (%i) ",i,(char)c,c);
|
||||||
|
++i;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("\n");
|
||||||
|
break; // no data available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} while( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_keypress();
|
||||||
|
}
|
37
cmKeyboard.h
Normal file
37
cmKeyboard.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef cmKeyboard_h
|
||||||
|
#define cmKeyboard_h
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidKId,
|
||||||
|
kAsciiKId,
|
||||||
|
kLeftArrowKId,
|
||||||
|
kRightArrowKId,
|
||||||
|
kUpArrowKId,
|
||||||
|
kDownArrowKId,
|
||||||
|
kHomeKId,
|
||||||
|
kEndKId,
|
||||||
|
kPgUpKId,
|
||||||
|
kPgDownKId,
|
||||||
|
kInsertKId,
|
||||||
|
kDeleteKId,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned code;
|
||||||
|
char ch;
|
||||||
|
bool ctlFl;
|
||||||
|
bool altFl;
|
||||||
|
} cmKbRecd;
|
||||||
|
|
||||||
|
// Set 'p' to NULL if the value of the key is not required.
|
||||||
|
void cmKeyPress( cmKbRecd* p );
|
||||||
|
|
||||||
|
#endif
|
910
cmLex.c
Normal file
910
cmLex.c
Normal file
@ -0,0 +1,910 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmLex.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmFile.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned code;
|
||||||
|
const cmChar_t* msg;
|
||||||
|
} cmLexErrorRecd;
|
||||||
|
|
||||||
|
|
||||||
|
cmLexErrorRecd cmLexErrorArray[] =
|
||||||
|
{
|
||||||
|
{ kOkLexRC, "No error. The operation completed successfully."},
|
||||||
|
{ kDuplicateTokenLexRC, "The text or id passed as a user token is already in use by another token."},
|
||||||
|
{ kMissingCmtEndLexRC, "The end of a block comment could not be found."},
|
||||||
|
{ kMissingEndQuoteLexRC, "The end of a quoted string could not be found."},
|
||||||
|
{ kNoMatchLexRC, "The lexer encountered a string which could not be classified."},
|
||||||
|
{ kFileOpenErrLexRC, "File open failed on cmLexSetFile()"},
|
||||||
|
{ kFileSeekErrLexRC, "File seek failed on cmLexSetFile()"},
|
||||||
|
{ kFileTellErrLexRC, "File tell failed on cmLexSetFile()"},
|
||||||
|
{ kFileReadErrLexRC, "File read failed on cmLexSetFile()"},
|
||||||
|
{ kFileCloseErrLexRC, "File close failed on cmLexSetFile()"},
|
||||||
|
{ kMemAllocErrLexRC, "An attempted memory allocation failed"},
|
||||||
|
{ kEofRC, "The end of the input text was encountered (this is a normal condition not an error)"},
|
||||||
|
{ kInvalidLexRC, "Unknown lexer error code." }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cmLex_str;
|
||||||
|
|
||||||
|
typedef unsigned (*cmLexMatcherFuncPtr_t)( struct cmLex_str* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr );
|
||||||
|
|
||||||
|
// token match function record
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned typeId; // token type this matcher recognizes
|
||||||
|
cmLexMatcherFuncPtr_t funcPtr; // recognizer function (only used if userPtr==NULL)
|
||||||
|
cmChar_t* tokenStr; // fixed string data used by the recognizer (only used if userPtr==NULL)
|
||||||
|
cmLexUserMatcherPtr_t userPtr; // user defined recognizer function (only used if funcPtr==NULL)
|
||||||
|
} cmLexMatcher;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct cmLex_str
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
const cmChar_t* cp; // character buffer
|
||||||
|
unsigned cn; // count of characters in buffer
|
||||||
|
unsigned ci; // current buffer index position
|
||||||
|
unsigned flags; // lexer control flags
|
||||||
|
|
||||||
|
unsigned curTokenId; // type id of the current token
|
||||||
|
unsigned curTokenCharIdx; // index into cp[] of the current token
|
||||||
|
unsigned curTokenCharCnt; // count of characters in the current token
|
||||||
|
unsigned curLine; // line number of the current token
|
||||||
|
unsigned curCol; // column number of the current token
|
||||||
|
|
||||||
|
unsigned nextLine;
|
||||||
|
unsigned nextCol;
|
||||||
|
|
||||||
|
cmChar_t* blockBegCmtStr;
|
||||||
|
cmChar_t* blockEndCmtStr;
|
||||||
|
cmChar_t* lineCmtStr;
|
||||||
|
|
||||||
|
cmLexMatcher* mfp; // base of matcher array
|
||||||
|
unsigned mfi; // next available matcher array slot
|
||||||
|
unsigned mfn; // count of elementes in mfp[]
|
||||||
|
|
||||||
|
cmChar_t* textBuf; // text buf used by cmLexSetFile()
|
||||||
|
|
||||||
|
} cmLex;
|
||||||
|
|
||||||
|
|
||||||
|
cmLexH cmLexNullH = { NULL };
|
||||||
|
|
||||||
|
bool _cmLexIsNewline( cmChar_t c )
|
||||||
|
{ return c == '\n'; }
|
||||||
|
|
||||||
|
bool _cmLexIsCommentTypeId( unsigned typeId )
|
||||||
|
{ return typeId == kBlockCmtLexTId || typeId == kLineCmtLexTId; }
|
||||||
|
|
||||||
|
cmLex* _cmLexHandleToPtr( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = (cmLex*)h.h;
|
||||||
|
assert(p != NULL);
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
|
||||||
|
cmRC_t _cmLexError( cmLex* p, unsigned rc, const char* fmt, ... )
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,fmt);
|
||||||
|
|
||||||
|
unsigned bufCharCnt = 512;
|
||||||
|
char buf[ bufCharCnt+1 ];
|
||||||
|
snprintf(buf,bufCharCnt,"Error on line:%i ", p->curLine);
|
||||||
|
|
||||||
|
unsigned sn = strlen(buf);
|
||||||
|
vsnprintf(buf+sn,bufCharCnt-sn,fmt,vl);
|
||||||
|
buf[bufCharCnt]=0;
|
||||||
|
|
||||||
|
cmErrMsg(&p->err,rc,"%s",buf);
|
||||||
|
|
||||||
|
va_end(vl);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _cmLexScanTo( const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned i = 0;
|
||||||
|
unsigned n = strlen(keyStr);
|
||||||
|
|
||||||
|
if( n <= cn )
|
||||||
|
for(; i<=cn-n; ++i)
|
||||||
|
if( strncmp(cp + i, keyStr, n ) == 0 )
|
||||||
|
return i+n;
|
||||||
|
|
||||||
|
return cmInvalidIdx;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned _cmLexExactStringMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned n = strlen(keyStr);
|
||||||
|
return strncmp(keyStr,cp,n) == 0 ? n : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned _cmLexSpaceMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned i=0;
|
||||||
|
for(; i<cn; ++i)
|
||||||
|
if( !isspace(cp[i]) )
|
||||||
|
break;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _cmLexRealMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned i = 0;
|
||||||
|
unsigned n = 0; // decimal point counter
|
||||||
|
unsigned d = 0; // digit counter
|
||||||
|
bool fl = false; // true if this real includes an exponent
|
||||||
|
|
||||||
|
for(; i<cn && n<=1; ++i)
|
||||||
|
{
|
||||||
|
if( i==0 && cp[i]=='-' ) // allow a leading '-'
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( isdigit(cp[i]) ) // allow digits
|
||||||
|
{
|
||||||
|
++d;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cp[i] == '.' && n==0 ) // allow exactly one decimal point
|
||||||
|
++n;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there was at least one digit and the next char is an 'e'
|
||||||
|
if( d>0 && i<cn && (cp[i] == 'e' || cp[i] == 'E') )
|
||||||
|
{
|
||||||
|
d=0;
|
||||||
|
++i;
|
||||||
|
unsigned j = i;
|
||||||
|
|
||||||
|
for(; i<cn; ++i)
|
||||||
|
{
|
||||||
|
if( i==j && cp[i]=='-' ) // allow the char following the e to be '-'
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( isdigit(cp[i]) )
|
||||||
|
{
|
||||||
|
++d;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop at the first non-digit
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// an exp exists if digits follwed the 'e'
|
||||||
|
fl = d > 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return i>1 && (n==1 || fl) ? i : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _cmLexIntMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned i = 0;
|
||||||
|
for(; i<cn; ++i)
|
||||||
|
{
|
||||||
|
if( i==0 && cp[i]=='-' )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( !isdigit(cp[i]) )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BUG BUG BUG
|
||||||
|
// If an integer is specified using 'e' notiation
|
||||||
|
// (see _cmLexRealMatcher()) and the number of exponent places
|
||||||
|
// specified following the 'e' is positive and >= number of
|
||||||
|
// digits following the decimal point (in effect zeros are
|
||||||
|
// padded on the right side) then the value is an integer.
|
||||||
|
//
|
||||||
|
// The current implementation recognizes all numeric strings
|
||||||
|
// containing a decimal point as reals.
|
||||||
|
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _cmLexHexMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
|
if( cn < 3 )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if( cp[0]=='0' && cp[1]=='x')
|
||||||
|
for(i=2; i<cn; ++i)
|
||||||
|
if( !isxdigit(cp[i]) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned _cmLexIdentMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned i = 0;
|
||||||
|
if( isalpha(cp[0]) || (cp[0]== '_'))
|
||||||
|
{
|
||||||
|
i = 1;
|
||||||
|
for(; i<cn; ++i)
|
||||||
|
if( !isalnum(cp[i]) && (cp[i] != '_') )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned _cmLexQStrMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
cmChar_t qStr[]="\"";
|
||||||
|
unsigned n = strlen(qStr);
|
||||||
|
if( strncmp(qStr,cp,n) == 0 )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
if((i = _cmLexScanTo(cp+n, cn-n, qStr)) == cmInvalidIdx )
|
||||||
|
{
|
||||||
|
_cmLexError( p, kMissingEndQuoteLexRC, "Missing string end quote.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return n+i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned _cmLexBlockCmtMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned n = strlen(p->blockBegCmtStr);
|
||||||
|
|
||||||
|
if( strncmp( p->blockBegCmtStr, cp, n ) == 0 )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
if((i = _cmLexScanTo(cp + n, cn-n,p->blockEndCmtStr)) == cmInvalidIdx )
|
||||||
|
{
|
||||||
|
_cmLexError(p, kMissingCmtEndLexRC, "Missing end of block comment.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n + i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _cmLexLineCmtMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
|
||||||
|
{
|
||||||
|
unsigned n = strlen(p->lineCmtStr);
|
||||||
|
if( strncmp( p->lineCmtStr, cp, n ) == 0)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
const char newlineStr[] = "\n";
|
||||||
|
if((i = _cmLexScanTo(cp + n, cn-n, newlineStr)) == cmInvalidIdx )
|
||||||
|
{
|
||||||
|
// no EOL was found so the comment must be on the last line of the source
|
||||||
|
return cn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n + i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t _cmLexInstallMatcher( cmLex* p, unsigned typeId, cmLexMatcherFuncPtr_t funcPtr, const cmChar_t* keyStr, cmLexUserMatcherPtr_t userPtr )
|
||||||
|
{
|
||||||
|
assert( funcPtr==NULL || userPtr==NULL );
|
||||||
|
assert( !(funcPtr==NULL && userPtr==NULL));
|
||||||
|
|
||||||
|
// if there is no space in the user token array - then expand it
|
||||||
|
if( p->mfi == p->mfn )
|
||||||
|
{
|
||||||
|
int incr_cnt = 10;
|
||||||
|
cmLexMatcher* np = cmMemAllocZ( cmLexMatcher, p->mfn + incr_cnt );
|
||||||
|
memcpy(np,p->mfp,p->mfi*sizeof(cmLexMatcher));
|
||||||
|
cmMemPtrFree(&p->mfp);
|
||||||
|
p->mfp = np;
|
||||||
|
p->mfn += incr_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->mfp[p->mfi].tokenStr = NULL;
|
||||||
|
p->mfp[p->mfi].typeId = typeId;
|
||||||
|
p->mfp[p->mfi].funcPtr = funcPtr;
|
||||||
|
p->mfp[p->mfi].userPtr = userPtr;
|
||||||
|
|
||||||
|
if( keyStr != NULL )
|
||||||
|
{
|
||||||
|
// allocate space for the token string and store it
|
||||||
|
p->mfp[p->mfi].tokenStr = cmMemAlloc( cmChar_t, sizeof(cmChar_t) * (strlen(keyStr)+1) );
|
||||||
|
strcpy(p->mfp[p->mfi].tokenStr, keyStr );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
p->mfi++;
|
||||||
|
return kOkLexRC;
|
||||||
|
}
|
||||||
|
cmRC_t _cmLexReset( cmLex* p )
|
||||||
|
{
|
||||||
|
|
||||||
|
p->ci = 0;
|
||||||
|
|
||||||
|
p->curTokenId = kErrorLexTId;
|
||||||
|
p->curTokenCharIdx = cmInvalidIdx;
|
||||||
|
p->curTokenCharCnt = 0;
|
||||||
|
|
||||||
|
p->curLine = 0;
|
||||||
|
p->curCol = 0;
|
||||||
|
p->nextLine = 0;
|
||||||
|
p->nextCol = 0;
|
||||||
|
|
||||||
|
cmErrClearRC(&p->err);
|
||||||
|
|
||||||
|
return kOkLexRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t _cmLexSetTextBuffer( cmLex* p, const cmChar_t* cp, unsigned cn )
|
||||||
|
{
|
||||||
|
p->cp = cp;
|
||||||
|
p->cn = cn;
|
||||||
|
|
||||||
|
return _cmLexReset(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmLexH cmLexInit( const cmChar_t* cp, unsigned cn, unsigned flags, cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmLexH h;
|
||||||
|
cmChar_t dfltLineCmt[] = "//";
|
||||||
|
cmChar_t dfltBlockBegCmt[] = "/*";
|
||||||
|
cmChar_t dfltBlockEndCmt[] = "*/";
|
||||||
|
|
||||||
|
cmLex* p = cmMemAllocZ( cmLex, 1 );
|
||||||
|
|
||||||
|
cmErrSetup(&p->err,rpt,"Lexer");
|
||||||
|
|
||||||
|
p->flags = flags;
|
||||||
|
|
||||||
|
_cmLexSetTextBuffer( p, cp, cn );
|
||||||
|
|
||||||
|
/*
|
||||||
|
p->cp = (cn==0) ? NULL : cp;
|
||||||
|
p->cn = (cp==NULL) ? 0 : cn;
|
||||||
|
|
||||||
|
p->ci = 0;
|
||||||
|
|
||||||
|
|
||||||
|
p->curTokenId = kErrorLexTId;
|
||||||
|
p->curTokenCharIdx = cmInvalidIdx;
|
||||||
|
p->curTokenCharCnt = 0;
|
||||||
|
|
||||||
|
p->curLine = 0;
|
||||||
|
p->curCol = 0;
|
||||||
|
p->nextLine = 0;
|
||||||
|
p->nextCol = 0;
|
||||||
|
*/
|
||||||
|
|
||||||
|
int init_mfn = 10;
|
||||||
|
p->mfp = cmMemAllocZ( cmLexMatcher, init_mfn );
|
||||||
|
p->mfn = init_mfn;
|
||||||
|
p->mfi = 0;
|
||||||
|
|
||||||
|
|
||||||
|
p->lineCmtStr = cmMemAlloc( cmChar_t, strlen(dfltLineCmt)+1 );
|
||||||
|
strcpy( p->lineCmtStr, dfltLineCmt );
|
||||||
|
|
||||||
|
p->blockBegCmtStr = cmMemAlloc( cmChar_t, strlen(dfltBlockBegCmt)+1 );
|
||||||
|
strcpy( p->blockBegCmtStr, dfltBlockBegCmt );
|
||||||
|
|
||||||
|
p->blockEndCmtStr = cmMemAlloc( cmChar_t, strlen(dfltBlockEndCmt)+1 );
|
||||||
|
strcpy( p->blockEndCmtStr, dfltBlockEndCmt );
|
||||||
|
|
||||||
|
|
||||||
|
_cmLexInstallMatcher( p, kSpaceLexTId, _cmLexSpaceMatcher, NULL, NULL );
|
||||||
|
_cmLexInstallMatcher( p, kRealLexTId, _cmLexRealMatcher, NULL, NULL );
|
||||||
|
_cmLexInstallMatcher( p, kIntLexTId, _cmLexIntMatcher, NULL, NULL );
|
||||||
|
_cmLexInstallMatcher( p, kHexLexTId, _cmLexHexMatcher, NULL, NULL );
|
||||||
|
_cmLexInstallMatcher( p, kIdentLexTId, _cmLexIdentMatcher, NULL, NULL );
|
||||||
|
_cmLexInstallMatcher( p, kQStrLexTId, _cmLexQStrMatcher, NULL, NULL );
|
||||||
|
_cmLexInstallMatcher( p, kBlockCmtLexTId, _cmLexBlockCmtMatcher, NULL, NULL );
|
||||||
|
_cmLexInstallMatcher( p, kLineCmtLexTId, _cmLexLineCmtMatcher, NULL, NULL );
|
||||||
|
|
||||||
|
h.h = p;
|
||||||
|
|
||||||
|
_cmLexReset(p);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmLexFinal( cmLexH* hp )
|
||||||
|
{
|
||||||
|
if( hp == NULL )
|
||||||
|
return cmOkRC;
|
||||||
|
|
||||||
|
cmLex* p = _cmLexHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if( p != NULL )
|
||||||
|
{
|
||||||
|
|
||||||
|
if( p->mfp != NULL )
|
||||||
|
{
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
|
// free the user token strings
|
||||||
|
for(; i<p->mfi; ++i)
|
||||||
|
if( p->mfp[i].tokenStr != NULL )
|
||||||
|
cmMemPtrFree(&p->mfp[i].tokenStr);
|
||||||
|
|
||||||
|
// free the matcher array
|
||||||
|
cmMemPtrFree(&p->mfp);
|
||||||
|
p->mfi = 0;
|
||||||
|
p->mfn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemPtrFree(&p->lineCmtStr);
|
||||||
|
cmMemPtrFree(&p->blockBegCmtStr);
|
||||||
|
cmMemPtrFree(&p->blockEndCmtStr);
|
||||||
|
cmMemPtrFree(&p->textBuf);
|
||||||
|
|
||||||
|
// free the lexer object
|
||||||
|
cmMemPtrFree(&p);
|
||||||
|
hp->h = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkLexRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmLexReset( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
return _cmLexReset(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool cmLexIsValid( cmLexH h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
cmRC_t cmLexSetTextBuffer( cmLexH h, const cmChar_t* cp, unsigned cn )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
return _cmLexSetTextBuffer(p,cp,cn);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmLexSetFile( cmLexH h, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmRC_t rc = kOkLexRC;
|
||||||
|
cmFileH_t fh = cmFileNullHandle;
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
long n = 0;
|
||||||
|
|
||||||
|
assert( fn != NULL && p != NULL );
|
||||||
|
|
||||||
|
// open the file
|
||||||
|
if( cmFileOpen(&fh,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
|
||||||
|
return kFileOpenErrLexRC;
|
||||||
|
|
||||||
|
// seek to the end of the file
|
||||||
|
if( cmFileSeek(fh,kEndFileFl,0) != kOkFileRC )
|
||||||
|
return kFileSeekErrLexRC;
|
||||||
|
|
||||||
|
// get the length of the file
|
||||||
|
if( cmFileTell(fh,&n) != kOkFileRC )
|
||||||
|
return kFileTellErrLexRC;
|
||||||
|
|
||||||
|
// rewind to the beginning of the file
|
||||||
|
if( cmFileSeek(fh,kBeginFileFl,0) != kOkFileRC )
|
||||||
|
return kFileSeekErrLexRC;
|
||||||
|
|
||||||
|
// allocate the text buffer
|
||||||
|
if((p->textBuf = cmMemResizeZ( char, p->textBuf, n+1)) == NULL )
|
||||||
|
{
|
||||||
|
rc = _cmLexError(p,kMemAllocErrLexRC,"Unable to allocate the text file buffer for:'%s'.",fn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the file into the buffer
|
||||||
|
if( cmFileRead(fh,p->textBuf,n) != kOkFileRC )
|
||||||
|
return kFileReadErrLexRC;
|
||||||
|
|
||||||
|
if((rc = _cmLexSetTextBuffer( p, p->textBuf, n )) != kOkLexRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
// close the file
|
||||||
|
if( cmFileClose(&fh) != kOkFileRC )
|
||||||
|
return kFileCloseErrLexRC;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
cmRC_t cmLexSetFile( cmLexH h, const cmChar_t* fn )
|
||||||
|
{
|
||||||
|
cmRC_t rc = kOkLexRC;
|
||||||
|
FILE* fp = NULL;
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
unsigned n = 0;
|
||||||
|
|
||||||
|
assert( fn != NULL && p != NULL );
|
||||||
|
|
||||||
|
// open the file
|
||||||
|
if((fp = fopen(fn,"rb")) == NULL )
|
||||||
|
return _cmLexError(p,kFileOpenErrLexRC,"Unable to open the file:'%s'.",fn);
|
||||||
|
|
||||||
|
// seek to the end
|
||||||
|
if( fseek(fp,0,SEEK_END) != 0 )
|
||||||
|
{
|
||||||
|
rc= _cmLexError(p,kFileSeekErrLexRC,"Unable to seek to the end of '%s'.",fn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the length of the file
|
||||||
|
if( (n=ftell(fp)) == 0 )
|
||||||
|
{
|
||||||
|
rc = _cmLexError(p,kFileOpenErrLexRC,"The file '%s' appears to be empty.",fn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewind the file
|
||||||
|
if( fseek(fp,0,SEEK_SET) != 0 )
|
||||||
|
{
|
||||||
|
rc = _cmLexError(p,kFileSeekErrLexRC,"Unable to seek to the beginning of '%s'.",fn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate the text buffer
|
||||||
|
if((p->textBuf = cmMemResizeZ( char, p->textBuf, n+1)) == NULL )
|
||||||
|
{
|
||||||
|
rc = _cmLexError(p,kMemAllocErrLexRC,"Unable to allocate the text file buffer for:'%s'.",fn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the file into the text buffer
|
||||||
|
if( fread(p->textBuf,n,1,fp) != 1 )
|
||||||
|
{
|
||||||
|
rc = _cmLexError(p,kFileReadErrLexRC,"File read failed on:'%s'.",fn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = _cmLexSetTextBuffer( p, p->textBuf, n )) != kOkLexRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
|
||||||
|
// close the file
|
||||||
|
if( fclose(fp) != 0 )
|
||||||
|
{
|
||||||
|
rc = _cmLexError(p,kFileCloseErrLexRC,"File close failed on:'%s'.",fn);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
cmLexMatcher* _cmLexFindUserToken( cmLex* p, unsigned id, const cmChar_t* tokenStr )
|
||||||
|
{
|
||||||
|
unsigned i = 0;
|
||||||
|
for(; i<p->mfi; ++i)
|
||||||
|
{
|
||||||
|
if( id != cmInvalidId && p->mfp[i].typeId == id )
|
||||||
|
return p->mfp + i;
|
||||||
|
|
||||||
|
if( p->mfp[i].tokenStr != NULL && tokenStr != NULL && strcmp(p->mfp[i].tokenStr,tokenStr)==0 )
|
||||||
|
return p->mfp + i;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmRC_t cmLexRegisterToken( cmLexH h, unsigned id, const cmChar_t* tokenStr )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
|
||||||
|
// prevent duplicate tokens
|
||||||
|
if( _cmLexFindUserToken( p, id, tokenStr ) != NULL )
|
||||||
|
return _cmLexError( p, kDuplicateTokenLexRC, "id:%i token:%s duplicates the token string or id", id, tokenStr );
|
||||||
|
|
||||||
|
|
||||||
|
return _cmLexInstallMatcher( p, id, _cmLexExactStringMatcher, tokenStr, NULL );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRC_t cmLexRegisterMatcher( cmLexH h, unsigned id, cmLexUserMatcherPtr_t userPtr )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
|
||||||
|
// prevent duplicate tokens
|
||||||
|
if( _cmLexFindUserToken( p, id, NULL ) != NULL )
|
||||||
|
return _cmLexError( p, kDuplicateTokenLexRC, "A token matching function has already been installed for token id: %i", id );
|
||||||
|
|
||||||
|
return _cmLexInstallMatcher( p, id, NULL, NULL, userPtr );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned cmLexGetNextToken( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
|
||||||
|
if( cmErrLastRC(&p->err) != kOkLexRC )
|
||||||
|
return kErrorLexTId;
|
||||||
|
|
||||||
|
while( p->ci < p->cn )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
unsigned mi = 0;
|
||||||
|
unsigned maxCharCnt = 0;
|
||||||
|
unsigned maxIdx = cmInvalidIdx;
|
||||||
|
|
||||||
|
p->curTokenId = kErrorLexTId;
|
||||||
|
p->curTokenCharIdx = cmInvalidIdx;
|
||||||
|
p->curTokenCharCnt = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for(; mi<p->mfi; ++mi)
|
||||||
|
{
|
||||||
|
unsigned charCnt = 0;
|
||||||
|
if( p->mfp[mi].funcPtr != NULL )
|
||||||
|
charCnt = p->mfp[mi].funcPtr(p, p->cp + p->ci, p->cn - p->ci, p->mfp[mi].tokenStr );
|
||||||
|
else
|
||||||
|
charCnt = p->mfp[mi].userPtr( p->cp + p->ci, p->cn - p->ci);
|
||||||
|
|
||||||
|
if( cmErrLastRC(&p->err) != kOkLexRC )
|
||||||
|
return kErrorLexTId;
|
||||||
|
|
||||||
|
// if this matched token is longer then the prev. matched token or
|
||||||
|
// if the prev matched token was an identifier and this matched token is an equal length user defined token
|
||||||
|
if( (charCnt > maxCharCnt) || (charCnt>0 && charCnt==maxCharCnt && p->mfp[maxIdx].typeId==kIdentLexTId && p->mfp[mi].typeId >=kUserLexTId ) )
|
||||||
|
{
|
||||||
|
maxCharCnt = charCnt;
|
||||||
|
maxIdx = mi;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// no token was matched
|
||||||
|
if( maxIdx == cmInvalidIdx )
|
||||||
|
{
|
||||||
|
_cmLexError( p, kNoMatchLexRC, "Unable to recognize token:'%c'.",*(p->cp+p->ci));
|
||||||
|
return kErrorLexTId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the current line and column position
|
||||||
|
p->curLine = p->nextLine;
|
||||||
|
p->curCol = p->nextCol;
|
||||||
|
|
||||||
|
|
||||||
|
// find the next column and line position
|
||||||
|
for(i=0; i<maxCharCnt; ++i)
|
||||||
|
{
|
||||||
|
if( _cmLexIsNewline(p->cp[ p->ci + i ]) )
|
||||||
|
{
|
||||||
|
p->nextLine++;
|
||||||
|
p->nextCol = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
p->nextCol++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool returnFl = true;
|
||||||
|
|
||||||
|
// check the space token filter
|
||||||
|
if( (p->mfp[ maxIdx ].typeId == kSpaceLexTId) && (cmIsFlag(p->flags,kReturnSpaceLexFl)==0) )
|
||||||
|
returnFl = false;
|
||||||
|
|
||||||
|
// check the comment token filter
|
||||||
|
if( _cmLexIsCommentTypeId(p->mfp[ maxIdx ].typeId) && (cmIsFlag(p->flags,kReturnCommentsLexFl)==0) )
|
||||||
|
returnFl = false;
|
||||||
|
|
||||||
|
// update the lexer state
|
||||||
|
p->curTokenId = p->mfp[ maxIdx ].typeId;
|
||||||
|
p->curTokenCharIdx = p->ci;
|
||||||
|
p->curTokenCharCnt = maxCharCnt;
|
||||||
|
|
||||||
|
// advance the text buffer
|
||||||
|
p->ci += maxCharCnt;
|
||||||
|
|
||||||
|
if( returnFl )
|
||||||
|
return p->curTokenId;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmErrSetRC(&p->err,kEofRC);
|
||||||
|
|
||||||
|
return kEofLexTId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmLexTokenId( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
|
||||||
|
return p->curTokenId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmChar_t* cmLexTokenText( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
|
||||||
|
if( p->curTokenCharIdx == cmInvalidIdx )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
unsigned n = p->curTokenId == kQStrLexTId ? 1 : 0;
|
||||||
|
|
||||||
|
return p->cp + p->curTokenCharIdx + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned cmLexTokenCharCount( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
|
||||||
|
if( p->curTokenCharIdx == cmInvalidIdx )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned n = p->curTokenId == kQStrLexTId ? 2 : 0;
|
||||||
|
|
||||||
|
return p->curTokenCharCnt - n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmLexTokenInt( cmLexH h )
|
||||||
|
{ return strtol( cmLexTokenText(h),NULL,0 ); }
|
||||||
|
|
||||||
|
unsigned cmLexTokenUInt( cmLexH h )
|
||||||
|
{ return strtol( cmLexTokenText(h),NULL,0 ); }
|
||||||
|
|
||||||
|
float cmLexTokenFloat( cmLexH h )
|
||||||
|
{ return strtof( cmLexTokenText(h),NULL ); }
|
||||||
|
|
||||||
|
double cmLexTokenDouble( cmLexH h )
|
||||||
|
{ return strtod( cmLexTokenText(h),NULL ); }
|
||||||
|
|
||||||
|
unsigned cmLexCurrentLineNumber( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
return p->curLine + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmLexCurrentColumnNumber( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
return p->curCol + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmLexErrorRC( cmLexH h )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
return cmErrLastRC(&p->err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmChar_t* cmLexIdToLabel( cmLexH h, unsigned typeId )
|
||||||
|
{
|
||||||
|
cmLex* p = _cmLexHandleToPtr(h);
|
||||||
|
|
||||||
|
switch( typeId )
|
||||||
|
{
|
||||||
|
case kErrorLexTId: return "<error>";
|
||||||
|
case kEofLexTId: return "<EOF>";
|
||||||
|
case kSpaceLexTId: return "<space>";
|
||||||
|
case kRealLexTId: return "<real>";
|
||||||
|
case kIntLexTId: return "<int>";
|
||||||
|
case kHexLexTId: return "<hex>";
|
||||||
|
case kIdentLexTId: return "<ident>";
|
||||||
|
case kQStrLexTId: return "<qstr>";
|
||||||
|
case kBlockCmtLexTId: return "<bcmt>";
|
||||||
|
case kLineCmtLexTId: return "<lcmt>";
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
cmLexMatcher* mp;
|
||||||
|
if((mp = _cmLexFindUserToken(p,typeId,NULL)) == NULL )
|
||||||
|
return "<unknown>";
|
||||||
|
return mp->tokenStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "<invalid>";
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmChar_t* cmLexRcToMsg( unsigned rc )
|
||||||
|
{
|
||||||
|
unsigned i=0;
|
||||||
|
for(i=0; cmLexErrorArray[i].code != kInvalidLexRC; ++i)
|
||||||
|
if( cmLexErrorArray[i].code == rc )
|
||||||
|
break;
|
||||||
|
|
||||||
|
return cmLexErrorArray[i].msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//{ { label:cmLexEx }
|
||||||
|
//(
|
||||||
|
// cmLexTest() gives a simple cmLex example.
|
||||||
|
//)
|
||||||
|
|
||||||
|
//[
|
||||||
|
void cmLexTest( cmRpt_t* rpt)
|
||||||
|
{
|
||||||
|
cmChar_t buf[] =
|
||||||
|
"123ident0\n\
|
||||||
|
123.456\n\
|
||||||
|
ident0\n\
|
||||||
|
0xa12+.2\n\
|
||||||
|
// comment \n\
|
||||||
|
/* block \n\
|
||||||
|
comment */\
|
||||||
|
\"quoted string\"\
|
||||||
|
ident1\
|
||||||
|
// last line comment\
|
||||||
|
";
|
||||||
|
|
||||||
|
// initialize a lexer with a buffer of text
|
||||||
|
cmLexH h = cmLexInit(buf,strlen(buf),
|
||||||
|
kReturnSpaceLexFl | kReturnCommentsLexFl,rpt);
|
||||||
|
|
||||||
|
// verify that the lexer initialization succeded.
|
||||||
|
if( cmLexIsValid(h) == false )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"Lexer initialization failed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// register some additional recoginizers
|
||||||
|
cmLexRegisterToken(h,kUserLexTId+1,"+");
|
||||||
|
cmLexRegisterToken(h,kUserLexTId+2,"-");
|
||||||
|
|
||||||
|
unsigned tid;
|
||||||
|
|
||||||
|
// ask for token id's
|
||||||
|
while( (tid = cmLexGetNextToken(h)) != kEofLexTId )
|
||||||
|
{
|
||||||
|
// print information about each token
|
||||||
|
cmRptPrintf(rpt,"%i %i %s '%.*s' (%i) ",
|
||||||
|
cmLexCurrentLineNumber(h),
|
||||||
|
cmLexCurrentColumnNumber(h),
|
||||||
|
cmLexIdToLabel(h,tid),
|
||||||
|
cmLexTokenCharCount(h),
|
||||||
|
cmLexTokenText(h) ,
|
||||||
|
cmLexTokenCharCount(h));
|
||||||
|
|
||||||
|
// if the token is a number ...
|
||||||
|
if( tid==kIntLexTId || tid==kRealLexTId || tid==kHexLexTId )
|
||||||
|
{
|
||||||
|
// ... then request the numbers value
|
||||||
|
int iv = cmLexTokenInt(h);
|
||||||
|
double dv = cmLexTokenDouble(h);
|
||||||
|
|
||||||
|
cmRptPrintf(rpt,"%i %f",iv,dv);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRptPrintf(rpt,"\n");
|
||||||
|
|
||||||
|
// handle errors
|
||||||
|
if( tid == kErrorLexTId )
|
||||||
|
{
|
||||||
|
cmRptPrintf(rpt,"Error:%i\n", cmLexErrorRC(h));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalize the lexer
|
||||||
|
cmLexFinal(&h);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//]
|
||||||
|
//}
|
139
cmLex.h
Normal file
139
cmLex.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#ifndef cmLex_h
|
||||||
|
#define cmLex_h
|
||||||
|
|
||||||
|
//{
|
||||||
|
//(
|
||||||
|
//)
|
||||||
|
|
||||||
|
//(
|
||||||
|
|
||||||
|
|
||||||
|
// Predefined Lexer Id's
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kErrorLexTId, // 0 the lexer was unable to identify the current token
|
||||||
|
kEofLexTId, // 1 the lexer reached the end of input
|
||||||
|
kSpaceLexTId, // 2 white space
|
||||||
|
kRealLexTId, // 3 real number (contains a decimal point or is in scientific notation)
|
||||||
|
kIntLexTId, // 4 decimal integer
|
||||||
|
kHexLexTId, // 5 hexidecimal integer
|
||||||
|
kIdentLexTId, // 6 identifier
|
||||||
|
kQStrLexTId, // 7 quoted string
|
||||||
|
kBlockCmtLexTId, // 8 block comment
|
||||||
|
kLineCmtLexTId, // 9 line comment
|
||||||
|
kUserLexTId // 10 user registered token (See cmLexRegisterToken().)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lexer control flags used with cmLexInit().
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kReturnSpaceLexFl = 0x01, //< Return space tokens
|
||||||
|
kReturnCommentsLexFl = 0x02 //< Return comment tokens
|
||||||
|
};
|
||||||
|
|
||||||
|
// cmLex result codes.
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkLexRC = cmOkRC, //< 0 No error. The operation completed successfully
|
||||||
|
kDuplicateTokenLexRC, //< 1 The text or id passed as a user token is already in use by another token
|
||||||
|
kMissingCmtEndLexRC, //< 2 The end of a block comment could not be found.
|
||||||
|
kMissingEndQuoteLexRC, //< 3 The end of a quoted string could not be found.
|
||||||
|
kNoMatchLexRC, //< 4 The lexer encountered a string which could not be classified.
|
||||||
|
kFileOpenErrLexRC, //< 5 File open failed on cmLexSetFile()
|
||||||
|
kFileSeekErrLexRC, //< 6 File seek failed on cmLexSetFile()
|
||||||
|
kFileTellErrLexRC, //< 7 File tell failed on cmLexSetFile()
|
||||||
|
kFileReadErrLexRC, //< 8 File read failed on cmLexSetFile()
|
||||||
|
kFileCloseErrLexRC, //< 9 File close failed on cmLexSetFile()
|
||||||
|
kMemAllocErrLexRC, //< 10 An attempted memory allocation failed
|
||||||
|
kEofRC, //< 11 The end of the input text was encountered (this is a normal condition not an error)
|
||||||
|
kInvalidLexRC //< 12 Sentinal value.
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmHandle_t cmLexH;
|
||||||
|
|
||||||
|
extern cmLexH cmLexNullH;
|
||||||
|
|
||||||
|
|
||||||
|
// Iniitalize the lexer and receive a lexer handle in return.
|
||||||
|
// Set cp to NULL if the buffer will be later via cmLexSetTextBuffer();
|
||||||
|
// See the kXXXLexFl enum's above for possible flag values.
|
||||||
|
cmLexH cmLexInit( const cmChar_t* cp, unsigned cn, unsigned flags, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
// Finalize a lexer created by an earlier call to cmLexInit()
|
||||||
|
cmRC_t cmLexFinal( cmLexH* hp );
|
||||||
|
|
||||||
|
// Rewind the lexer to the begining of the buffer (the same as post initialize state)
|
||||||
|
cmRC_t cmLexReset( cmLexH h );
|
||||||
|
|
||||||
|
// Verify that a lexer handle is valid
|
||||||
|
bool cmLexIsValid( cmLexH h );
|
||||||
|
|
||||||
|
// Set a new text buffer and reset the lexer to the post initialize state.
|
||||||
|
cmRC_t cmLexSetTextBuffer( cmLexH h, const cmChar_t* cp, unsigned cn );
|
||||||
|
cmRC_t cmLexSetFile( cmLexH h, const cmChar_t* fn );
|
||||||
|
|
||||||
|
// Register a user defined token. The id of the first user defined token should be
|
||||||
|
// kUserLexTId+1. Neither the id or token text can be used by a previously registered
|
||||||
|
// or built-in token.
|
||||||
|
cmRC_t cmLexRegisterToken( cmLexH h, unsigned id, const cmChar_t* token );
|
||||||
|
|
||||||
|
// Register a user defined token recognition function. This function should return the count
|
||||||
|
// of initial, consecutive, characters in 'cp' which match its token pattern.
|
||||||
|
typedef unsigned (*cmLexUserMatcherPtr_t)( const cmChar_t* cp, unsigned cn );
|
||||||
|
|
||||||
|
cmRC_t cmLexRegisterMatcher( cmLexH h, unsigned id, cmLexUserMatcherPtr_t funcPtr );
|
||||||
|
|
||||||
|
// Return the type id of the current token and advances to the next token
|
||||||
|
unsigned cmLexGetNextToken( cmLexH h );
|
||||||
|
|
||||||
|
// Return the type id associated with the current token. This is the same value
|
||||||
|
// returned by the previous call to cmLexGetNextToken().
|
||||||
|
unsigned cmLexTokenId( cmLexH h );
|
||||||
|
|
||||||
|
// Return a pointer to the first character of text associated with the
|
||||||
|
// current token. The returned pointer directly references the text contained
|
||||||
|
// in the buffer given to the lexer in the call to cmLexInit(). The string
|
||||||
|
// is therefore not zero terminated. Use cmLexTokenCharCount() to get the
|
||||||
|
// length of the token string.
|
||||||
|
const cmChar_t* cmLexTokenText( cmLexH h );
|
||||||
|
|
||||||
|
// Return the count of characters in the text associated with the current token.
|
||||||
|
// This is the only way to get this count since the string returned by
|
||||||
|
// cmLexTokenText() is not zero terminated.
|
||||||
|
unsigned cmLexTokenCharCount( cmLexH h );
|
||||||
|
|
||||||
|
// Return the value of the current token as an integer.
|
||||||
|
int cmLexTokenInt( cmLexH h );
|
||||||
|
|
||||||
|
// Return the value of the current token as an integer.
|
||||||
|
unsigned cmLexTokenUInt( cmLexH h );
|
||||||
|
|
||||||
|
// Return the value of the current token as an integer.
|
||||||
|
float cmLexTokenFloat( cmLexH h );
|
||||||
|
|
||||||
|
// Return the value of the current token as a double.
|
||||||
|
double cmLexTokenDouble( cmLexH h );
|
||||||
|
|
||||||
|
// Return the line number associated with the current token
|
||||||
|
unsigned cmLexCurrentLineNumber( cmLexH h );
|
||||||
|
|
||||||
|
// Return the starting column of the current token
|
||||||
|
unsigned cmLexCurrentColumnNumber( cmLexH h );
|
||||||
|
|
||||||
|
// Return the RC code associated with the last error
|
||||||
|
unsigned cmLexErrorRC( cmLexH h );
|
||||||
|
|
||||||
|
// Return the label associated with a token id
|
||||||
|
const cmChar_t* cmLexIdToLabel( cmLexH h, unsigned typeId );
|
||||||
|
|
||||||
|
// Return the text message associated with a return code.
|
||||||
|
const cmChar_t* cmLexRcToMsg( unsigned rc );
|
||||||
|
|
||||||
|
// Lexer testing stub.
|
||||||
|
void cmLexTest( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endif
|
309
cmLib.c
Normal file
309
cmLib.c
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmFileSys.h"
|
||||||
|
|
||||||
|
#include "cmLib.h"
|
||||||
|
|
||||||
|
#ifdef OS_LINUX
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
typedef void* _libH_t;
|
||||||
|
|
||||||
|
bool _cmLibIsNull( _libH_t lh )
|
||||||
|
{ return lh == NULL; };
|
||||||
|
|
||||||
|
const cmChar_t* _cmLibSysError()
|
||||||
|
{
|
||||||
|
const char* msg = dlerror();
|
||||||
|
if( msg == NULL )
|
||||||
|
msg = "<none>";
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
_libH_t _cmLibOpen( const char* libFn )
|
||||||
|
{ return dlopen(libFn,RTLD_LAZY); }
|
||||||
|
|
||||||
|
bool _cmLibClose( _libH_t* lH )
|
||||||
|
{
|
||||||
|
if( *lH != NULL )
|
||||||
|
{
|
||||||
|
if( dlclose(*lH) == 0 )
|
||||||
|
*lH = NULL;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* _cmLibSym( _libH_t h, const char* symLabel )
|
||||||
|
{ return dlsym(h,symLabel); }
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct cmLibNode_str
|
||||||
|
{
|
||||||
|
cmChar_t* fn;
|
||||||
|
unsigned id;
|
||||||
|
_libH_t lH;
|
||||||
|
struct cmLibNode_str* link;
|
||||||
|
} cmLibNode_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmLibNode_t* nodes;
|
||||||
|
unsigned id;
|
||||||
|
cmFileSysH_t fsH;
|
||||||
|
} cmLib_t;
|
||||||
|
|
||||||
|
cmLib_t* _cmLibHandleToPtr( cmLibH_t h )
|
||||||
|
{
|
||||||
|
cmLib_t* p = (cmLib_t*)h.h;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmLibH_t cmLibNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
|
||||||
|
cmLibRC_t _cmLibNodeFree( cmLib_t* p, cmLibNode_t* np )
|
||||||
|
{
|
||||||
|
if( !_cmLibClose( &np->lH ) )
|
||||||
|
return cmErrMsg(&p->err,kCloseFailLibRC,"Library close failed. System Message: %s", _cmLibSysError());
|
||||||
|
|
||||||
|
// free np->fn and set np->fn to NULL - so the node may be reused.
|
||||||
|
cmMemPtrFree(&np->fn);
|
||||||
|
np->id = cmInvalidId;
|
||||||
|
|
||||||
|
return kOkLibRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cmLibRC_t _cmLibFinalize( cmLib_t* p )
|
||||||
|
{
|
||||||
|
cmLibNode_t* np = p->nodes;
|
||||||
|
|
||||||
|
while( np != NULL )
|
||||||
|
{
|
||||||
|
cmLibNode_t* pp = np->link;
|
||||||
|
_cmLibNodeFree(p,np);
|
||||||
|
cmMemFree(np);
|
||||||
|
np = pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmFileSysIsValid(p->fsH) )
|
||||||
|
cmFileSysFinalize(&p->fsH);
|
||||||
|
|
||||||
|
cmMemFree(p);
|
||||||
|
|
||||||
|
return kOkLibRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmLibRC_t cmLibInitialize( cmCtx_t* ctx, cmLibH_t* hp, const cmChar_t* dirStr )
|
||||||
|
{
|
||||||
|
cmLibRC_t rc = kOkLibRC;
|
||||||
|
cmLib_t* p = cmMemAllocZ(cmLib_t,1);
|
||||||
|
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"cmLib");
|
||||||
|
|
||||||
|
cmFileSysInitialize(&p->fsH,ctx,"cmLibFs");
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
if( dirStr != NULL )
|
||||||
|
if((rc = cmLibScan(*hp,dirStr)) != kOkLibRC )
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
if( rc != kOkLibRC )
|
||||||
|
_cmLibFinalize(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmLibRC_t cmLibFinalize( cmLibH_t* hp )
|
||||||
|
{
|
||||||
|
cmLibRC_t rc;
|
||||||
|
|
||||||
|
if( hp == NULL || hp->h == NULL )
|
||||||
|
return kOkLibRC;
|
||||||
|
|
||||||
|
cmLib_t* p = _cmLibHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if((rc = _cmLibFinalize(p)) == kOkLibRC )
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmLibIsValid( cmLibH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
|
||||||
|
unsigned cmLibOpen( cmLibH_t h, const cmChar_t* libFn )
|
||||||
|
{
|
||||||
|
cmLib_t* p = _cmLibHandleToPtr(h);
|
||||||
|
_libH_t lH = _cmLibOpen(libFn);
|
||||||
|
cmLibNode_t* np = p->nodes;
|
||||||
|
unsigned idx = 0;
|
||||||
|
|
||||||
|
if( _cmLibIsNull(lH) )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kOpenFailLibRC,"Library load failed. System Message: %s", _cmLibSysError() );
|
||||||
|
return cmInvalidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
while( np != NULL )
|
||||||
|
{
|
||||||
|
if( np->fn == NULL )
|
||||||
|
break;
|
||||||
|
|
||||||
|
np = np->link;
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( np == NULL )
|
||||||
|
{
|
||||||
|
np = cmMemAllocZ(cmLibNode_t,1);
|
||||||
|
np->link = p->nodes;
|
||||||
|
p->nodes = np;
|
||||||
|
}
|
||||||
|
|
||||||
|
np->fn = cmMemAllocStr(libFn);
|
||||||
|
np->lH = lH;
|
||||||
|
np->id = p->id++;
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmLibNode_t* _cmLibIdToNode( cmLib_t* p, unsigned libId )
|
||||||
|
{
|
||||||
|
cmLibNode_t* np = p->nodes;
|
||||||
|
|
||||||
|
while( np != NULL )
|
||||||
|
{
|
||||||
|
if( np->id == libId )
|
||||||
|
return np;
|
||||||
|
|
||||||
|
np = np->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmLibRC_t cmLibClose( cmLibH_t h, unsigned libId )
|
||||||
|
{
|
||||||
|
cmLib_t* p = _cmLibHandleToPtr(h);
|
||||||
|
cmLibNode_t* np = _cmLibIdToNode(p,libId);
|
||||||
|
|
||||||
|
if( (np == NULL) || _cmLibIsNull(np->lH) )
|
||||||
|
return cmErrMsg(&p->err,kInvalidIdLibRC,"The library id %i is not valid or the library is closed.",libId);
|
||||||
|
|
||||||
|
|
||||||
|
return kOkLibRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* cmLibSym( cmLibH_t h, unsigned libId, const cmChar_t* funcStr )
|
||||||
|
{
|
||||||
|
void* f;
|
||||||
|
cmLib_t* p = _cmLibHandleToPtr(h);
|
||||||
|
cmLibNode_t* np = _cmLibIdToNode(p,libId);
|
||||||
|
|
||||||
|
if( (np == NULL) || _cmLibIsNull(np->lH) )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kInvalidIdLibRC,"The library id %i is not valid or the library is closed.",libId);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((f = _cmLibSym(np->lH,funcStr)) == NULL)
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kSymFailLibRC,"The dynamic symbol '%s' was not found. System Message: %s", cmStringNullGuard(funcStr), _cmLibSysError());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmLibRC_t cmLibScan( cmLibH_t h, const cmChar_t* dirStr )
|
||||||
|
{
|
||||||
|
cmLib_t* p = _cmLibHandleToPtr(h);
|
||||||
|
unsigned dirEntryCnt = 0;
|
||||||
|
cmFileSysDirEntry_t* d = NULL;
|
||||||
|
cmLibRC_t rc = kOkLibRC;
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
|
if( cmFileSysIsValid(p->fsH) == false )
|
||||||
|
return cmErrMsg(&p->err,kFileSysFailLibRC,"The file system object was not successfully initialized.");
|
||||||
|
|
||||||
|
if((d = cmFileSysDirEntries(p->fsH, dirStr, kFileFsFl, &dirEntryCnt )) != NULL )
|
||||||
|
return cmErrMsg(&p->err,kFileSysFailLibRC,"The scan of directory '%s' failed.",cmStringNullGuard(dirStr));
|
||||||
|
|
||||||
|
for(i=0; i<dirEntryCnt; ++i)
|
||||||
|
cmLibOpen(h,d[i].name);
|
||||||
|
|
||||||
|
cmFileSysDirFreeEntries(p->fsH,d);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmLibCount( cmLibH_t h )
|
||||||
|
{
|
||||||
|
cmLib_t* p = _cmLibHandleToPtr(h);
|
||||||
|
cmLibNode_t* np = p->nodes;
|
||||||
|
unsigned n = 0;
|
||||||
|
|
||||||
|
while( np != NULL )
|
||||||
|
{
|
||||||
|
np = np->link;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmLibIndexToId( cmLibH_t h, unsigned idx )
|
||||||
|
{
|
||||||
|
cmLib_t* p = _cmLibHandleToPtr(h);
|
||||||
|
cmLibNode_t* np = p->nodes;
|
||||||
|
unsigned i = 0;
|
||||||
|
|
||||||
|
while( np != NULL )
|
||||||
|
{
|
||||||
|
if( i == idx )
|
||||||
|
return np->id;
|
||||||
|
|
||||||
|
np = np->link;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(np == NULL )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kInvalidIdLibRC,"The library index %i is not valid.",idx);
|
||||||
|
return cmInvalidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return np->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmChar_t* cmLibName( cmLibH_t h, unsigned libId )
|
||||||
|
{
|
||||||
|
cmLib_t* p = _cmLibHandleToPtr(h);
|
||||||
|
cmLibNode_t* np = _cmLibIdToNode(p,libId);
|
||||||
|
|
||||||
|
if( (np == NULL) || _cmLibIsNull(np->lH) )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kInvalidIdLibRC,"The library id %i is not valid or the library is closed.",libId);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return np->fn;
|
||||||
|
}
|
63
cmLib.h
Normal file
63
cmLib.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef cmLib_h
|
||||||
|
#define cmLib_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkLibRC = cmOkRC,
|
||||||
|
kOpenFailLibRC,
|
||||||
|
kCloseFailLibRC,
|
||||||
|
kSymFailLibRC,
|
||||||
|
kInvalidIdLibRC,
|
||||||
|
kFileSysFailLibRC
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef unsigned cmLibRC_t;
|
||||||
|
typedef cmHandle_t cmLibH_t;
|
||||||
|
|
||||||
|
|
||||||
|
extern cmLibH_t cmLibNullHandle;
|
||||||
|
|
||||||
|
// Initialize a dynamic library manager and scan a directory for dynamic libraries
|
||||||
|
// to load. 'dirStr' is optional.
|
||||||
|
cmLibRC_t cmLibInitialize( cmCtx_t* ctx, cmLibH_t* hp, const cmChar_t* dirStr );
|
||||||
|
|
||||||
|
// Release a dynamic library manager and close any open libraries it may own.
|
||||||
|
cmLibRC_t cmLibFinalize( cmLibH_t* hp );
|
||||||
|
|
||||||
|
// Return true if the dynamic library mgr. is initialized.
|
||||||
|
bool cmLibIsValid( cmLibH_t h );
|
||||||
|
|
||||||
|
// Open a dynamic library.
|
||||||
|
// Return cmInvalidId on error.
|
||||||
|
unsigned cmLibOpen( cmLibH_t h, const cmChar_t* libFn );
|
||||||
|
|
||||||
|
// Close a dynamic library.
|
||||||
|
cmLibRC_t cmLibClose( cmLibH_t h, unsigned libId );
|
||||||
|
|
||||||
|
// Return a pointer to a symbol from a dynamic library.
|
||||||
|
void* cmLibSym( cmLibH_t h, unsigned libId, const cmChar_t* fn );
|
||||||
|
|
||||||
|
// Scan a directory for dynamic libraries.
|
||||||
|
cmLibRC_t cmLibScan( cmLibH_t h, const cmChar_t* dirStr );
|
||||||
|
|
||||||
|
// Return the count of open libraries.
|
||||||
|
unsigned cmLibCount( cmLibH_t h );
|
||||||
|
|
||||||
|
// Return a library id given an index
|
||||||
|
unsigned cmLibIndexToId( cmLibH_t h, unsigned idx );
|
||||||
|
|
||||||
|
// Return the libraries file name.
|
||||||
|
const cmChar_t* cmLibName( cmLibH_t h, unsigned libId );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
358
cmLinkedHeap.c
Normal file
358
cmLinkedHeap.c
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmLinkedHeap.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
|
||||||
|
typedef struct cmLhBlock_str
|
||||||
|
{
|
||||||
|
char* basePtr; // base of block
|
||||||
|
char* nextPtr; // next avail location in block
|
||||||
|
char* endPtr; // one past end of block
|
||||||
|
struct cmLhBlock_str* prevBlkPtr; // backward block link
|
||||||
|
struct cmLhBlock_str* nextBlkPtr; // forward block link
|
||||||
|
unsigned freeCnt; // track orphaned space that is unavailable for reuse
|
||||||
|
} cmLhBlock_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned dfltBlockByteCnt; // size of each block in chain
|
||||||
|
cmLhBlock_t* first; // first block in chain
|
||||||
|
cmLhBlock_t* last; // last block in chain
|
||||||
|
cmMmH_t mmH;
|
||||||
|
} cmLHeap_t;
|
||||||
|
|
||||||
|
cmLHeapH_t cmLHeapNullHandle = { NULL };
|
||||||
|
|
||||||
|
cmLHeap_t* _cmLHeapHandleToPtr( cmLHeapH_t h )
|
||||||
|
{
|
||||||
|
cmLHeap_t* lhp = (cmLHeap_t*)h.h;
|
||||||
|
assert( lhp != NULL);
|
||||||
|
return lhp;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmLhBlock_t* _cmLHeapAllocBlock( cmLHeap_t* lhp, unsigned blockByteCnt )
|
||||||
|
{
|
||||||
|
// note: the entire block (control record and data space) is allocated
|
||||||
|
// as one contiguous chunk of memory.
|
||||||
|
|
||||||
|
cmLhBlock_t* lbp = (cmLhBlock_t*)cmMemMallocZ( sizeof(cmLhBlock_t) + blockByteCnt );
|
||||||
|
|
||||||
|
// setup the new block
|
||||||
|
lbp->basePtr = (char*)(lbp+1);
|
||||||
|
lbp->nextPtr = lbp->basePtr;
|
||||||
|
lbp->endPtr = lbp->basePtr + blockByteCnt;
|
||||||
|
lbp->prevBlkPtr = lhp->last;
|
||||||
|
lbp->nextBlkPtr = NULL;
|
||||||
|
|
||||||
|
// link the new block into the chain
|
||||||
|
if( lhp->last != NULL )
|
||||||
|
lhp->last->nextBlkPtr = lbp;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert( lhp->first == NULL );
|
||||||
|
lhp->first = lbp;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhp->last = lbp;
|
||||||
|
|
||||||
|
return lbp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* _cmLHeapAlloc( cmLHeap_t* lhp, unsigned dataByteCnt )
|
||||||
|
{
|
||||||
|
void* retPtr = NULL;
|
||||||
|
cmLhBlock_t* lbp = lhp->last;
|
||||||
|
unsigned allocByteCnt = dataByteCnt + sizeof(unsigned);
|
||||||
|
|
||||||
|
// go backwards down the chain looking for the first block with enough
|
||||||
|
// free space to hold the allocation (we go backward under the assumption
|
||||||
|
// that the last block is most likely to have available space)
|
||||||
|
while( (lbp != NULL) && ((lbp->endPtr - lbp->nextPtr) < allocByteCnt) )
|
||||||
|
lbp = lbp->prevBlkPtr;
|
||||||
|
|
||||||
|
// no space large enough to provide the requested memory - allocate a new block
|
||||||
|
if( lbp == NULL )
|
||||||
|
lbp = _cmLHeapAllocBlock(lhp, cmMax( lhp->dfltBlockByteCnt, allocByteCnt ));
|
||||||
|
|
||||||
|
assert( lbp != NULL );
|
||||||
|
|
||||||
|
// store the sizeof the allocation at the beginning of the allocated block
|
||||||
|
*(unsigned*)lbp->nextPtr = allocByteCnt;
|
||||||
|
|
||||||
|
// the returned block ptr begins just after the block size
|
||||||
|
retPtr = lbp->nextPtr + sizeof(unsigned);
|
||||||
|
|
||||||
|
|
||||||
|
lbp->nextPtr += allocByteCnt;
|
||||||
|
|
||||||
|
return retPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void* _cmLHeapAllocCb(void* funcArgPtr, unsigned byteCnt)
|
||||||
|
{
|
||||||
|
cmLHeap_t* p = (cmLHeap_t*)funcArgPtr;
|
||||||
|
assert( p != NULL );
|
||||||
|
return _cmLHeapAlloc(p,byteCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _cmLHeapFree( cmLHeap_t* lhp, void* dataPtr )
|
||||||
|
{
|
||||||
|
if( dataPtr == NULL )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
cmLhBlock_t* lbp = lhp->first;
|
||||||
|
|
||||||
|
// locate the block containing the area to free
|
||||||
|
while( (lbp != NULL ) && (((char*)dataPtr < lbp->basePtr) || ((char*)dataPtr >= lbp->endPtr)))
|
||||||
|
lbp = lbp->nextBlkPtr;
|
||||||
|
|
||||||
|
// the pointer must be in one of the blocks
|
||||||
|
if( lbp == NULL )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
unsigned* allocPtr = ((unsigned*)dataPtr)-1;
|
||||||
|
unsigned dataByteCnt = *allocPtr - sizeof(unsigned);
|
||||||
|
|
||||||
|
// the data to be freed is at the end of the blocks space ...
|
||||||
|
if( dataPtr == lbp->nextPtr - dataByteCnt )
|
||||||
|
lbp->nextPtr = (char*)allocPtr; // ... then make it the space to alloc
|
||||||
|
else
|
||||||
|
lbp->freeCnt += *allocPtr; // ... otherwise increase the free count
|
||||||
|
//(freeCnt tracks unused space that is not at the end of the block and therefore cannot be reused.)
|
||||||
|
|
||||||
|
// if all the space for this block has been freed then the
|
||||||
|
// next space to allocate must be at the base
|
||||||
|
if( lbp->freeCnt == lbp->endPtr - lbp->basePtr )
|
||||||
|
lbp->nextPtr = lbp->basePtr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool _cmLHeapFreeCb(void* funcArgPtr, void* ptr)
|
||||||
|
{
|
||||||
|
cmLHeap_t* lhp = (cmLHeap_t*)funcArgPtr;
|
||||||
|
return _cmLHeapFree(lhp,ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmLHeapH_t cmLHeapCreate( unsigned dfltBlockByteCnt, cmCtx_t* ctx )
|
||||||
|
{
|
||||||
|
cmLHeapH_t h;
|
||||||
|
cmLHeap_t* lhp = cmMemAllocZ( cmLHeap_t, 1 );
|
||||||
|
|
||||||
|
// We are not going to defer freeing each allocation because it will result in using
|
||||||
|
// a lot of memory. Commenting out this line however would result in
|
||||||
|
// checking all allocations for corruption during cmLHeapDestroy().
|
||||||
|
// This may be a good way to hunt down subtle memory corruption problems.
|
||||||
|
// See cmLHeapDestroy() and cmLHeapClear() for kDeferFreeMMFl related
|
||||||
|
// items.
|
||||||
|
unsigned mmFlags = cmClrFlag(ctx->mmFlags,kDeferFreeMmFl);
|
||||||
|
|
||||||
|
if( cmMmInitialize(&lhp->mmH,_cmLHeapAllocCb,_cmLHeapFreeCb,lhp,ctx->guardByteCnt,ctx->alignByteCnt,mmFlags,&ctx->rpt) != kOkMmRC )
|
||||||
|
return cmLHeapNullHandle;
|
||||||
|
|
||||||
|
lhp->dfltBlockByteCnt = dfltBlockByteCnt;
|
||||||
|
|
||||||
|
_cmLHeapAllocBlock( lhp, lhp->dfltBlockByteCnt );
|
||||||
|
|
||||||
|
h.h = lhp;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmLHeapDestroy( cmLHeapH_t* hp )
|
||||||
|
{
|
||||||
|
if( hp==NULL || hp->h == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(*hp);
|
||||||
|
|
||||||
|
// check for corruption
|
||||||
|
if( cmIsFlag(cmMmInitializeFlags(lhp->mmH),kDeferFreeMmFl))
|
||||||
|
cmMmReport(lhp->mmH, kSuppressSummaryMmFl | kIgnoreLeaksMmFl | kIgnoreNormalMmFl );
|
||||||
|
|
||||||
|
cmMmFinalize(&lhp->mmH);
|
||||||
|
|
||||||
|
cmLhBlock_t* lbp = lhp->first;
|
||||||
|
while( lbp != NULL )
|
||||||
|
{
|
||||||
|
cmLhBlock_t* t = lbp;
|
||||||
|
|
||||||
|
lbp = lbp->nextBlkPtr;
|
||||||
|
|
||||||
|
cmMemFree(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemPtrFree(&hp->h);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void* cmLHeapAllocate(cmLHeapH_t h, void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine )
|
||||||
|
{ return cmMmAllocate(_cmLHeapHandleToPtr(h)->mmH,orgDataPtr,eleCnt,eleByteCnt,flags,fileStr,funcStr,fileLine); }
|
||||||
|
|
||||||
|
cmChar_t* cmLHeapAllocStr(cmLHeapH_t h, void* orgDataPtr, const cmChar_t* str, unsigned charCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine )
|
||||||
|
{
|
||||||
|
if( str == NULL )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
unsigned n = charCnt + 1;
|
||||||
|
cmChar_t* cp = cmLHeapAllocate(h, orgDataPtr, n, sizeof(cmChar_t), flags, fileStr, funcStr, fileLine );
|
||||||
|
strncpy(cp,str,n);
|
||||||
|
cp[n-1] = 0;
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmLHeapFree( cmLHeapH_t h, void* dataPtr )
|
||||||
|
{
|
||||||
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
||||||
|
cmMmFree(lhp->mmH,dataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmLHeapFreeDebug( cmLHeapH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine )
|
||||||
|
{
|
||||||
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
||||||
|
cmMmFreeDebug(lhp->mmH,dataPtr,fileName,funcName,fileLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmLHeapFreePtr( cmLHeapH_t h, void** ptrPtr )
|
||||||
|
{
|
||||||
|
assert( ptrPtr != NULL );
|
||||||
|
cmLHeapFree(h,*ptrPtr);
|
||||||
|
*ptrPtr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmLHeapFreePtrDebug( cmLHeapH_t h, void** ptrPtr, const char* fileName, const char* funcName, unsigned fileLine )
|
||||||
|
{
|
||||||
|
assert( ptrPtr != NULL );
|
||||||
|
cmLHeapFreeDebug(h,*ptrPtr,fileName,funcName,fileLine);
|
||||||
|
*ptrPtr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned cmLHeapDefaultBlockByteCount( cmLHeapH_t h )
|
||||||
|
{
|
||||||
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
||||||
|
return p->dfltBlockByteCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmLHeapGuardByteCount( cmLHeapH_t h )
|
||||||
|
{
|
||||||
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
||||||
|
return cmMmGuardByteCount( p->mmH );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmLHeapAlignByteCount( cmLHeapH_t h )
|
||||||
|
{
|
||||||
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
||||||
|
return cmMmAlignByteCount( p->mmH );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmLHeapInitializeFlags( cmLHeapH_t h )
|
||||||
|
{
|
||||||
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
||||||
|
return cmMmInitializeFlags( p->mmH );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmLHeapIsValid( cmLHeapH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
void cmLHeapClear( cmLHeapH_t h, bool releaseFl )
|
||||||
|
{
|
||||||
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
||||||
|
|
||||||
|
// If we are deferring freeing memory until the heap is destroyed
|
||||||
|
// then we cannot clear the block list in this function.
|
||||||
|
// If the block list was cleared here then later calls to _cmLHeapFreeCb()
|
||||||
|
// would fail because the pointers to the deferred allocations
|
||||||
|
// would no longer exist in any of the blocks.
|
||||||
|
if( cmIsFlag(cmMmInitializeFlags(p->mmH),kDeferFreeMmFl))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cmLhBlock_t* bp = p->first;
|
||||||
|
while( bp != NULL )
|
||||||
|
{
|
||||||
|
bp->nextPtr = bp->basePtr;
|
||||||
|
bp->freeCnt = bp->endPtr - bp->basePtr;
|
||||||
|
|
||||||
|
cmLhBlock_t* nbp = bp->nextBlkPtr;
|
||||||
|
|
||||||
|
if( releaseFl )
|
||||||
|
cmMemFree(bp);
|
||||||
|
|
||||||
|
bp = nbp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( releaseFl )
|
||||||
|
{
|
||||||
|
p->first = NULL;
|
||||||
|
p->last = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmLHeapReportErrors( cmLHeapH_t h, unsigned mmFlags )
|
||||||
|
{
|
||||||
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
||||||
|
return cmMmReport(lhp->mmH, mmFlags );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmLHeapReport( cmLHeapH_t h )
|
||||||
|
{
|
||||||
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
||||||
|
cmLhBlock_t* lbp = lhp->first;
|
||||||
|
unsigned i;
|
||||||
|
for(i=0; lbp != NULL; ++i )
|
||||||
|
{
|
||||||
|
printf("%u : %li available %i free\n", i, lbp->endPtr-lbp->nextPtr, lbp->freeCnt );
|
||||||
|
lbp = lbp->nextBlkPtr;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmLHeapTestPrint( void* userPtr, const char* text )
|
||||||
|
{
|
||||||
|
fputs(text,stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmLHeapTest()
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int n = 5;
|
||||||
|
unsigned guardByteCnt = 8;
|
||||||
|
unsigned alignByteCnt = 16;
|
||||||
|
unsigned mmFlags = kTrackMmFl; // | kDeferFreeMmFl;
|
||||||
|
cmCtx_t ctx;
|
||||||
|
cmCtxSetup(&ctx,"Heap Test",cmLHeapTestPrint,cmLHeapTestPrint,NULL,guardByteCnt,alignByteCnt,mmFlags);
|
||||||
|
|
||||||
|
cmLHeapH_t h = cmLHeapCreate( 16, &ctx );
|
||||||
|
|
||||||
|
void* pArray[ n ];
|
||||||
|
|
||||||
|
cmLHeapReport(h);
|
||||||
|
|
||||||
|
for(i=0; i<n; ++i)
|
||||||
|
{
|
||||||
|
unsigned byteCnt = (i+1) * 2;
|
||||||
|
printf("Allocating:%li\n",byteCnt+sizeof(unsigned));
|
||||||
|
pArray[i] = cmLHeapAlloc(h,byteCnt);
|
||||||
|
|
||||||
|
cmLHeapReport(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=n-1; i>=0; i-=2)
|
||||||
|
{
|
||||||
|
printf("Freeing:%li\n",((i+1) * 2) + sizeof(unsigned));
|
||||||
|
cmLHeapFree(h,pArray[i]);
|
||||||
|
cmLHeapReport(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmLHeapReportErrors(h,0);
|
||||||
|
|
||||||
|
cmLHeapDestroy(&h);
|
||||||
|
}
|
90
cmLinkedHeap.h
Normal file
90
cmLinkedHeap.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#ifndef cmLinkedHeap_h
|
||||||
|
#define cmLinkedHeap_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//{
|
||||||
|
//(
|
||||||
|
typedef cmHandle_t cmLHeapH_t;
|
||||||
|
|
||||||
|
extern cmLHeapH_t cmLHeapNullHandle;
|
||||||
|
|
||||||
|
cmLHeapH_t cmLHeapCreate( unsigned dfltBlockByteCnt, cmCtx_t* ctx );
|
||||||
|
void cmLHeapDestroy( cmLHeapH_t* hp );
|
||||||
|
|
||||||
|
void* cmLHeapAllocate( cmLHeapH_t h, void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine );
|
||||||
|
cmChar_t* cmLHeapAllocStr( cmLHeapH_t h, void* orgDataPtr, const cmChar_t* str, unsigned charCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine );
|
||||||
|
|
||||||
|
// If ptr==NULL the function returns with no side effect.
|
||||||
|
void cmLHeapFree( cmLHeapH_t h, void* ptr );
|
||||||
|
void cmLHeapFreeDebug( cmLHeapH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine );
|
||||||
|
|
||||||
|
// If *ptr==NULL the function returns with no side effect.
|
||||||
|
void cmLHeapFreePtr( cmLHeapH_t h, void** ptrPtr );
|
||||||
|
void cmLHeapFreePtrDebug( cmLHeapH_t h, void** dataPtrPtr, const char* fileName, const char* funcName, unsigned fileLine );
|
||||||
|
|
||||||
|
bool cmLHeapIsValid( cmLHeapH_t h );
|
||||||
|
|
||||||
|
// Return the values set in call to cmLHeapCreate()
|
||||||
|
unsigned cmLHeapDefaultBlockByteCount( cmLHeapH_t h );
|
||||||
|
unsigned cmLHeapGuardByteCount( cmLHeapH_t h );
|
||||||
|
unsigned cmLHeapAlignByteCount( cmLHeapH_t h );
|
||||||
|
unsigned cmLHeapInitializeFlags( cmLHeapH_t h );
|
||||||
|
|
||||||
|
// If releaseFl==false then marks all internal memory blocks as empty but does not actually
|
||||||
|
// release the associated memory, otherwise releases all memory blocks.
|
||||||
|
void cmLHeapClear( cmLHeapH_t h, bool releaseFl );
|
||||||
|
|
||||||
|
// mmFlags take the same values as the flags parameter to cmMmReport().
|
||||||
|
cmMmRC_t cmLHeapReportErrors( cmLHeapH_t h, unsigned mmFlags );
|
||||||
|
void cmLHeapReport( cmLHeapH_t h );
|
||||||
|
void cmLHeapTest();
|
||||||
|
|
||||||
|
#if cmDEBUG_FL == 0
|
||||||
|
|
||||||
|
#define cmLHeapAlloc( h, byteCnt ) cmLHeapAllocate(h,NULL,1,byteCnt, kAlignMmFl, NULL,NULL,0)
|
||||||
|
#define cmLHeapAllocZ(h, byteCnt ) cmLHeapAllocate(h,NULL,1,byteCnt, kAlignMmFl | kZeroMmFl, NULL,NULL,0)
|
||||||
|
|
||||||
|
#define cmLhAlloc(h,t,n) ((t*)cmLHeapAllocate(h,NULL,n,sizeof(t), kAlignMmFl, NULL,NULL,0))
|
||||||
|
#define cmLhAllocZ(h,t,n) ((t*)cmLHeapAllocate(h,NULL,n,sizeof(t), kAlignMmFl | kZeroMmFl, NULL,NULL,0))
|
||||||
|
#define cmLhResizeN( h,t,p,n) ((t*)cmLHeapAllocate(h,p, n,sizeof(t), kAlignMmFl, NULL,NULL,0))
|
||||||
|
#define cmLhResizeNZ(h,t,p,n) ((t*)cmLHeapAllocate(h,p, n,sizeof(t), kAlignMmFl | kZeroMmFl, NULL,NULL,0))
|
||||||
|
|
||||||
|
#define cmLhAllocStr( h, str ) cmLHeapAllocStr( h, NULL, str, strlen(str), kAlignMmFl, NULL,NULL,0 )
|
||||||
|
#define cmLhAllocStrN( h, str, n ) cmLHeapAllocStr( h, NULL, str, n, kAlignMmFl, NULL,NULL,0 )
|
||||||
|
#define cmLhResizeStr( h, p, str ) cmLHeapAllocStr( h, p, str, strlen(str), kAlignMmFl, NULL,NULL,0 )
|
||||||
|
#define cmLhResizeStrN( h, p, str, n ) cmLHeapAllocStr( h, p, str, n, kAlignMmFl, NULL,NULL,0 )
|
||||||
|
|
||||||
|
#define cmLhFree( h, p ) cmLHeapFree( h, p )
|
||||||
|
#define cmLhFreePtr(h, p ) cmLHeapFreePtr( h, p )
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define cmLHeapAlloc( h, byteCnt ) cmLHeapAllocate(h,NULL,1,byteCnt, kAlignMmFl, __FILE__,__FUNCTION__,__LINE__)
|
||||||
|
#define cmLHeapAllocZ(h, byteCnt ) cmLHeapAllocate(h,NULL,1,byteCnt, kAlignMmFl | kZeroMmFl, __FILE__,__FUNCTION__,__LINE__)
|
||||||
|
|
||||||
|
#define cmLhAlloc(h,t,n) ((t*)cmLHeapAllocate(h,NULL,n,sizeof(t), kAlignMmFl, __FILE__,__FUNCTION__,__LINE__))
|
||||||
|
#define cmLhAllocZ(h,t,n) ((t*)cmLHeapAllocate(h,NULL,n,sizeof(t), kAlignMmFl | kZeroMmFl, __FILE__,__FUNCTION__,__LINE__))
|
||||||
|
#define cmLhResizeN( h,t,p,n) ((t*)cmLHeapAllocate(h,p, n,sizeof(t), kAlignMmFl, __FILE__,__FUNCTION__,__LINE__))
|
||||||
|
#define cmLhResizeNZ(h,t,p,n) ((t*)cmLHeapAllocate(h,p, n,sizeof(t), kAlignMmFl | kZeroMmFl, __FILE__,__FUNCTION__,__LINE__))
|
||||||
|
|
||||||
|
#define cmLhAllocStr( h, str ) cmLHeapAllocStr( h, NULL, str, strlen(str), kAlignMmFl, __FILE__,__FUNCTION__,__LINE__ )
|
||||||
|
#define cmLhAllocStrN( h, str, n ) cmLHeapAllocStr( h, NULL, str, n, kAlignMmFl, __FILE__,__FUNCTION__,__LINE__ )
|
||||||
|
#define cmLhResizeStr( h, p, str ) cmLHeapAllocStr( h, p, str, strlen(str), kAlignMmFl, __FILE__,__FUNCTION__,__LINE__ )
|
||||||
|
#define cmLhResizeStrN( h, p, str, n ) cmLHeapAllocStr( h, p, str, n, kAlignMmFl, __FILE__,__FUNCTION__,__LINE__ )
|
||||||
|
|
||||||
|
#define cmLhFree( h, p ) cmLHeapFreeDebug( h, p, __FILE__,__FUNCTION__,__LINE__ )
|
||||||
|
#define cmLhFreePtr(h, p ) cmLHeapFreePtrDebug( h, p, __FILE__,__FUNCTION__,__LINE__ )
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
33
cmMain.c
Normal file
33
cmMain.c
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmLinkedHeap.h"
|
||||||
|
|
||||||
|
void print(void* cmRptUserPtr, const char* text)
|
||||||
|
{ printf(text); }
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[] )
|
||||||
|
{
|
||||||
|
|
||||||
|
// initialize the heap check library
|
||||||
|
bool memDebugFl = true;
|
||||||
|
unsigned memPadByteCnt = memDebugFl ? 8 : 0;
|
||||||
|
unsigned memAlignByteCnt = 16;
|
||||||
|
unsigned memFlags = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;
|
||||||
|
|
||||||
|
cmRpt_t rpt;
|
||||||
|
cmRptSetup(&rpt,print,print,NULL);
|
||||||
|
|
||||||
|
//cmMdTest(&rpt);
|
||||||
|
//return 0;
|
||||||
|
|
||||||
|
cmMdInitialize( memPadByteCnt, memAlignByteCnt, memFlags, &rpt );
|
||||||
|
|
||||||
|
cmLHeapTest();
|
||||||
|
|
||||||
|
cmMdReport();
|
||||||
|
cmMdFinalize();
|
||||||
|
return 0;
|
||||||
|
}
|
136
cmMallocDebug.c
Normal file
136
cmMallocDebug.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
|
||||||
|
cmMmH_t _cmMdH = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
void* _cmMdAlloc(void* funcArgPtr, unsigned byteCnt)
|
||||||
|
{ return malloc(byteCnt); }
|
||||||
|
|
||||||
|
bool _cmMdFree(void* funcArgPtr, void* ptr)
|
||||||
|
{ free(ptr); return true; }
|
||||||
|
|
||||||
|
|
||||||
|
cmMmRC_t cmMdInitialize( unsigned guardByteCnt, unsigned alignByteCnt, unsigned flags, cmRpt_t* rptPtr )
|
||||||
|
{ return cmMmInitialize(&_cmMdH,_cmMdAlloc,_cmMdFree,NULL,guardByteCnt,alignByteCnt,flags,rptPtr); }
|
||||||
|
|
||||||
|
cmMmRC_t cmMdFinalize()
|
||||||
|
{ return cmMmFinalize(&_cmMdH); }
|
||||||
|
|
||||||
|
bool cmMdIsValid()
|
||||||
|
{ return _cmMdH.h != NULL; }
|
||||||
|
|
||||||
|
unsigned cmMdGuardByteCount() { return cmMmGuardByteCount( _cmMdH); }
|
||||||
|
unsigned cmMdAlignByteCount() { return cmMmAlignByteCount( _cmMdH); }
|
||||||
|
unsigned cmMdInitializeFlags(){ return cmMmInitializeFlags(_cmMdH); }
|
||||||
|
|
||||||
|
|
||||||
|
void* cmMdAllocate( void *orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags)
|
||||||
|
{ return cmMmAllocate(_cmMdH,orgDataPtr,eleCnt,eleByteCnt,flags,NULL,NULL,0); }
|
||||||
|
|
||||||
|
void* cmMdAllocateDebug( void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* func, const char* fn, unsigned line)
|
||||||
|
{ return cmMmAllocate(_cmMdH,orgDataPtr,eleCnt,eleByteCnt,flags,fn,func,line); }
|
||||||
|
|
||||||
|
void cmMdFree( void* p )
|
||||||
|
{ cmMmFree(_cmMdH,p); }
|
||||||
|
|
||||||
|
void cmMdFreeDebug(void* p, const char* func, const char* fn, unsigned line )
|
||||||
|
{ cmMmFreeDebug(_cmMdH,p,fn,func,line); }
|
||||||
|
|
||||||
|
void cmMdFreePtr( void* p )
|
||||||
|
{ cmMmFreePtr(_cmMdH,p); }
|
||||||
|
|
||||||
|
void cmMdFreePtrDebug(void* p, const char* func, const char* fn, unsigned line )
|
||||||
|
{ cmMmFreePtrDebug(_cmMdH,p,fn,func,line); }
|
||||||
|
|
||||||
|
cmChar_t* cmMdAllocStr( void* orgStrPtr, const cmChar_t* str, unsigned n, unsigned flags )
|
||||||
|
{
|
||||||
|
if( str==NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
//unsigned n = strlen(str)+1;
|
||||||
|
n += 1;
|
||||||
|
cmChar_t* cp = cmMdAllocate(orgStrPtr,n,sizeof(cmChar_t),flags);
|
||||||
|
strncpy(cp,str,n);
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmChar_t* cmMdAllocStrDebug( void* orgStrPtr, const cmChar_t* str, unsigned n, unsigned flags, const char* func, const char* fn, unsigned line )
|
||||||
|
{
|
||||||
|
if( str==NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
n += 1;
|
||||||
|
cmChar_t* cp = cmMdAllocateDebug((void*)orgStrPtr,n,sizeof(cmChar_t),flags,func,fn,line);
|
||||||
|
strncpy(cp,str,n);
|
||||||
|
cp[n-1] = 0;
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmMmRC_t cmMdIsGuardCorrupt( unsigned id )
|
||||||
|
{ return cmMmIsGuardCorrupt(_cmMdH,id); }
|
||||||
|
|
||||||
|
cmMmRC_t cmMdCheckAllGuards( cmRpt_t* rpt )
|
||||||
|
{ return cmMmCheckAllGuards(_cmMdH); }
|
||||||
|
|
||||||
|
unsigned cmMdDebugId( const void* dataPtr)
|
||||||
|
{ return cmMmDebugId(_cmMdH,dataPtr); }
|
||||||
|
|
||||||
|
cmMmRC_t cmMdReport( unsigned mmFlags )
|
||||||
|
{ return cmMmReport(_cmMdH,mmFlags); }
|
||||||
|
|
||||||
|
//! [cmMdExample]
|
||||||
|
void cmMdTest( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
|
||||||
|
bool memDebugFl = true;
|
||||||
|
unsigned memGuardByteCnt = memDebugFl ? 0 : 8;
|
||||||
|
unsigned memAlignByteCnt = 16;
|
||||||
|
unsigned memFlags = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl | kFillFreedMmFl : 0;
|
||||||
|
|
||||||
|
// initialize the library
|
||||||
|
cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, rpt );
|
||||||
|
|
||||||
|
// Allocate a block of 16 bytes of aligned and zeroed memory.
|
||||||
|
void* d0p = cmMdAllocateDebug(NULL, 1, 16, kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ );
|
||||||
|
|
||||||
|
// Allocate a block of 20 bytes of non-aligned, zeroed memory.
|
||||||
|
unsigned* d1p = cmMdAllocateDebug(NULL, 1, 20, /*kAlignMmFl |*/ kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ );
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// Intentionally overwrite the guard bytes by writing
|
||||||
|
// 24 bytes where only 20 where allocated
|
||||||
|
for(i=0; i<3; ++i)
|
||||||
|
d1p[i] = i;
|
||||||
|
|
||||||
|
// Print a report showing the state of each memory block.
|
||||||
|
// This should show that d1p[] has been corrupted and
|
||||||
|
// memory leaks because active blocks exist.
|
||||||
|
cmMdReport( 0 );
|
||||||
|
|
||||||
|
// Expand d1p[] preserving the existing 20 bytes.
|
||||||
|
d1p = cmMdAllocateDebug(d1p, 1, 24, kPreserveMmFl | /*kAlignMmFl |*/ kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ );
|
||||||
|
|
||||||
|
// Print the contents of the expanded d1p[]
|
||||||
|
for(i=0; i<3; ++i)
|
||||||
|
printf("%i ",d1p[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Free d0p[] and d1p[];
|
||||||
|
cmMdFreeDebug(d0p, __FUNCTION__, __FILE__, __LINE__);
|
||||||
|
cmMdFreeDebug(d1p, __FUNCTION__, __FILE__, __LINE__);
|
||||||
|
|
||||||
|
// Perform a write after free on d0p[].
|
||||||
|
*(unsigned*)d0p = 1;
|
||||||
|
|
||||||
|
// Print another report showing to test write-after-free detection.
|
||||||
|
cmMdReport( 0 );
|
||||||
|
|
||||||
|
// Close the library.
|
||||||
|
cmMdFinalize();
|
||||||
|
|
||||||
|
}
|
||||||
|
//! [cmMdExample]
|
168
cmMallocDebug.h
Normal file
168
cmMallocDebug.h
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
//{ { label:cmMd }
|
||||||
|
//(
|
||||||
|
// Implements an extended memory allocation and tracking manager.
|
||||||
|
//
|
||||||
|
// cmMallocDebug is a wrapper to cmMem.h for calls to malloc() and free().
|
||||||
|
// Most of the cmMdXXX() calls are directly associated with same named
|
||||||
|
// functions in cmMem.h. In effect this library is an implementation of the
|
||||||
|
// cmMm object where malloc() and free() are the callback functions
|
||||||
|
// provided to cmMmInitialize().
|
||||||
|
//
|
||||||
|
//)
|
||||||
|
|
||||||
|
#ifndef cmMallocDebug_h
|
||||||
|
#define cmMallocDebug_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
//(
|
||||||
|
// Initialize the malloc debug manager. guardByteCnt, alignByteeCnt, flags, and rpt
|
||||||
|
// are used to initialize an internal cmMm object. See cmMm for their semantics.
|
||||||
|
cmMmRC_t cmMdInitialize( unsigned guardByteCnt, unsigned alignByteCnt, unsigned flags, cmRpt_t* rptPtr );
|
||||||
|
|
||||||
|
// Finalize the malloc debug manager which was previously iniitalized via
|
||||||
|
// a call to cmMdInitialize().
|
||||||
|
cmMmRC_t cmMdFinalize();
|
||||||
|
|
||||||
|
// Returns true if the malloc debug manager has been initialized.
|
||||||
|
bool cmMdIsValid();
|
||||||
|
|
||||||
|
unsigned cmMdGuardByteCount(); //< Guard byte count set in cmMdInitialize().
|
||||||
|
unsigned cmMdAlignByteCount(); //< Byte alignment set in cmMdInitialize().
|
||||||
|
unsigned cmMdInitializeFlags(); //< cmMdInitialize() configuration flags.
|
||||||
|
|
||||||
|
// Allocate a block of memory in release compile mode. This function is generally called
|
||||||
|
// via one of the cmMemXXX() macros.
|
||||||
|
void* cmMdAllocate( void *orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags);
|
||||||
|
|
||||||
|
// Allocate a block of memory in debug compile mode. This function is generally called
|
||||||
|
// via one of the cmMmXXX() macros.
|
||||||
|
void* cmMdAllocateDebug( void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* func, const char* fn, unsigned line);
|
||||||
|
|
||||||
|
// Free a block of memory allocated through cmMdAllocate().
|
||||||
|
void cmMdFree( void* p );
|
||||||
|
|
||||||
|
// Free a block of memory allocated through cmMdAllocateDebug().
|
||||||
|
void cmMdFreeDebug(void* p, const char* func, const char* fn, unsigned line );
|
||||||
|
|
||||||
|
// Free a block of memory allocated through cmMdAllocateI() and set the
|
||||||
|
// pointer variable to NULL. This function combines the act of releasing memory
|
||||||
|
// and then setting the memory variable to NULL in order to indicate that the
|
||||||
|
// variable no longer points to a valid data area.
|
||||||
|
//
|
||||||
|
// The following two function calls cmMdFree/Debug(p) internally and then
|
||||||
|
// set *p=NULL. In almost all cases this is what we want to do when freeing
|
||||||
|
// allocated memory since it will prevent the pointer from being accidently
|
||||||
|
// reused.
|
||||||
|
//
|
||||||
|
// cmMdFreePtr( void* p ) would ideally be written as
|
||||||
|
// cmMdFreePtr( void** p ) because it is really taking a pointer to a pointer.
|
||||||
|
// void** however produces compiler warning because while any pointer will be
|
||||||
|
// automatically cast to void* the same is not true of void** (because void**
|
||||||
|
// is naturally more restrictive - asking for a pointer to the specific type void).
|
||||||
|
void cmMdFreePtr( void* p );
|
||||||
|
void cmMdFreePtrDebug(void* p, const char* func, const char* fn, unsigned line );
|
||||||
|
|
||||||
|
// void cmMdFreePtr( void** p )
|
||||||
|
// void cmMdFreePtrDebug( void** p, ... )
|
||||||
|
|
||||||
|
// Allocate a block of memory and then copy a string into it. This function is generally
|
||||||
|
// called by the release compile mode version of the cmMemXXXStr() macros.
|
||||||
|
cmChar_t* cmMdAllocStr( void* orgStrPtr, const cmChar_t* str, unsigned strCharCnt, unsigned flags );
|
||||||
|
|
||||||
|
// Allocate a block of memory and then copy a string into it. This function is generally
|
||||||
|
// called by the debug compile mode version of the cmMemXXXStr() macros.
|
||||||
|
cmChar_t* cmMdAllocStrDebug( void* orgStrPtr, const cmChar_t* str, unsigned strCharCnt, unsigned flags, const char* func, const char* fn, unsigned line );
|
||||||
|
|
||||||
|
// Check if the guard bytes associated with a specified memory block are corrupt.
|
||||||
|
// This call is implemented as a direct call the cmMmIsGuardCorrupt().
|
||||||
|
cmMmRC_t cmMdIsGuardCorrupt( unsigned id );
|
||||||
|
|
||||||
|
// Check if any of the currently allocated blocks contain corrupt guard bytes.
|
||||||
|
// This call is implemented as a direct call the cmMmCheckAllGuards().
|
||||||
|
cmMmRC_t cmMdCheckAllGuards();
|
||||||
|
|
||||||
|
// Return the unique id associated with a address returned from cmMdAllocateXXX().
|
||||||
|
unsigned cmMdDebugId( const void* dataPtr);
|
||||||
|
|
||||||
|
// Report the status of the memory manager and all blocks.
|
||||||
|
// This call is implemented as a direct call to cmMmReport().
|
||||||
|
cmMmRC_t cmMdReport( unsigned mmFlags );
|
||||||
|
|
||||||
|
// An example and test function for the cmMallocDebug manager.
|
||||||
|
void cmMdTest( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
#if cmDEBUG_FL == 0
|
||||||
|
|
||||||
|
// Memory Allocation and Release Macros:
|
||||||
|
// Release compile mode memory macros generally used in place of direct calls
|
||||||
|
// to cmMdAllocate() or cmMdFree().
|
||||||
|
//
|
||||||
|
|
||||||
|
#define cmMemAllocate( type, p, eleCnt, fl ) ((type*)cmMdAllocate( p, eleCnt, sizeof(type), fl ))
|
||||||
|
#define cmMemMalloc( byteCnt ) cmMdAllocate( NULL, byteCnt, 1, kAlignMmFl)
|
||||||
|
#define cmMemMallocZ( byteCnt ) cmMdAllocate( NULL, byteCnt, 1, kAlignMmFl | kZeroMmFl)
|
||||||
|
#define cmMemAlloc( type, eleCnt ) ((type*)cmMdAllocate( NULL, eleCnt, sizeof(type), kAlignMmFl))
|
||||||
|
#define cmMemAllocZ( type, eleCnt ) ((type*)cmMdAllocate( NULL, eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl))
|
||||||
|
#define cmMemAllocStr( str ) cmMdAllocStr( NULL, str, strlen(str), kAlignMmFl )
|
||||||
|
#define cmMemAllocStrN( str, charCnt ) cmMdAllocStr( NULL, str, charCnt, kAlignMmFl )
|
||||||
|
#define cmMemResizeStr( p, str ) cmMdAllocStr( p, str, strlen(str), kAlignMmFl )
|
||||||
|
#define cmMemResizeStrN(p, str, charCnt ) cmMdAllocStr( p, str, charCnt, kAlignMmFl )
|
||||||
|
#define cmMemResizeN( n, p, eleCnt ) (cmMdAllocate( p, eleCnt, n, kAlignMmFl))
|
||||||
|
#define cmMemResizeNZ( n, p, eleCnt ) (cmMdAllocate( p, eleCnt, n, kAlignMmFl | kZeroMmFl ))
|
||||||
|
#define cmMemResize( type, p, eleCnt ) ((type*)cmMdAllocate( p, eleCnt, sizeof(type), kAlignMmFl))
|
||||||
|
#define cmMemResizeZ( type, p, eleCnt ) ((type*)cmMdAllocate( p, eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl))
|
||||||
|
#define cmMemResizeP( type, p, eleCnt ) ((type*)cmMdAllocate( p, eleCnt, sizeof(type), kAlignMmFl | kPreserveMmFl))
|
||||||
|
#define cmMemResizePZ( type, p, eleCnt ) ((type*)cmMdAllocate( p, eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl | kPreserveMmFl))
|
||||||
|
#define cmMemFree( ptr ) cmMdFree( ptr )
|
||||||
|
#define cmMemPtrFree( ptrPtr ) cmMdFreePtr(ptrPtr);
|
||||||
|
|
||||||
|
|
||||||
|
#define cmIsPadCorrupt( id ) (kOkMmRC)
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Memory Allocation and Release Macros:
|
||||||
|
// These are debug compile mode memory allocation macros generally used in place of
|
||||||
|
// direct calls to cmMdAllocateDebug() or cmMdFree().
|
||||||
|
//
|
||||||
|
//
|
||||||
|
#define cmMemAllocate( type, p, eleCnt, pre, fl ) ((type*)cmMdAllocateDebug( p, eleCnt, sizeof(type), fl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemMalloc( byteCnt ) cmMdAllocateDebug( NULL, 1, byteCnt, kAlignMmFl, __FUNCTION__, __FILE__, __LINE__ )
|
||||||
|
#define cmMemMallocZ( byteCnt ) cmMdAllocateDebug( NULL, 1, byteCnt, kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ )
|
||||||
|
#define cmMemAlloc( type, eleCnt ) ((type*)cmMdAllocateDebug( NULL, eleCnt, sizeof(type), kAlignMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemAllocZ( type, eleCnt ) ((type*)cmMdAllocateDebug( NULL, eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemAllocStr( str ) (cmMdAllocStrDebug( NULL, str, strlen(str), kAlignMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemAllocStrN(str, charCnt ) (cmMdAllocStrDebug( NULL, str, charCnt, kAlignMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemResizeStr(p, str ) (cmMdAllocStrDebug( p, str, strlen(str), kAlignMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemResizeStrN(p, str, charCnt ) (cmMdAllocStrDebug( p, str, charCnt, kAlignMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemResizeN( n, p, eleCnt ) (cmMdAllocateDebug( p, eleCnt, n, kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemResizeNZ( n, p, eleCnt ) (cmMdAllocateDebug( p, eleCnt, n, kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemResize( type, p, eleCnt ) ((type*)cmMdAllocateDebug( p, eleCnt, sizeof(type), kAlignMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemResizeZ( type, p, eleCnt ) ((type*)cmMdAllocateDebug( p, eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemResizeP( type, p, eleCnt ) ((type*)cmMdAllocateDebug( p, eleCnt, sizeof(type), kAlignMmFl | kPreserveMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemResizePZ( type, p, eleCnt ) ((type*)cmMdAllocateDebug( p, eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl | kPreserveMmFl, __FUNCTION__, __FILE__, __LINE__ ))
|
||||||
|
#define cmMemFree( ptr ) cmMdFreeDebug( ptr, __FUNCTION__, __FILE__, __LINE__ )
|
||||||
|
#define cmMemPtrFree( ptrPtr ) cmMdFreePtrDebug( (void**)ptrPtr, __FUNCTION__, __FILE__, __LINE__ )
|
||||||
|
|
||||||
|
#define cmIsPadCorrupt( id ) cmMdIsPadCorrupt(id)
|
||||||
|
|
||||||
|
// Reports corrupt blocks and returns false if any corrupt blocks are found
|
||||||
|
#define cmCheckAllPads( file ) cmMdCheckAllPads(file)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endif
|
369
cmMath.c
Normal file
369
cmMath.c
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmMath.h"
|
||||||
|
#include <sys/types.h> // u_char
|
||||||
|
|
||||||
|
// TODO: rewrite to avoid copying
|
||||||
|
// this code comes via csound source ...
|
||||||
|
double cmX80ToDouble( unsigned char rate[10] )
|
||||||
|
{
|
||||||
|
char sign;
|
||||||
|
short exp = 0;
|
||||||
|
unsigned long mant1 = 0;
|
||||||
|
unsigned long mant0 = 0;
|
||||||
|
double val;
|
||||||
|
unsigned char* p = (unsigned char*)rate;
|
||||||
|
|
||||||
|
exp = *p++;
|
||||||
|
exp <<= 8;
|
||||||
|
exp |= *p++;
|
||||||
|
sign = (exp & 0x8000) ? 1 : 0;
|
||||||
|
exp &= 0x7FFF;
|
||||||
|
|
||||||
|
mant1 = *p++;
|
||||||
|
mant1 <<= 8;
|
||||||
|
mant1 |= *p++;
|
||||||
|
mant1 <<= 8;
|
||||||
|
mant1 |= *p++;
|
||||||
|
mant1 <<= 8;
|
||||||
|
mant1 |= *p++;
|
||||||
|
|
||||||
|
mant0 = *p++;
|
||||||
|
mant0 <<= 8;
|
||||||
|
mant0 |= *p++;
|
||||||
|
mant0 <<= 8;
|
||||||
|
mant0 |= *p++;
|
||||||
|
mant0 <<= 8;
|
||||||
|
mant0 |= *p++;
|
||||||
|
|
||||||
|
/* special test for all bits zero meaning zero
|
||||||
|
- else pow(2,-16383) bombs */
|
||||||
|
if (mant1 == 0 && mant0 == 0 && exp == 0 && sign == 0)
|
||||||
|
return 0.0;
|
||||||
|
else {
|
||||||
|
val = ((double)mant0) * pow(2.0,-63.0);
|
||||||
|
val += ((double)mant1) * pow(2.0,-31.0);
|
||||||
|
val *= pow(2.0,((double) exp) - 16383.0);
|
||||||
|
return sign ? -val : val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: rewrite to avoid copying
|
||||||
|
/*
|
||||||
|
* Convert double to IEEE 80 bit floating point
|
||||||
|
* Should be portable to all C compilers.
|
||||||
|
* 19aug91 aldel/dpwe covered for MSB bug in Ultrix 'cc'
|
||||||
|
*/
|
||||||
|
|
||||||
|
void cmDoubleToX80(double val, unsigned char rate[10])
|
||||||
|
{
|
||||||
|
char sign = 0;
|
||||||
|
short exp = 0;
|
||||||
|
unsigned long mant1 = 0;
|
||||||
|
unsigned long mant0 = 0;
|
||||||
|
unsigned char* p = (unsigned char*)rate;
|
||||||
|
|
||||||
|
if (val < 0.0) { sign = 1; val = -val; }
|
||||||
|
|
||||||
|
if (val != 0.0) /* val identically zero -> all elements zero */
|
||||||
|
{
|
||||||
|
exp = (short)(log(val)/log(2.0) + 16383.0);
|
||||||
|
val *= pow(2.0, 31.0+16383.0-(double)exp);
|
||||||
|
mant1 =((unsigned)val);
|
||||||
|
val -= ((double)mant1);
|
||||||
|
val *= pow(2.0, 32.0);
|
||||||
|
mant0 =((double)val);
|
||||||
|
}
|
||||||
|
|
||||||
|
*p++ = ((sign<<7)|(exp>>8));
|
||||||
|
*p++ = (u_char)(0xFF & exp);
|
||||||
|
*p++ = (u_char)(0xFF & (mant1>>24));
|
||||||
|
*p++ = (u_char)(0xFF & (mant1>>16));
|
||||||
|
*p++ = (u_char)(0xFF & (mant1>> 8));
|
||||||
|
*p++ = (u_char)(0xFF & (mant1));
|
||||||
|
*p++ = (u_char)(0xFF & (mant0>>24));
|
||||||
|
*p++ = (u_char)(0xFF & (mant0>>16));
|
||||||
|
*p++ = (u_char)(0xFF & (mant0>> 8));
|
||||||
|
*p++ = (u_char)(0xFF & (mant0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmIsPowerOfTwo( unsigned x )
|
||||||
|
{
|
||||||
|
return !( (x < 2) || (x & (x-1)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmNextPowerOfTwo( unsigned val )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
unsigned mask = 1;
|
||||||
|
unsigned msb = 0;
|
||||||
|
unsigned cnt = 0;
|
||||||
|
|
||||||
|
// if val is a power of two return it
|
||||||
|
if( cmIsPowerOfTwo(val) )
|
||||||
|
return val;
|
||||||
|
|
||||||
|
// next pow of zero is 2
|
||||||
|
if( val == 0 )
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
// if the next power of two can't be represented in 32 bits
|
||||||
|
if( val > 0x80000000)
|
||||||
|
{
|
||||||
|
assert(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find most sig. bit that is set - the number with only the next msb set is next pow 2
|
||||||
|
for(i=0; i<31; i++,mask<<=1)
|
||||||
|
if( mask & val )
|
||||||
|
{
|
||||||
|
msb = i;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 1 << (msb + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmNearPowerOfTwo( unsigned i )
|
||||||
|
{
|
||||||
|
unsigned vh = cmNextPowerOfTwo(i);
|
||||||
|
|
||||||
|
if( vh == 2 )
|
||||||
|
return vh;
|
||||||
|
|
||||||
|
unsigned vl = vh / 2;
|
||||||
|
|
||||||
|
if( vh - i < i - vl )
|
||||||
|
return vh;
|
||||||
|
return vl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmIsOddU( unsigned v ) { return v % 2 == 1; }
|
||||||
|
bool cmIsEvenU( unsigned v ) { return !cmIsOddU(v); }
|
||||||
|
unsigned cmNextOddU( unsigned v ) { return cmIsOddU(v) ? v : v+1; }
|
||||||
|
unsigned cmPrevOddU( unsigned v ) { return cmIsOddU(v) ? v : v-1; }
|
||||||
|
unsigned cmNextEvenU( unsigned v ) { return cmIsEvenU(v) ? v : v+1; }
|
||||||
|
unsigned cmPrevEvenU( unsigned v ) { return cmIsEvenU(v) ? v : v-1; }
|
||||||
|
|
||||||
|
// modified bessel function of first kind, order 0
|
||||||
|
// ref: orfandis appendix B io.m
|
||||||
|
double cmBessel0( double x )
|
||||||
|
{
|
||||||
|
double eps = pow(10.0,-9.0);
|
||||||
|
double n = 1.0;
|
||||||
|
double S = 1.0;
|
||||||
|
double D = 1.0;
|
||||||
|
|
||||||
|
while(D > eps*S)
|
||||||
|
{
|
||||||
|
double T = x /(2.0*n);
|
||||||
|
n = n+1;
|
||||||
|
D = D * pow(T,2.0);
|
||||||
|
S = S + D;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//=================================================================
|
||||||
|
// The following elliptic-related function approximations come from
|
||||||
|
// Parks & Burrus, Digital Filter Design, Appendix program 9, pp. 317-326
|
||||||
|
// which in turn draws directly on other sources
|
||||||
|
|
||||||
|
// calculate complete elliptic integral (quarter period) K
|
||||||
|
// given *complimentary* modulus kc
|
||||||
|
cmReal_t cmEllipK( cmReal_t kc )
|
||||||
|
{
|
||||||
|
cmReal_t a = 1, b = kc, c = 1, tmp;
|
||||||
|
|
||||||
|
while( c > cmReal_EPSILON )
|
||||||
|
{
|
||||||
|
c = 0.5*(a-b);
|
||||||
|
tmp = 0.5*(a+b);
|
||||||
|
b = sqrt(a*b);
|
||||||
|
a = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return M_PI/(2*a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate elliptic modulus k
|
||||||
|
// given ratio of complete elliptic integrals r = K/K'
|
||||||
|
// (solves the "degree equation" for fixed N = K*K1'/K'K1)
|
||||||
|
cmReal_t cmEllipDeg( cmReal_t r )
|
||||||
|
{
|
||||||
|
cmReal_t q,a,b,c,d;
|
||||||
|
a = b = c = 1;
|
||||||
|
d = q = exp(-M_PI*r);
|
||||||
|
|
||||||
|
while( c > cmReal_EPSILON )
|
||||||
|
{
|
||||||
|
a = a + 2*c*d;
|
||||||
|
c = c*d*d;
|
||||||
|
b = b + c;
|
||||||
|
d = d*q;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 4*sqrt(q)*pow(b/a,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate arc elliptic tangent u (elliptic integral of the 1st kind)
|
||||||
|
// given argument x = sc(u,k) and *complimentary* modulus kc
|
||||||
|
cmReal_t cmEllipArcSc( cmReal_t x, cmReal_t kc )
|
||||||
|
{
|
||||||
|
cmReal_t a = 1, b = kc, y = 1/x, tmp;
|
||||||
|
unsigned L = 0;
|
||||||
|
|
||||||
|
while( true )
|
||||||
|
{
|
||||||
|
tmp = a*b;
|
||||||
|
a += b;
|
||||||
|
b = 2*sqrt(tmp);
|
||||||
|
y -= tmp/y;
|
||||||
|
if( y == 0 )
|
||||||
|
y = sqrt(tmp) * 1E-10;
|
||||||
|
if( fabs(a-b)/a < cmReal_EPSILON )
|
||||||
|
break;
|
||||||
|
L *= 2;
|
||||||
|
if( y < 0 )
|
||||||
|
L++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( y < 0 )
|
||||||
|
L++;
|
||||||
|
|
||||||
|
return (atan(a/y) + M_PI*L)/a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate Jacobi elliptic functions sn, cn, and dn
|
||||||
|
// given argument u and *complimentary* modulus kc
|
||||||
|
cmRC_t cmEllipJ( cmReal_t u, cmReal_t kc, cmReal_t* sn, cmReal_t* cn, cmReal_t* dn )
|
||||||
|
{
|
||||||
|
assert( sn != NULL || cn != NULL || dn != NULL );
|
||||||
|
|
||||||
|
if( u == 0 )
|
||||||
|
{
|
||||||
|
if( sn != NULL ) *sn = 0;
|
||||||
|
if( cn != NULL ) *cn = 1;
|
||||||
|
if( dn != NULL ) *dn = 1;
|
||||||
|
return cmOkRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
cmReal_t a,b,c,d,e,tmp,_sn,_cn,_dn;
|
||||||
|
cmReal_t aa[16], bb[16];
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
b = kc;
|
||||||
|
|
||||||
|
for( i = 0; i < 16; i++ )
|
||||||
|
{
|
||||||
|
aa[i] = a;
|
||||||
|
bb[i] = b;
|
||||||
|
tmp = (a+b)/2;
|
||||||
|
b = sqrt(a*b);
|
||||||
|
a = tmp;
|
||||||
|
if( (a-b)/a < cmReal_EPSILON )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = a/tan(u*a);
|
||||||
|
d = 1;
|
||||||
|
|
||||||
|
for( ; i >= 0; i-- )
|
||||||
|
{
|
||||||
|
e = c*c/a;
|
||||||
|
c = c*d;
|
||||||
|
a = aa[i];
|
||||||
|
d = (e + bb[i]) / (e+a);
|
||||||
|
}
|
||||||
|
|
||||||
|
_sn = 1/sqrt(1+c*c);
|
||||||
|
_cn = _sn*c;
|
||||||
|
_dn = d;
|
||||||
|
|
||||||
|
if( sn != NULL ) *sn = _sn;
|
||||||
|
if( cn != NULL ) *cn = _cn;
|
||||||
|
if( dn != NULL ) *dn = _dn;
|
||||||
|
|
||||||
|
return cmOkRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=================================================================
|
||||||
|
// bilinear transform
|
||||||
|
// z = (2*sr + s)/(2*sr - s)
|
||||||
|
cmRC_t cmBlt( unsigned n, cmReal_t sr, cmReal_t* rp, cmReal_t* ip )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
cmReal_t a = 2*sr,
|
||||||
|
tr, ti, td;
|
||||||
|
|
||||||
|
for( i = 0; i < n; i++ )
|
||||||
|
{
|
||||||
|
tr = rp[i];
|
||||||
|
ti = ip[i];
|
||||||
|
td = pow(a-tr, 2) + ti*ti;
|
||||||
|
rp[i] = (a*a - tr*tr - ti*ti)/td;
|
||||||
|
ip[i] = 2*a*ti/td;
|
||||||
|
if( tr < -1E15 )
|
||||||
|
rp[i] = 0;
|
||||||
|
if( fabs(ti) > 1E15 )
|
||||||
|
ip[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmOkRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmHzToMidi( double hz )
|
||||||
|
{
|
||||||
|
|
||||||
|
float midi = 12.0 * log2(hz/13.75) + 9;
|
||||||
|
|
||||||
|
if( midi < 0 )
|
||||||
|
midi = 0;
|
||||||
|
if( midi > 127 )
|
||||||
|
midi = 127;
|
||||||
|
|
||||||
|
return (unsigned)lround(midi);
|
||||||
|
}
|
||||||
|
|
||||||
|
float cmMidiToHz( unsigned midi )
|
||||||
|
{
|
||||||
|
double m = midi <= 127 ? midi : 127;
|
||||||
|
|
||||||
|
return (float)( 13.75 * pow(2.0,(m - 9.0)/12.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================
|
||||||
|
// Floating point byte swapping
|
||||||
|
unsigned cmFfSwapFloatToUInt( float v )
|
||||||
|
{
|
||||||
|
assert( sizeof(float) == sizeof(unsigned));
|
||||||
|
return cmSwap32(*(unsigned*)&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
float cmFfSwapUIntToFloat( unsigned v )
|
||||||
|
{
|
||||||
|
assert( sizeof(float) == sizeof(unsigned));
|
||||||
|
v = cmSwap32(v);
|
||||||
|
return *((float*)&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long cmFfSwapDoubleToULLong( double v )
|
||||||
|
{
|
||||||
|
assert( sizeof(double) == sizeof(unsigned long long));
|
||||||
|
return cmSwap64(*(unsigned long long*)&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
double cmFfSwapULLongToDouble( unsigned long long v )
|
||||||
|
{
|
||||||
|
assert( sizeof(double) == sizeof(unsigned long long));
|
||||||
|
v = cmSwap64(v);
|
||||||
|
return *((double*)&v);
|
||||||
|
}
|
67
cmMath.h
Normal file
67
cmMath.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#ifndef cmMath_h
|
||||||
|
#define cmMath_h
|
||||||
|
|
||||||
|
double cmX80ToDouble( unsigned char s[10] );
|
||||||
|
void cmDoubleToX80( double v, unsigned char s[10] );
|
||||||
|
|
||||||
|
bool cmIsPowerOfTwo( unsigned i );
|
||||||
|
unsigned cmNextPowerOfTwo( unsigned i );
|
||||||
|
unsigned cmNearPowerOfTwo( unsigned i );
|
||||||
|
|
||||||
|
bool cmIsOddU( unsigned v );
|
||||||
|
bool cmIsEvenU( unsigned v );
|
||||||
|
unsigned cmNextOddU( unsigned v );
|
||||||
|
unsigned cmPrevOddU( unsigned v );
|
||||||
|
unsigned cmNextEvenU( unsigned v );
|
||||||
|
unsigned cmPrevEvenU( unsigned v );
|
||||||
|
|
||||||
|
|
||||||
|
// modified bessel function of first kind, order 0
|
||||||
|
// ref: orfandis appendix B io.m
|
||||||
|
double cmBessel0( double x );
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================
|
||||||
|
// The following elliptic-related function approximations come from
|
||||||
|
// Parks & Burrus, Digital Filter Design, Appendix program 9, pp. 317-326
|
||||||
|
// which in turn draws directly on other sources
|
||||||
|
|
||||||
|
// calculate complete elliptic integral (quarter period) K
|
||||||
|
// given *complimentary* modulus kc
|
||||||
|
cmReal_t cmEllipK( cmReal_t kc );
|
||||||
|
|
||||||
|
// calculate elliptic modulus k
|
||||||
|
// given ratio of complete elliptic integrals r = K/K'
|
||||||
|
// (solves the "degree equation" for fixed N = K*K1'/K'K1)
|
||||||
|
cmReal_t cmEllipDeg( cmReal_t r );
|
||||||
|
|
||||||
|
// calculate arc elliptic tangent u (elliptic integral of the 1st kind)
|
||||||
|
// given argument x = sc(u,k) and *complimentary* modulus kc
|
||||||
|
cmReal_t cmEllipArcSc( cmReal_t x, cmReal_t kc );
|
||||||
|
|
||||||
|
// calculate Jacobi elliptic functions sn, cn, and dn
|
||||||
|
// given argument u and *complimentary* modulus kc
|
||||||
|
cmRC_t cmEllipJ( cmReal_t u, cmReal_t kc, cmReal_t* sn, cmReal_t* cn, cmReal_t* dn );
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================
|
||||||
|
// bilinear transform
|
||||||
|
// z = (2*sr + s)/(2*sr - s)
|
||||||
|
cmRC_t cmBlt( unsigned n, cmReal_t sr, cmReal_t* rp, cmReal_t* ip );
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================
|
||||||
|
// Pitch conversion
|
||||||
|
unsigned cmHzToMidi( double hz );
|
||||||
|
float cmMidiToHz( unsigned midi );
|
||||||
|
|
||||||
|
//=================================================================
|
||||||
|
// Floating point byte swapping
|
||||||
|
unsigned cmFfSwapFloatToUInt( float v );
|
||||||
|
float cmFfSwapUIntToFloat( unsigned v );
|
||||||
|
unsigned long long cmFfSwapDoubleToULLong( double v );
|
||||||
|
double cmFfSwapULLongToDouble( unsigned long long v );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
699
cmMem.c
Normal file
699
cmMem.c
Normal file
@ -0,0 +1,699 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmMath.h"
|
||||||
|
|
||||||
|
// Block layout
|
||||||
|
//
|
||||||
|
// |a |b |c |d |e |f
|
||||||
|
// |xx...xxx|yyyy|zzzz|gggggggg|ddd...ddd|gggggggg|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
// xxxx = alignment (prefix) bytes - size is given by yyyy (may contain up to alignByteCnt-1 bytes).
|
||||||
|
// yyyy = count of bytes preceding yyyy
|
||||||
|
// zzzz = count of data bytes
|
||||||
|
// gggg = guard area (size is fixed by guardByteCnt arg. to cmMemInitialize())
|
||||||
|
// dddd = data area
|
||||||
|
//
|
||||||
|
// d = e - guardByteCnt
|
||||||
|
// c = d - sizeof(unsigned)
|
||||||
|
// b = c - sizeof(unsigned)
|
||||||
|
// a = b - yyyy
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
// 1) The smallest allocation is dataByteCnt + (2*sizeof(unsigned)
|
||||||
|
// 2) If allocByteCnt > 0 then the smallest allocation is allocByteCnt + (2*sizeof(unsigned)) + dataByteCnt.
|
||||||
|
//
|
||||||
|
|
||||||
|
#define _cmMmDataToByteCntPtr( dp, gbc ) (dp==NULL?NULL:(((char*)(dp))-(gbc)-sizeof(unsigned)))
|
||||||
|
#define _cmMmDataToByteCnt( dp, gbc ) (dp==NULL?0 :(*(unsigned*)_cmMmDataToByteCntPtr(dp,gbc)))
|
||||||
|
#define _cmMmDataToPrefixCntPtr( dp, gbc ) (dp==NULL?NULL:( _cmMmDataToByteCntPtr(dp,gbc) - sizeof(unsigned)))
|
||||||
|
#define _cmMmDataToPrefixCnt( dp, gbc ) (dp==NULL?0 :(*(unsigned*)_cmMmDataToPrefixCntPtr(dp,gbc)))
|
||||||
|
#define _cmMmDataToBasePtr( dp, gbc ) (dp==NULL?NULL:(_cmMmDataToPrefixCntPtr(dp,gbc) - _cmMmDataToPrefixCnt(dp,gbc)))
|
||||||
|
#define _cmMmDataToTotalByteCnt( dp, gbc ) (dp==NULL?0 :(sizeof(unsigned) + (2*gbc) + _cmMmDataToByteCnt(dp,gbc) + (_cmMmDataToPrefixCnt(dp,gbc) + sizeof(unsigned))))
|
||||||
|
#define _cmMmDataToPreGuardPtr( dp, gbc ) (dp==NULL?NULL:(((char*)(dp))-(gbc)))
|
||||||
|
#define _cmMmDataToPostGuardPtr( dp, gbc ) (dp==NULL?NULL:(((char*)(dp))+_cmMmDataToByteCnt(dp,gbc)))
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kFreedMmFl = 0x01,
|
||||||
|
kDblFreeMmFl = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct cmMmRecd_str
|
||||||
|
{
|
||||||
|
unsigned uniqueId; //
|
||||||
|
void* dataPtr; // dataPtr may be NULL if the assoc'd alloc request was for 0 bytes.
|
||||||
|
unsigned dataByteCnt; // data area bytes on original allocation
|
||||||
|
unsigned fileLine;
|
||||||
|
const char* fileNameStr;
|
||||||
|
const char* funcNameStr;
|
||||||
|
unsigned flags;
|
||||||
|
struct cmMmRecd_str* linkPtr;
|
||||||
|
} cmMmRecd_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmRpt_t* rpt;
|
||||||
|
cmErr_t err;
|
||||||
|
cmMmRecd_t* listPtr;
|
||||||
|
unsigned nextId;
|
||||||
|
unsigned alignByteCnt;
|
||||||
|
unsigned guardByteCnt;
|
||||||
|
cmAllocMmFunc_t allocFunc;
|
||||||
|
cmFreeMmFunc_t freeFunc;
|
||||||
|
void* funcArgPtr;
|
||||||
|
char uninitChar;
|
||||||
|
char freeChar;
|
||||||
|
char guardChar;
|
||||||
|
unsigned flags;
|
||||||
|
} cmMm_t;
|
||||||
|
|
||||||
|
cmMmH_t cmMmNullHandle = { NULL };
|
||||||
|
|
||||||
|
cmMm_t* _cmMmHandleToPtr( cmMmH_t h )
|
||||||
|
{
|
||||||
|
assert(h.h != NULL );
|
||||||
|
return (cmMm_t*)h.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t _cmMmCheckGuards( cmMm_t* p, cmMmRecd_t* rp )
|
||||||
|
{
|
||||||
|
// it is possible that dataPtr is NULL if zero bytes was requested at allocation
|
||||||
|
if( rp->dataPtr == NULL )
|
||||||
|
return kOkMmRC;
|
||||||
|
|
||||||
|
// check the pre data area guard space
|
||||||
|
char* pp = _cmMmDataToPreGuardPtr( rp->dataPtr, p->guardByteCnt );
|
||||||
|
char* ep = pp + p->guardByteCnt;
|
||||||
|
|
||||||
|
for(;pp<ep; ++pp)
|
||||||
|
if( *pp != p->guardChar )
|
||||||
|
return kGuardCorruptMmRC;
|
||||||
|
|
||||||
|
|
||||||
|
// check the post data area guard space
|
||||||
|
pp = _cmMmDataToPostGuardPtr( rp->dataPtr, p->guardByteCnt );
|
||||||
|
ep = pp + p->guardByteCnt;
|
||||||
|
|
||||||
|
for(;pp<ep; ++pp)
|
||||||
|
if( *pp != p->guardChar )
|
||||||
|
return kGuardCorruptMmRC;
|
||||||
|
|
||||||
|
// if this block was freed and the kFillFreedMmFl was set then check for write-after-free
|
||||||
|
if( cmIsFlag(rp->flags,kFreedMmFl) && cmIsFlag(p->flags,kFillFreedMmFl) )
|
||||||
|
{
|
||||||
|
pp = rp->dataPtr;
|
||||||
|
ep = pp + _cmMmDataToByteCnt(pp,p->guardByteCnt);
|
||||||
|
|
||||||
|
for(; pp<ep; ++pp)
|
||||||
|
if( *pp != p->freeChar )
|
||||||
|
return kWriteAfterFreeMmRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkMmRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRecd_t* _cmMmFindRecd( cmMm_t* p, const void* dataPtr )
|
||||||
|
{
|
||||||
|
cmMmRecd_t* rp = p->listPtr;
|
||||||
|
|
||||||
|
while( rp != NULL )
|
||||||
|
{
|
||||||
|
if( rp->dataPtr == dataPtr )
|
||||||
|
break;
|
||||||
|
|
||||||
|
rp = rp->linkPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t _cmMmFree( cmMm_t* p, void* dataPtr, cmMmRecd_t* rp )
|
||||||
|
{
|
||||||
|
cmMmRC_t rc = kOkMmRC;
|
||||||
|
|
||||||
|
if( p->freeFunc(p->funcArgPtr, _cmMmDataToBasePtr(dataPtr,p->guardByteCnt)) == false )
|
||||||
|
{
|
||||||
|
if( rp == NULL )
|
||||||
|
rc = cmErrMsg(&p->err,kFreeFailMmRC,"Memory free failed on data area at %p.",dataPtr);
|
||||||
|
else
|
||||||
|
rc = cmErrMsg(&p->err,kFreeFailMmRC,"Memory free failed on data area at %p. Allocated with id:%i at %s line:%i %s.",dataPtr, rp->uniqueId,rp->funcNameStr,rp->fileLine,rp->fileNameStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t _cmMmFreeP( cmMm_t* p, void* dataPtr )
|
||||||
|
{
|
||||||
|
if( dataPtr == NULL )
|
||||||
|
return kOkMmRC;
|
||||||
|
|
||||||
|
cmMmRecd_t* rp = p->listPtr;
|
||||||
|
cmMmRC_t rc = kOkMmRC;
|
||||||
|
|
||||||
|
// if we are tracking alloc's and free's
|
||||||
|
if( cmIsFlag(p->flags,kTrackMmFl) )
|
||||||
|
{
|
||||||
|
// locate the tracking recd
|
||||||
|
rp = _cmMmFindRecd(p,dataPtr);
|
||||||
|
|
||||||
|
// if no tracking recd was found
|
||||||
|
if( rp == NULL )
|
||||||
|
return cmErrMsg(&p->err,kMissingRecdMmRC,"Unable to locate a tracking record associated with released data area pointer:%p.",dataPtr);
|
||||||
|
|
||||||
|
// if this block was already freed then this is a double free
|
||||||
|
if( cmIsFlag(rp->flags,kFreedMmFl) )
|
||||||
|
rp->flags = cmSetFlag(rp->flags,kDblFreeMmFl);
|
||||||
|
else
|
||||||
|
// otherwise fill the freed block with the freeChar (if requested)
|
||||||
|
if( cmIsFlag(p->flags,kFillFreedMmFl) )
|
||||||
|
memset(dataPtr, p->freeChar, _cmMmDataToByteCnt(dataPtr,p->guardByteCnt));
|
||||||
|
|
||||||
|
// mark the block as having been freed
|
||||||
|
rp->flags = cmSetFlag(rp->flags,kFreedMmFl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are not deferring free to the finalize stage ... then free the memory here
|
||||||
|
if( cmIsFlag(p->flags,kDeferFreeMmFl) == false)
|
||||||
|
rc = _cmMmFree(p,dataPtr,rp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* _cmMmAllocate(cmMm_t* p, void* orgDataPtr, unsigned newByteCnt, unsigned flags )
|
||||||
|
{
|
||||||
|
unsigned abc = cmIsFlag(flags,kAlignMmFl) ? p->alignByteCnt : 0;
|
||||||
|
unsigned gbc = p->guardByteCnt;
|
||||||
|
char* bp = NULL;
|
||||||
|
char* dp = NULL;
|
||||||
|
char* idp = NULL;
|
||||||
|
unsigned orgByteCnt = 0;
|
||||||
|
unsigned prefixByteCnt = 0;
|
||||||
|
|
||||||
|
// Determine the total allocation block size including the auxillary data.
|
||||||
|
//
|
||||||
|
// alignment bytes + data_byte_cnt + guard bytes + actual data
|
||||||
|
unsigned ttlByteCnt = abc+sizeof(unsigned) + sizeof(unsigned) + (2*gbc) + newByteCnt;
|
||||||
|
|
||||||
|
// if this is a reallocation
|
||||||
|
if( orgDataPtr != NULL )
|
||||||
|
{
|
||||||
|
// asking to reallocate a block with zero bytes free's the original block and returns NULL
|
||||||
|
// (this is in keeping with realloc() behaviour)
|
||||||
|
if( newByteCnt == 0 )
|
||||||
|
{
|
||||||
|
_cmMmFreeP(p,orgDataPtr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the size of the currently allocated block
|
||||||
|
orgByteCnt = _cmMmDataToByteCnt(orgDataPtr,gbc);
|
||||||
|
|
||||||
|
// if the requested data area is <= the alloc'd data area
|
||||||
|
if( newByteCnt <= orgByteCnt)
|
||||||
|
{
|
||||||
|
// if preservation was requested simply return with the original data area ptr
|
||||||
|
if( cmIsFlag(flags,kPreserveMmFl) )
|
||||||
|
return orgDataPtr;
|
||||||
|
|
||||||
|
// otherwise initialze the data area
|
||||||
|
dp = orgDataPtr;
|
||||||
|
goto initLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// expansion was requested
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if an empty data area was requested
|
||||||
|
if( newByteCnt == 0 )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// A new data block must be allocated
|
||||||
|
//
|
||||||
|
|
||||||
|
// allocate the memory block via a callback
|
||||||
|
if((bp = p->allocFunc(p->funcArgPtr,ttlByteCnt)) == NULL )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kAllocFailMmRC,"Attempt to allocate %i bytes failed.",ttlByteCnt);
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make the data area offset: data_byte_cnt + guard bytes
|
||||||
|
dp = bp + (2*sizeof(unsigned)) + gbc;
|
||||||
|
|
||||||
|
if( abc )
|
||||||
|
{
|
||||||
|
unsigned long alignMask = abc - 1;
|
||||||
|
|
||||||
|
// alignment requires an additional offset
|
||||||
|
dp += abc;
|
||||||
|
|
||||||
|
// align the data area to the specified boundary
|
||||||
|
char* ndp = ((char*)((unsigned long)dp & (~alignMask)));
|
||||||
|
|
||||||
|
prefixByteCnt = abc - (dp - ndp);
|
||||||
|
|
||||||
|
dp = ndp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the prefix byteCnt
|
||||||
|
*(unsigned*)_cmMmDataToPrefixCntPtr(dp,gbc) = prefixByteCnt;
|
||||||
|
|
||||||
|
// set the data area byte cnt
|
||||||
|
*(unsigned*)_cmMmDataToByteCntPtr(dp,gbc) = newByteCnt;
|
||||||
|
|
||||||
|
// uncomment this line to print memory layout information for each block
|
||||||
|
//printf("prefix:%i prefix*:%p bytes:%i bytes*:%p base:%p==%p data:%p\n",_cmMmDataToPrefixCnt(dp,gbc),_cmMmDataToPrefixCntPtr(dp,gbc),_cmMmDataToByteCnt(dp,gbc),_cmMmDataToByteCntPtr(dp,gbc),_cmMmDataToBasePtr(dp,gbc),bp,dp);
|
||||||
|
|
||||||
|
// set the guard bytes
|
||||||
|
if( gbc )
|
||||||
|
{
|
||||||
|
void* gp = _cmMmDataToPreGuardPtr( dp,gbc);
|
||||||
|
if( gp != NULL )
|
||||||
|
memset(gp, p->guardChar, gbc);
|
||||||
|
|
||||||
|
gp = _cmMmDataToPostGuardPtr( dp,gbc);
|
||||||
|
if( gp != NULL )
|
||||||
|
memset(gp, p->guardChar, gbc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
initLabel:
|
||||||
|
|
||||||
|
//
|
||||||
|
// initialize the new data area
|
||||||
|
//
|
||||||
|
|
||||||
|
idp = dp;
|
||||||
|
|
||||||
|
// if this is a reallocation with expansion ...
|
||||||
|
if( orgDataPtr != NULL && newByteCnt > orgByteCnt )
|
||||||
|
{
|
||||||
|
// ... and preservation was requested
|
||||||
|
if( cmIsFlag(flags,kPreserveMmFl) )
|
||||||
|
{
|
||||||
|
// copy original data into the new block
|
||||||
|
memcpy(idp,orgDataPtr,orgByteCnt);
|
||||||
|
|
||||||
|
idp += orgByteCnt;
|
||||||
|
newByteCnt -= orgByteCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cmMmFreeP(p,orgDataPtr); // free the original data block
|
||||||
|
}
|
||||||
|
|
||||||
|
// if zeroing was requested
|
||||||
|
if( cmIsFlag(flags,kZeroMmFl))
|
||||||
|
memset(idp,0,newByteCnt);
|
||||||
|
else
|
||||||
|
// if uninitialized data should be set to a known character
|
||||||
|
if( cmIsFlag(p->flags,kFillUninitMmFl) )
|
||||||
|
memset(idp,p->uninitChar,newByteCnt);
|
||||||
|
|
||||||
|
|
||||||
|
return dp;
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmMmRC_t cmMmInitialize(
|
||||||
|
cmMmH_t* hp,
|
||||||
|
cmAllocMmFunc_t allocFunc,
|
||||||
|
cmFreeMmFunc_t freeFunc,
|
||||||
|
void* funcArgPtr,
|
||||||
|
unsigned guardByteCnt,
|
||||||
|
unsigned alignByteCnt,
|
||||||
|
unsigned flags,
|
||||||
|
cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmMm_t* p;
|
||||||
|
cmErr_t err;
|
||||||
|
cmErrSetup(&err,rpt,"Memory Manager");
|
||||||
|
|
||||||
|
// validate alignByteCnt
|
||||||
|
if(alignByteCnt>0 && cmIsPowerOfTwo(alignByteCnt)==false)
|
||||||
|
return cmErrMsg(&err,kParamErrMmRC,"The 'alignByteCnt' parameter must be a power of two.");
|
||||||
|
|
||||||
|
// allocate the main object
|
||||||
|
if((p= calloc(1,sizeof(cmMm_t))) == NULL )
|
||||||
|
return cmErrMsg(&err,kObjAllocFailMmRC,"Object allocation failed.");
|
||||||
|
|
||||||
|
// setting kDeferFreeMmFl only makes sense when kTrackMmFl is also set
|
||||||
|
if(cmIsFlag(flags,kTrackMmFl)==false && cmIsFlag(flags,kDeferFreeMmFl)==true)
|
||||||
|
{
|
||||||
|
cmErrMsg(&err,kParamErrMmRC,"The flag 'kDeferFreeMmFl' may only be set if the 'kTrackMmFl' is also set. 'kDeferFreeMmFl' is begin disabled.");
|
||||||
|
flags = cmClrFlag(flags,kDeferFreeMmFl);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmErrClone(&p->err,&err);
|
||||||
|
p->rpt = rpt;
|
||||||
|
p->alignByteCnt = alignByteCnt;
|
||||||
|
p->guardByteCnt = guardByteCnt;
|
||||||
|
p->allocFunc = allocFunc;
|
||||||
|
p->freeFunc = freeFunc;
|
||||||
|
p->funcArgPtr = funcArgPtr;
|
||||||
|
p->uninitChar = 0x55; // non-zeroed data areas are automatically filled w/ this value on allocation
|
||||||
|
p->freeChar = 0x33; // data areas are overwritten with this value on free
|
||||||
|
p->guardChar = 0xAA; // guard areas are set with this value
|
||||||
|
|
||||||
|
p->flags = flags;
|
||||||
|
|
||||||
|
hp->h = p;
|
||||||
|
|
||||||
|
return kOkMmRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmMmFinalize( cmMmH_t* hp )
|
||||||
|
{
|
||||||
|
cmMm_t* p;
|
||||||
|
cmMmRC_t rc = kOkMmRC;
|
||||||
|
|
||||||
|
if( hp == NULL || cmMmIsValid(*hp)==false )
|
||||||
|
return kOkMmRC;
|
||||||
|
|
||||||
|
p = _cmMmHandleToPtr(*hp);
|
||||||
|
|
||||||
|
cmMmRecd_t* rp = p->listPtr;
|
||||||
|
while( rp != NULL )
|
||||||
|
{
|
||||||
|
cmMmRecd_t* tp = rp->linkPtr;
|
||||||
|
cmMmRC_t rrc;
|
||||||
|
|
||||||
|
if( cmIsFlag(p->flags,kDeferFreeMmFl) )
|
||||||
|
if((rrc = _cmMmFree(p,rp->dataPtr,rp)) != kOkMmRC )
|
||||||
|
rc = rrc;
|
||||||
|
|
||||||
|
free(rp);
|
||||||
|
rp = tp;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(p);
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmMmGuardByteCount( cmMmH_t h )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
return p->guardByteCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmMmAlignByteCount( cmMmH_t h )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
return p->alignByteCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmMmInitializeFlags( cmMmH_t h )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
return p->flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmMmIsValid( cmMmH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
void* cmMmAllocate(
|
||||||
|
cmMmH_t h,
|
||||||
|
void* orgDataPtr,
|
||||||
|
unsigned newEleCnt,
|
||||||
|
unsigned newEleByteCnt,
|
||||||
|
enum cmMmAllocFlags_t flags,
|
||||||
|
const char* fileName,
|
||||||
|
const char* funcName,
|
||||||
|
unsigned fileLine )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
unsigned newByteCnt = newEleCnt * newEleByteCnt;
|
||||||
|
void* ndp = _cmMmAllocate(p,orgDataPtr,newByteCnt,flags);
|
||||||
|
/*
|
||||||
|
if( p->nextId == 1575 )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kOkMmRC,"Breakpoint for memory allocation id:%i.",p->nextId);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// if we are tracking changes
|
||||||
|
if( cmIsFlag(p->flags,kTrackMmFl) )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// NOTE: it is possible that ndp is NULL if newByteCnt == 0.
|
||||||
|
//
|
||||||
|
|
||||||
|
cmMmRecd_t* rp = NULL;
|
||||||
|
|
||||||
|
// if a new memory block was allocated
|
||||||
|
if( orgDataPtr == NULL || orgDataPtr != ndp )
|
||||||
|
{
|
||||||
|
// allocate a new tracking recd
|
||||||
|
if( (rp = calloc(1,sizeof(cmMmRecd_t))) == NULL )
|
||||||
|
{
|
||||||
|
cmErrMsg(&p->err,kTrkAllocFailMmRC,"Unable to allocate a tracking record for %s line:%i %s.",funcName,fileLine,fileName);
|
||||||
|
return ndp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the new tracking recd
|
||||||
|
rp->uniqueId = p->nextId;
|
||||||
|
rp->dataPtr = ndp;
|
||||||
|
rp->dataByteCnt = newByteCnt;
|
||||||
|
rp->fileLine = fileLine;
|
||||||
|
rp->fileNameStr = fileName;
|
||||||
|
rp->funcNameStr = funcName;
|
||||||
|
rp->flags = 0;
|
||||||
|
rp->linkPtr = p->listPtr;
|
||||||
|
|
||||||
|
//printf("%i %i %s %i %s\n",rp->uniqueId,newByteCnt,funcName,fileLine,fileName);
|
||||||
|
|
||||||
|
p->listPtr = rp;
|
||||||
|
|
||||||
|
assert( _cmMmCheckGuards(p,rp) == kOkMmRC );
|
||||||
|
|
||||||
|
++p->nextId;
|
||||||
|
}
|
||||||
|
else // a reallocation occurred.
|
||||||
|
if( orgDataPtr == ndp )
|
||||||
|
{
|
||||||
|
if((rp = _cmMmFindRecd(p,orgDataPtr)) == NULL )
|
||||||
|
cmErrMsg(&p->err,kMissingRecdMmRC,"Unable to locate a tracking record associated with reallocation data area pointer:%p.",orgDataPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ndp;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmMmFree( cmMmH_t h, void* dataPtr )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
|
||||||
|
return _cmMmFreeP(p,dataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmMmFreeDebug( cmMmH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine )
|
||||||
|
{
|
||||||
|
cmMmRC_t rc;
|
||||||
|
if((rc = cmMmFree(h,dataPtr)) != kOkMmRC )
|
||||||
|
cmErrMsg(&_cmMmHandleToPtr(h)->err,rc,"Memory free failed at %s() line:%i %s",funcName,fileLine,fileName);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmMmFreePtr( cmMmH_t h, void** dataPtrPtr )
|
||||||
|
{
|
||||||
|
assert(dataPtrPtr != NULL );
|
||||||
|
|
||||||
|
cmMmRC_t rc = cmMmFree(h,*dataPtrPtr);
|
||||||
|
|
||||||
|
*dataPtrPtr = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmMmFreePtrDebug( cmMmH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine )
|
||||||
|
{
|
||||||
|
cmMmRC_t rc;
|
||||||
|
if((rc = cmMmFreePtr(h,dataPtr)) != kOkMmRC )
|
||||||
|
cmErrMsg(&_cmMmHandleToPtr(h)->err,rc,"Memory free failed at %s() line:%i %s",funcName,fileLine,fileName);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmMmByteCount( cmMmH_t h, const void* dataPtr )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
return _cmMmDataToByteCnt(dataPtr,p->guardByteCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmMmDebugId( cmMmH_t h, const void* dataPtr)
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
const cmMmRecd_t* rp = NULL;
|
||||||
|
|
||||||
|
// if we are tracking alloc's and free's
|
||||||
|
if( cmIsFlag(p->flags,kTrackMmFl) )
|
||||||
|
{
|
||||||
|
// locate the tracking recd
|
||||||
|
rp = _cmMmFindRecd(p,dataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rp==NULL ? cmInvalidId : rp->uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t _cmMmError( cmMm_t* p, cmMmRC_t rc, const cmMmRecd_t* rp, const char* msg )
|
||||||
|
{
|
||||||
|
return cmErrMsg(&p->err,rc,"%s detected on block id:%i from %s() line:%i %s.",msg,rp->uniqueId,rp->funcNameStr,rp->fileLine,rp->fileNameStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t _cmMmRecdPrint( cmMm_t* p, cmMmRecd_t* rp, cmMmRC_t rc )
|
||||||
|
{
|
||||||
|
void* dp = rp->dataPtr;
|
||||||
|
unsigned gbc = p->guardByteCnt;
|
||||||
|
char* lbl = NULL;
|
||||||
|
|
||||||
|
switch( rc )
|
||||||
|
{
|
||||||
|
case kOkMmRC: lbl = "Ok "; break;
|
||||||
|
case kLeakDetectedMmRC: lbl = "Memory Leak "; break;
|
||||||
|
case kWriteAfterFreeMmRC: lbl = "Write After Free"; break;
|
||||||
|
case kDblFreeDetectedMmRC: lbl = "Double Free "; break;
|
||||||
|
case kGuardCorruptMmRC: lbl = "Guard Corrupt "; break;
|
||||||
|
default:
|
||||||
|
lbl = "Unknown Status ";
|
||||||
|
}
|
||||||
|
|
||||||
|
cmRptPrintf(p->err.rpt,"%s id:%5i data:%p : data:%5i prefix:%5i total:%5i base:%p : %5i %s %s\n",
|
||||||
|
lbl,rp->uniqueId,rp->dataPtr,
|
||||||
|
_cmMmDataToByteCnt(dp,gbc),
|
||||||
|
_cmMmDataToPrefixCnt(dp,gbc),
|
||||||
|
_cmMmDataToTotalByteCnt(dp,gbc),
|
||||||
|
_cmMmDataToBasePtr(dp,gbc),
|
||||||
|
rp->fileLine,rp->funcNameStr,rp->fileNameStr );
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmMmReport( cmMmH_t h, unsigned flags )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
unsigned allocByteCnt = 0;
|
||||||
|
unsigned ttlByteCnt = 0;
|
||||||
|
unsigned dataFrByteCnt = 0;
|
||||||
|
unsigned ttlFrByteCnt = 0;
|
||||||
|
unsigned dataLkByteCnt = 0;
|
||||||
|
unsigned ttlLkByteCnt = 0;
|
||||||
|
unsigned allocBlkCnt = 0;
|
||||||
|
unsigned freeBlkCnt = 0;
|
||||||
|
unsigned leakBlkCnt = 0;
|
||||||
|
cmMmRecd_t* rp = p->listPtr;
|
||||||
|
cmMmRC_t ret_rc = kOkMmRC;
|
||||||
|
|
||||||
|
for(; rp != NULL; rp = rp->linkPtr,++allocBlkCnt )
|
||||||
|
{
|
||||||
|
cmMmRC_t rc = kOkMmRC;
|
||||||
|
unsigned blkDataByteCnt = _cmMmDataToByteCnt(rp->dataPtr,p->guardByteCnt);
|
||||||
|
unsigned blkTtlByteCnt = _cmMmDataToTotalByteCnt(rp->dataPtr,p->guardByteCnt);
|
||||||
|
|
||||||
|
allocByteCnt += blkDataByteCnt;
|
||||||
|
ttlByteCnt += blkTtlByteCnt;
|
||||||
|
|
||||||
|
// if this block was freed or never alloc'd
|
||||||
|
if( cmIsFlag(rp->flags,kFreedMmFl) || rp->dataPtr == NULL )
|
||||||
|
{
|
||||||
|
++freeBlkCnt;
|
||||||
|
dataFrByteCnt += blkDataByteCnt;
|
||||||
|
ttlFrByteCnt += blkTtlByteCnt;
|
||||||
|
}
|
||||||
|
else // if this block leaked
|
||||||
|
{
|
||||||
|
++leakBlkCnt;
|
||||||
|
dataLkByteCnt += blkDataByteCnt;
|
||||||
|
ttlLkByteCnt += blkTtlByteCnt;
|
||||||
|
if( cmIsFlag(flags,kIgnoreLeaksMmFl) == false )
|
||||||
|
rc = _cmMmRecdPrint(p,rp,kLeakDetectedMmRC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this block was double freed
|
||||||
|
if( cmIsFlag(rp->flags,kDblFreeMmFl) )
|
||||||
|
rc = _cmMmRecdPrint(p,rp,kDblFreeDetectedMmRC);
|
||||||
|
|
||||||
|
// check for guard corruption and write-after-free
|
||||||
|
cmMmRC_t t_rc = _cmMmCheckGuards(p,rp);
|
||||||
|
|
||||||
|
switch( t_rc )
|
||||||
|
{
|
||||||
|
case kOkMmRC:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kGuardCorruptMmRC:
|
||||||
|
rc = _cmMmRecdPrint(p,rp,t_rc);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kWriteAfterFreeMmRC:
|
||||||
|
rc = _cmMmRecdPrint(p,rp,t_rc);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( rc != kOkMmRC )
|
||||||
|
ret_rc = rc;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( cmIsFlag(flags,kIgnoreNormalMmFl)==false )
|
||||||
|
_cmMmRecdPrint(p,rp,rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( p->listPtr != NULL && cmIsFlag(flags,kSuppressSummaryMmFl)==false )
|
||||||
|
{
|
||||||
|
cmRptPrintf(p->err.rpt,"Blocks Allocated:%i Freed:%i Leaked:%i\n",allocBlkCnt,freeBlkCnt,leakBlkCnt);
|
||||||
|
cmRptPrintf(p->err.rpt,"Bytes Allocated: data=%i total=%i Freed: data=%i total=%i Leaked: data=%i total=%i\n",allocByteCnt,ttlByteCnt,dataFrByteCnt,ttlFrByteCnt,dataLkByteCnt,ttlLkByteCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmMmIsGuardCorrupt( cmMmH_t h, unsigned id )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
cmMmRecd_t* rp = p->listPtr;
|
||||||
|
|
||||||
|
while(rp != NULL )
|
||||||
|
{
|
||||||
|
if( rp->uniqueId == id )
|
||||||
|
break;
|
||||||
|
rp = rp->linkPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( rp == NULL )
|
||||||
|
return cmErrMsg(&p->err,kMissingRecdMmRC,"Unable to locate the tracking record associated with id %i.",id);
|
||||||
|
|
||||||
|
return _cmMmCheckGuards(p,rp);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMmRC_t cmMmCheckAllGuards( cmMmH_t h )
|
||||||
|
{
|
||||||
|
cmMm_t* p = _cmMmHandleToPtr(h);
|
||||||
|
cmMmRecd_t* rp = p->listPtr;
|
||||||
|
cmMmRC_t rc = kOkMmRC;
|
||||||
|
|
||||||
|
while(rp != NULL )
|
||||||
|
{
|
||||||
|
if((rc = _cmMmCheckGuards(p,rp)) != kOkMmRC )
|
||||||
|
rc = cmErrMsg(&p->err,rc,"A corrupt guard or 'write after free' was detected in the data area allocated with id:%i at %s (line:%i) %s.",rp->uniqueId,rp->funcNameStr,rp->fileLine,rp->fileNameStr);
|
||||||
|
|
||||||
|
rp = rp->linkPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
231
cmMem.h
Normal file
231
cmMem.h
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
//{
|
||||||
|
//(
|
||||||
|
// The cmMem class implements a memory allocation manager interface.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Using cmMem allows memory leaks and some instances of memory corruption
|
||||||
|
// to be be detected. It can also perform memory block alignment.
|
||||||
|
//
|
||||||
|
// The cmMm class acts as an interface for implementing functions designed to replace
|
||||||
|
// malloc() and free(). cmMm does not actually allocate memory itself but rather
|
||||||
|
// tracks and conditions block of memory provided by other sources. In this sense
|
||||||
|
// it acts as a backend for a memory allocation manager.
|
||||||
|
// cmMallocDebug.h gives an example of using cmMm to interface to malloc() and free().
|
||||||
|
// cmLinkedHeap.h gives an example of using cmMm to link to an alternate heap manager.
|
||||||
|
// See cmMdTest() and cmLHeapTest() for usage examples of cmMm.
|
||||||
|
//
|
||||||
|
// cmMm works as follows:
|
||||||
|
//
|
||||||
|
// 1. A client memory manager creates and configures a cmMm object via cmMmInitialize().
|
||||||
|
// As part of the configuration the client gives callback functions which implement
|
||||||
|
// actual memory allocation and release. In practice this means the callback probably
|
||||||
|
// call malloc() or free().
|
||||||
|
// 2. At some point later when the client needs to allocate a block of memory it calls
|
||||||
|
// cmMmAllocate() with the size of the requested block. cmMm translates this request
|
||||||
|
// into a call to the client provided memory allocation callback to get a block of raw
|
||||||
|
// memory which is slightly larger than the request block.
|
||||||
|
// 3. Given the raw memory block cmMm conditions it in the following ways and returns
|
||||||
|
// it to the client.
|
||||||
|
// * The base of the blocks data area is shifted such that it is has an arbitrary
|
||||||
|
// address aligned according to the value set by the alignByteCnt parameter to cmMmInitialize().
|
||||||
|
// Address aligment is sometimes required by routines which make use of the the SIMD
|
||||||
|
// unit on some CPUs.
|
||||||
|
// * 'Guard' bytes are prepended and appended to the blocks data area.
|
||||||
|
// These bytes are set to the known fixed value (0xaa). At some point later cmMm can
|
||||||
|
// then test for accidental writes just before or just after the legal data area by
|
||||||
|
// checking the value of these guard bytes.
|
||||||
|
// * The number of bytes allocated is written just prior to the leading guard bytes.
|
||||||
|
// This allows the memory manager to track the
|
||||||
|
// size of the memory and thereby makes reallocations() to smaller or equal data areas
|
||||||
|
// very fast. This also allows the size of the data area to be known just by having a
|
||||||
|
// pointer to the data area (see cmMmByteCount()). This basic information is not availabe
|
||||||
|
// via malloc().
|
||||||
|
// * A record is added to an internal database to track the allocation code location
|
||||||
|
// (file name, file line, function name) and the allocation status (active or released).
|
||||||
|
// * The client may request that a new block of memory be automatically filled with zeros.
|
||||||
|
// If automatic zeroing is not requested then the block is filled with 0x55 to indicate that
|
||||||
|
// it is not initialized. This can be useful when attempting to recognize uninitialized
|
||||||
|
// memory during debugging.
|
||||||
|
//
|
||||||
|
// When a client requests that a block of memory is released cmMm does the following:
|
||||||
|
//
|
||||||
|
// 1. If deferred release is enabled (kDeferFreeFl) then the block is filled with 0x33
|
||||||
|
// but the callback to freeFunc() is not actually made. This allows cmMm to track attempted
|
||||||
|
// writes to freed memory areas. When deferred release is enabled the freeFunc() is not called
|
||||||
|
// on any blocks until cmMmFinalize(). If the program continually allocates memory over the
|
||||||
|
// life of the program this may mean that the program will eventually exhaust physical memory.
|
||||||
|
// 2. If tracking is enabled (kTrackMmFl) then the block pointer is looked up in the internal database.
|
||||||
|
// If the pointer is not found then a kMissingRecdRC is returned indicating an attempt to release
|
||||||
|
// a non-allocated block.
|
||||||
|
// 3. If tracking is enabled (kTrackMmFl) then the block is marked as released in the
|
||||||
|
// internal tracking database. At the end of the program all blocks should be marked for release
|
||||||
|
// otherwise they are considered leaks.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// At any time during the life of the cmMm object the client can request a report of the
|
||||||
|
// allocated blocks cmMmReport(). This report examines each allocated block for corrupt guard bytes,
|
||||||
|
// double frees (attempts to release an allocated block that was already released), and
|
||||||
|
// leaked blocks (active blocks).
|
||||||
|
//
|
||||||
|
//)
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef cmMem_h
|
||||||
|
#define cmMem_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//(
|
||||||
|
|
||||||
|
typedef cmHandle_t cmMmH_t; //< cmMm handle type.
|
||||||
|
typedef cmRC_t cmMmRC_t; //< cmMm result code types.
|
||||||
|
|
||||||
|
// cmMm result codes
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkMmRC = cmOkRC,
|
||||||
|
kObjAllocFailMmRC,
|
||||||
|
kTrkAllocFailMmRC,
|
||||||
|
kAllocFailMmRC,
|
||||||
|
kFreeFailMmRC,
|
||||||
|
kMissingRecdMmRC,
|
||||||
|
kGuardCorruptMmRC,
|
||||||
|
kWriteAfterFreeMmRC,
|
||||||
|
kLeakDetectedMmRC,
|
||||||
|
kDblFreeDetectedMmRC,
|
||||||
|
kParamErrMmRC
|
||||||
|
};
|
||||||
|
|
||||||
|
// All cmMmH_t variables should be initialized with this value prior to calling cmMmInitialize().
|
||||||
|
extern cmMmH_t cmMmNullHandle;
|
||||||
|
|
||||||
|
// Function signature for data allocation routine client provided to cmMmInitialize().
|
||||||
|
// Return NULL if byteCnt == 0.
|
||||||
|
typedef void* (*cmAllocMmFunc_t)(void* funcArgPtr, unsigned byteCnt);
|
||||||
|
|
||||||
|
// Function signature for data release routine client provided to cmMmInitialize().
|
||||||
|
// Return true on success and false on failure. Return true if ptr==NULL.
|
||||||
|
typedef bool (*cmFreeMmFunc_t)( void* funcArgPtr, void* ptr);
|
||||||
|
|
||||||
|
// Flags for use with cmMmInitialize()
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kTrackMmFl = 0x01, //< Track alloc's and free's for use by cmMmReport().
|
||||||
|
kDeferFreeMmFl = 0x02, //< Defer memory release until cmMmFinalize() (ignored unless kTrackMmFl is set.) Allows checks for 'write after release'.
|
||||||
|
kFillUninitMmFl = 0x04, //< Fill uninitialized (non-zeroed) memory with a 0x55 upon allocation
|
||||||
|
kFillFreedMmFl = 0x08 //< Fill freed memory with 0x33. This allow checks for wite-after-free.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a new cmMm object.
|
||||||
|
// If *hp was not initalized by an earlier call to cmMmInitialize() then it should
|
||||||
|
// be set to cmMmNullHandle prior to calling this function. If *hp is a valid handle
|
||||||
|
// then it is automatically finalized by an internal call to cmMmFinalize() prior to
|
||||||
|
// being re-iniitalized.
|
||||||
|
cmMmRC_t cmMmInitialize(
|
||||||
|
cmMmH_t* hp, //< Pointer to a client provided cmMmH_t handle to recieve the handle of the new object.
|
||||||
|
cmAllocMmFunc_t allocFunc, //< The memory allocation function equivalent to malloc().
|
||||||
|
cmFreeMmFunc_t freeFunc, //< The memory release function equivalent to free().
|
||||||
|
void* funcArgPtr, //< An application supplied data value sent with call backs to allocFunc() and freeFunc().
|
||||||
|
unsigned guardByteCnt, //< Count of guardBytes to precede and follow each allocated block.
|
||||||
|
unsigned alignByteCnt, //< Address alignment to provide for each allocated block.
|
||||||
|
unsigned flags, //< Configuration flags (See cmXXXMmFl).
|
||||||
|
cmRpt_t* rptPtr //< Pointer to an error reporting object.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Release a cmMm object created by an earlier call to cmMmInitialize(). Upon successful completion *hp is set to cmMmNullHandle.
|
||||||
|
cmMmRC_t cmMmFinalize( cmMmH_t* hp );
|
||||||
|
|
||||||
|
unsigned cmMmGuardByteCount( cmMmH_t h ); //< Return the count of guard bytes this cmMm object is applying.
|
||||||
|
unsigned cmMmAlignByteCount( cmMmH_t h ); //< Return the byte alignment this cmMm object is applying.
|
||||||
|
unsigned cmMmInitializeFlags( cmMmH_t h ); //< Return the configuration flags this cmMm object was initialized with.
|
||||||
|
|
||||||
|
// Return true if 'h' is a valid handle for an existing cmMm object.
|
||||||
|
bool cmMmIsValid( cmMmH_t h );
|
||||||
|
|
||||||
|
// flags for use with cmMmAllocate()
|
||||||
|
enum cmMmAllocFlags_t
|
||||||
|
{
|
||||||
|
kZeroMmFl = 0x01, //< Initialize new memory area to zero.
|
||||||
|
kAlignMmFl = 0x02, //< Align the returned memory according to the alignByteCnt set in cmMmInitialize().
|
||||||
|
kPreserveMmFl = 0x04 //< Preserve existing memory contents during reallocation (orgDataPtr!=NULL).
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate a block of memory.
|
||||||
|
// Calling this function results in a call to the function named in allocFunc() in cmMmInitialize().
|
||||||
|
void* cmMmAllocate(
|
||||||
|
cmMmH_t h, //< Handle for this cmMm object returned from an earlier successful call to cmMmInitialize().
|
||||||
|
void* orgDataPtr, //< If this is a re-allocation then this pointer should point to the original allocation otherwise it should be NULL.
|
||||||
|
unsigned newEleCnt, //< Count of elmements in this allocation.
|
||||||
|
unsigned newEleByteCnt, //< Bytes per element in this allocation. The total memory request is newEleCnt*newEleByteCnt.
|
||||||
|
enum cmMmAllocFlags_t flags, //< See cmMmAllocFlags_t.
|
||||||
|
const char* fileName, //< Name of the C file from which the allocation request is being made.
|
||||||
|
const char* funcName, //< Name of the C function from which the allocation request is being made.
|
||||||
|
unsigned fileLine //< Line in the C file on which the allocation request is being made.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Free memory pointed to by dataPtr.
|
||||||
|
// If dataPtr==NULL then the functon does nothing and returns.
|
||||||
|
// Calling this function results in a call to the function named in freeFunc() in cmMmInitialize().
|
||||||
|
// This is the release mode memory free routine. See cmMmFreeDebug() for the debug mode memory release routine.
|
||||||
|
// See \ref debug_mode for more about debug vs. release mode.
|
||||||
|
cmMmRC_t cmMmFree( cmMmH_t h, void* dataPtr );
|
||||||
|
|
||||||
|
// Debug mode version of cmMmFree(). See cmMmFree() for the release mode memory free routine.
|
||||||
|
// See debug_mode for more about debug vs. release mode.
|
||||||
|
// This routine is functionally identical to the cmMmFree() but takes the calling
|
||||||
|
// location information for use in tracking the block of memory.
|
||||||
|
cmMmRC_t cmMmFreeDebug( cmMmH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine );
|
||||||
|
|
||||||
|
// This function is identical to cmMmFree() but takes the address of the pointer
|
||||||
|
// to the block of memory to free. Upon successful completion *dataPtrPtr is
|
||||||
|
// set to NULL. In general this should be the preferred version of the free routine
|
||||||
|
// because it helps to eliminate problems of reusing deallocated memory blocks.
|
||||||
|
// Note that although dataPtrPtr must point to a valid address *dataPtrPtr may be NULL.
|
||||||
|
// This routine is generally only used in the release compile mode.
|
||||||
|
// See cmMmFreePtrDebug() for the debug mode version. See \ref debug_mode for more
|
||||||
|
// about compile vs. release mode.
|
||||||
|
cmMmRC_t cmMmFreePtr( cmMmH_t h, void** dataPtrPtr );
|
||||||
|
|
||||||
|
// Debug compile mode version of cmMmFreePtr().
|
||||||
|
// This function is functionally identical to cmMmFreePtr() but accepts information
|
||||||
|
// on the location of the call to aid in debuging.
|
||||||
|
cmMmRC_t cmMmFreePtrDebug( cmMmH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine );
|
||||||
|
|
||||||
|
// Return the size of a memory block returned from cmMmAllocate().
|
||||||
|
unsigned cmMmByteCount( cmMmH_t h, const void* dataPtr );
|
||||||
|
|
||||||
|
// Return the unique id associated with an address returned from cmMmAllocate().
|
||||||
|
unsigned cmMmDebugId( cmMmH_t h, const void* dataPtr);
|
||||||
|
|
||||||
|
// Flags for use with cmMmReport().
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kSuppressSummaryMmFl = 0x01, //< Do not print a memory use summary report.
|
||||||
|
kIgnoreNormalMmFl = 0x02, //< Do not print information for non-leaked,non-corrupt memory blocks.
|
||||||
|
kIgnoreLeaksMmFl = 0x04 //< Do not print information for leaked blocks.
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Report on the memory tracking data.
|
||||||
|
// Returns kMmOkRC if no errors were found otherwise returns the error of the
|
||||||
|
// last anomoly reported.
|
||||||
|
cmMmRC_t cmMmReport( cmMmH_t h, unsigned flags );
|
||||||
|
|
||||||
|
// Analyze the memory assoc'd with a specific tracking record for corruption.
|
||||||
|
// Returns: kOkMmRC,kGuardCorruptMmRC,kWriteAfterFreeMmRc, or kMissingRecdMmRC.
|
||||||
|
// This function is only useful if kTrackMmFl was set in cmMmInitialize().
|
||||||
|
// Write-after-free errors are only detectable if kDeferFreeMmFl was set in cmMmInitialize().
|
||||||
|
cmMmRC_t cmMmIsGuardCorrupt( cmMmH_t h, unsigned id );
|
||||||
|
|
||||||
|
// Check all tracking records by calling cmMmmIsGuardCorrupt() on each record.
|
||||||
|
cmMmRC_t cmMmCheckAllGuards( cmMmH_t h );
|
||||||
|
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
227
cmMidi.c
Normal file
227
cmMidi.c
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmMidi.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
mdStatusDescLabelCharCnt = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmMidiByte_t status;
|
||||||
|
cmMidiByte_t byteCnt;
|
||||||
|
char label[ mdStatusDescLabelCharCnt+1 ];
|
||||||
|
} cmMidiStatusDesc;
|
||||||
|
|
||||||
|
cmMidiStatusDesc _cmMidiStatusDescArray[] =
|
||||||
|
{
|
||||||
|
// channel messages
|
||||||
|
{ kNoteOffMdId, 2, "nof" },
|
||||||
|
{ kNoteOnMdId, 2, "non" },
|
||||||
|
{ kPolyPresMdId,2, "ppr" },
|
||||||
|
{ kCtlMdId, 2, "ctl" },
|
||||||
|
{ kPgmMdId, 1, "pgm" },
|
||||||
|
{ kChPresMdId, 1, "cpr" },
|
||||||
|
{ kPbendMdId, 2, "pb" },
|
||||||
|
|
||||||
|
|
||||||
|
{ kSysExMdId, kInvalidMidiByte,"sex" },
|
||||||
|
|
||||||
|
// system common
|
||||||
|
{ kSysComMtcMdId, 1, "mtc" },
|
||||||
|
{ kSysComSppMdId, 2, "spp" },
|
||||||
|
{ kSysComSelMdId, 1, "sel" },
|
||||||
|
{ kSysComUndef0MdId, 0, "cu0" },
|
||||||
|
{ kSysComUndef1MdId, 0, "cu1" },
|
||||||
|
{ kSysComTuneMdId, 0, "tun" },
|
||||||
|
{ kSysComEoxMdId, 0, "eox" },
|
||||||
|
|
||||||
|
// system real-time
|
||||||
|
{ kSysRtClockMdId, 0, "clk" },
|
||||||
|
{ kSysRtUndef0MdId,0, "ud0" },
|
||||||
|
{ kSysRtStartMdId, 0, "beg" },
|
||||||
|
{ kSysRtContMdId, 0, "cnt" },
|
||||||
|
{ kSysRtStopMdId, 0, "end" },
|
||||||
|
{ kSysRtUndef1MdId,0, "ud1" },
|
||||||
|
{ kSysRtSenseMdId, 0, "sns" },
|
||||||
|
{ kSysRtResetMdId, 0, "rst" },
|
||||||
|
|
||||||
|
{ kInvalidStatusMdId, kInvalidMidiByte, "ERR" }
|
||||||
|
};
|
||||||
|
|
||||||
|
cmMidiStatusDesc _cmMidiMetaStatusDescArray[] =
|
||||||
|
{
|
||||||
|
{ kSeqNumbMdId, 2, "seqn" },
|
||||||
|
{ kTextMdId, -1, "text" },
|
||||||
|
{ kCopyMdId, -1, "copy" },
|
||||||
|
{ kTrkNameMdId, -1, "name" },
|
||||||
|
{ kInstrNameMdId, -1, "instr" },
|
||||||
|
{ kLyricsMdId, -1, "lyric" },
|
||||||
|
{ kMarkerMdId, -1, "mark" },
|
||||||
|
{ kCuePointMdId, -1, "cue" },
|
||||||
|
{ kMidiChMdId, 1, "chan" },
|
||||||
|
{ kEndOfTrkMdId, 0, "eot" },
|
||||||
|
{ kTempoMdId, 3, "tempo" },
|
||||||
|
{ kSmpteMdId, 5, "smpte" },
|
||||||
|
{ kTimeSigMdId, 4, "tsig" },
|
||||||
|
{ kKeySigMdId, 2, "ksig" },
|
||||||
|
{ kSeqSpecMdId, -1, "seqs" },
|
||||||
|
{ kInvalidMetaMdId, kInvalidMidiByte, "ERROR"}
|
||||||
|
};
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
|
||||||
|
const char* cmMidiStatusToLabel( cmMidiByte_t status )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if( !cmMidiIsStatus(status) )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// remove the channel value from ch msg status bytes
|
||||||
|
if( cmMidiIsChStatus(status) )
|
||||||
|
status &= 0xf0;
|
||||||
|
|
||||||
|
for(i=0; _cmMidiStatusDescArray[i].status != kInvalidStatusMdId; ++i)
|
||||||
|
if( _cmMidiStatusDescArray[i].status == status )
|
||||||
|
return _cmMidiStatusDescArray[i].label;
|
||||||
|
|
||||||
|
return _cmMidiStatusDescArray[i].label;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* cmMidiMetaStatusToLabel( cmMidiByte_t metaStatus )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i=0; _cmMidiMetaStatusDescArray[i].status != kInvalidMetaMdId; ++i)
|
||||||
|
if( _cmMidiMetaStatusDescArray[i].status == metaStatus )
|
||||||
|
break;
|
||||||
|
|
||||||
|
return _cmMidiMetaStatusDescArray[i].label;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMidiByte_t cmMidiStatusToByteCount( cmMidiByte_t status )
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if( !cmMidiIsStatus(status) )
|
||||||
|
return kInvalidMidiByte;
|
||||||
|
|
||||||
|
// remove the channel value from ch msg status bytes
|
||||||
|
if( cmMidiIsChStatus(status) )
|
||||||
|
status &= 0xf0;
|
||||||
|
|
||||||
|
for(i=0; _cmMidiStatusDescArray[i].status != kInvalidStatusMdId; ++i)
|
||||||
|
if( _cmMidiStatusDescArray[i].status == status )
|
||||||
|
return _cmMidiStatusDescArray[i].byteCnt;
|
||||||
|
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
const char* cmMidiToSciPitch( cmMidiByte_t pitch, char* label, unsigned labelCharCnt )
|
||||||
|
{
|
||||||
|
static char buf[ kMidiSciPitchCharCnt ];
|
||||||
|
|
||||||
|
if( label == NULL || labelCharCnt == 0 )
|
||||||
|
{
|
||||||
|
label = buf;
|
||||||
|
labelCharCnt = kMidiSciPitchCharCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( labelCharCnt >= kMidiSciPitchCharCnt );
|
||||||
|
|
||||||
|
if( /*pitch < 0 ||*/ pitch > 127 )
|
||||||
|
{
|
||||||
|
label[0] = 0;
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( labelCharCnt >= 5 && /*pitch >= 0 &&*/ pitch <= 127 );
|
||||||
|
|
||||||
|
char noteV[] = { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' };
|
||||||
|
char shrpV[] = { ' ', '#', ' ', '#', ' ', ' ', '#', ' ', '#', ' ', '#', ' ' };
|
||||||
|
int octave = (pitch / 12)-1;
|
||||||
|
unsigned noteIdx = pitch % 12;
|
||||||
|
char noteCh = noteV[ noteIdx ];
|
||||||
|
char sharpCh = shrpV[ noteIdx ];
|
||||||
|
unsigned idx = 1;
|
||||||
|
|
||||||
|
label[labelCharCnt-1] = 0;
|
||||||
|
label[0] = noteCh;
|
||||||
|
|
||||||
|
if( sharpCh != ' ' )
|
||||||
|
{
|
||||||
|
label[1] = sharpCh;
|
||||||
|
idx = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( -1 <= octave && octave <= 9);
|
||||||
|
|
||||||
|
snprintf(label+idx,kMidiSciPitchCharCnt-idx-1,"%i",octave);
|
||||||
|
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cmMidiByte_t cmSciPitchToMidi( const char* sciPitchStr )
|
||||||
|
{
|
||||||
|
const char* cp = sciPitchStr;
|
||||||
|
bool sharpFl = false;
|
||||||
|
bool flatFl = false;
|
||||||
|
int octave;
|
||||||
|
int idx = -1;
|
||||||
|
|
||||||
|
if( sciPitchStr==NULL || strlen(sciPitchStr) > 5 )
|
||||||
|
return kInvalidMidiPitch;
|
||||||
|
|
||||||
|
switch(tolower(*cp))
|
||||||
|
{
|
||||||
|
case 'a': idx = 9; break;
|
||||||
|
case 'b': idx = 11; break;
|
||||||
|
case 'c': idx = 0; break;
|
||||||
|
case 'd': idx = 2; break;
|
||||||
|
case 'e': idx = 4; break;
|
||||||
|
case 'f': idx = 5; break;
|
||||||
|
case 'g': idx = 7; break;
|
||||||
|
default:
|
||||||
|
return kInvalidMidiPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
++cp;
|
||||||
|
|
||||||
|
if( !(*cp) )
|
||||||
|
return kInvalidMidiPitch;
|
||||||
|
|
||||||
|
|
||||||
|
if((sharpFl = *cp=='#') == true )
|
||||||
|
++idx;
|
||||||
|
else
|
||||||
|
if((flatFl = *cp=='b') == true )
|
||||||
|
--idx;
|
||||||
|
|
||||||
|
if( sharpFl || flatFl )
|
||||||
|
{
|
||||||
|
++cp;
|
||||||
|
|
||||||
|
if( !(*cp) )
|
||||||
|
return kInvalidMidiPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( isdigit(*cp) == false && *cp!='-' )
|
||||||
|
return kInvalidMidiPitch;
|
||||||
|
|
||||||
|
octave = atoi(cp);
|
||||||
|
|
||||||
|
unsigned rv = (octave*12) + idx + 12;
|
||||||
|
|
||||||
|
if( 0 <= rv && rv <= 127 )
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
return kInvalidMidiPitch;
|
||||||
|
|
||||||
|
}
|
140
cmMidi.h
Normal file
140
cmMidi.h
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#ifndef cmMidi_h
|
||||||
|
#define cmMidi_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kMidiChCnt = 16,
|
||||||
|
kInvalidMidiByte = 128,
|
||||||
|
kMidiNoteCnt = kInvalidMidiByte,
|
||||||
|
kMidiCtlCnt = kInvalidMidiByte,
|
||||||
|
kMidiPgmCnt = kInvalidMidiByte,
|
||||||
|
kInvalidMidiPitch = kInvalidMidiByte,
|
||||||
|
kInvalidMidiVelocity = kInvalidMidiByte,
|
||||||
|
kInvalidMidiCtl = kInvalidMidiByte,
|
||||||
|
kInvalidMidiPgm = kInvalidMidiByte,
|
||||||
|
kMidiSciPitchCharCnt = 5 // A#-1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// MIDI status bytes
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInvalidStatusMdId = 0x00,
|
||||||
|
kNoteOffMdId = 0x80,
|
||||||
|
kNoteOnMdId = 0x90,
|
||||||
|
kPolyPresMdId = 0xa0,
|
||||||
|
kCtlMdId = 0xb0,
|
||||||
|
kPgmMdId = 0xc0,
|
||||||
|
kChPresMdId = 0xd0,
|
||||||
|
kPbendMdId = 0xe0,
|
||||||
|
kSysExMdId = 0xf0,
|
||||||
|
|
||||||
|
kSysComMtcMdId = 0xf1,
|
||||||
|
kSysComSppMdId = 0xf2,
|
||||||
|
kSysComSelMdId = 0xf3,
|
||||||
|
kSysComUndef0MdId = 0xf4,
|
||||||
|
kSysComUndef1MdId = 0xf5,
|
||||||
|
kSysComTuneMdId = 0xf6,
|
||||||
|
kSysComEoxMdId = 0xf7,
|
||||||
|
|
||||||
|
kSysRtClockMdId = 0xf8,
|
||||||
|
kSysRtUndef0MdId = 0xf9,
|
||||||
|
kSysRtStartMdId = 0xfa,
|
||||||
|
kSysRtContMdId = 0xfb,
|
||||||
|
kSysRtStopMdId = 0xfc,
|
||||||
|
kSysRtUndef1MdId = 0xfd,
|
||||||
|
kSysRtSenseMdId = 0xfe,
|
||||||
|
kSysRtResetMdId = 0xff,
|
||||||
|
kMetaStId = 0xff,
|
||||||
|
|
||||||
|
kSeqNumbMdId = 0x00,
|
||||||
|
kTextMdId = 0x01,
|
||||||
|
kCopyMdId = 0x02,
|
||||||
|
kTrkNameMdId = 0x03,
|
||||||
|
kInstrNameMdId = 0x04,
|
||||||
|
kLyricsMdId = 0x05,
|
||||||
|
kMarkerMdId = 0x06,
|
||||||
|
kCuePointMdId = 0x07,
|
||||||
|
kMidiChMdId = 0x20,
|
||||||
|
kEndOfTrkMdId = 0x2f,
|
||||||
|
kTempoMdId = 0x51,
|
||||||
|
kSmpteMdId = 0x54,
|
||||||
|
kTimeSigMdId = 0x58,
|
||||||
|
kKeySigMdId = 0x59,
|
||||||
|
kSeqSpecMdId = 0x7f,
|
||||||
|
kInvalidMetaMdId = 0x80,
|
||||||
|
|
||||||
|
kSustainCtlMdId = 64
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef unsigned char cmMidiByte_t;
|
||||||
|
|
||||||
|
//===============================================================================================
|
||||||
|
// Utility Functions
|
||||||
|
//
|
||||||
|
|
||||||
|
#define cmMidiIsStatus( s ) (kNoteOffMdId <= (s) /*&& ((unsigned)(s)) <= kSysRtResetMdId*/ )
|
||||||
|
#define cmMidiIsChStatus( s ) (kNoteOffMdId <= (s) && (s) < kSysExMdId)
|
||||||
|
|
||||||
|
|
||||||
|
const char* cmMidiStatusToLabel( cmMidiByte_t status );
|
||||||
|
const char* cmMidiMetaStatusToLabel( cmMidiByte_t metaStatus );
|
||||||
|
|
||||||
|
// Returns kInvalidMidiByte if status is not a valid status byte
|
||||||
|
cmMidiByte_t cmMidiStatusToByteCount( cmMidiByte_t status );
|
||||||
|
|
||||||
|
|
||||||
|
//===============================================================================================
|
||||||
|
// MIDI Communication data types
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned deltaUs; // time since last MIDI msg in microseconds
|
||||||
|
cmMidiByte_t status; // midi status byte
|
||||||
|
cmMidiByte_t d0; // midi data byte 0
|
||||||
|
cmMidiByte_t d1; // midi data byte 1
|
||||||
|
cmMidiByte_t pad;
|
||||||
|
} cmMidiMsg;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
void* cbDataPtr; // application supplied reference value from mdParserCreate()
|
||||||
|
unsigned devIdx; // the device the msg originated from
|
||||||
|
unsigned portIdx; // the port index on the source device
|
||||||
|
cmMidiMsg* msgArray; // pointer to an array of 'msgCnt' mdMsg records or NULL if sysExMsg is non-NULL
|
||||||
|
cmMidiByte_t* sysExMsg; // pointer to a sys-ex msg or NULL if msgArray is non-NULL (see note below)
|
||||||
|
unsigned msgCnt; // count of mdMsg records or sys-ex bytes
|
||||||
|
} cmMidiPacket_t;
|
||||||
|
|
||||||
|
// Notes: If the sys-ex message can be contained in a single msg then
|
||||||
|
// then the first msg byte is kSysExMdId and the last is kSysComEoxMdId.
|
||||||
|
// If the sys-ex message is broken into multiple pieces then only the
|
||||||
|
// first will begin with kSysExMdId and the last will end with kSysComEoxMdId.
|
||||||
|
|
||||||
|
|
||||||
|
// If label is NULL or labelCharCnt==0 then a pointer to an internal static
|
||||||
|
// buffer is returned. If label[] is given the it
|
||||||
|
// should have at least 5 (kMidiPitchCharCnt) char's (including the terminating zero).
|
||||||
|
// If 'pitch' is outside of the range 0-127 then a blank string is returned.
|
||||||
|
const char* cmMidiToSciPitch( cmMidiByte_t pitch, char* label, unsigned labelCharCnt );
|
||||||
|
|
||||||
|
|
||||||
|
// Scientific pitch string: [A-Ga-g][#b][#] where # may be -1 to 9.
|
||||||
|
// Return kInvalidMidiPitch if sciPtichStr does not contain a valid
|
||||||
|
// scientific pitch string. This function will convert C-1 to G9 to
|
||||||
|
// valid MIDI pitch values 0 to 127. Scientific pitch strings outside
|
||||||
|
// of this range will be returned as kInvalidMidiPitch.
|
||||||
|
cmMidiByte_t cmSciPitchToMidi( const char* sciPitchStr );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1036
cmMidiFile.c
Normal file
1036
cmMidiFile.c
Normal file
File diff suppressed because it is too large
Load Diff
163
cmMidiFile.h
Normal file
163
cmMidiFile.h
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#ifndef cmMidiFile_h
|
||||||
|
#define cmMidiFile_h
|
||||||
|
|
||||||
|
// MIDI file timing:
|
||||||
|
// Messages in the MIDI file are time tagged with a delta offset in 'ticks'
|
||||||
|
// from the previous message in the same track.
|
||||||
|
//
|
||||||
|
// A 'tick' can be converted to microsends as follows:
|
||||||
|
//
|
||||||
|
// microsecond per tick = micros per quarter note / ticks per quarter note
|
||||||
|
//
|
||||||
|
// MpT = MpQN / TpQN
|
||||||
|
//
|
||||||
|
// TpQN is given as a constant in the MIDI file header.
|
||||||
|
// MpQN is given as the value of the MIDI file tempo message.
|
||||||
|
//
|
||||||
|
// See cmMidiFileSeekUSecs() for an example of converting ticks to milliseconds.
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmHandle_t cmMidiFileH_t;
|
||||||
|
typedef unsigned cmMfRC_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmMidiByte_t hr;
|
||||||
|
cmMidiByte_t min;
|
||||||
|
cmMidiByte_t sec;
|
||||||
|
cmMidiByte_t frm;
|
||||||
|
cmMidiByte_t sfr;
|
||||||
|
} cmMidiSmpte_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmMidiByte_t num;
|
||||||
|
cmMidiByte_t den;
|
||||||
|
cmMidiByte_t metro;
|
||||||
|
cmMidiByte_t th2s;
|
||||||
|
} cmMidiTimeSig_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmMidiByte_t key;
|
||||||
|
cmMidiByte_t scale;
|
||||||
|
} cmMidiKeySig_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmMidiByte_t ch;
|
||||||
|
cmMidiByte_t d0;
|
||||||
|
cmMidiByte_t d1;
|
||||||
|
unsigned durTicks; // note duration calc'd by
|
||||||
|
} cmMidiChMsg_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct cmMidiTrackMsg_str
|
||||||
|
{
|
||||||
|
unsigned dtick; // delta ticks
|
||||||
|
cmMidiByte_t status; // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
|
||||||
|
cmMidiByte_t metaId; //
|
||||||
|
unsigned short trkIdx; //
|
||||||
|
unsigned byteCnt; // length of data pointed to by u.voidPtr (or any other pointer in the union)
|
||||||
|
struct cmMidiTrackMsg_str* link; // link to next record in this track
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
cmMidiByte_t bVal;
|
||||||
|
unsigned iVal;
|
||||||
|
unsigned short sVal;
|
||||||
|
const char* text;
|
||||||
|
const void* voidPtr;
|
||||||
|
const cmMidiSmpte_t* smptePtr;
|
||||||
|
const cmMidiTimeSig_t* timeSigPtr;
|
||||||
|
const cmMidiKeySig_t* keySigPtr;
|
||||||
|
const cmMidiChMsg_t* chMsgPtr;
|
||||||
|
const cmMidiByte_t* sysExPtr;
|
||||||
|
} u;
|
||||||
|
} cmMidiTrackMsg_t;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkMfRC = cmOkRC, // 0
|
||||||
|
kSysFopenFailMfRC, // 1
|
||||||
|
kSysFreadFailMfRC, // 2
|
||||||
|
kSysFseekFailMfRC, // 3
|
||||||
|
kSysFtellFailMfRC, // 4
|
||||||
|
kSysFcloseFailMfRC, // 5
|
||||||
|
kNotAMidiFileMfRC, // 6
|
||||||
|
kMemAllocFailMfRC, // 7
|
||||||
|
kFileCorruptMfRC, // 8
|
||||||
|
kMissingEoxMfRC, // 9
|
||||||
|
kUnknownMetaIdMfRC, // 10
|
||||||
|
kInvalidHandleMfRC, // 11
|
||||||
|
kMissingNoteOffMfRC, // 12
|
||||||
|
kInvalidStatusMfRC // 13
|
||||||
|
};
|
||||||
|
|
||||||
|
extern cmMidiFileH_t cmMidiFileNullHandle;
|
||||||
|
|
||||||
|
cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx );
|
||||||
|
cmMfRC_t cmMidiFileClose( cmMidiFileH_t* hp );
|
||||||
|
|
||||||
|
// Returns track count or kInvalidCnt if 'h' is invalid.
|
||||||
|
unsigned cmMidiFileTrackCount( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Return midi file format id (0,1,2) or kInvalidId if 'h' is invalid.
|
||||||
|
unsigned cmMidiFileType( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Returns ticks per quarter note or kInvalidMidiByte if 'h' is invalid or 0 if file uses SMPTE ticks per frame time base.
|
||||||
|
unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// The file name used in an earlier call to midiFileOpen() or NULL if this
|
||||||
|
// midi file did not originate from an actual file.
|
||||||
|
const char* cmMidiFileName( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
|
||||||
|
cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
|
||||||
|
cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Returns count of records in track 'trackIdx' or kInvalidCnt if 'h' is invalid.
|
||||||
|
unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx );
|
||||||
|
|
||||||
|
// Returns base of record chain from track 'trackIdx' or NULL if 'h' is invalid.
|
||||||
|
const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx );
|
||||||
|
|
||||||
|
// Returns the total count of records in the midi file and the number in the array returned by cmMidiFileMsgArray().
|
||||||
|
// Return kInvalidCnt if 'h' is invalid.
|
||||||
|
unsigned cmMidiFileMsgCount( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Returns a pointer to the base of an array of pointers to each record in the file sorted in ascending time order.
|
||||||
|
// Returns NULL if 'h' is invalid.
|
||||||
|
const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no
|
||||||
|
// msg exists after 'usecsOffs'. Note that 'usecOffs' is an offset from the beginning
|
||||||
|
// of the file.
|
||||||
|
// On return *'msgUsecsPtr' is set to the actual time of the msg.
|
||||||
|
// (which will be equal to or greater than 'usecsOffs').
|
||||||
|
unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned usecsOffs, unsigned* msgUsecsPtr, unsigned* newMicrosPerTickPtr );
|
||||||
|
|
||||||
|
double cmMidiFileDurSecs( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Convert the track message 'dtick' field to delta-microseconds.
|
||||||
|
void cmMidiFileTickToMicros( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Calculate Note Duration
|
||||||
|
void cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
|
||||||
|
|
||||||
|
// Set the delay prior to the first non-zero msg.
|
||||||
|
void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks );
|
||||||
|
|
||||||
|
// This function packs a track msg into a single consecutive
|
||||||
|
// block of memory buf[ bufByteCnt ]. Call cmMidiFilePackTracMsgBufByteCount()
|
||||||
|
// to get the required buffer length for any given cmMidiTrackMsg_t instance.
|
||||||
|
cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt );
|
||||||
|
unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m );
|
||||||
|
|
||||||
|
void cmMidiFilePrint( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt );
|
||||||
|
bool cmMidiFileIsNull( cmMidiFileH_t h );
|
||||||
|
void cmMidiFileTest( const char* fn, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
#endif
|
567
cmMidiFilePlay.c
Normal file
567
cmMidiFilePlay.c
Normal file
@ -0,0 +1,567 @@
|
|||||||
|
#include <sys/time.h> // gettimeofday()
|
||||||
|
#include <unistd.h> // usleep()
|
||||||
|
//#include <time.h> // clock_gettime()
|
||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmFile.h"
|
||||||
|
#include "cmMidi.h"
|
||||||
|
#include "cmMidiPort.h"
|
||||||
|
#include "cmMidiFile.h"
|
||||||
|
#include "cmMidiFilePlay.h"
|
||||||
|
|
||||||
|
#ifdef OS_OSX
|
||||||
|
#include "osx/clock_gettime_stub.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OS_LINUX
|
||||||
|
#include <time.h> // clock_gettime()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
cmCtx_t ctx;
|
||||||
|
cmMfpCallback_t cbFunc;
|
||||||
|
void* userCbPtr;
|
||||||
|
void* printDataPtr;
|
||||||
|
unsigned memBlockByteCnt;
|
||||||
|
cmMidiFileH_t mfH; // midi file handle
|
||||||
|
bool closeFileFl; // true mfH should be closed when this midi file player is closed
|
||||||
|
unsigned ticksPerQN; // global for file
|
||||||
|
unsigned microsPerTick; // set via tempo
|
||||||
|
unsigned etime; // usecs elapsed since transmitting prev msg
|
||||||
|
unsigned mtime; // usecs to wait before transmitting next msg
|
||||||
|
unsigned msgN; // count of pointers in msgV[]
|
||||||
|
unsigned msgIdx; // index into msgV[] of next msg to transmit
|
||||||
|
const cmMidiTrackMsg_t** msgV; // array of msg pointers
|
||||||
|
|
||||||
|
} cmMfp_t;
|
||||||
|
|
||||||
|
cmMfpH_t cmMfpNullHandle = cmSTATIC_NULL_HANDLE;
|
||||||
|
|
||||||
|
#define _cmMfpError( mfp, rc ) _cmMfpOnError(mfp, rc, __LINE__,__FILE__,__FUNCTION__ )
|
||||||
|
|
||||||
|
// note: mfp may be NULL
|
||||||
|
cmMfpRC_t _cmMfpOnError( cmMfp_t* mfp, cmMfpRC_t rc, int line, const char* fn, const char* func )
|
||||||
|
{
|
||||||
|
return cmErrMsg(&mfp->err,rc,"rc:%i %i %s %s\n",rc,line,func,fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMfp_t* _cmMfpHandleToPtr( cmMfpH_t h )
|
||||||
|
{
|
||||||
|
cmMfp_t* p = (cmMfp_t*)h.h;
|
||||||
|
assert(p != NULL);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmMfpUpdateMicrosPerTick( cmMfp_t* mfp, unsigned microsPerQN )
|
||||||
|
{
|
||||||
|
mfp->microsPerTick = microsPerQN / mfp->ticksPerQN;
|
||||||
|
printf("microsPerTick: %i bpm:%i ticksPerQN:%i\n", mfp->microsPerTick,microsPerQN,mfp->ticksPerQN);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpCreate( cmMfpH_t* hp, cmMfpCallback_t cbFunc, void* userCbPtr, cmCtx_t* ctx )
|
||||||
|
{
|
||||||
|
cmMfp_t* p = cmMemAllocZ( cmMfp_t, 1 );
|
||||||
|
|
||||||
|
cmErrSetup(&p->err,&ctx->rpt,"MIDI File Player");
|
||||||
|
p->ctx = *ctx;
|
||||||
|
p->cbFunc = cbFunc;
|
||||||
|
p->userCbPtr = userCbPtr;
|
||||||
|
p->mfH.h = NULL;
|
||||||
|
p->closeFileFl = false;
|
||||||
|
p->ticksPerQN = 0;
|
||||||
|
p->microsPerTick = 0;
|
||||||
|
p->etime = 0;
|
||||||
|
p->msgN = 0;
|
||||||
|
p->msgV = NULL;
|
||||||
|
p->msgIdx = 0;
|
||||||
|
hp->h = p;
|
||||||
|
return kOkMfpRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpDestroy( cmMfpH_t* hp )
|
||||||
|
{
|
||||||
|
if( hp == NULL )
|
||||||
|
return kOkMfpRC;
|
||||||
|
|
||||||
|
if( cmMfpIsValid(*hp) )
|
||||||
|
{
|
||||||
|
cmMfp_t* p = _cmMfpHandleToPtr(*hp);
|
||||||
|
|
||||||
|
if( cmMidiFileIsNull(p->mfH)==false && p->closeFileFl==true )
|
||||||
|
cmMidiFileClose(&p->mfH);
|
||||||
|
|
||||||
|
cmMemFree(p);
|
||||||
|
hp->h = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkMfpRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmMfpIsValid( cmMfpH_t h )
|
||||||
|
{ return h.h != NULL; }
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpLoadFile( cmMfpH_t h, const char* fn )
|
||||||
|
{
|
||||||
|
cmMfpRC_t rc = kOkMfpRC;
|
||||||
|
cmMfp_t* p = _cmMfpHandleToPtr(h);
|
||||||
|
cmMidiFileH_t mfH = cmMidiFileNullHandle;
|
||||||
|
|
||||||
|
if((rc = cmMidiFileOpen( fn, &mfH, &p->ctx )) != kOkMfRC )
|
||||||
|
return _cmMfpError(p,kFileOpenFailMfpRC);
|
||||||
|
|
||||||
|
if((rc= cmMfpLoadHandle( h, mfH )) == kOkMfpRC )
|
||||||
|
p->closeFileFl = true;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpLoadHandle( cmMfpH_t h, cmMidiFileH_t mfH )
|
||||||
|
{
|
||||||
|
cmMfp_t* p = _cmMfpHandleToPtr(h);
|
||||||
|
|
||||||
|
// if a file has already been assigned to this player
|
||||||
|
if( (cmMidiFileIsNull(p->mfH) == false) && p->closeFileFl)
|
||||||
|
{
|
||||||
|
// close the existing file
|
||||||
|
cmMidiFileClose(&p->mfH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the count of msg's in the new midi file
|
||||||
|
if((p->msgN = cmMidiFileMsgCount(mfH)) == cmInvalidCnt )
|
||||||
|
return _cmMfpError(p,kInvalidFileMfpRC);
|
||||||
|
|
||||||
|
// get a pointer to the first mesage
|
||||||
|
if((p->msgV = cmMidiFileMsgArray(mfH)) == NULL )
|
||||||
|
return _cmMfpError(p,kInvalidFileMfpRC);
|
||||||
|
|
||||||
|
// get the count of ticks per qn
|
||||||
|
if((p->ticksPerQN = cmMidiFileTicksPerQN( mfH )) == 0 )
|
||||||
|
return _cmMfpError(p,kSmpteTickNotImplMfpRC);
|
||||||
|
|
||||||
|
// set the initial tempo to 120
|
||||||
|
_cmMfpUpdateMicrosPerTick(p,60000000/120);
|
||||||
|
|
||||||
|
p->msgIdx = 0;
|
||||||
|
p->mfH = mfH;
|
||||||
|
p->etime = 0;
|
||||||
|
p->mtime = 0;
|
||||||
|
p->closeFileFl= false;
|
||||||
|
|
||||||
|
//if( p->msgIdx > 0 )
|
||||||
|
// p->mtime = p->msgV[0]->tick * p->microsPerTick;
|
||||||
|
|
||||||
|
return kOkMfpRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpSeek( cmMfpH_t h, unsigned offsUsecs )
|
||||||
|
{
|
||||||
|
cmMfp_t* p = _cmMfpHandleToPtr(h);
|
||||||
|
unsigned msgOffsUsecs = 0;
|
||||||
|
unsigned msgIdx;
|
||||||
|
unsigned newMicrosPerTick;
|
||||||
|
|
||||||
|
// if the requested offset is past the end of the file then return EOF
|
||||||
|
if((msgIdx = cmMidiFileSeekUsecs( p->mfH, offsUsecs, &msgOffsUsecs, &newMicrosPerTick )) == cmInvalidIdx )
|
||||||
|
{
|
||||||
|
p->msgIdx = p->msgN;
|
||||||
|
return _cmMfpError(p,kEndOfFileMfpRC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( msgIdx < p->msgIdx )
|
||||||
|
p->msgIdx = 0;
|
||||||
|
|
||||||
|
p->mtime = msgOffsUsecs;
|
||||||
|
p->etime = 0;
|
||||||
|
p->microsPerTick = newMicrosPerTick;
|
||||||
|
p->msgIdx = msgIdx;
|
||||||
|
|
||||||
|
assert(p->mtime >= 0);
|
||||||
|
|
||||||
|
return kOkMfpRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// p 0 1 n 2
|
||||||
|
// v v v v v
|
||||||
|
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
// 012345678901234567890123456780
|
||||||
|
// 0 1 2
|
||||||
|
//
|
||||||
|
// p = 3 = prev msg sent
|
||||||
|
// n = 19 = next msg to send
|
||||||
|
// 0 = 6 = call to cmMfpClock()
|
||||||
|
// 1 = 12 = call to cmMfpClock()
|
||||||
|
// 2 = 22 = call to cmMfpClock()
|
||||||
|
//
|
||||||
|
// dusecs etime mtime
|
||||||
|
// 0 n/a 3 13
|
||||||
|
// 1 6 9 7
|
||||||
|
// 2 10 19 -3
|
||||||
|
//
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpClock( cmMfpH_t h, unsigned dusecs )
|
||||||
|
{
|
||||||
|
cmMfp_t* p = _cmMfpHandleToPtr(h);
|
||||||
|
|
||||||
|
if( p->msgIdx >= p->msgN )
|
||||||
|
return kEndOfFileMfpRC;
|
||||||
|
|
||||||
|
// get a pointer to the next msg to send
|
||||||
|
const cmMidiTrackMsg_t* mp = p->msgV[p->msgIdx];
|
||||||
|
|
||||||
|
// p->etime is the interval of time between when the last msg was
|
||||||
|
// sent and the end of the time window for this mfpClock() cycle
|
||||||
|
p->etime += dusecs;
|
||||||
|
|
||||||
|
//printf("init e:%i d:%i\n",p->etime, p->mtime);
|
||||||
|
|
||||||
|
// if the elapsed time (etime) since the last msg is greater or equal
|
||||||
|
// to the delta time to the next msg (mtime)
|
||||||
|
while( p->etime >= p->mtime )
|
||||||
|
{
|
||||||
|
//printf("e:%i d:%i\n",p->etime, p->mtime);
|
||||||
|
|
||||||
|
if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
|
||||||
|
_cmMfpUpdateMicrosPerTick(p,mp->u.iVal );
|
||||||
|
|
||||||
|
|
||||||
|
p->cbFunc( p->userCbPtr, p->mtime, mp );
|
||||||
|
|
||||||
|
++(p->msgIdx);
|
||||||
|
|
||||||
|
if( p->msgIdx >= p->msgN )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// get the next msg to send
|
||||||
|
mp = p->msgV[p->msgIdx];
|
||||||
|
|
||||||
|
// we probably went past the actual mtime - so update etime
|
||||||
|
// with the delta usecs from the msg just sent and the current time
|
||||||
|
p->etime -= p->mtime;
|
||||||
|
|
||||||
|
// calc the delta usecs from the message just sent to the next msg to send
|
||||||
|
//p->mtime = (mp->tick - p->msgV[p->msgIdx-1]->tick) * p->microsPerTick;
|
||||||
|
p->mtime = mp->dtick * p->microsPerTick;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return p->msgIdx >= p->msgN ? kEndOfFileMfpRC : kOkMfpRC;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mfpPrint( void* userDataPtr, const char* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
vprintf(fmt,vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this assumes that the seconds have been normalized to a recent start time
|
||||||
|
// so as to avoid overflow
|
||||||
|
unsigned _cmMfpElapsedMicroSecs( const struct timespec* t0, const struct timespec* t1 )
|
||||||
|
{
|
||||||
|
// convert seconds to usecs
|
||||||
|
long u0 = t0->tv_sec * 1000000;
|
||||||
|
long u1 = t1->tv_sec * 1000000;
|
||||||
|
|
||||||
|
// convert nanoseconds to usec
|
||||||
|
u0 += t0->tv_nsec / 1000;
|
||||||
|
u1 += t1->tv_nsec / 1000;
|
||||||
|
|
||||||
|
// take diff between t1 and t0
|
||||||
|
return u1 - u0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmMfpTestTimer()
|
||||||
|
{
|
||||||
|
useconds_t suspendUsecs = 15 * 1000;
|
||||||
|
struct timespec t0,t1,t2;
|
||||||
|
unsigned accum = 0;
|
||||||
|
unsigned i;
|
||||||
|
unsigned n = 4000;
|
||||||
|
|
||||||
|
// t0 will be the base time which all other times will be
|
||||||
|
// set relative to.
|
||||||
|
clock_gettime(CLOCK_REALTIME,&t0);
|
||||||
|
t2 = t0;
|
||||||
|
t2.tv_sec = 0;
|
||||||
|
|
||||||
|
for(i=0; i<n; ++i)
|
||||||
|
{
|
||||||
|
usleep(suspendUsecs);
|
||||||
|
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_REALTIME,&t1);
|
||||||
|
t1.tv_sec -= t0.tv_sec;
|
||||||
|
|
||||||
|
unsigned d0usec = _cmMfpElapsedMicroSecs(&t0,&t1);
|
||||||
|
unsigned d1usec = _cmMfpElapsedMicroSecs(&t2,&t1);
|
||||||
|
|
||||||
|
accum += d1usec;
|
||||||
|
|
||||||
|
if( i == n-1 )
|
||||||
|
printf("%i %i %i\n",d0usec,d1usec,accum);
|
||||||
|
|
||||||
|
t2 = t1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// midi file player callback test function
|
||||||
|
void _cmMfpCallbackTest( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr )
|
||||||
|
{
|
||||||
|
if( kNoteOffMdId <= msgPtr->status && msgPtr->status <= kPbendMdId )
|
||||||
|
cmMpDeviceSend( 0, 0, msgPtr->status+msgPtr->u.chMsgPtr->ch, msgPtr->u.chMsgPtr->d0,msgPtr->u.chMsgPtr->d1);
|
||||||
|
|
||||||
|
//printf("%i 0x%x 0x%x %i\n",msgPtr->tick,msgPtr->status,msgPtr->metaId,msgPtr->trkIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// midi port callback test function
|
||||||
|
void _cmMpCallbackTest( const cmMidiPacket_t* pktArray, unsigned pktCnt )
|
||||||
|
{}
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpTest( const char* fn, cmCtx_t* ctx )
|
||||||
|
{
|
||||||
|
cmMfpH_t mfpH = cmMfpNullHandle;
|
||||||
|
cmMfpRC_t rc;
|
||||||
|
useconds_t suspendUsecs = 15 * 1000;
|
||||||
|
struct timespec t0,t1,base;
|
||||||
|
//unsigned i;
|
||||||
|
//unsigned n = 4000;
|
||||||
|
unsigned mdParserBufByteCnt = 1024;
|
||||||
|
|
||||||
|
printf("Initializing MIDI Devices...\n");
|
||||||
|
cmMpInitialize( _cmMpCallbackTest, NULL, mdParserBufByteCnt,"app", &ctx->rpt );
|
||||||
|
|
||||||
|
//mdReport();
|
||||||
|
|
||||||
|
printf("Creating Player...\n");
|
||||||
|
if((rc = cmMfpCreate( &mfpH, _cmMfpCallbackTest, NULL, ctx )) != kOkMfpRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
printf("Loading MIDI file...\n");
|
||||||
|
if((rc = cmMfpLoadFile( mfpH, fn )) != kOkMfpRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
if((rc = cmMfpSeek( mfpH, 60 * 1000000 )) != kOkMfpRC )
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_REALTIME,&base);
|
||||||
|
t0 = base;
|
||||||
|
t0.tv_sec = 0;
|
||||||
|
|
||||||
|
//for(i=0; i<n; ++i)
|
||||||
|
while(rc != kEndOfFileMfpRC)
|
||||||
|
{
|
||||||
|
usleep(suspendUsecs);
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_REALTIME,&t1);
|
||||||
|
t1.tv_sec -= base.tv_sec;
|
||||||
|
|
||||||
|
unsigned dusecs = _cmMfpElapsedMicroSecs(&t0,&t1);
|
||||||
|
|
||||||
|
rc = cmMfpClock( mfpH, dusecs );
|
||||||
|
//printf("%i %i\n",dusecs,rc);
|
||||||
|
t0 = t1;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
cmMfpDestroy(&mfpH);
|
||||||
|
|
||||||
|
cmMpFinalize();
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------------------
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmComplexTypes.h"
|
||||||
|
#include "cmLinkedHeap.h"
|
||||||
|
#include "cmSymTbl.h"
|
||||||
|
#include "cmAudioFile.h"
|
||||||
|
#include "cmProcObj.h"
|
||||||
|
#include "cmProcTemplateMain.h"
|
||||||
|
#include "cmVectOps.h"
|
||||||
|
|
||||||
|
#include "cmProc.h"
|
||||||
|
#include "cmProc2.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkMfptRC = cmOkRC,
|
||||||
|
kMfpFailMfptRC,
|
||||||
|
kAudioFileFailMfptRC,
|
||||||
|
kProcObjFailMfptRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t* err;
|
||||||
|
cmMidiSynth* msp;
|
||||||
|
} _cmMfpTest2CbData_t;
|
||||||
|
|
||||||
|
|
||||||
|
// Called by the MIDI file player to send a msg to the MIDI synth.
|
||||||
|
void _cmMfpCb( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr )
|
||||||
|
{
|
||||||
|
if( kNoteOffMdId <= msgPtr->status && msgPtr->status <= kPbendMdId )
|
||||||
|
{
|
||||||
|
cmMidiPacket_t pkt;
|
||||||
|
cmMidiMsg msg;
|
||||||
|
_cmMfpTest2CbData_t* d = (_cmMfpTest2CbData_t*)userCbPtr;
|
||||||
|
|
||||||
|
msg.deltaUs = dmicros;
|
||||||
|
msg.status = msgPtr->status + msgPtr->u.chMsgPtr->ch;
|
||||||
|
msg.d0 = msgPtr->u.chMsgPtr->d0;
|
||||||
|
msg.d1 = msgPtr->u.chMsgPtr->d1;
|
||||||
|
|
||||||
|
pkt.cbDataPtr = NULL;
|
||||||
|
pkt.devIdx = cmInvalidIdx;
|
||||||
|
pkt.portIdx = cmInvalidIdx;
|
||||||
|
pkt.msgArray = &msg;
|
||||||
|
pkt.sysExMsg = NULL;
|
||||||
|
pkt.msgCnt = 1;
|
||||||
|
|
||||||
|
if( cmMidiSynthOnMidi( d->msp, &pkt, 1 ) != cmOkRC )
|
||||||
|
cmErrMsg(d->err,kProcObjFailMfptRC,"Synth. MIDI receive failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by the MIDI synth to send a msg to the voice bank.
|
||||||
|
int _cmMidiSynthCb( struct cmMidiVoice_str* voicePtr, unsigned sel, cmSample_t* outChArray[], unsigned outChCnt )
|
||||||
|
{
|
||||||
|
return cmWtVoiceBankExec( ((cmWtVoiceBank*)voicePtr->pgm.cbDataPtr), voicePtr, sel, outChArray, outChCnt );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// BUG BUG BUG: THIS FUNCTION IS NOT TESTED!!!!!
|
||||||
|
cmRC_t cmMfpTest2( const char* midiFn, const char* audioFn, cmCtx_t* ctx )
|
||||||
|
{
|
||||||
|
cmRC_t rc = kOkMfptRC;
|
||||||
|
cmMfpH_t mfpH = cmMfpNullHandle;
|
||||||
|
_cmMfpTest2CbData_t cbData;
|
||||||
|
cmErr_t err;
|
||||||
|
cmAudioFileH_t afH = cmNullAudioFileH;
|
||||||
|
cmRC_t afRC = kOkAfRC;
|
||||||
|
double afSrate = 44100;
|
||||||
|
unsigned afBits = 16;
|
||||||
|
unsigned afChCnt = 1;
|
||||||
|
cmCtx* cctx;
|
||||||
|
cmMidiSynth* msp;
|
||||||
|
cmWtVoiceBank* vbp;
|
||||||
|
unsigned msPgmCnt = 127;
|
||||||
|
cmMidiSynthPgm msPgmArray[ msPgmCnt ];
|
||||||
|
unsigned msVoiceCnt = 36;
|
||||||
|
unsigned procSmpCnt = 64;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
cmErrSetup(&err,&ctx->rpt,"MFP Test 2");
|
||||||
|
|
||||||
|
// create the MIDI file player
|
||||||
|
if( cmMfpCreate(&mfpH, _cmMfpCb, &cbData, ctx ) != kOkMfpRC )
|
||||||
|
return cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player create failed.");
|
||||||
|
|
||||||
|
// create an output audio file
|
||||||
|
if( cmAudioFileIsValid( afH = cmAudioFileNewCreate(audioFn, afSrate, afBits, afChCnt, &afRC, &ctx->rpt))==false)
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&err,kAudioFileFailMfptRC,"The audio file create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the midi file into the player
|
||||||
|
if( cmMfpLoadFile( mfpH, midiFn ) != kOkMfpRC )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&err,kMfpFailMfptRC,"MIDI file load failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the proc obj context
|
||||||
|
if((cctx = cmCtxAlloc(NULL, &ctx->rpt, cmLHeapNullHandle, cmSymTblNullHandle )) == NULL)
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&err,kProcObjFailMfptRC,"cmCtx allocate failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the voice bank
|
||||||
|
if((vbp = cmWtVoiceBankAlloc(cctx, NULL, afSrate, procSmpCnt, msVoiceCnt, afChCnt )) == NULL)
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&err,kProcObjFailMfptRC,"WT voice bank allocate failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a MIDI synth
|
||||||
|
if((msp = cmMidiSynthAlloc(cctx, NULL, msPgmArray, msPgmCnt, msVoiceCnt, procSmpCnt, afChCnt, afSrate )) == NULL )
|
||||||
|
{
|
||||||
|
rc = cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth allocate failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
cbData.msp = msp;
|
||||||
|
cbData.err = &err;
|
||||||
|
|
||||||
|
// load all of the the MIDI pgm recds with the same settings
|
||||||
|
for(i=0; i<msPgmCnt; ++i)
|
||||||
|
{
|
||||||
|
msPgmArray[i].pgm = i;
|
||||||
|
msPgmArray[i].cbPtr = _cmMidiSynthCb; // Call this function to update voices using this pgm
|
||||||
|
msPgmArray[i].cbDataPtr = vbp; // Voice bank containing the voice states.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned dusecs = floor((double)procSmpCnt * 1000000. / afSrate);
|
||||||
|
|
||||||
|
while(rc != kEndOfFileMfpRC)
|
||||||
|
{
|
||||||
|
// update the MFP's current time and call _cmMfpCb() for MIDI msgs whose time has elapsed
|
||||||
|
rc = cmMfpClock( mfpH, dusecs );
|
||||||
|
|
||||||
|
// check for MFP errors
|
||||||
|
if(rc!=kOkMfpRC && rc!=kEndOfFileMfpRC)
|
||||||
|
{
|
||||||
|
cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player exec failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate audio based on the current state of the synth voices
|
||||||
|
if( cmMidiSynthExec(msp, NULL, 0 ) != cmOkRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth exec. failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the last frame of synth. generated audio to the output file
|
||||||
|
if( cmAudioFileWriteSample(afH, procSmpCnt, msp->outChCnt, msp->outChArray ) != kOkAfRC )
|
||||||
|
{
|
||||||
|
cmErrMsg(&err,kProcObjFailMfptRC,"Audio file write failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
if( cmMidiSynthFree(&msp) != cmOkRC )
|
||||||
|
cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth. free failed.");
|
||||||
|
|
||||||
|
if( cmWtVoiceBankFree(&vbp) != cmOkRC )
|
||||||
|
cmErrMsg(&err,kProcObjFailMfptRC,"WT voice free failed.");
|
||||||
|
|
||||||
|
if( cmCtxFree(&cctx) != cmOkRC )
|
||||||
|
cmErrMsg(&err,kProcObjFailMfptRC,"cmCtx free failed.");
|
||||||
|
|
||||||
|
if( cmAudioFileDelete(&afH) )
|
||||||
|
cmErrMsg(&err,kAudioFileFailMfptRC,"The audio file close failed.");
|
||||||
|
|
||||||
|
if( cmMfpDestroy(&mfpH) != kOkMfpRC )
|
||||||
|
cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player destroy failed.");
|
||||||
|
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
59
cmMidiFilePlay.h
Normal file
59
cmMidiFilePlay.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef midiFilePlay_h
|
||||||
|
#define midiFilePlay_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef cmHandle_t cmMfpH_t;
|
||||||
|
typedef cmRC_t cmMfpRC_t;
|
||||||
|
|
||||||
|
typedef void (*cmMfpCallback_t)( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr );
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkMfpRC = cmOkRC, // 0
|
||||||
|
kInvalidHandleMfpRC, // 1
|
||||||
|
kFileOpenFailMfpRC, // 2
|
||||||
|
kInvalidFileMfpRC, // 3
|
||||||
|
kMemAllocFailMfpRC, // 4
|
||||||
|
kSmpteTickNotImpleMfpRC, // 5
|
||||||
|
kEndOfFileMfpRC, // 6
|
||||||
|
kSmpteTickNotImplMfpRC // 7
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern cmMfpH_t cmMfpNullHandle;
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpCreate( cmMfpH_t* hp, cmMfpCallback_t cbFunc, void* userCbPtr, cmCtx_t* ctx );
|
||||||
|
cmMfpRC_t cmMfpDestroy( cmMfpH_t* hp );
|
||||||
|
bool cmMfpIsValid( cmMfpH_t h );
|
||||||
|
|
||||||
|
// Load a MIDI file into the player. This MIDI file will be automatically
|
||||||
|
// closed when a new file is loaded at a later time or the MIDI file player handle is destroyed.
|
||||||
|
cmMfpRC_t cmMfpLoadFile( cmMfpH_t h, const char* fn );
|
||||||
|
|
||||||
|
// Load a MIDI file into the player using a file owned by the host.
|
||||||
|
// This file will NOT be closed when a new file is loaded at a later time
|
||||||
|
// or the MIDI file player handle is destroyed.
|
||||||
|
cmMfpRC_t cmMfpLoadHandle( cmMfpH_t h, cmMidiFileH_t mfH );
|
||||||
|
|
||||||
|
// Reset the play position of the player to an offset in microseconds from
|
||||||
|
// the beginning of the file. If there are no message at or after 'offsMicrosecs'
|
||||||
|
// then the function will return kEndOfFileMfpRC.
|
||||||
|
cmMfpRC_t cmMfpSeek( cmMfpH_t h, unsigned offsMicrosecs );
|
||||||
|
|
||||||
|
// This is the driving clock call for the player. 'deltaMicroSecs' is the
|
||||||
|
// elapsed time in microseconds since the last call to this function.
|
||||||
|
// Call to 'cbFunc', as set in by cmMfpCreate() occur from this function.
|
||||||
|
cmMfpRC_t cmMfpClock( cmMfpH_t h, unsigned deltaMicroSecs );
|
||||||
|
|
||||||
|
cmMfpRC_t cmMfpTest( const char* fn, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
cmRC_t cmMfpTest2( const char* midiFn, const char* audioFn, cmCtx_t* ctx );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
458
cmMidiPort.c
Normal file
458
cmMidiPort.c
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmMem.h"
|
||||||
|
#include "cmMallocDebug.h"
|
||||||
|
#include "cmMidi.h"
|
||||||
|
#include "cmMidiPort.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===================================================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kBufByteCnt = 1024,
|
||||||
|
|
||||||
|
kExpectStatusStId=0, // 0
|
||||||
|
kExpectDataStId, // 1
|
||||||
|
kExpectStatusOrDataStId, // 2
|
||||||
|
kExpectEOXStId // 3
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct cmMpParserCb_str
|
||||||
|
{
|
||||||
|
cmMpCallback_t cbFunc;
|
||||||
|
void* cbDataPtr;
|
||||||
|
struct cmMpParserCb_str* linkPtr;
|
||||||
|
} cmMpParserCb_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cmErr_t err;
|
||||||
|
|
||||||
|
cmMpParserCb_t* cbChain;
|
||||||
|
|
||||||
|
cmMidiPacket_t pkt;
|
||||||
|
|
||||||
|
unsigned state; // parser state id
|
||||||
|
unsigned errCnt; // accumlated error count
|
||||||
|
cmMidiByte_t status; // running status
|
||||||
|
cmMidiByte_t data0; // data byte 0
|
||||||
|
unsigned dataCnt; // data byte cnt for current status
|
||||||
|
unsigned dataIdx; // index (0 or 1) of next data byte
|
||||||
|
cmMidiByte_t* buf; // output buffer
|
||||||
|
unsigned bufByteCnt; // output buffer byte cnt
|
||||||
|
unsigned bufIdx; // next output buffer index
|
||||||
|
unsigned msgCnt; // count of channel messages in the buffer
|
||||||
|
} cmMpParser_t;
|
||||||
|
|
||||||
|
cmMpParser_t* _cmMpParserFromHandle( cmMpParserH_t h )
|
||||||
|
{
|
||||||
|
cmMpParser_t* p = (cmMpParser_t*)h.h;
|
||||||
|
assert(p!=NULL);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmMpParserReport( cmMpParser_t* p )
|
||||||
|
{
|
||||||
|
cmRptPrintf(p->err.rpt,"state:%i st:0x%x d0:%i dcnt:%i didx:%i buf[%i]->%i msg:%i err:%i\n",p->state,p->status,p->data0,p->dataCnt,p->dataIdx,p->bufByteCnt,p->bufIdx,p->msgCnt,p->errCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmMpParserDestroy( cmMpParser_t* p )
|
||||||
|
{
|
||||||
|
cmMemPtrFree(&p->buf);
|
||||||
|
|
||||||
|
cmMpParserCb_t* c = p->cbChain;
|
||||||
|
while(c != NULL)
|
||||||
|
{
|
||||||
|
cmMpParserCb_t* nc = c->linkPtr;
|
||||||
|
cmMemFree(c);
|
||||||
|
c = nc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMemPtrFree(&p);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMpParserH_t cmMpParserCreate( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned bufByteCnt, cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
cmMpRC_t rc = kOkMpRC;
|
||||||
|
cmMpParserH_t h;
|
||||||
|
cmMpParser_t* p = cmMemAllocZ( cmMpParser_t, 1 );
|
||||||
|
|
||||||
|
cmErrSetup(&p->err,rpt,"MIDI Parser");
|
||||||
|
|
||||||
|
p->pkt.devIdx = devIdx;
|
||||||
|
p->pkt.portIdx = portIdx;
|
||||||
|
|
||||||
|
//p->cbChain = cmMemAllocZ( cmMpParserCb_t, 1 );
|
||||||
|
//p->cbChain->cbFunc = cbFunc;
|
||||||
|
//p->cbChain->cbDataPtr = cbDataPtr;
|
||||||
|
//p->cbChain->linkPtr = NULL;
|
||||||
|
p->cbChain = NULL;
|
||||||
|
p->buf = cmMemAllocZ( cmMidiByte_t, bufByteCnt );
|
||||||
|
p->bufByteCnt = bufByteCnt;
|
||||||
|
p->bufIdx = 0;
|
||||||
|
p->msgCnt = 0;
|
||||||
|
p->state = kExpectStatusStId;
|
||||||
|
p->dataIdx = cmInvalidIdx;
|
||||||
|
p->dataCnt = cmInvalidCnt;
|
||||||
|
p->status = kInvalidStatusMdId;
|
||||||
|
|
||||||
|
h.h = p;
|
||||||
|
|
||||||
|
if( cbFunc != NULL )
|
||||||
|
rc = cmMpParserInstallCallback(h, cbFunc, cbDataPtr );
|
||||||
|
|
||||||
|
|
||||||
|
if( rc != kOkMpRC )
|
||||||
|
{
|
||||||
|
h.h = NULL;
|
||||||
|
_cmMpParserDestroy(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmMpParserDestroy( cmMpParserH_t* hp )
|
||||||
|
{
|
||||||
|
if( hp==NULL || hp->h == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
cmMpParser_t* p = _cmMpParserFromHandle(*hp);
|
||||||
|
|
||||||
|
_cmMpParserDestroy(p);
|
||||||
|
|
||||||
|
hp->h = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmMpParserErrorCount( cmMpParserH_t h )
|
||||||
|
{
|
||||||
|
cmMpParser_t* p = _cmMpParserFromHandle(h);
|
||||||
|
if( p == NULL )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return p->errCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmMpParserCb( cmMpParser_t* p, cmMidiPacket_t* pkt, unsigned pktCnt )
|
||||||
|
{
|
||||||
|
cmMpParserCb_t* c = p->cbChain;
|
||||||
|
for(; c!=NULL; c=c->linkPtr)
|
||||||
|
{
|
||||||
|
pkt->cbDataPtr = c->cbDataPtr;
|
||||||
|
c->cbFunc( pkt, pktCnt );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmMpTransmitChMsgs( cmMpParser_t* p )
|
||||||
|
{
|
||||||
|
if( p->msgCnt > 0 )
|
||||||
|
{
|
||||||
|
p->pkt.msgArray = (cmMidiMsg*)p->buf;
|
||||||
|
p->pkt.msgCnt = p->msgCnt;
|
||||||
|
p->pkt.sysExMsg = NULL;
|
||||||
|
|
||||||
|
//p->cbFunc( &p->pkt, 1 );
|
||||||
|
_cmMpParserCb(p,&p->pkt,1);
|
||||||
|
|
||||||
|
p->bufIdx = 0;
|
||||||
|
p->msgCnt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmMpTransmitSysEx( cmMpParser_t* p )
|
||||||
|
{
|
||||||
|
p->pkt.msgArray = NULL;
|
||||||
|
p->pkt.sysExMsg = p->buf;
|
||||||
|
p->pkt.msgCnt = p->bufIdx;
|
||||||
|
//p->cbFunc( &p->pkt, 1 );
|
||||||
|
_cmMpParserCb(p,&p->pkt,1);
|
||||||
|
p->bufIdx = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cmMpParserStoreChMsg( cmMpParser_t* p, unsigned deltaMicroSecs, cmMidiByte_t d )
|
||||||
|
{
|
||||||
|
// if there is not enough room left in the buffer then transmit the current messages
|
||||||
|
if( p->bufByteCnt - p->bufIdx < sizeof(cmMidiMsg) )
|
||||||
|
_cmMpTransmitChMsgs(p);
|
||||||
|
|
||||||
|
|
||||||
|
assert( p->bufByteCnt - p->bufIdx >= sizeof(cmMidiMsg) );
|
||||||
|
|
||||||
|
// get a pointer to the next msg in the buffer
|
||||||
|
cmMidiMsg* msgPtr = (cmMidiMsg*)(p->buf + p->bufIdx);
|
||||||
|
|
||||||
|
// fill the buffer msg
|
||||||
|
msgPtr->deltaUs = deltaMicroSecs;
|
||||||
|
msgPtr->status = p->status;
|
||||||
|
|
||||||
|
switch( p->dataCnt )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
msgPtr->d0 = d;
|
||||||
|
msgPtr->d1 = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
msgPtr->d0 = p->data0;
|
||||||
|
msgPtr->d1 = d;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the msg count and next buffer
|
||||||
|
++p->msgCnt;
|
||||||
|
|
||||||
|
p->bufIdx += sizeof(cmMidiMsg);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmMpParseMidiData( cmMpParserH_t h, unsigned deltaMicroSecs, const cmMidiByte_t* iBuf, unsigned iByteCnt )
|
||||||
|
{
|
||||||
|
|
||||||
|
cmMpParser_t* p = _cmMpParserFromHandle(h);
|
||||||
|
|
||||||
|
if( p == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const cmMidiByte_t* ip = iBuf;
|
||||||
|
const cmMidiByte_t* ep = iBuf + iByteCnt;
|
||||||
|
|
||||||
|
for(; ip < ep; ++ip )
|
||||||
|
{
|
||||||
|
// if this byte is a status byte
|
||||||
|
if( cmMidiIsStatus(*ip) )
|
||||||
|
{
|
||||||
|
if( p->state != kExpectStatusStId && p->state != kExpectStatusOrDataStId )
|
||||||
|
++p->errCnt;
|
||||||
|
|
||||||
|
p->status = *ip;
|
||||||
|
p->dataCnt = cmMidiStatusToByteCount(*ip);
|
||||||
|
|
||||||
|
switch( p->status )
|
||||||
|
{
|
||||||
|
case kSysExMdId: // if this is the start of a sys-ex msg ...
|
||||||
|
// ... clear the buffer to prepare from sys-ex data
|
||||||
|
_cmMpTransmitChMsgs(p);
|
||||||
|
|
||||||
|
p->state = kExpectEOXStId;
|
||||||
|
p->dataCnt = cmInvalidCnt;
|
||||||
|
p->dataIdx = cmInvalidIdx;
|
||||||
|
p->buf[ p->bufIdx++ ] = kSysExMdId;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kSysComEoxMdId: // if this is the end of a sys-ex msg
|
||||||
|
assert( p->bufIdx < p->bufByteCnt );
|
||||||
|
p->buf[p->bufIdx++] = *ip;
|
||||||
|
_cmMpTransmitSysEx(p);
|
||||||
|
p->state = kExpectStatusStId;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // ... otherwise it is a 1,2, or 3 byte msg status
|
||||||
|
if( p->dataCnt > 0 )
|
||||||
|
{
|
||||||
|
p->state = kExpectDataStId;
|
||||||
|
p->dataIdx = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// this is a status only msg - store it
|
||||||
|
_cmMpParserStoreChMsg(p,deltaMicroSecs,*ip);
|
||||||
|
|
||||||
|
p->state = kExpectStatusStId;
|
||||||
|
p->dataIdx = cmInvalidIdx;
|
||||||
|
p->dataCnt = cmInvalidCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point the current byte (*ip) is a data byte
|
||||||
|
|
||||||
|
switch(p->state)
|
||||||
|
{
|
||||||
|
|
||||||
|
case kExpectStatusOrDataStId:
|
||||||
|
assert( p->dataIdx == 0 );
|
||||||
|
|
||||||
|
case kExpectDataStId:
|
||||||
|
switch( p->dataIdx )
|
||||||
|
{
|
||||||
|
case 0: // expecting data byte 0 ...
|
||||||
|
|
||||||
|
switch( p->dataCnt )
|
||||||
|
{
|
||||||
|
case 1: // ... of a 1 byte msg - the msg is complete
|
||||||
|
_cmMpParserStoreChMsg(p,deltaMicroSecs,*ip);
|
||||||
|
p->state = kExpectStatusOrDataStId;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // ... of a 2 byte msg - prepare to recv the second data byte
|
||||||
|
p->state = kExpectDataStId;
|
||||||
|
p->dataIdx = 1;
|
||||||
|
p->data0 = *ip;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // expecting data byte 1 of a two byte msg
|
||||||
|
assert( p->dataCnt == 2 );
|
||||||
|
assert( p->state == kExpectDataStId );
|
||||||
|
|
||||||
|
_cmMpParserStoreChMsg(p,deltaMicroSecs,*ip);
|
||||||
|
p->state = kExpectStatusOrDataStId;
|
||||||
|
p->dataIdx = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kExpectEOXStId:
|
||||||
|
assert( p->bufIdx < p->bufByteCnt );
|
||||||
|
|
||||||
|
p->buf[p->bufIdx++] = *ip;
|
||||||
|
|
||||||
|
// if the buffer is full - then transmit it
|
||||||
|
if( p->bufIdx == p->bufByteCnt )
|
||||||
|
_cmMpTransmitSysEx(p);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // ip loop
|
||||||
|
|
||||||
|
_cmMpTransmitChMsgs(p);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMpRC_t cmMpParserInstallCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr )
|
||||||
|
{
|
||||||
|
cmMpParser_t* p = _cmMpParserFromHandle(h);
|
||||||
|
cmMpParserCb_t* newCbPtr = cmMemAllocZ( cmMpParserCb_t, 1 );
|
||||||
|
cmMpParserCb_t* c = p->cbChain;
|
||||||
|
|
||||||
|
newCbPtr->cbFunc = cbFunc;
|
||||||
|
newCbPtr->cbDataPtr = cbDataPtr;
|
||||||
|
newCbPtr->linkPtr = NULL;
|
||||||
|
|
||||||
|
if( p->cbChain == NULL )
|
||||||
|
p->cbChain = newCbPtr;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while( c->linkPtr != NULL )
|
||||||
|
c = c->linkPtr;
|
||||||
|
|
||||||
|
c->linkPtr = newCbPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kOkMpRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMpRC_t cmMpParserRemoveCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr )
|
||||||
|
{
|
||||||
|
cmMpParser_t* p = _cmMpParserFromHandle(h);
|
||||||
|
cmMpParserCb_t* c1 = p->cbChain; // target link
|
||||||
|
cmMpParserCb_t* c0 = NULL; // link pointing to target
|
||||||
|
|
||||||
|
// search for the cbFunc to remove
|
||||||
|
for(; c1!=NULL; c1=c1->linkPtr)
|
||||||
|
{
|
||||||
|
if( c1->cbFunc == cbFunc && c1->cbDataPtr == cbDataPtr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
c0 = c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the cbFunc was not found
|
||||||
|
if( c1 == NULL )
|
||||||
|
return cmErrMsg(&p->err,kCbNotFoundMpRC,"Unable to locate the callback function %p for removal.",cbFunc);
|
||||||
|
|
||||||
|
// the cbFunc to remove was found
|
||||||
|
|
||||||
|
// if it was the first cb in the chain
|
||||||
|
if( c0 == NULL )
|
||||||
|
p->cbChain = c1->linkPtr;
|
||||||
|
else
|
||||||
|
c0->linkPtr = c1->linkPtr;
|
||||||
|
|
||||||
|
cmMemFree(c1);
|
||||||
|
|
||||||
|
return kOkMpRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmMpParserHasCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr )
|
||||||
|
{
|
||||||
|
cmMpParser_t* p = _cmMpParserFromHandle(h);
|
||||||
|
cmMpParserCb_t* c = p->cbChain; // target link
|
||||||
|
|
||||||
|
// search for the cbFunc to remove
|
||||||
|
for(; c!=NULL; c=c->linkPtr)
|
||||||
|
if( c->cbFunc == cbFunc && c->cbDataPtr == cbDataPtr )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
void cmMpTestPrint( void* userDataPtr, const char* fmt, va_list vl )
|
||||||
|
{
|
||||||
|
vprintf(fmt,vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmMpTestCb( const cmMidiPacket_t* pktArray, unsigned pktCnt )
|
||||||
|
{
|
||||||
|
unsigned i,j;
|
||||||
|
for(i=0; i<pktCnt; ++i)
|
||||||
|
{
|
||||||
|
const cmMidiPacket_t* p = pktArray + i;
|
||||||
|
|
||||||
|
for(j=0; j<p->msgCnt; ++j)
|
||||||
|
if( p->msgArray != NULL )
|
||||||
|
printf("%8i 0x%x %i %i\n", p->msgArray[j].deltaUs/1000, p->msgArray[j].status,p->msgArray[j].d0, p->msgArray[j].d1);
|
||||||
|
else
|
||||||
|
printf("0x%x ",p->sysExMsg[j]);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmMpTest( cmRpt_t* rpt )
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
unsigned parserBufByteCnt = 1024;
|
||||||
|
cmMpInitialize(cmMpTestCb,NULL,parserBufByteCnt,"app",rpt);
|
||||||
|
cmMpReport(rpt);
|
||||||
|
|
||||||
|
|
||||||
|
cmRptPrintf(rpt,"<return> to continue\n");
|
||||||
|
|
||||||
|
while((ch = getchar()) != 'q')
|
||||||
|
{
|
||||||
|
cmMpDeviceSend(0,0,0x90,60,60);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMpFinalize();
|
||||||
|
}
|
89
cmMidiPort.h
Normal file
89
cmMidiPort.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#ifndef cmMidiPort_h
|
||||||
|
#define cmMidiPort_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef unsigned cmMpRC_t;
|
||||||
|
|
||||||
|
// Flags used to identify input and output ports on MIDI devices
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kInMpFl = 0x01,
|
||||||
|
kOutMpFl = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkMpRC = cmOkRC,
|
||||||
|
kCfStringErrMpRC,
|
||||||
|
kSysErrMpRC,
|
||||||
|
kInvalidArgMpRC,
|
||||||
|
kMemAllocFailMpRC,
|
||||||
|
kNotImplMpRC,
|
||||||
|
kCbNotFoundMpRC
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*cmMpCallback_t)( const cmMidiPacket_t* pktArray, unsigned pktCnt );
|
||||||
|
|
||||||
|
|
||||||
|
//===============================================================================================
|
||||||
|
// MIDI Parser
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef cmHandle_t cmMpParserH_t;
|
||||||
|
|
||||||
|
// 'cbFunc' and 'cbDataPtr' are optional. If 'cbFunc' is not supplied in the call to
|
||||||
|
// cmMpParserCreate() it may be supplied later by cmMpParserInstallCallback().
|
||||||
|
// 'bufByteCnt' defines is the largest complete system-exclusive message the parser will
|
||||||
|
// by able to transmit. System-exclusive messages larger than this will be broken into
|
||||||
|
// multiple sequential callbacks.
|
||||||
|
cmMpParserH_t cmMpParserCreate( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned bufByteCnt, cmRpt_t* rpt );
|
||||||
|
void cmMpParserDestroy( cmMpParserH_t* hp );
|
||||||
|
unsigned cmMpParserErrorCount( cmMpParserH_t h );
|
||||||
|
void cmMpParseMidiData( cmMpParserH_t h, unsigned deltaMicroSecs, const cmMidiByte_t* buf, unsigned bufByteCnt );
|
||||||
|
|
||||||
|
// Install/Remove additional callbacks.
|
||||||
|
cmMpRC_t cmMpParserInstallCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr );
|
||||||
|
cmMpRC_t cmMpParserRemoveCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr );
|
||||||
|
|
||||||
|
// Returns true if the parser uses the given callback.
|
||||||
|
bool cmMpParserHasCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr );
|
||||||
|
|
||||||
|
|
||||||
|
//===============================================================================================
|
||||||
|
// MIDI Device Interface
|
||||||
|
//
|
||||||
|
|
||||||
|
// 'cbFunc' and 'cbDataPtr' are optional (they may be set to NULL). In this case
|
||||||
|
// 'cbFunc' and 'cbDataPtr' may be set in a later call to cmMpInstallCallback().
|
||||||
|
cmMpRC_t cmMpInitialize( cmMpCallback_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, const char* appNameStr, cmRpt_t* rpt );
|
||||||
|
cmMpRC_t cmMpFinalize();
|
||||||
|
bool cmMpIsInitialized();
|
||||||
|
|
||||||
|
unsigned cmMpDeviceCount();
|
||||||
|
const char* cmMpDeviceName( unsigned devIdx );
|
||||||
|
unsigned cmMpDevicePortCount( unsigned devIdx, unsigned flags );
|
||||||
|
const char* cmMpDevicePortName( unsigned devIdx, unsigned flags, unsigned portIdx );
|
||||||
|
cmMpRC_t cmMpDeviceSend( unsigned devIdx, unsigned portIdx, cmMidiByte_t st, cmMidiByte_t d0, cmMidiByte_t d1 );
|
||||||
|
cmMpRC_t cmMpDeviceSendData( unsigned devIdx, unsigned portIdx, const cmMidiByte_t* dataPtr, unsigned byteCnt );
|
||||||
|
|
||||||
|
// Set devIdx to -1 to assign the callback to all devices.
|
||||||
|
// Set portIdx to -1 to assign the callback to all ports on the specified devices.
|
||||||
|
//
|
||||||
|
cmMpRC_t cmMpInstallCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr );
|
||||||
|
cmMpRC_t cmMpRemoveCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr );
|
||||||
|
bool cmMpUsesCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr );
|
||||||
|
|
||||||
|
void cmMpReport( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
void cmMpTest( cmRpt_t* rpt );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
122
cmMsgProtocol.c
Normal file
122
cmMsgProtocol.c
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmRpt.h"
|
||||||
|
#include "cmErr.h"
|
||||||
|
#include "cmCtx.h"
|
||||||
|
#include "cmJson.h"
|
||||||
|
#include "dsp/cmDspValue.h"
|
||||||
|
#include "cmMsgProtocol.h"
|
||||||
|
|
||||||
|
// buffer layout is:
|
||||||
|
// [ cmDspUiHdr_t <data> ]
|
||||||
|
// The format of the <data> is determiend by hdr.value.
|
||||||
|
// Since hdr.value is the last field in the cmDspUiHdr_t record
|
||||||
|
// the data follows this value.
|
||||||
|
cmMsgRC_t cmMsgSend(
|
||||||
|
cmErr_t* err,
|
||||||
|
unsigned asSubIdx,
|
||||||
|
unsigned msgTypeId,
|
||||||
|
unsigned selId,
|
||||||
|
unsigned flags,
|
||||||
|
unsigned instId,
|
||||||
|
unsigned instVarId,
|
||||||
|
const cmDspValue_t* valPtr,
|
||||||
|
cmMsgSendFuncPtr_t sendFunc,
|
||||||
|
void* cbDataPtr )
|
||||||
|
{
|
||||||
|
unsigned bufByteCnt = sizeof(cmDspUiHdr_t);
|
||||||
|
unsigned dataByteCnt = 0;
|
||||||
|
|
||||||
|
if( valPtr != NULL )
|
||||||
|
dataByteCnt = cmDsvSerialDataByteCount(valPtr);
|
||||||
|
|
||||||
|
bufByteCnt += dataByteCnt;
|
||||||
|
|
||||||
|
char buf[ bufByteCnt ];
|
||||||
|
cmDspUiHdr_t* hdr = (cmDspUiHdr_t*)buf;
|
||||||
|
|
||||||
|
hdr->asSubIdx = asSubIdx;
|
||||||
|
hdr->uiId = msgTypeId; // see kXXXSelAsId identifiers in cmAudioSys.h
|
||||||
|
hdr->selId = selId; // if msgTypeId==kUiSelAsId then see kXXXDuId in cmDspUi.h
|
||||||
|
hdr->flags = flags;
|
||||||
|
hdr->instId = instId;
|
||||||
|
hdr->instVarId = instVarId;
|
||||||
|
|
||||||
|
if( valPtr == NULL )
|
||||||
|
cmDsvSetNull(&hdr->value);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// this function relies on the 'hdr.value' field being the last field in the 'hdr'.
|
||||||
|
if( cmDsvSerialize( valPtr, &hdr->value, sizeof(cmDspValue_t) + dataByteCnt) != kOkDsvRC )
|
||||||
|
return cmErrMsg(err,kSerializeFailMsgRC,"An attempt to serialize a UI msg failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* vp = buf;
|
||||||
|
if( sendFunc(cbDataPtr,bufByteCnt,vp) != cmOkRC )
|
||||||
|
return cmErrMsg(err,kSendFailMsgRC,"An attempt to transmit a msg to the host failed.");
|
||||||
|
|
||||||
|
|
||||||
|
return kOkMsgRC;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the unsigned value at the specified byte offset into the msg buffer.
|
||||||
|
cmMsgRC_t _cmMsgPeekAtUInt( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned offset, unsigned* retValPtr )
|
||||||
|
{
|
||||||
|
unsigned i,j,k;
|
||||||
|
|
||||||
|
for(k=0,i=0; i<segCnt; ++i)
|
||||||
|
for(j=0; j<msgByteCntArray[i]; ++j,++k)
|
||||||
|
if( k == offset )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( i == segCnt )
|
||||||
|
return kDecodeFailMsgRC;
|
||||||
|
|
||||||
|
*retValPtr = *((unsigned*)((char*)(msgArray[i]) + j));
|
||||||
|
return kOkMsgRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMsgRC_t cmMsgPeekAsSubIndex( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
|
||||||
|
{
|
||||||
|
cmDspUiHdr_t h;
|
||||||
|
unsigned offset = ((char*)(&h.asSubIdx)) - ((char*)&h);
|
||||||
|
return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMsgRC_t cmMsgPeekMsgTypeId( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
|
||||||
|
{
|
||||||
|
cmDspUiHdr_t h;
|
||||||
|
unsigned offset = ((char*)(&h.uiId)) - ((char*)&h);
|
||||||
|
return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMsgRC_t cmMsgPeekSelId( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
|
||||||
|
{
|
||||||
|
cmDspUiHdr_t h;
|
||||||
|
unsigned offset = ((char*)(&h.selId)) - ((char*)&h);
|
||||||
|
return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMsgRC_t cmMsgPeekFlags( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
|
||||||
|
{
|
||||||
|
cmDspUiHdr_t h;
|
||||||
|
unsigned offset = ((char*)(&h.flags)) - ((char*)&h);
|
||||||
|
return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMsgRC_t cmMsgPeekInstId( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
|
||||||
|
{
|
||||||
|
cmDspUiHdr_t h;
|
||||||
|
unsigned offset = ((char*)(&h.instId)) - ((char*)&h);
|
||||||
|
return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmMsgRC_t cmMsgPeekInstVarId( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
|
||||||
|
{
|
||||||
|
cmDspUiHdr_t h;
|
||||||
|
unsigned offset = ((char*)(&h.instVarId)) - ((char*)&h);
|
||||||
|
return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
|
||||||
|
}
|
||||||
|
|
233
cmMsgProtocol.h
Normal file
233
cmMsgProtocol.h
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#ifndef cmMsgProtocol_h
|
||||||
|
#define cmMsgProtocol_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define cmAudDspSys_FILENAME "aud_dsp.js"
|
||||||
|
|
||||||
|
|
||||||
|
/// Reserved DSP message selector id's (second field of all host<->audio system messages)
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kMidiMsgArraySelAsId = 1000,
|
||||||
|
kMidiSysExSelAsId,
|
||||||
|
kUiSelAsId, // indicates a cmDspUiHdr_t msg
|
||||||
|
kUiMstrSelAsId, // indicates a cmDspUiHdr_t msg containing master control information for the audio system
|
||||||
|
kSsInitSelAsId, // indicates the msg is of type cmAudioSysSsInitMsg_t
|
||||||
|
kStatusSelAsId, // indicates the msg is of type cmAudioSysStatus_t
|
||||||
|
kNetSyncSelAsId, // sent with a cmDspNetMsg_t object
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// UI seletor id's used in the cmDspUiHdr_t selId field
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kPrintDuiId, // ui<--eng print the value to the console
|
||||||
|
kSliderDuiId, // ui<--eng create a slider control
|
||||||
|
kButtonDuiId, // ui<--eng create button control
|
||||||
|
kCheckDuiId, // ui<--eng create a check box control
|
||||||
|
kLabelDuiId, // ui<--end create a label control
|
||||||
|
kNumberDuiId, // ui<--eng create a number box
|
||||||
|
kTextDuiId, // ui<--eng create a text entry control
|
||||||
|
kFnameDuiId, // ui<--eng create a file/directory picker control
|
||||||
|
kMsgListDuiId, // ui<--eng create a msg list control
|
||||||
|
kMeterDuiId, // ui<--eng create a meter display
|
||||||
|
kValueDuiId, // ui<->eng a control changed values
|
||||||
|
kColumnDuiId, // ui<--eng start a new column
|
||||||
|
kHBorderDuiId, // ui<--eng insert a vertical border
|
||||||
|
kPageDuiId, // ui<--eng insert a new control page
|
||||||
|
|
||||||
|
kAudioSysCfgDuiId, // ui<--audio system cfg label
|
||||||
|
kSubSysCntDuiId, // ui<--eng audio sub-system count
|
||||||
|
kDeviceDuiId, // ui<--eng device label
|
||||||
|
kProgramDuiId, // ui<--eng program label
|
||||||
|
|
||||||
|
// The following selId's are used by cmAudDsp to indicate various commands.
|
||||||
|
kSetAudioCfgDuiId, // 1) select an audio system setup
|
||||||
|
kSetAudioDevDuiId, // 2) (optional) set an audio device on an audio sub-system
|
||||||
|
kSetSampleRateDuiId, // 3) (optional) set the sample rate of an audio sub-system
|
||||||
|
kSetPgmDuiId, // 4) select a program
|
||||||
|
kEnableDuiId, // 5) enable/disable the audio system (also resets the DSP system)
|
||||||
|
kSyncDuiId, // 6) sent by cmAudDsp to client to indicate sync success or failure.
|
||||||
|
kSetNotifyEnableDuiId, // enable/disable periodic status notification from the audio system.
|
||||||
|
kClientMsgPollDuiId, // Periodic check for and deliver messages waiting in the audio system for delivery to the client.
|
||||||
|
kSendMsgDuiId, // forward msg to the audio system
|
||||||
|
kDevReportDuiId, // print a device report
|
||||||
|
|
||||||
|
kRightAlignDuiId = 0, // label alignment id used by kLabelDuiId
|
||||||
|
kLeftAlignDuiId,
|
||||||
|
kCenterAlignDuiId
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kDuplexDuiFl = 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
// Header record for all messages between the host and the DSP controllers.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned asSubIdx; // the audio sub-system this UI belongs to
|
||||||
|
unsigned uiId; // msg type kXXXAsId
|
||||||
|
unsigned selId; // action to perform see above
|
||||||
|
unsigned flags; //
|
||||||
|
unsigned instId; // DSP instance id
|
||||||
|
unsigned instVarId; // DSP instance var id
|
||||||
|
|
||||||
|
// The cmDspValue_t field must come last in the structure in
|
||||||
|
// order for the cmDsvSerialize() to work in cmDspUI.c:_cmDspUiMsg().
|
||||||
|
cmDspValue_t value; // Data value associated with this msg.
|
||||||
|
} cmDspUiHdr_t;
|
||||||
|
|
||||||
|
// All of the UI messages that create a UI control contain an array of integers
|
||||||
|
// as in the 'value' field. The array contains the id's associated with
|
||||||
|
// the different programmable paramters which are part of the control.
|
||||||
|
// For example a slider control has minimum,maximum, step size, and value
|
||||||
|
// parameters. The location in the array is hard coded according to the
|
||||||
|
// parameters meaning but the actual value of the id is left up to the
|
||||||
|
// engine. This allows the engine to use whatever values work best for
|
||||||
|
// it on a per instance basis.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// The cmDspUiHdr_t.instId of UI control messages associated with master
|
||||||
|
/// control encode the device,channel,in/out, and control type. These macros
|
||||||
|
/// should be used for encoding and decoding.
|
||||||
|
#define cmAudioSysFormUiInstId(dev,ch,ifl,ctl) (((dev)<<16) + ((ch)<<4) + ((ifl)<<3) + (ctl))
|
||||||
|
#define cmAudioSysUiInstIdToDevIndex(instId) ( (instId) >> 16)
|
||||||
|
#define cmAudioSysUiInstIdToChIndex(instId) (((instId) & 0x0000ffff) >> 4)
|
||||||
|
#define cmAudioSysUiInstIdToInFlag(instId) ( (instId) & 0x08)
|
||||||
|
#define cmAudioSysUiInstIdToCtlId(instId) ( (instId) & 0x07)
|
||||||
|
|
||||||
|
/// Control id's used to identify the control type of master contols.
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kSliderUiAsId = 0,
|
||||||
|
kMeterUiAsId = 1,
|
||||||
|
kMuteUiAsId = 2,
|
||||||
|
kToneUiAsId = 3,
|
||||||
|
kPassUiAsId = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// This message is transmitted to the host application just prior to returning
|
||||||
|
/// from cmAudioSysInitialize().
|
||||||
|
/// When transmitted to the host this record acts as a message header.
|
||||||
|
/// This header is followed by two zero terminated char arrays containing the device
|
||||||
|
/// labels associated with the input and output devices.
|
||||||
|
/// Message Layout: [ cmAudioSysInitMsg_t "In Device Label" "Out Device Label"]
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned asSubIdx; ///< asSubIdx of this sub-system
|
||||||
|
unsigned selId; ///< always kSsInitAsId
|
||||||
|
unsigned asSubCnt; ///< count of sub-systems
|
||||||
|
unsigned inDevIdx; ///< input device index
|
||||||
|
unsigned outDevIdx; ///< output device index
|
||||||
|
unsigned inChCnt; ///< input device channel count
|
||||||
|
unsigned outChCnt; ///< outut device channel count
|
||||||
|
} cmAudioSysSsInitMsg_t;
|
||||||
|
|
||||||
|
/// Audio sub-system status record - this message can be transmitted to the host at
|
||||||
|
/// periodic intervals. See cmAudioSysStatusNotifyEnable().
|
||||||
|
/// When transmitted to the host this record acts as the message header.
|
||||||
|
/// This header is followed by two arrays of doubles containing the input and output meter values
|
||||||
|
/// associated with the input and output audio devices.
|
||||||
|
/// Message Layout: [ asSubIdx kStatusSelId cmAudioSysStatus_t iMeterArray[iMeterCnt] oMeterArray[oMeterCnt] ]
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned asSubIdx; ///< originating audio sub-system
|
||||||
|
|
||||||
|
unsigned updateCnt; ///< count of callbacks from the audio devices.
|
||||||
|
unsigned wakeupCnt; ///< count of times the audio system thread has woken up after the cond. var has been signaled by the audio update thread.
|
||||||
|
unsigned msgCbCnt; ///< count of msgs delivered via cmAsCallback() .
|
||||||
|
unsigned audioCbCnt; ///< count of times the DSP execution was requested via cmAsCallback().
|
||||||
|
|
||||||
|
unsigned iDevIdx; ///< Input device index
|
||||||
|
unsigned oDevIdx; ///< Output device index
|
||||||
|
|
||||||
|
unsigned overflowCnt; ///< count of times the audio input buffers overflowed
|
||||||
|
unsigned underflowCnt; ///< count of times the audio output buffers underflowed
|
||||||
|
unsigned iMeterCnt; ///< count of input meter channels
|
||||||
|
unsigned oMeterCnt; ///< count of output meter channels
|
||||||
|
|
||||||
|
} cmAudioSysStatus_t;
|
||||||
|
|
||||||
|
// cmDspNetMsg_t sub-selector id's
|
||||||
|
enum {
|
||||||
|
kNetHelloSelAsId, // node->node awake msg
|
||||||
|
kNetDstIdReqSelAsId, // src->dst request a dst id
|
||||||
|
kNetDstIdReqDoneAsId, // src->dst all requests have been sent
|
||||||
|
kNetDstIdSelAsId, // dst->src provide dst id
|
||||||
|
kNetDoneSelAsId, // node->node sync done
|
||||||
|
kNetErrSelAsId, // node->node sync error
|
||||||
|
kNetEvtSelAsId // src->dst send cmDspEvnt_t
|
||||||
|
};
|
||||||
|
|
||||||
|
// Message Layout [ cmDspNetMsg_t dstInstLabel[] dstVarLabel[] ]
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned asSubIdx;
|
||||||
|
unsigned selId; // kNetSyncSelAsId
|
||||||
|
unsigned subSelId; // see above kNetXXXSelAsId
|
||||||
|
unsigned srcId;
|
||||||
|
unsigned dstId;
|
||||||
|
cmDspValue_t value;
|
||||||
|
// char dstInstLabel[] - with kNetSyncSelAsId only
|
||||||
|
// char dstVarLabel[] - with kNetSyncSelAsId only
|
||||||
|
} cmDspNetMsg_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned asSubIdx;
|
||||||
|
unsigned selId; // kNetEvtSelAsId
|
||||||
|
unsigned dstId;
|
||||||
|
|
||||||
|
// The cmDspValue_t field must come last in the structure in
|
||||||
|
// order for the cmDsvSerialize() to work.
|
||||||
|
cmDspValue_t value; // Data value associated with this msg.
|
||||||
|
|
||||||
|
} cmDspNetEvt_t;
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkMsgRC = cmOkRC,
|
||||||
|
kSerializeFailMsgRC,
|
||||||
|
kSendFailMsgRC,
|
||||||
|
kDecodeFailMsgRC
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmMsgRC_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef cmMsgRC_t (*cmMsgSendFuncPtr_t)(void* cbDataPtr, unsigned msgByteCnt, const void* msg );
|
||||||
|
|
||||||
|
cmMsgRC_t cmMsgSend(
|
||||||
|
cmErr_t* err,
|
||||||
|
unsigned asSubIdx,
|
||||||
|
unsigned msgTypeId,
|
||||||
|
unsigned selId,
|
||||||
|
unsigned flags,
|
||||||
|
unsigned instId,
|
||||||
|
unsigned instVarId,
|
||||||
|
const cmDspValue_t* valPtr,
|
||||||
|
cmMsgSendFuncPtr_t sendFunc,
|
||||||
|
void* cbDataPtr );
|
||||||
|
|
||||||
|
cmMsgRC_t cmMsgPeekAsSubIndex( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
|
||||||
|
cmMsgRC_t cmMsgPeekMsgTypeId( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
|
||||||
|
cmMsgRC_t cmMsgPeekSelId( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
|
||||||
|
cmMsgRC_t cmMsgPeekFlags( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
|
||||||
|
cmMsgRC_t cmMsgPeekInstId( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
|
||||||
|
cmMsgRC_t cmMsgPeekInstVarId( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
83
cmOp.c
Normal file
83
cmOp.c
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include "cmPrefix.h"
|
||||||
|
#include "cmGlobal.h"
|
||||||
|
#include "cmFloatTypes.h"
|
||||||
|
#include "cmOp.h"
|
||||||
|
|
||||||
|
void vs_Zero( cmSample_t v[], unsigned vn)
|
||||||
|
{ memset(v,0,sizeof(v[0])*vn); }
|
||||||
|
|
||||||
|
|
||||||
|
cmReal_t vs_Sine( cmSample_t v[], unsigned vn, cmReal_t hzRad, cmReal_t initPhs )
|
||||||
|
{
|
||||||
|
const cmSample_t* ep = v + vn;
|
||||||
|
double phs = initPhs;
|
||||||
|
|
||||||
|
while(v<ep)
|
||||||
|
{
|
||||||
|
*v++ = (cmSample_t)sin( phs );
|
||||||
|
phs += hzRad;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (cmReal_t)phs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vs_Rand( cmSample_t v[], unsigned vn, cmSample_t min, cmSample_t max )
|
||||||
|
{
|
||||||
|
const cmSample_t* ep = v + vn;
|
||||||
|
while(v<ep)
|
||||||
|
*v++ = ((cmSample_t)rand()/RAND_MAX) * (max-min) + min;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void vs_MultVVS( cmSample_t d[], const cmSample_t s[], unsigned n, cmReal_t mult )
|
||||||
|
{
|
||||||
|
const cmSample_t* ep = d + n;
|
||||||
|
while(d<ep)
|
||||||
|
*d++ = *s++ * mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vs_SumMultVVS( cmSample_t d[], const cmSample_t s[], unsigned n, cmReal_t mult )
|
||||||
|
{
|
||||||
|
const cmSample_t* ep = d + n;
|
||||||
|
while(d<ep)
|
||||||
|
*d++ += *s++ * mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void vs_Copy( cmSample_t d[], const cmSample_t s[], unsigned n )
|
||||||
|
{
|
||||||
|
memcpy(d,s,n*sizeof(d[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
cmSample_t vs_SquaredSum( const cmSample_t s[], unsigned n )
|
||||||
|
{
|
||||||
|
cmSample_t sum = 0;
|
||||||
|
const cmSample_t* ep = s + n;
|
||||||
|
|
||||||
|
for(;s<ep;++s)
|
||||||
|
sum += *s * *s;
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
unsigned vs_Interp2( cmSample_t v[], unsigned vn, const cmSample_t[] xx, const cmSample_t y[], unsigned yn )
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
for(; i<vn; ++i)
|
||||||
|
{
|
||||||
|
double x = fmod(*xx++,yn);
|
||||||
|
unsigned x0 = floor(x);
|
||||||
|
unsigned x1 = x0 + 1;
|
||||||
|
double d = x - x0;
|
||||||
|
|
||||||
|
if( x1>=yn || x0>=yn)
|
||||||
|
break;
|
||||||
|
|
||||||
|
*v++ = y[x0] + (y[x1] - y[x0]) * d;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
*/
|
30
cmOp.h
Normal file
30
cmOp.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef cmOp_h
|
||||||
|
#define cmOp_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void vs_Zero( cmSample_t v[], unsigned vn);
|
||||||
|
cmReal_t vs_Sine( cmSample_t v[], unsigned vn, cmReal_t hzRad, cmReal_t initPhs );
|
||||||
|
void vs_Rand( cmSample_t v[], unsigned vn, cmSample_t min, cmSample_t max );
|
||||||
|
|
||||||
|
void vs_Copy( cmSample_t d[], const cmSample_t s[], unsigned n );
|
||||||
|
|
||||||
|
cmSample_t vs_SquaredSum( const cmSample_t s[], unsigned n );
|
||||||
|
|
||||||
|
// d[] = s[] * mult;
|
||||||
|
void vs_MultVVS( cmSample_t d[], const cmSample_t s[], unsigned n, cmReal_t mult );
|
||||||
|
|
||||||
|
//d[] += s[] * mult
|
||||||
|
void vs_SumMultVVS(cmSample_t d[], const cmSample_t s[], unsigned n, cmReal_t mult );
|
||||||
|
|
||||||
|
// Interpolate the values of y[yn] at the points defined by x[vn] and store the result in v[vn].
|
||||||
|
// User linear interpolation.
|
||||||
|
//void vs_Interp2( cmSample_t v[], unsigned vn, const cmSample_t[] x, const cmSample_t y[], unsigned yn );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1149
cmPgmOpts.c
Normal file
1149
cmPgmOpts.c
Normal file
File diff suppressed because it is too large
Load Diff
191
cmPgmOpts.h
Normal file
191
cmPgmOpts.h
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
#ifndef cmPgmOpts_h
|
||||||
|
#define cmPgmOpts_h
|
||||||
|
|
||||||
|
//{
|
||||||
|
//(
|
||||||
|
// Command line program option syntax:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// '-'<charId>* <value>+ - a dash followed by one or more one character id's optionally followed by a parameter value.
|
||||||
|
// '--'<wordId> <value>+ - a double dash followed by a word id optionally followed by a parameter value.
|
||||||
|
//
|
||||||
|
// A char id is a single character.
|
||||||
|
// A word id is a string of characters with no intervening white space.
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
// 1) There is no way to give multiple <values> without an intervening character or word id.
|
||||||
|
// A <value> must therefore always be immediately preceded by an id.
|
||||||
|
// 2) There must never be a space between the dash(es) and the characters forming the identifier.
|
||||||
|
// 3) There must always be a space between the identifier and any subsequent <value>.
|
||||||
|
// 4) See src/mas/src/main.c for a complete example.
|
||||||
|
//
|
||||||
|
// Terms:
|
||||||
|
// Parameter - Description of the allowable types and constraints for a program option.
|
||||||
|
// Argument - An instance of a parameter or the values associated with a parameter.
|
||||||
|
//
|
||||||
|
//)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//(
|
||||||
|
|
||||||
|
// cmPgmOpts Result Codes
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kOkPoRC = cmOkRC,
|
||||||
|
kSyntaxErrPoRC,
|
||||||
|
kInvalidIdxPoRC,
|
||||||
|
kParseFailPoRC,
|
||||||
|
kNoReqArgPoRC,
|
||||||
|
kDuplicateIdPoRC,
|
||||||
|
kArgCntErrPoRC,
|
||||||
|
kParmNotFoundPoRC,
|
||||||
|
kInstNotFoundPoRC,
|
||||||
|
kTypeErrPoRC
|
||||||
|
};
|
||||||
|
|
||||||
|
// cmPgmOpts parameter id's
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kPrintHelpPoId,
|
||||||
|
kVersionPoId,
|
||||||
|
kPrintParmsPoId,
|
||||||
|
kNoExecPoId,
|
||||||
|
kBasePoId
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef cmRC_t cmPoRC_t;
|
||||||
|
typedef cmHandle_t cmPgmOptH_t;
|
||||||
|
|
||||||
|
extern cmPgmOptH_t cmPgmOptNullHandle;
|
||||||
|
|
||||||
|
// Initialize a program options parser.
|
||||||
|
cmPoRC_t cmPgmOptInitialize(cmCtx_t* c, cmPgmOptH_t* hp, const cmChar_t* helpBegStr, const cmChar_t* helpEndStr );
|
||||||
|
|
||||||
|
// Finalize a program options parser.
|
||||||
|
cmPoRC_t cmPgmOptFinalize( cmPgmOptH_t* hp );
|
||||||
|
|
||||||
|
// Return true if the program options parser was successfully initialized.
|
||||||
|
bool cmPgmOptIsValid( cmPgmOptH_t h );
|
||||||
|
|
||||||
|
// Flag used by the 'flags' arg. to cmPgmOptInstall().
|
||||||
|
enum {
|
||||||
|
kNoPoFlags = 0x000,
|
||||||
|
kReqPoFl = 0x001, // this is a required parameter
|
||||||
|
kBoolPoFl = 0x002, // this parameter takes a value
|
||||||
|
kCharPoFl = 0x004, // parm. value is a character
|
||||||
|
kIntPoFl = 0x008, // parm. value is a decimal int
|
||||||
|
kUIntPoFl = 0x010, // parm. value is a decimal unsigned int
|
||||||
|
kHexPoFl = 0x020, // parm. value is a hex. unsigned int
|
||||||
|
kDblPoFl = 0x040, // parm. value is a decimal double
|
||||||
|
kStrPoFl = 0x080, // parm. value is a string (default)
|
||||||
|
kEnumPoFl = 0x100, // parm. valus is a enum type (automatically set by a non-zero enumId)
|
||||||
|
|
||||||
|
kTypeMaskPoFl = 0x1f6
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Define a parameter.
|
||||||
|
//
|
||||||
|
// unsigned numId, - numeric id used to identify this parameter
|
||||||
|
// const cmChar_t charId, - a character used to identify this parameter
|
||||||
|
// const cmChar_t* wordId, - a label used to identify this parameter
|
||||||
|
// unsigned flags, - kNoPoFlags | kReqPoFl (the type flags are automatically assigned)
|
||||||
|
// unsigned enumId, - non-zero value used to group enumerated parameter values (ignored for non-enum types)
|
||||||
|
// unsigned cnt, - count of times this parameter may repeated or 0 for an unlimited repetitions
|
||||||
|
// const cmChar_t* helpStr - a textual description of this parameter
|
||||||
|
//
|
||||||
|
// Notes
|
||||||
|
// 1) 'numId','charId', and 'wordId' must all be unique among all parameter definitions.
|
||||||
|
// 2) If no parameter value type flag is given then the type is assumed to be of type bool.
|
||||||
|
// 3) For all parameter value types except the string type arguments are automatically parsed to the
|
||||||
|
// defined type. To avoid automatic parsing simply define the type as a string (using cmPgmOptInstallStr()).
|
||||||
|
// 4) All expected parameters must be defined prior to calling cmPgmOptParse().
|
||||||
|
// 5) One call to cmPgmOPtInstallEnum() is made for each possible enumeration value - where the 'enumId' gives the value.
|
||||||
|
// A given set of associated enum values is grouped by giving a common 'numId'.
|
||||||
|
// Example:
|
||||||
|
// cmPgmOptInstallEnum(h,colorId,...,redId,...);
|
||||||
|
// cmPgmOptInstallEnum(h,colorId,...,greenId,...);
|
||||||
|
// cmPgmOptInstallEnum(h,colorId,...,blueId,...);
|
||||||
|
//
|
||||||
|
// 6) The following id's are used for built-in actions and are therefore restricted from use by the client:
|
||||||
|
// a. -h --help Print the program usage information.
|
||||||
|
// b. -v --version Print the program version informatoin.
|
||||||
|
// c. -p --parms Print the program parameter values.
|
||||||
|
//
|
||||||
|
cmPoRC_t cmPgmOptInstallChar(cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, cmChar_t dfltVal, cmChar_t* retValPtr, unsigned cnt, const cmChar_t* helpStr );
|
||||||
|
cmPoRC_t cmPgmOptInstallBool(cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, bool dfltVal, bool* retValPtr, unsigned cnt, const cmChar_t* helpStr );
|
||||||
|
cmPoRC_t cmPgmOptInstallInt( cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, int dfltVal, int* retValPtr, unsigned cnt, const cmChar_t* helpStr );
|
||||||
|
cmPoRC_t cmPgmOptInstallUInt(cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, unsigned dfltVal, unsigned* retValPtr, unsigned cnt, const cmChar_t* helpStr );
|
||||||
|
cmPoRC_t cmPgmOptInstallDbl( cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, double dfltVal, double* retValPtr, unsigned cnt, const cmChar_t* helpStr );
|
||||||
|
cmPoRC_t cmPgmOptInstallStr( cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, const cmChar_t* dfltVal, const cmChar_t** retValPtr, unsigned cnt, const cmChar_t* helpStr );
|
||||||
|
cmPoRC_t cmPgmOptInstallEnum(cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, unsigned enumId, unsigned dfltVal, unsigned* retValPtr, unsigned cnt, const cmChar_t* helpStr );
|
||||||
|
|
||||||
|
// Parse a set of command line arguments.
|
||||||
|
cmPoRC_t cmPgmOptParse( cmPgmOptH_t h, unsigned argCnt, char* argArray[] );
|
||||||
|
|
||||||
|
// Get the total count of arguments passed to cmPgmOptParse().
|
||||||
|
unsigned cmPgmOptArgCount( cmPgmOptH_t h);
|
||||||
|
|
||||||
|
// Get the numeric id associated with each argument.
|
||||||
|
unsigned cmPgmOptNumId( cmPgmOptH_t h, unsigned argIdx );
|
||||||
|
|
||||||
|
// Manually convert each argument string into the specified type.
|
||||||
|
// These functions are useful if all of the parameters were defined using cmPgmOptInstallStr().
|
||||||
|
// Use cmPgmOptRC() to check for errors.
|
||||||
|
char cmPgmOptParseArgChar(cmPgmOptH_t h, unsigned argIdx );
|
||||||
|
bool cmPgmOptParseArgBool(cmPgmOptH_t h, unsigned argIdx );
|
||||||
|
int cmPgmOptParseArgInt( cmPgmOptH_t h, unsigned argIdx );
|
||||||
|
unsigned cmPgmOptParseArgUInt(cmPgmOptH_t h, unsigned argIdx );
|
||||||
|
double cmPgmOptParseArgDbl( cmPgmOptH_t h, unsigned argIdx );
|
||||||
|
const char* cmPgmOptParseArgStr( cmPgmOptH_t h, unsigned argIdx );
|
||||||
|
|
||||||
|
// Get the count of arg's for a given parameter.
|
||||||
|
unsigned cmPgmOptParmArgCount( cmPgmOptH_t h, unsigned numId );
|
||||||
|
|
||||||
|
// Get the value associated with each parsed argument.
|
||||||
|
// If no argument was given for the requested parameter
|
||||||
|
// (cmPgmOptParmArgCount(numId)==0) and 'instIdx' == 0 then the default value is returned.
|
||||||
|
// Use cmPgOptRC() to check for errors.
|
||||||
|
//
|
||||||
|
// The parameter identified by numId must has been defined by an earlier call to
|
||||||
|
// cmPgmOptInstallChar() or this function
|
||||||
|
char cmPgmOptArgChar( cmPgmOptH_t h, unsigned numId, unsigned instIdx );
|
||||||
|
|
||||||
|
// No matter the type of the parameter it will be converted to a bool.
|
||||||
|
bool cmPgmOptArgBool( cmPgmOptH_t h, unsigned numId, unsigned instIdx );
|
||||||
|
|
||||||
|
// All types, except strings, are converted to type int. Doubles are rounded.
|
||||||
|
int cmPgmOptArgInt( cmPgmOptH_t h, unsigned numId, unsigned instIdx );
|
||||||
|
|
||||||
|
// All types, except strings, are converted to type unsigned. Doubles are rounded.
|
||||||
|
unsigned cmPgmOptArgUInt( cmPgmOptH_t h, unsigned numId, unsigned instIdx );
|
||||||
|
|
||||||
|
// All types except strings, are converted to double.
|
||||||
|
double cmPgmOptArgDbl( cmPgmOptH_t h, unsigned numId, unsigned instIdx );
|
||||||
|
|
||||||
|
// If the parameter is not defined as a string then the arg. string value us returned.
|
||||||
|
const char* cmPgmOptArgStr( cmPgmOptH_t h, unsigned numId, unsigned instIdx );
|
||||||
|
|
||||||
|
|
||||||
|
// Get and set the current result code.
|
||||||
|
cmPoRC_t cmPgmOptRC( cmPgmOptH_t h, cmPoRC_t rc );
|
||||||
|
|
||||||
|
// Returns 'true' only if non- built-in options were selected
|
||||||
|
bool cmPgmOptHandleBuiltInActions( cmPgmOptH_t h, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
void cmPgmOptPrintHelp( cmPgmOptH_t h, cmRpt_t* rpt );
|
||||||
|
void cmPgmOptPrintVersion( cmPgmOptH_t h, cmRpt_t* rpt );
|
||||||
|
void cmPgmOptPrintParms( cmPgmOptH_t h, cmRpt_t* rpt );
|
||||||
|
|
||||||
|
//)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
16
cmPrefix.h
Normal file
16
cmPrefix.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef cmPrefix_h
|
||||||
|
#define cmPrefix_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user