Merge branch 'master' of gitea.larke.org:klarke/libcw

# Conflicts:
#	Makefile.am
This commit is contained in:
kevin 2020-10-11 14:42:28 -04:00
commit 1aba926fda
19 changed files with 1293 additions and 39 deletions

View File

@ -4,8 +4,8 @@ libcwSRC =
libcwHDR += src/libcw/cwCommon.h src/libcw/cwCommonImpl.h src/libcw/cwMem.h src/libcw/cwLog.h src/libcw/cwUtility.h
libcwSRC += src/libcw/cwCommonImpl.cpp src/libcw/cwMem.cpp src/libcw/cwLog.cpp src/libcw/cwUtility.cpp
libcwHDR += src/libcw/cwVectOps.h src/libcw/cwMtx.h
libcwSRC += src/libcw/cwMtx.cpp
libcwHDR += src/libcw/cwString.h src/libcw/cwVectOps.h src/libcw/cwMtx.h
libcwSRC += src/libcw/cwString.cpp src/libcw/cwMtx.cpp
libcwHDR += src/libcw/cwFileSys.h src/libcw/cwText.h src/libcw/cwFile.h src/libcw/cwTime.h src/libcw/cwLex.h src/libcw/cwNumericConvert.h
libcwSRC += src/libcw/cwFileSys.cpp src/libcw/cwText.cpp src/libcw/cwFile.cpp src/libcw/cwTime.cpp src/libcw/cwLex.cpp
@ -22,8 +22,8 @@ libcwSRC += src/libcw/cwThread.cpp src/libcw/cwMutex.cpp src/libcw/cwThreadMach
libcwHDR += src/libcw/cwMpScNbQueue.h src/libcw/cwSpScBuf.h src/libcw/cwSpScQueueTmpl.h
libcwSRC += src/libcw/cwSpScBuf.cpp src/libcw/cwSpScQueueTmpl.cpp
libcwHDR += src/libcw/cwSvg.h src/libcw/cwAudioFile.h src/libcw/cwAudioFileOps.h
libcwSRC += src/libcw/cwSvg.cpp src/libcw/cwAudioFile.cpp src/libcw/cwAudioFileOps.cpp
libcwHDR += src/libcw/cwAudioFile.h src/libcw/cwAudioFileOps.h
libcwSRC += src/libcw/cwAudioFile.cpp src/libcw/cwAudioFileOps.cpp
if cwWEBSOCK
libcwHDR += src/libcw/cwWebSock.h src/libcw/cwWebSockSvr.h
@ -59,6 +59,9 @@ libcwSRC += src/libcw/cwSocket.cpp
libcwHDR += src/libcw/cwTcpSocket.h src/libcw/cwTcpSocketSrv.h src/libcw/cwTcpSocketTest.h
libcwSRC += src/libcw/cwTcpSocket.cpp src/libcw/cwTcpSocketSrv.cpp src/libcw/cwTcpSocketTest.cpp
libcwHDR += src/libcw/cwDsp.h
libcwSRC += src/libcw/cwDsp.cpp
if cwWEBSOCK
libcwHDR += src/libcw/cwIo.h src/libcw/cwIoTest.h
libcwSRC += src/libcw/cwIo.cpp src/libcw/cwIoTest.cpp
@ -67,6 +70,9 @@ endif
libcwHDR += src/libcw/cwMdns.h src/libcw/cwEuCon.h src/libcw/cwDnsSd.h src/libcw/dns_sd/dns_sd.h src/libcw/dns_sd/dns_sd_print.h src/libcw/dns_sd/dns_sd_const.h src/libcw/dns_sd/fader.h src/libcw/dns_sd/rpt.h
libcwSRC += src/libcw/cwMdns.cpp src/libcw/cwEuCon.cpp src/libcw/cwDnsSd.cpp src/libcw/dns_sd/dns_sd.cpp src/libcw/dns_sd/dns_sd_print.cpp src/libcw/dns_sd/fader.cpp src/libcw/dns_sd/rpt.cpp
if cwWEB
# libcwHDR += src/libcw/cwDataSets.h
# libcwSRC += src/libcw/cwDataSets.cpp
else
# libcwHDR += src/libcw/cwSvg.h src/libcw/cwDataSets.h
# libcwSRC += src/libcw/cwSvg.cpp src/libcw/cwDataSets.cpp
endif

View File

@ -923,8 +923,9 @@ void cw::audio::buf::report(handle_t h)
/// [cwAudioBufExample]
void cw::audio::buf::test()
cw::rc_t cw::audio::buf::test()
{
rc_t rc = kOkRC;
unsigned devIdx = 0;
unsigned devCnt = 1 ;
unsigned dspFrameCnt = 10;
@ -1021,6 +1022,8 @@ void cw::audio::buf::test()
cwLogInfo("\n");
destroy(h);
return rc;
}
/// [cwAudioBufExample]

View File

@ -232,7 +232,7 @@ namespace cw
void report( handle_t h );
// Run a buffer usage simulation to test the class. cmAudioPortTest.c calls this function.
void test();
rc_t test();
//)

View File

@ -1590,7 +1590,7 @@ void cw::audio::device::alsa::deviceRealTimeReport(struct driver_str* drv, unsig
//)
//[
void cw::audio::device::alsa::report( handle_t h )
cw::rc_t cw::audio::device::alsa::report( handle_t h )
{
alsa_t* p = _handleToPtr(h);
unsigned i;
@ -1603,17 +1603,21 @@ void cw::audio::device::alsa::report( handle_t h )
}
snd_config_update_free_global();
return kOkRC;
}
void cw::audio::device::alsa::report()
cw::rc_t cw::audio::device::alsa::report()
{
rc_t rc = kOkRC;
handle_t h;
driver_t* d;
if( create(h,d) == kOkRC )
{
report(h);
rc = report(h);
destroy(h);
}
return rc;
}
//]

View File

@ -26,8 +26,8 @@ namespace cw
bool deviceIsStarted( struct driver_str* drv, unsigned devIdx );
void deviceRealTimeReport( struct driver_str* drv, unsigned devIdx );
void report(handle_t h );
void report();
rc_t report(handle_t h );
rc_t report();
}
}
}

View File

@ -78,6 +78,7 @@ namespace cw
p->fp = nullptr;
}
mem::release(p->fn);
mem::release(p);
}
@ -1296,6 +1297,62 @@ cw::rc_t cw::audiofile::getSumFloat( const char* fn, unsigned begFrmIdx, un
cw::rc_t cw::audiofile::getSumDouble( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr, info_t* afInfoPtr )
{ return _getDouble( fn, begFrmIdx, frmCnt, chIdx, chCnt, buf, actualFrmCntPtr, afInfoPtr, true ); }
cw::rc_t cw::audiofile::allocFloatBuf( const char* fn, float**& chBufRef, unsigned& chCntRef, unsigned& frmCntRef, info_t& afInfo, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt )
{
rc_t rc;
unsigned actualFrmCnt = 0;
frmCntRef = 0;
chCntRef = 0;
if((rc = getInfo(fn, &afInfo )) != kOkRC )
goto errLabel;
if( chCnt == 0 )
chCnt = afInfo.chCnt;
else
{
if( chIdx + chCnt > afInfo.chCnt )
{
cwLogError(kInvalidArgRC,"Requested channel indexes %i to %i exceeds available channel count %i.",chIdx,chIdx+chCnt-1,afInfo,chCnt);
goto errLabel;
}
}
if( frmCnt == 0 )
frmCnt = afInfo.frameCnt;
if( begFrmIdx + frmCnt > afInfo.frameCnt )
{
cwLogError(kInvalidArgRC,"Requested frames %i to %i exceeds available frame count %i.",begFrmIdx,begFrmIdx+frmCnt,afInfo.frameCnt);
goto errLabel;
}
chBufRef = mem::allocZ< float* >(chCnt);
for(unsigned i=0; i<chCnt; ++i)
chBufRef[i] = mem::alloc<float>(frmCnt);
if((rc = getFloat(fn, begFrmIdx, frmCnt, chIdx, chCnt, chBufRef, &actualFrmCnt, nullptr)) != kOkRC )
goto errLabel;
frmCntRef = actualFrmCnt;
chCntRef = chCnt;
errLabel:
if( rc != kOkRC )
cwLogError(rc,"Audio file allocFloat() failed.");
return rc;
}
cw::rc_t cw::audiofile::freeFloatBuf( float** floatBuf, unsigned chCnt )
{
for(unsigned i=0; i<chCnt; ++i)
mem::release(floatBuf[i]);
mem::release(floatBuf);
return kOkRC;
}
cw::rc_t cw::audiofile::writeInt( handle_t h, unsigned frmCnt, unsigned chCnt, int** srcPtrPtr )
@ -1623,6 +1680,21 @@ void cw::audiofile::printInfo( const info_t* infoPtr, log::handle_t logH )
}
cw::rc_t cw::audiofile::reportInfo( const char* audioFn )
{
rc_t rc;
info_t info;
if((rc = getInfo(audioFn,&info)) != kOkRC )
return rc;
printInfo(&info,log::globalHandle());
return rc;
}
cw::rc_t cw::audiofile::report( handle_t h, log::handle_t logH, unsigned frmIdx, unsigned frmCnt )
{
rc_t rc = kOkRC;

View File

@ -129,6 +129,10 @@ namespace cw
rc_t getSumFloat( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float** buf, unsigned* actualFrmCntPtr, info_t* afInfoPtr);
rc_t getSumDouble( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr, info_t* afInfoPtr);
// Allocate a buffer and read the file into it
rc_t allocFloatBuf( const char* fn, float**& chBufRef, unsigned& chCntRef, unsigned& frmCntRef, info_t& afInfoPtrRef, unsigned begFrmIdx=0, unsigned frmCnt=0, unsigned chIdx=0, unsigned chCnt=0 );
rc_t freeFloatBuf( float** floatBufRef, unsigned chCnt );
// Sample Writing Functions
rc_t writeInt( handle_t h, unsigned frmCnt, unsigned chCnt, int** bufPtrPtr );
rc_t writeFloat( handle_t h, unsigned frmCnt, unsigned chCnt, float** bufPtrPtr );

View File

@ -8,6 +8,8 @@
#include "cwUtility.h"
#include "cwFileSys.h"
#include "cwAudioFileOps.h"
#include "cwVectOps.h"
#include "cwDsp.h"
cw::rc_t cw::afop::sine( const char* fn, double srate, unsigned bits, double hz, double gain, double secs )
{
@ -375,6 +377,9 @@ namespace cw
xArgL[i].srcFadeOutFrmN = floor(argL[i].srcEndFadeSec * srate_Ref);
xArgL[i].dstFrmIdx = floor(argL[i].dstBegSec * srate_Ref);
//printf("cm beg:%f end:%f dst:%f gain:%f %s\n", argL[i].srcBegSec, argL[i].srcEndSec, argL[i].dstBegSec, argL[i].gain, argL[i].srcFn );
chN_Ref = std::max( chN_Ref, xArgL[i].afInfo.chCnt );
maxSrcFrmN_Ref = std::max( maxSrcFrmN_Ref, xArgL[i].srcFrmN );
@ -482,7 +487,7 @@ cw::rc_t cw::afop::cutAndMix( const char* dstFn, unsigned dstBits, const char* s
dstV = mem::allocZ<float>(dstFrmN*dstChN); // output signal buffer
srcV = mem::alloc<float>(maxSrcFrmN*dstChN); // source signal buffer
// create the src read buffer
// create the src read/ dst write buffer
for(unsigned i=0; i<dstChN; ++i)
{
dstChBufL[i] = dstV + (i*dstFrmN);
@ -497,6 +502,7 @@ cw::rc_t cw::afop::cutAndMix( const char* dstFn, unsigned dstBits, const char* s
unsigned srcFrmN = xArgL[i].srcFrmN;
unsigned srcChN = xArgL[i].afInfo.chCnt;
// read the source segment
if((rc = audiofile::getFloat( xArgL[i].srcFn, xArgL[i].srcFrmIdx, srcFrmN, chIdx, srcChN, srcChBufL, &actualFrmN, nullptr)) != kOkRC )
{
@ -515,7 +521,7 @@ cw::rc_t cw::afop::cutAndMix( const char* dstFn, unsigned dstBits, const char* s
for(unsigned k = 0; k<srcFrmN; ++k)
{
assert( xArgL[i].dstFrmIdx + k < dstFrmN );
dstChBufL[j][ xArgL[i].dstFrmIdx + k ] += srcChBufL[j][k];
dstChBufL[j][ xArgL[i].dstFrmIdx + k ] += xArgL[i].arg->gain * srcChBufL[j][k];
}
}
@ -582,15 +588,16 @@ cw::rc_t cw::afop::cutAndMix( const object_t* cfg )
argL[i].dstBegSec = argL[i].srcBegSec; // By default the src is moved to the same location
argL[i].srcBegFadeSec = crossFadeSec; // By default the beg/end fade is the global fade time.
argL[i].srcEndFadeSec = crossFadeSec;
argL[i].gain = 1;
// parse the optional parameters
if((rc = o->getv_opt("dstBegSec", argL[i].dstBegSec, "srcBegFadeSec", argL[i].srcBegFadeSec, "srcEndFadeSec", argL[i].srcEndFadeSec )) != kOkRC )
if((rc = o->getv_opt("dstBegSec", argL[i].dstBegSec, "srcBegFadeSec", argL[i].srcBegFadeSec, "srcEndFadeSec", argL[i].srcEndFadeSec, "gain", argL[i].gain )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"Invalid crossfade optional argument at argument index %i.",i);
}
}
//printf("cm beg:%f end:%f dst:%f %s\n", argL[i].srcBegSec, argL[i].srcEndSec, argL[i].dstBegSec, argL[i].srcFn );
//printf("cm beg:%f end:%f dst:%f gain:%f %s\n", argL[i].srcBegSec, argL[i].srcEndSec, argL[i].dstBegSec, argL[i].gain, argL[i].srcFn );
}
@ -629,7 +636,9 @@ cw::rc_t cw::afop::parallelMix( const char* dstFn, unsigned dstBits, const char*
cmArgL[i].srcEndSec = argL[i].srcEndSec + argL[i].fadeOutSec;
cmArgL[i].srcBegFadeSec = fadeInSec;
cmArgL[i].srcEndFadeSec = argL[i].fadeOutSec;
cmArgL[i].dstBegSec = dstBegSec;
//cmArgL[i].dstBegSec = dstBegSec;
cmArgL[i].dstBegSec = argL[i].srcBegSec - argL[0].srcBegSec;
cmArgL[i].gain = argL[i].gain;
dstBegSec += argL[i].srcEndSec - argL[i].srcBegSec;
fadeInSec = argL[i].fadeOutSec;
@ -670,17 +679,17 @@ cw::rc_t cw::afop::parallelMix( const object_t* cfg )
// parse the non-optional parameters
if((rc = o->getv("srcBegSec", argL[i].srcBegSec, "srcEndSec", argL[i].srcEndSec, "fadeOutSec", argL[i].fadeOutSec, "srcFn", argL[i].srcFn )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"Invalid crossfade argument at argument index %i.",i);
rc = cwLogError(kInvalidArgRC,"Invalid xform app argument at argument index %i.",i);
}
else
{
/*
argL[i].gain = 1;
// parse the optional parameters
if((rc = o->getv_opt("dstBegSec", argL[i].dstBegSec, "srcBegFadeSec", argL[i].srcBegFadeSec, "srcEndFadeSec", argL[i].srcEndFadeSec )) != kOkRC )
if((rc = o->getv_opt("gain", argL[i].gain )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"Invalid crossfade optional argument at argument index %i.",i);
rc = cwLogError(kInvalidArgRC,"Invalid xform app optional argument at argument index %i.",i);
}
*/
}
//printf("beg:%f end:%f fade:%f %s\n", argL[i].srcBegSec, argL[i].srcEndSec, argL[i].fadeOutSec, argL[i].srcFn );
@ -704,6 +713,222 @@ cw::rc_t cw::afop::parallelMix( const object_t* cfg )
return rc;
}
cw::rc_t cw::afop::transformApp( const object_t* cfg )
{
rc_t rc = kOkRC;
const char* srcDir = nullptr;
const char* dryFn = nullptr;
const char* dstPreFn = nullptr;
const char* dstRevFn = nullptr;
unsigned dstBits = 16;
const object_t* argNodeL = nullptr;
bool irEnableFl=false;
const char* irFn = nullptr;
double irScale = 1;
char* expSrcDir = nullptr;
char* expDryFn = nullptr;
char* expDstPreFn = nullptr;
char* expDstRevFn = nullptr;
char* expIrFn = nullptr;
unsigned i;
// read the top level cfg record
if((rc = cfg->getv("dstPreFn",dstPreFn,"dstRevFn",dstRevFn,"dstBits",dstBits,"srcDir",srcDir,"argL",argNodeL,"dryFn",dryFn,"irEnableFl",irEnableFl,"irFn",irFn,"irScale",irScale)) != kOkRC )
goto errLabel;
expSrcDir = filesys::expandPath(srcDir);
expDryFn = filesys::expandPath(dryFn);
expDstPreFn = filesys::expandPath(dstPreFn);
expDstRevFn = filesys::expandPath(dstRevFn);
if( argNodeL == nullptr )
{
rc = cwLogError(kInvalidArgRC,"No crossfades were specified.");
}
else
{
unsigned argN = argNodeL->child_count() * 2;
parallelMixArg_t argL[ argN ];
// for each source file
for(i=0; i<argNodeL->child_count(); ++i)
{
const object_t* o = argNodeL->child_ele(i);
unsigned j = i*2;
// parse the non-optional parameters
if((rc = o->getv("srcBegSec", argL[j].srcBegSec, "srcEndSec", argL[j].srcEndSec, "fadeOutSec", argL[j].fadeOutSec, "srcFn", argL[j].srcFn )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"Invalid xform app argument at argument index %i.",i);
}
else
{
argL[j].gain = 1;
// parse the optional parameters
if((rc = o->getv_opt("wetGain", argL[j].gain )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"Invalid xform app optional argument at argument index %i.",i);
}
}
// expand the source file name
argL[j].srcFn = filesys::makeFn(expSrcDir, argL[j].srcFn, NULL, NULL);
// form the dry file name
argL[j+1] = argL[j];
argL[j+1].srcFn = expDryFn;
argL[j+1].gain = 1.0 - argL[j].gain;
}
// call cross-fader
rc = parallelMix( expDstPreFn, dstBits, "", argL, argN );
for(unsigned i=0; i<argN; i+=2)
mem::release( const_cast<char*&>(argL[i].srcFn));
if( rc == kOkRC && irEnableFl )
{
expIrFn = filesys::expandPath(irFn);
rc = convolve( expDstRevFn, dstBits, expDstPreFn, expIrFn, irScale );
}
}
errLabel:
mem::release(expSrcDir);
mem::release(expDryFn);
mem::release(expDstPreFn);
mem::release(expDstRevFn);
mem::release(expIrFn);
if( rc != kOkRC )
rc = cwLogError(rc,"Transform-app failed.");
return rc;
}
cw::rc_t cw::afop::convolve( const char* dstFn, unsigned dstBits, const char* srcFn, const char* impulseResponseFn, float irScale )
{
rc_t rc = kOkRC;
float** hChBuf = nullptr;
unsigned hChN = 0;
unsigned hFrmN = 0;
double hSrate = 0;
float** xChBuf = nullptr;
unsigned xChN = 0;
unsigned xFrmN = 0;
audiofile::info_t info;
audiofile::reportInfo(impulseResponseFn);
audiofile::reportInfo(srcFn);
// read the impulse response audio file
if((rc = audiofile::allocFloatBuf(impulseResponseFn, hChBuf, hChN, hFrmN, info)) != kOkRC )
{
rc = cwLogError(rc,"The impulse audio file read failed on '%s'.", cwStringNullGuard(impulseResponseFn));
goto errLabel;
}
hSrate = info.srate;
// read the source audio file
if((rc = audiofile::allocFloatBuf(srcFn, xChBuf, xChN, xFrmN, info)) != kOkRC )
{
rc = cwLogError(rc,"The source audio file read failed on '%s'.", cwStringNullGuard(srcFn));
goto errLabel;
}
// the sample rate of impulse response and src audio signals must be the same
if( hSrate != info.srate )
{
rc = cwLogError(kInvalidArgRC,"The soure file sample rate %f does not match the impulse response sample rate %f.",info.srate,hSrate);
}
else
{
// allocate the output buffer
float* yChBuf[ xChN ];
unsigned yFrmN = xFrmN + hFrmN;
for(unsigned i=0; i<xChN; ++i)
yChBuf[i] = mem::allocZ<float>( yFrmN );
//printf("xFrmN:%i xChN:%i hFrmN:%i hChN:%i yFrmN:%i\n",xFrmN,xChN,hFrmN,hChN,yFrmN);
// scale the impulse response
for(unsigned i=0; i<hChN; ++i)
vop::mul( hChBuf[i], irScale, hFrmN );
// for each source channel
for(unsigned i=0; i<xChN && rc == kOkRC; ++i)
{
unsigned j = i >= hChN ? 0 : i; // select the impulse response channel
// convolve this channel with the impulse response and store into the output buffer
rc = dsp::convolve::apply( xChBuf[i], xFrmN, hChBuf[j], hFrmN, yChBuf[i], yFrmN );
}
// write the output file.
if( rc == kOkRC )
rc = audiofile::writeFileFloat( dstFn, info.srate, dstBits, yFrmN, xChN, yChBuf);
// release the output buffer
for(unsigned i=0; i<xChN; ++i)
mem::release( yChBuf[i] );
}
errLabel:
if(rc != kOkRC )
cwLogError(rc,"Audio file convolve failed.");
audiofile::freeFloatBuf(hChBuf, hChN );
audiofile::freeFloatBuf(xChBuf, xChN );
return rc;
}
cw::rc_t cw::afop::convolve( const object_t* cfg )
{
rc_t rc = kOkRC;
const char* srcFn = nullptr;
const char* dstFn = nullptr;
const char* irFn = nullptr;
float irScale = 1.0;
unsigned dstBits = 16;
// read the top level cfg record
if((rc = cfg->getv("dstFn",dstFn,"dstBits",dstBits,"srcFn",srcFn,"irFn",irFn,"irScale",irScale)) != kOkRC )
{
cwLogError(rc,"convolve() arg. parse failed.");
}
else
{
char* sFn = filesys::expandPath(srcFn);
char* dFn = filesys::expandPath(dstFn);
char* iFn = filesys::expandPath(irFn);
rc = convolve( dFn, dstBits, sFn, iFn, irScale );
mem::release(sFn);
mem::release(dFn);
mem::release(iFn);
}
return rc;
}
cw::rc_t cw::afop::test( const object_t* cfg )
{

View File

@ -19,7 +19,7 @@ namespace cw
rc_t selectToFile( const object_t* cfg );
// Cross fader
// Arbitrary cross fader
typedef struct {
const char* srcFn; // source audio file name
double srcBegSec; // source clip begin
@ -27,22 +27,31 @@ namespace cw
double srcBegFadeSec; // length of fade in (fade begins at srcBegSec and ends at srcBegSec+srcBegFadeSec)
double srcEndFadeSec; // length of fade out (fade begins at srcEndSec-srcEndFadeSec and ends at srcEndSec)
double dstBegSec; // clip output location
double gain; // scale the signal
} cutMixArg_t;
rc_t cutAndMix( const char* outFn, unsigned outBits, const char* srcDir, const cutMixArg_t* argL, unsigned argN );
rc_t cutAndMix( const object_t* cfg );
// Given a collection of overlapping tracks fade in/out sections of the tracks at specified times.
// This is a wrapper around cutAndMix()
typedef struct
{
const char* srcFn;
double srcBegSec;
double srcEndSec;
double fadeOutSec;
double gain;
} parallelMixArg_t;
rc_t parallelMix( const char* dstFn, unsigned dstBits, const char* srcDir, const parallelMixArg_t* argL, unsigned argN );
rc_t parallelMix( const object_t* cfg );
rc_t transformApp( const object_t* cfg );
rc_t convolve( const char* dstFn, unsigned dstBits, const char* srcFn, const char* impulseResponseFn, float irScale=1 );
rc_t convolve( const object_t* cfg );
rc_t test( const object_t* cfg );
}

View File

@ -16,6 +16,9 @@
#include <limits> // std::numeric_limits<
#include <atomic>
#include <cstdint>
#include <cmath>
#include <complex>
#if defined(OS_LINUX) || defined(OS_OSX)
#define cwPOSIX_FILE_SYS
@ -176,7 +179,28 @@ namespace cw
void sleepUs( unsigned us ); // sleep seconds
void sleepNs( unsigned ns ); // sleep nanoseconds
template< typename T >
bool is_even( const T& t )
{
assert( is_integral(t) );
return (t % 2) == 0;
}
#if defined(cwWEB)
template< typename T>
bool is_int(const T& x)
{ return false; }
template<> inline bool is_int<signed char>( const signed char& x ) { return true; }
template<> inline bool is_int<unsigned char>( const unsigned char& x ) { return true; }
template<> inline bool is_int<signed short>( const signed short& x ) { return true; }
template<> inline bool is_int<unsigned short>( const unsigned short& x ) { return true; }
template<> inline bool is_int<signed long>( const signed long& x ) { return true; }
template<> inline bool is_int<unsigned long>( const unsigned long& x ) { return true; }
template<> inline bool is_int<signed long long>( const signed long long& x ) { return true; }
template<> inline bool is_int<unsigned long long>( const unsigned long long& x ) { return true; }
#endif
}
#endif

144
cwDsp.cpp Normal file
View File

@ -0,0 +1,144 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwUtility.h"
#include "cwVectOps.h"
#include "cwDsp.h"
//----------------------------------------------------------------------------------------------------------------------
// fft
//
cw::rc_t cw::dsp::fft::test()
{
typedef float real_t;
rc_t rc = kOkRC;
unsigned flags = kToPolarFl;
unsigned xN = 16;
real_t srate = xN;
real_t hz = 1;
real_t xV[xN];
struct ptr_str<real_t>* p = create<real_t>(xN,flags);
if(p != nullptr )
{
rc = cwLogError(kOpFailRC,"FFT procedure allocation failed.");
goto errLabel;
}
vop::zero(xV,xN);
vop::sine(xV,xN,srate,hz);
exec(p,xV,xN);
vop::print( xV, xN, "%f ", "sin " );
vop::print( magn(p), bin_count(p), "%f ", "mag " );
vop::print( phase(p), bin_count(p), "%f ", "phs " );
errLabel:
destroy(p);
return rc;
}
//----------------------------------------------------------------------------------------------------------------------
// ifft
//
cw::rc_t cw::dsp::ifft::test()
{
typedef double real_t;
struct fft::ptr_str<real_t>* ft = nullptr;
struct ptr_str<real_t>* ift = nullptr;
rc_t rc = kOkRC;
unsigned xN = 16;
real_t xV[xN];
if( (ft = fft::create<real_t>(xN,fft::kToPolarFl)) == nullptr )
{
rc = cwLogError(kOpFailRC,"FFT procedure allocation failed.");
goto errLabel;
}
if((ift = create<real_t>(fft::bin_count(ft))) == nullptr )
{
rc = cwLogError(kOpFailRC,"IFFT procedure allocation failed.");
goto errLabel;
}
vop::zero(xV,xN);
vop::sine(xV,xN,(double)xN,1.0);
fft::exec(ft,xV,xN);
exec(ift, fft::magn(ft), fft::phase(ft) );
vop::print( xV, xN, "%f ", "sin " );
vop::print( magn(ft), fft::bin_count(ft), "%f ", "mag " );
vop::print( phase(ft), fft::bin_count(ft), "%f ", "phs " );
vop::print( out(ift), out_count(ift), "%f ", "sig " );
errLabel:
destroy(ft);
destroy(ift);
return rc;
}
//----------------------------------------------------------------------------------------------------------------------
// convolve
//
cw::rc_t cw::dsp::convolve::test()
{
typedef float real_t;
real_t hV[] = { 1, .5, .25, .1, .05 };
unsigned hN = sizeof(hV) / sizeof(hV[0]);
real_t xV[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 };
unsigned xN = 4; //sizeof(xV) / sizeof(xV[0]);
real_t yV[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
unsigned yN = sizeof(yV) / sizeof(yV[0]);
// correct output from apply: real_t zV[] = { 1., 0.5, 0.25,0.1, 1.05,0.5, 0.25,0.1, 1.05,0.5, 0.25,0.1, 0.05,0.0, 0.0 };
ptr_str<real_t>* p = create(hV,hN,xN);
exec(p,xV,xN);
vop::print( p->outV, p->outN , "%f ", "out ");
vop::print( p->olaV, p->olaN, "%f ", "ola " );
exec(p,xV+4,xN);
vop::print( p->outV, p->outN , "%f ", "out ");
vop::print( p->olaV, p->olaN, "%f ", "ola " );
exec(p,xV+8,xN);
vop::print( p->outV, p->outN , "%f ", "out ");
vop::print( p->olaV, p->olaN, "%f ", "ola " );
xN = sizeof(xV) / sizeof(xV[0]);
apply(xV,xN,hV,hN,yV,yN);
vop::print( yV, yN , "%4.2f ", "yV ");
destroy(p);
return kOkRC;
}
// 1. 0.5 0.25 0.1 1.05 0.5 0.25 0.1 1.05 0.5 0.25 0.1 0.05 0.0. 0. ]
// 1.0 0.5 0.25 0.1 1.05 0.5 0.25 0.1 1.05 0.5 0.25 0.1 1.05 1.0 0.75 0.

587
cwDsp.h Normal file
View File

@ -0,0 +1,587 @@
#ifndef cwDsp_H
#define cwDsp_H
#include <fftw3.h>
#include <type_traits>
namespace cw
{
namespace dsp
{
typedef std::complex<double> complex_d_t;
typedef std::complex<float> complex_f_t;
//---------------------------------------------------------------------------------------------------------------------------------
// Window functions
//
template< typename T >
T kaiser_beta_from_sidelobe_reject( const T& sidelobeRejectDb )
{
double beta;
if( sidelobeRejectDb < 13.26 )
sidelobeRejectDb = 13.26;
else
if( sidelobeRejectDb > 120.0)
sidelobeRejectDb = 120.0;
if( sidelobeRejectDb < 60.0 )
beta = (0.76609 * pow(sidelobeRejectDb - 13.26,0.4)) + (0.09834*(sidelobeRejectDb-13.26));
else
beta = 0.12438 * (sidelobeRejectDb + 6.3);
return beta;
}
template< typename T >
T kaiser_freq_resolution_factor( const T& sidelobeRejectDb )
{ return (6.0 * (sidelobeRejectDb + 12.0))/155.0; }
template< typename T >
T* kaiser( T* dbp, unsigned n, const T& beta )
{
bool zeroFl = false;
int M = 0;
double den = cmBessel0(beta); // wnd func denominator
int cnt = n;
int i;
assert( n >= 3 );
// force ele cnt to be odd
if( is_even(cnt) )
{
cnt--;
zeroFl = true;
}
// at this point cnt is odd and >= 3
// calc half the window length
M = (int)((cnt - 1.0)/2.0);
double Msqrd = M*M;
for(i=0; i<cnt; i++)
{
double v0 = (double)(i - M);
double num = cmBessel0(beta * sqrt(1.0 - ((v0*v0)/Msqrd)));
dbp[i] = (T)(num/den);
}
if( zeroFl )
dbp[cnt] = 0.0; // zero the extra element in the output array
return dbp;
}
template< typename T >
T* gaussian( T* dbp, unsigned dn, double mean, double variance )
{
int M = dn-1;
double sqrt2pi = sqrt(2.0*M_PI);
unsigned i;
for(i=0; i<dn; i++)
{
double arg = ((((double)i/M) - 0.5) * M);
arg = pow( (double)(arg-mean), 2.0);
arg = exp( -arg / (2.0*variance));
dbp[i] = (T)(arg / (sqrt(variance) * sqrt2pi));
}
return dbp;
}
template< typename T >
T* hamming( T* dbp, unsigned dn )
{
const T* dep = dbp + dn;
T* dp = dbp;
double fact = 2.0 * M_PI / (dn-1);
unsigned i;
for(i=0; dbp < dep; ++i )
*dbp++ = (T)(.54 - (.46 * cos(fact*i)));
return dp;
}
template< typename T >
T* hann( T* dbp, unsigned dn )
{
const T* dep = dbp + dn;
T* dp = dbp;
double fact = 2.0 * M_PI / (dn-1);
unsigned i;
for(i=0; dbp < dep; ++i )
*dbp++ = (T)(.5 - (.5 * cos(fact*i)));
return dp;
}
template< typename T >
T* hann_matlab( T* dbp, unsigned dn )
{
const T* dep = dbp + dn;
T* dp = dbp;
double fact = 2.0 * M_PI / (dn+1);
unsigned i;
for(i=0; dbp < dep; ++i )
*dbp++ = (T)(0.5*(1.0-cos(fact*(i+1))));
return dp;
}
template< typename T >
T* triangle( T* dbp, unsigned dn )
{
unsigned n = dn/2;
T incr = 1.0/n;
seq(dbp,n,0,incr);
seq(dbp+n,dn-n,1,-incr);
return dbp;
}
template< typename T >
T* gauss_window( T* dbp, unsigned dn, double arg )
{
const T* dep = dbp + dn;
T* rp = dbp;
int N = (dep - dbp) - 1;
int n = -N/2;
if( N == 0 )
*dbp = 1.0;
else
{
while( dbp < dep )
{
double a = (arg * n++) / (N/2);
*dbp++ = (T)exp( -(a*a)/2 );
}
}
return rp;
}
//---------------------------------------------------------------------------------------------------------------------------------
// FFT
//
namespace fft
{
enum
{
kToPolarFl = 0x01, // convert to polar (magn./phase)
kToRectFl = 0x02, // convert to rect (real/imag)
};
template< typename T >
struct ptr_str
{
unsigned flags;
T* inV;
std::complex<T>* cplxV;
T* magV;
T* phsV;
unsigned inN;
unsigned binN;
union
{
fftw_plan dplan;
fftwf_plan fplan;
} u;
};
template< typename T >
struct ptr_str<T>* create( unsigned xN, unsigned flags=kToPolarFl )
{
struct ptr_str<T>* p = mem::allocZ< ptr_str<T> >(1);
p->flags = flags;
p->inN = xN;
p->binN = xN/2 + 1;
p->magV = mem::allocZ<T>(p->binN);
p->phsV = mem::allocZ<T>(p->binN);
if( std::is_same<T,float>::value )
{
p->inV = (T*)fftwf_malloc( sizeof(T)*xN );
p->cplxV = (std::complex<T>*)fftwf_malloc( sizeof(std::complex<T>)*xN);
p->u.fplan = fftwf_plan_dft_r2c_1d((int)xN, (float*)p->inV, reinterpret_cast<fftwf_complex*>(p->cplxV), FFTW_MEASURE );
}
else
{
p->inV = (T*)fftw_malloc( sizeof(T)*xN );
p->cplxV = (std::complex<T>*)fftw_malloc( sizeof(std::complex<T>)*xN);
p->u.dplan = fftw_plan_dft_r2c_1d((int)xN, (double*)p->inV, reinterpret_cast<fftw_complex*>(p->cplxV), FFTW_MEASURE );
}
return p;
}
template< typename T >
rc_t destroy( struct ptr_str<T>*& p )
{
if( p == nullptr )
return kOkRC;
if( std::is_same<T,float>::value )
{
fftwf_destroy_plan( p->u.fplan );
fftwf_free(p->inV);
fftwf_free(p->cplxV);
}
else
{
fftw_destroy_plan( p->u.dplan );
fftw_free(p->inV);
fftw_free(p->cplxV);
}
p->u.dplan = nullptr;
mem::release(p->magV);
mem::release(p->phsV);
mem::release(p);
return kOkRC;
}
template< typename T >
rc_t exec( struct ptr_str<T>* p, const T* xV, unsigned xN )
{
rc_t rc = kOkRC;
assert( xN <= p->inN);
// if the incoming vector size is less than the FT buffer size
// then zero the extra values at the end of the buffer
if( xN < p->inN )
memset(p->inV + xN, 0, sizeof(T) * (p->inN-xN) );
// copy the incoming samples into the input buffer
memcpy(p->inV,xV,sizeof(T)*xN);
// execute the FT
if( std::is_same<T,float>::value )
fftwf_execute(p->u.fplan);
else
fftw_execute(p->u.dplan);
// convert to polar
if( cwIsFlag(p->flags,kToPolarFl) )
{
for(unsigned i=0; i<p->binN; ++i)
{
p->magV[i] = std::abs(p->cplxV[i])/(p->inN/2);
p->phsV[i] = std::arg(p->cplxV[i]);
}
}
else
// convert to rect
if( cwIsFlag(p->flags,kToRectFl) )
{
for(unsigned i=0; i<p->binN; ++i)
{
p->magV[i] = std::real(p->cplxV[i]);
p->phsV[i] = std::imag(p->cplxV[i]);
}
}
else
{
// do nothing - leave the result in p->cplxV[]
}
return rc;
}
template< typename T >
unsigned bin_count( ptr_str<T>* p ) { return p->binN; }
template< typename T >
const T* magn( ptr_str<T>* p ) { return p->magV; }
template< typename T >
const T* phase( ptr_str<T>* p ) { return p->phsV; }
rc_t test();
}
//---------------------------------------------------------------------------------------------------------------------------------
// IFFT
//
namespace ifft
{
template< typename T >
struct ptr_str
{
T *outV;
std::complex<T> *cplxV;
unsigned outN;
unsigned binN;
union
{
fftw_plan dplan;
fftwf_plan fplan;
} u;
};
template< typename T >
struct ptr_str<T>* create( unsigned binN )
{
struct ptr_str<T>* p = mem::allocZ< ptr_str<T> >(1);
p->binN = binN;
p->outN = (binN-1)*2;
if( std::is_same<T,float>::value )
{
p->outV = (T*)fftwf_malloc( sizeof(T)*p->outN );
p->cplxV = (std::complex<T>*)fftwf_malloc( sizeof(std::complex<T>)*p->outN);
p->u.fplan = fftwf_plan_dft_c2r_1d((int)p->outN, reinterpret_cast<fftwf_complex*>(p->cplxV), (float*)p->outV, FFTW_BACKWARD | FFTW_MEASURE );
}
else
{
p->outV = (T*)fftw_malloc( sizeof(T)*p->outN );
p->cplxV = (std::complex<T>*)fftw_malloc( sizeof(std::complex<T>)*p->outN);
p->u.dplan = fftw_plan_dft_c2r_1d((int)p->outN, reinterpret_cast<fftw_complex*>(p->cplxV), (double*)p->outV, FFTW_BACKWARD | FFTW_MEASURE );
}
return p;
}
template< typename T >
rc_t destroy( struct ptr_str<T>*& p )
{
if( p == nullptr )
return kOkRC;
if( std::is_same<T,float>::value )
{
fftwf_destroy_plan( p->u.fplan );
fftwf_free(p->outV);
fftwf_free(p->cplxV);
}
else
{
fftw_destroy_plan( p->u.dplan );
fftw_free(p->outV);
fftw_free(p->cplxV);
}
p->u.dplan = nullptr;
mem::release(p);
return kOkRC;
}
template< typename T >
rc_t exec( struct ptr_str<T>* p, const T* magV, const T* phsV )
{
rc_t rc = kOkRC;
if( magV != nullptr && phsV != nullptr )
{
for(unsigned i=0; i<p->binN; ++i)
p->cplxV[i] = std::polar( magV[i] / 2, phsV[i] );
for(unsigned i=p->outN-1,j=1; j<p->binN-1; --i,++j)
p->cplxV[i] = std::polar( magV[j] / 2, phsV[j] );
}
if( std::is_same<T,float>::value )
fftwf_execute(p->u.fplan);
else
fftw_execute(p->u.dplan);
return rc;
}
template< typename T >
unsigned out_count( struct ptr_str<T>* p ) { return p->outN; }
template< typename T >
const T* out( struct ptr_str<T>* p ) { return p->outV; }
rc_t test();
}
//---------------------------------------------------------------------------------------------------------------------------------
// Convolution
//
namespace convolve
{
template< typename T >
struct ptr_str
{
struct fft::ptr_str<T>* ft;
struct ifft::ptr_str<T>* ift;
std::complex<T>* hV;
unsigned hN;
T* olaV; // olaV[olaN]
unsigned olaN; // olaN == cN - procSmpN
T* outV; // outV[procSmpN]
unsigned outN; // outN == procSmpN
};
template< typename T >
struct ptr_str<T>* create(const T* hV, unsigned hN, unsigned procSmpN, T hScale=1 )
{
struct ptr_str<T>* p = mem::allocZ<struct ptr_str<T>>(1);
unsigned cN = nextPowerOfTwo( hN + procSmpN - 1 );
p->ft = fft::create<T>(cN,0);
unsigned binN = fft::bin_count( p->ft );
p->ift = ifft::create<T>(binN);
p->hN = hN;
p->hV = mem::allocZ< std::complex<T> >(binN);
p->outV = mem::allocZ<T>( cN );
p->outN = procSmpN;
p->olaV = p->outV + procSmpN; // olaV[] overlaps outV[] with an offset of procSmpN
p->olaN = cN - procSmpN;
fft::exec( p->ft, hV, hN );
for(unsigned i=0; i<binN; ++i)
p->hV[i] = hScale * p->ft->cplxV[i] / ((T)cN);
printf("procN:%i cN:%i hN:%i binN:%i outN:%i\n", procSmpN, cN, hN, binN, p->outN );
return p;
}
template< typename T >
rc_t destroy( struct ptr_str<T>*& pRef )
{
if( pRef == nullptr )
return kOkRC;
fft::destroy(pRef->ft);
ifft::destroy(pRef->ift);
mem::release(pRef->hV);
mem::release(pRef->outV);
mem::release(pRef);
return kOkRC;
}
template< typename T >
rc_t exec( struct ptr_str<T>* p, const T* xV, unsigned xN )
{
// take FT of input signal
fft::exec( p->ft, xV, xN );
// multiply the signal spectra of the input signal and impulse response
for(unsigned i=0; i<p->ft->binN; ++i)
p->ift->cplxV[i] = p->hV[i] * p->ft->cplxV[i];
// take the IFFT of the convolved spectrum
ifft::exec<T>(p->ift,nullptr,nullptr);
// sum with previous impulse response tail
vop::add( p->outV, (const T*)p->olaV, (const T*)p->ift->outV, p->outN-1 );
// first sample of the impulse response tail is complete
p->outV[p->outN-1] = p->ift->outV[p->outN-1];
// store the new impulse response tail
vop::copy(p->olaV, p->ift->outV + p->outN, p->hN-1 );
return kOkRC;
}
template< typename T >
rc_t apply( const T* xV, unsigned xN, const T* hV, unsigned hN, T* yV, unsigned yN, T hScale=1 )
{
unsigned procSmpN = std::min(xN,hN);
ptr_str<T> *p = create(hV,hN,procSmpN,hScale);
unsigned yi = 0;
//printf("procSmpN:%i\n",procSmpN);
for(unsigned xi=0; xi<xN && yi<yN; xi+=procSmpN )
{
exec<T>(p,xV+xi,std::min(procSmpN,xN-xi));
unsigned outN = std::min(yN-yi,p->outN);
vop::copy(yV+yi, p->outV, outN );
//printf("xi:%i yi:%i outN:%i\n", xi, yi, outN );
//vop::print( yV+yi, outN, "%f ", "outV ");
yi += outN;
}
//printf("yi:%i\n",yi);
/*
// if the tail of the hV[] is still in the OLA buffer
if( yi < yN )
{
unsigned outN = std::min(yN-yi, p->olaN);
// fill yV[] with as much of OLA as is available
vop::copy(yV + yi, p->olaV, outN);
yi += outN;
// zero any remaining space in yV[]
vop::zero(yV + yi, yN-yi );
}
*/
destroy(p);
return kOkRC;
}
rc_t test();
}
}
}
#endif

View File

@ -4,6 +4,7 @@
#include "cwFileSys.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwString.h"
#ifdef OS_LINUX
#include <libgen.h> // basename() dirname()
@ -242,16 +243,19 @@ char* cw::filesys::expandPath( const char* dir )
goto errLabel;
}
if( res.we_wordc > 1 )
switch( res.we_wordc )
{
rc = cwLogError(kOpFailRC,"Unexpected word expansion count: %i.", res.we_wordc );
goto errLabel;
}
case 0:
newDir = str::dupl(dir);
break;
if( res.we_wordc == 1 )
newDir = mem::duplStr(res.we_wordv[0]);
else
newDir = mem::duplStr(dir);
case 1:
newDir = str::dupl(res.we_wordv[0]);
break;
default:
newDir = str::join(" ", (const char**)res.we_wordv, res.we_wordc );
}
errLabel:
if( rc != kOkRC )

View File

@ -87,6 +87,17 @@ namespace cw
rc_t _objTypeValueFromNonValue( const object_t* o, unsigned tid, void* dst )
{
switch(tid)
{
case kCStringTId:
*(const char**)dst = nullptr;
return kOkRC;
case kStringTId:
*(char**)dst = nullptr;
return kOkRC;
}
return cwLogError(kInvalidArgRC, "There is no conversion from '%s' to '%s'.", _objTypeIdToLabel(tid), o->type->label);
}
@ -97,6 +108,7 @@ namespace cw
*(const char**)dst = o->u.str;
return kOkRC;
}
return _objTypeValueFromNonValue(o,tid,dst);
}
@ -114,6 +126,13 @@ namespace cw
*(char**)dst = o->u.str;
return kOkRC;
}
if( tid == kNullTId )
{
*(char**)dst = nullptr;
return kOkRC;
}
return _objTypeValueFromNonValue(o,tid,dst);
}

47
cwString.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwString.h"
unsigned cw::str::len( const char* s)
{
if( s == nullptr )
return 0;
return strlen(s);
}
char* cw::str::dupl( const char* s )
{ return mem::duplStr(s); }
char* cw::str::join( const char* sep, const char** subStrArray, unsigned ssN )
{
unsigned sN = 0;
char* s = nullptr;
for(unsigned i=0; i<ssN; ++i)
sN += len(subStrArray[i]);
if( ssN >= 1 )
sN += (ssN-1) * len(sep);
if( sN == 0 )
return nullptr;
sN += 1;
s = mem::alloc<char>(sN);
s[0] = 0;
for(unsigned i=0; i<ssN; ++i)
{
strcat(s,subStrArray[i]);
if( sep != nullptr && ssN>=1 && i<ssN-1 )
strcat(s,sep);
assert( len(s) < sN );
}
return s;
}

16
cwString.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef cwString_H
#define cwString_H
namespace cw
{
namespace str
{
unsigned len( const char* s );
char* dupl( const char* s );
char* join( const char* sep, const char** subStrArray, unsigned ssN );
}
}
#endif

View File

@ -121,3 +121,56 @@ void cw::doubleToX80(double val, unsigned char rate[10])
*p++ = (u_char)(0xFF & (mant0));
}
bool cw::isPowerOfTwo( unsigned x )
{
return !( (x < 2) || (x & (x-1)) );
}
unsigned cw::nextPowerOfTwo( unsigned val )
{
unsigned i;
unsigned mask = 1;
unsigned msb = 0;
unsigned cnt = 0;
// if val is a power of two return it
if( isPowerOfTwo(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 cw::nearestPowerOfTwo( unsigned i )
{
unsigned vh = nextPowerOfTwo(i);
if( vh == 2 )
return vh;
unsigned vl = vh / 2;
if( vh - i < i - vl )
return vh;
return vl;
}

View File

@ -8,6 +8,11 @@ namespace cw
double x80ToDouble( unsigned char s[10] );
void doubleToX80( double v, unsigned char s[10] );
bool isPowerOfTwo( unsigned x );
unsigned nextPowerOfTwo( unsigned val );
unsigned nearestPowerOfTwo( unsigned val );
}
#endif

View File

@ -249,6 +249,38 @@ namespace cw
y[i] = v;
}
template< typename T >
T seq( T* dbp, unsigned dn, const T& beg, const T& incr )
{
const T* dep = dbp + dn;
unsigned i = 0;
for(; dbp<dep; ++i)
*dbp++ = beg + (incr*i);
return beg + (incr*i);
}
template< typename T >
unsigned phasor( T* y, unsigned n, T srate, T hz, unsigned init_idx=0 )
{
for(unsigned i=init_idx; i<n; ++i)
y[i] = (M_PI*2*hz*i) / srate;
return init_idx + n;
}
template< typename T >
unsigned sine( T* y, unsigned n, T srate, T hz, unsigned init_idx=0 )
{
init_idx = phasor(y,n,srate,hz,init_idx);
for(unsigned i=0; i<n; ++i)
y[i] = sin(y[i]);
return init_idx;
}
}
}