Initial commit

This commit is contained in:
kevin 2012-10-29 20:52:39 -07:00
commit b108da1911
192 changed files with 110710 additions and 0 deletions

75
Makefile.am Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

217
app/cmTimeLine.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

55
cmAudDsp.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

305
cmAudioFile.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

298
cmAudioSys.h Normal file
View 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
View 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

1146
cmCsv.c Normal file

File diff suppressed because it is too large Load Diff

149
cmCsv.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

279
cmFeatFile.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

231
cmFileSys.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

360
cmFrameFile.h Normal file
View 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
View File

@ -0,0 +1,2 @@

154
cmGlobal.h Normal file
View 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

File diff suppressed because it is too large Load Diff

102
cmGnuPlot.h Normal file
View 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

2670
cmGr.c Normal file

File diff suppressed because it is too large Load Diff

871
cmGr.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

164
cmGrPage.h Normal file
View 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

File diff suppressed because it is too large Load Diff

260
cmGrPlot.h Normal file
View 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
View 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
View 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

4077
cmJson.c Normal file

File diff suppressed because it is too large Load Diff

504
cmJson.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

163
cmMidiFile.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

191
cmPgmOpts.h Normal file
View 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
View 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