Merge branch 'master' of gitea.larke.org:klarke/libcw
# Conflicts: # Makefile.am
This commit is contained in:
commit
1aba926fda
18
Makefile.am
18
Makefile.am
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
|
||||
//)
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
//]
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,8 @@ namespace cw
|
||||
fclose(p->fp);
|
||||
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;
|
||||
|
@ -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 );
|
||||
|
@ -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 )
|
||||
{
|
||||
@ -374,7 +376,10 @@ namespace cw
|
||||
xArgL[i].srcFadeInFrmN = floor(argL[i].srcBegFadeSec * srate_Ref);
|
||||
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);
|
||||
@ -496,7 +501,8 @@ cw::rc_t cw::afop::cutAndMix( const char* dstFn, unsigned dstBits, const char* s
|
||||
unsigned actualFrmN = 0;
|
||||
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 )
|
||||
{
|
||||
|
@ -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 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 );
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
@ -175,8 +178,29 @@ namespace cw
|
||||
void sleepMs( unsigned ms ); // sleep milliseconds
|
||||
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
144
cwDsp.cpp
Normal 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
587
cwDsp.h
Normal 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
|
@ -4,6 +4,7 @@
|
||||
#include "cwFileSys.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwString.h"
|
||||
|
||||
#ifdef OS_LINUX
|
||||
#include <libgen.h> // basename() dirname()
|
||||
@ -213,7 +214,7 @@ char* cw::filesys::expandPath( const char* dir )
|
||||
wordexp_t res;
|
||||
|
||||
memset(&res,0,sizeof(res));
|
||||
|
||||
|
||||
if((sysRC = wordexp(dir,&res,flags)) != 0)
|
||||
{
|
||||
switch(sysRC)
|
||||
@ -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;
|
||||
|
||||
case 1:
|
||||
newDir = str::dupl(res.we_wordv[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
newDir = str::join(" ", (const char**)res.we_wordv, res.we_wordc );
|
||||
}
|
||||
|
||||
if( res.we_wordc == 1 )
|
||||
newDir = mem::duplStr(res.we_wordv[0]);
|
||||
else
|
||||
newDir = mem::duplStr(dir);
|
||||
|
||||
errLabel:
|
||||
if( rc != kOkRC )
|
||||
|
19
cwObject.cpp
19
cwObject.cpp
@ -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
47
cwString.cpp
Normal 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
16
cwString.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -7,7 +7,12 @@ 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
|
||||
|
32
cwVectOps.h
32
cwVectOps.h
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user