diff --git a/cmProc5.c b/cmProc5.c index beaf543..22c6324 100644 --- a/cmProc5.c +++ b/cmProc5.c @@ -16,7 +16,11 @@ #include "cmProcObj.h" #include "cmProcTemplate.h" #include "cmMath.h" +#include "cmFile.h" +#include "cmTime.h" +#include "cmMidi.h" #include "cmProc.h" +#include "cmProc2.h" #include "cmProc5.h" #include "cmVectOps.h" @@ -123,3 +127,219 @@ cmRC_t cmGoertzelExec( cmGoertzel* p, const cmSample_t* inpV, unsigned procSmpCn } +//======================================================================================================================= +double _cmGoldSigSinc( double t, double T ) +{ + double x = t/T; + return x == 0 ? 1.0 : sin(M_PI*x)/(M_PI*x); +} + +void _cmGoldSigRaisedCos( cmSample_t* yV, int yN, double sPc, double beta ) +{ + int i; + + for(i=0; ia.samplesPerChip; + int osf = p->a.rcosOSFact; + + // for each bit in the spreading-code + for(i=0; imlsN; ++i) + { + int j = (i*sPc) + sPc/2; // index into bbV[] of center of impulse response + int k = j - (sPc*osf)/2; // index into bbV[] of start of impulse response + int h; + + // for each sample in the impulse response + for(h=0; hrcosN; ++h,++k) + { + + while( k<0 ) + k += p->sigN; + + while( k>=p->sigN ) + k -= p->sigN; + + p->ch[chIdx].bbV[k] += p->ch[chIdx].pnV[i] * p->rcosV[h]; + } + } +} + +void _cmGoldSigModulate( cmGoldSig_t* p, unsigned chIdx ) +{ + unsigned i; + double rps = 2.0 * M_PI * p->a.carrierHz / p->a.srate; + cmSample_t* yV = p->ch[chIdx].mdV; + cmSample_t* bbV = p->ch[chIdx].bbV; + + for(i=0; isigN; ++i) + yV[ i ] = bbV[i]*cos(rps*i) + bbV[i]*sin(rps*i); + + // apply a half Hann envelope to the onset/offset of the id signal + if( p->a.envMs > 0 ) + { + unsigned wndMs = p->a.envMs * 2; + unsigned wndN = wndMs * p->a.srate / 1000; + wndN += wndN % 2 ? 0 : 1; // force the window length to be odd + unsigned wNo2 = wndN/2 + 1; + cmSample_t wndV[ wndN ]; + cmVOS_Hann(wndV,wndN); + cmVOS_MultVV(yV,wNo2,wndV); + cmVOS_MultVV(yV + p->sigN - wNo2, wNo2, wndV + wNo2 - 1); + } + +} + +cmGoldSig_t* cmGoldSigAlloc( cmCtx* ctx, cmGoldSig_t* p, const cmGoldSigArg_t* a ) +{ + cmGoldSig_t* op = cmObjAlloc(cmGoldSig_t,ctx,p); + + if( a != NULL ) + if( cmGoldSigInit(op,a) != cmOkRC ) + cmGoldSigFree(&op); + + return op; + +} + +cmRC_t cmGoldSigFree( cmGoldSig_t** pp ) +{ + cmRC_t rc = cmOkRC; + + if( pp == NULL || *pp == NULL ) + return rc; + + cmGoldSig_t* p = *pp; + + if((rc = cmGoldSigFinal(p)) != cmOkRC ) + return rc; + + unsigned i; + for(i=0; ia.chN; ++i) + { + cmMemFree(p->ch[i].bbV); + cmMemFree(p->ch[i].mdV); + } + + cmMemFree(p->ch); + cmMemFree(p->rcosV); + cmMemFree(p->pnM); + cmMemFree(p); + *pp = NULL; + + return rc; +} + +cmRC_t cmGoldSigInit( cmGoldSig_t* p, const cmGoldSigArg_t* a ) +{ + cmRC_t rc = cmOkRC; + unsigned i; + + p->a = *a; // store arg recd + p->ch = cmMemResizeZ(cmGoldSigCh_t,p->ch,a->chN); // alloc channel array + p->mlsN = (1 << a->lfsrN) - 1; // calc spreading code length + p->rcosN = a->samplesPerChip * a->rcosOSFact; // calc rcos imp. resp. length + p->rcosN += (p->rcosN % 2)==0; // force rcos imp. length odd + p->rcosV = cmMemResizeZ(cmSample_t,p->rcosV,p->rcosN); // alloc rcos imp. resp. vector + p->pnM = cmMemResizeZ(int,p->pnM,p->mlsN*a->chN); // alloc spreading-code mtx + p->sigN = p->mlsN * a->samplesPerChip; // calc audio signal length + + // generate spreading codes + if( cmGenGoldCodes(a->lfsrN, a->mlsCoeff0, a->mlsCoeff1, a->chN, p->pnM, p->mlsN ) == false ) + { + rc = cmCtxRtCondition(&p->obj,cmSubSysFailRC,"Unable to generate sufficient balanced Gold codes."); + goto errLabel; + } + + // generate the rcos impulse response + _cmGoldSigRaisedCos(p->rcosV,p->rcosN,a->samplesPerChip,a->rcosBeta); + + // for each channel + for(i=0; ichN; ++i) + { + // Note: if (i*p->mlsN) is set to 0 in the following line then all channels + // will use the same spreading code. + p->ch[i].pnV = p->pnM + (i*p->mlsN); // get ch. spreading code + p->ch[i].bbV = cmMemResizeZ(cmSample_t,p->ch[i].bbV,p->sigN); // alloc baseband signal vector + p->ch[i].mdV = cmMemResizeZ(cmSample_t,p->ch[i].mdV,p->sigN); // alloc output audio vector + + // Convolve spreading code with rcos impulse reponse to form baseband signal. + _cmGoldSigConv(p, i ); + + // Modulate baseband signal to carrier frq. and apply attack/decay envelope. + _cmGoldSigModulate(p, i ); + } + + errLabel: + if((rc = cmErrLastRC(&p->obj.err)) != cmOkRC ) + cmGoldSigFree(&p); + + return rc; +} + +cmRC_t cmGoldSigFinal( cmGoldSig_t* p ) +{ return cmOkRC; } + +cmRC_t cmGoldSigWrite( cmCtx* ctx, cmGoldSig_t* p, const char* fn ) +{ + cmVectArray_t* vap = NULL; + unsigned i; + + vap = cmVectArrayAlloc(ctx,kSampleVaFl); + + for(i=0; ia.chN; ++i) + { + cmVectArrayAppendS(vap,p->ch[i].bbV,p->sigN); + cmVectArrayAppendS(vap,p->ch[i].mdV,p->sigN); + } + + cmVectArrayWrite(vap,fn); + + cmVectArrayFree(&vap); + + return cmOkRC; +} + + +cmRC_t cmGoldSigGen( cmGoldSig_t* p, unsigned chIdx, unsigned prefixN, unsigned dsN, unsigned *bsiV, unsigned bsiN, double noiseGain, cmSample_t** yVRef, unsigned* yNRef ) +{ + unsigned yN = prefixN + bsiN * (p->sigN + dsN); + cmSample_t* yV = cmMemAllocZ(cmSample_t,yN); + unsigned i; + + cmVOS_Random(yV, yN, -noiseGain, noiseGain ); + + for(i=0; isigN + dsN); + + cmVOS_AddVV(yV + bsiV[i], p->sigN, p->ch[chIdx].mdV ); + } + + if( yVRef != NULL ) + *yVRef = yV; + + if( yNRef != NULL ) + *yNRef = yN; + + return cmOkRC; +} + + + diff --git a/cmProc5.h b/cmProc5.h index 927d158..8213f98 100644 --- a/cmProc5.h +++ b/cmProc5.h @@ -39,7 +39,76 @@ extern "C" { cmRC_t cmGoertzelExec( cmGoertzel* p, const cmSample_t* in, unsigned procSmpCnt, double* outV, unsigned chCnt ); + //======================================================================================================================= + // Gold Code Signal Generator + // + typedef struct + { + unsigned chN; // count of channels (each channel has a unique id) + double srate; // system sample rate (samples/second) + unsigned lfsrN; // linear feedback shift register (LFSR) length used to form Gold codes + unsigned mlsCoeff0; // LFSR coeff. 0 + unsigned mlsCoeff1; // LFSR coeff. 1 + unsigned samplesPerChip; // samples per spreading code bit + double rcosBeta; // raised cosine impulse response beta coeff. + unsigned rcosOSFact; // raised cosine impulse response oversample factor + double carrierHz; // carrier frequency + double envMs; // attack/decay envelope duration + } cmGoldSigArg_t; + + typedef struct + { + int* pnV; // pnV[ mlsN ] spread code (aliased from pnM[:,i]) + cmSample_t* bbV; // bbV[ sigN ] baseband signal at audio rate + cmSample_t* mdV; // mdV[ sigN ] modulated signal at audio rate + } cmGoldSigCh_t; + + typedef struct + { + cmObj obj; // + cmGoldSigArg_t a; // argument record + cmGoldSigCh_t* ch; // ch[ chN ] channel array + int* pnM; // pnM[mlsN,chN] (aliased to ch[].pnV) + cmSample_t* rcosV; // rcosV[rcosN] raised cosine impulse response + unsigned rcosN; // length of raised cosine impulse response + unsigned mlsN; // length of Gold codes (Maximum length sequence length) + unsigned sigN; // length of channel signals bbV[] and mdV[] + } cmGoldSig_t; + + + cmGoldSig_t* cmGoldSigAlloc( cmCtx* ctx, cmGoldSig_t* p, const cmGoldSigArg_t* a ); + cmRC_t cmGoldSigFree( cmGoldSig_t** pp ); + + cmRC_t cmGoldSigInit( cmGoldSig_t* p, const cmGoldSigArg_t* a ); + cmRC_t cmGoldSigFinal( cmGoldSig_t* p ); + + cmRC_t cmGoldSigWrite( cmCtx* ctx, cmGoldSig_t* p, const char* fn ); + + // Generate a signal consisting of underlying white noise with + // bsiN repeated copies of the id signal associated with + // channel 'chIdx'. Each discrete id signal copy is separated by 'dsN' samples. + // The signal will be prefixed with 'prefixN' samples of silence (noise). + // On return sets 'yVRef' to point to the generated signal and 'yNRef' + // to the count of samples in 'yVRef'. + // On error sets yVRef to NULL and yNRef to zero. + // The vector returned in 'yVRef' should be freed via atMemFree(). + // On return sets bsiV[bsiN] to the onset sample index of each id signal copy. + // The background noise signal is limited to the range -noiseGain to noiseGain. + cmRC_t cmGoldSigGen( + cmGoldSig_t* p, + unsigned chIdx, + unsigned prefixN, + unsigned dsN, + unsigned *bsiV, + unsigned bsiN, + double noiseGain, + cmSample_t** yVRef, + unsigned* yNRef ); + + cmRC_t cmGoldSigTest( cmCtx* ctx ); + + #ifdef __cplusplus } #endif