diff --git a/Makefile.am b/Makefile.am index 1987ecd..616ba90 100644 --- a/Makefile.am +++ b/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 diff --git a/cwAudioBuf.cpp b/cwAudioBuf.cpp index 20691e2..8f4458c 100644 --- a/cwAudioBuf.cpp +++ b/cwAudioBuf.cpp @@ -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] diff --git a/cwAudioBuf.h b/cwAudioBuf.h index d9c4ba5..def3e27 100644 --- a/cwAudioBuf.h +++ b/cwAudioBuf.h @@ -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(); //) diff --git a/cwAudioDeviceAlsa.cpp b/cwAudioDeviceAlsa.cpp index 804db8c..771dec1 100644 --- a/cwAudioDeviceAlsa.cpp +++ b/cwAudioDeviceAlsa.cpp @@ -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; } //] diff --git a/cwAudioDeviceAlsa.h b/cwAudioDeviceAlsa.h index 63f96bd..69b5271 100644 --- a/cwAudioDeviceAlsa.h +++ b/cwAudioDeviceAlsa.h @@ -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(); } } } diff --git a/cwAudioFile.cpp b/cwAudioFile.cpp index e2abe43..1f0e80b 100644 --- a/cwAudioFile.cpp +++ b/cwAudioFile.cpp @@ -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(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(dstFrmN*dstChN); // output signal buffer srcV = mem::alloc(maxSrcFrmN*dstChN); // source signal buffer - // create the src read buffer + // create the src read/ dst write buffer for(unsigned i=0; igain * 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; ichild_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(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( 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 ? 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; igetv("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 ) { diff --git a/cwAudioFileOps.h b/cwAudioFileOps.h index 06c09b3..1d67650 100644 --- a/cwAudioFileOps.h +++ b/cwAudioFileOps.h @@ -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 ); } diff --git a/cwCommonImpl.h b/cwCommonImpl.h index ae78b3d..a033b4a 100644 --- a/cwCommonImpl.h +++ b/cwCommonImpl.h @@ -16,6 +16,9 @@ #include // std::numeric_limits< #include #include +#include +#include + #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( const signed char& x ) { return true; } + template<> inline bool is_int( const unsigned char& x ) { return true; } + template<> inline bool is_int( const signed short& x ) { return true; } + template<> inline bool is_int( const unsigned short& x ) { return true; } + template<> inline bool is_int( const signed long& x ) { return true; } + template<> inline bool is_int( const unsigned long& x ) { return true; } + template<> inline bool is_int( const signed long long& x ) { return true; } + template<> inline bool is_int( const unsigned long long& x ) { return true; } +#endif } #endif diff --git a/cwDsp.cpp b/cwDsp.cpp new file mode 100644 index 0000000..48c08c2 --- /dev/null +++ b/cwDsp.cpp @@ -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* p = create(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* ft = nullptr; + struct ptr_str* ift = nullptr; + rc_t rc = kOkRC; + unsigned xN = 16; + real_t xV[xN]; + + if( (ft = fft::create(xN,fft::kToPolarFl)) == nullptr ) + { + rc = cwLogError(kOpFailRC,"FFT procedure allocation failed."); + goto errLabel; + } + + if((ift = create(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* 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. diff --git a/cwDsp.h b/cwDsp.h new file mode 100644 index 0000000..453de25 --- /dev/null +++ b/cwDsp.h @@ -0,0 +1,587 @@ +#ifndef cwDsp_H +#define cwDsp_H + +#include +#include + +namespace cw +{ + namespace dsp + { + typedef std::complex complex_d_t; + typedef std::complex 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 + 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 + 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* cplxV; + + T* magV; + T* phsV; + + unsigned inN; + unsigned binN; + + union + { + fftw_plan dplan; + fftwf_plan fplan; + } u; + + }; + + template< typename T > + struct ptr_str* create( unsigned xN, unsigned flags=kToPolarFl ) + { + struct ptr_str* p = mem::allocZ< ptr_str >(1); + + p->flags = flags; + p->inN = xN; + p->binN = xN/2 + 1; + p->magV = mem::allocZ(p->binN); + p->phsV = mem::allocZ(p->binN); + + if( std::is_same::value ) + { + p->inV = (T*)fftwf_malloc( sizeof(T)*xN ); + p->cplxV = (std::complex*)fftwf_malloc( sizeof(std::complex)*xN); + p->u.fplan = fftwf_plan_dft_r2c_1d((int)xN, (float*)p->inV, reinterpret_cast(p->cplxV), FFTW_MEASURE ); + + } + else + { + p->inV = (T*)fftw_malloc( sizeof(T)*xN ); + p->cplxV = (std::complex*)fftw_malloc( sizeof(std::complex)*xN); + p->u.dplan = fftw_plan_dft_r2c_1d((int)xN, (double*)p->inV, reinterpret_cast(p->cplxV), FFTW_MEASURE ); + } + + return p; + } + + template< typename T > + rc_t destroy( struct ptr_str*& p ) + { + if( p == nullptr ) + return kOkRC; + + if( std::is_same::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* 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::value ) + fftwf_execute(p->u.fplan); + else + fftw_execute(p->u.dplan); + + // convert to polar + if( cwIsFlag(p->flags,kToPolarFl) ) + { + for(unsigned i=0; ibinN; ++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; ibinN; ++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* p ) { return p->binN; } + + template< typename T > + const T* magn( ptr_str* p ) { return p->magV; } + + template< typename T > + const T* phase( ptr_str* p ) { return p->phsV; } + + rc_t test(); + } + + //--------------------------------------------------------------------------------------------------------------------------------- + // IFFT + // + namespace ifft + { + + template< typename T > + struct ptr_str + { + T *outV; + std::complex *cplxV; + + unsigned outN; + unsigned binN; + + union + { + fftw_plan dplan; + fftwf_plan fplan; + } u; + }; + + template< typename T > + struct ptr_str* create( unsigned binN ) + { + struct ptr_str* p = mem::allocZ< ptr_str >(1); + + p->binN = binN; + p->outN = (binN-1)*2; + + if( std::is_same::value ) + { + p->outV = (T*)fftwf_malloc( sizeof(T)*p->outN ); + p->cplxV = (std::complex*)fftwf_malloc( sizeof(std::complex)*p->outN); + p->u.fplan = fftwf_plan_dft_c2r_1d((int)p->outN, reinterpret_cast(p->cplxV), (float*)p->outV, FFTW_BACKWARD | FFTW_MEASURE ); + } + else + { + p->outV = (T*)fftw_malloc( sizeof(T)*p->outN ); + p->cplxV = (std::complex*)fftw_malloc( sizeof(std::complex)*p->outN); + p->u.dplan = fftw_plan_dft_c2r_1d((int)p->outN, reinterpret_cast(p->cplxV), (double*)p->outV, FFTW_BACKWARD | FFTW_MEASURE ); + } + + return p; + } + + template< typename T > + rc_t destroy( struct ptr_str*& p ) + { + if( p == nullptr ) + return kOkRC; + + if( std::is_same::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* p, const T* magV, const T* phsV ) + { + + rc_t rc = kOkRC; + + if( magV != nullptr && phsV != nullptr ) + { + for(unsigned i=0; ibinN; ++i) + p->cplxV[i] = std::polar( magV[i] / 2, phsV[i] ); + + for(unsigned i=p->outN-1,j=1; jbinN-1; --i,++j) + p->cplxV[i] = std::polar( magV[j] / 2, phsV[j] ); + } + + if( std::is_same::value ) + fftwf_execute(p->u.fplan); + else + fftw_execute(p->u.dplan); + + return rc; + } + + template< typename T > + unsigned out_count( struct ptr_str* p ) { return p->outN; } + + template< typename T > + const T* out( struct ptr_str* p ) { return p->outV; } + + + rc_t test(); + } + + //--------------------------------------------------------------------------------------------------------------------------------- + // Convolution + // + namespace convolve + { + + template< typename T > + struct ptr_str + { + struct fft::ptr_str* ft; + struct ifft::ptr_str* ift; + + std::complex* 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* create(const T* hV, unsigned hN, unsigned procSmpN, T hScale=1 ) + { + struct ptr_str* p = mem::allocZ>(1); + + unsigned cN = nextPowerOfTwo( hN + procSmpN - 1 ); + + p->ft = fft::create(cN,0); + + unsigned binN = fft::bin_count( p->ft ); + + p->ift = ifft::create(binN); + p->hN = hN; + p->hV = mem::allocZ< std::complex >(binN); + p->outV = mem::allocZ( 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; ihV[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*& 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* 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; ift->binN; ++i) + p->ift->cplxV[i] = p->hV[i] * p->ft->cplxV[i]; + + // take the IFFT of the convolved spectrum + ifft::exec(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 *p = create(hV,hN,procSmpN,hScale); + unsigned yi = 0; + + //printf("procSmpN:%i\n",procSmpN); + + for(unsigned xi=0; xi(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 diff --git a/cwFileSys.cpp b/cwFileSys.cpp index 8b8f24e..f647aff 100644 --- a/cwFileSys.cpp +++ b/cwFileSys.cpp @@ -4,6 +4,7 @@ #include "cwFileSys.h" #include "cwCommonImpl.h" #include "cwMem.h" +#include "cwString.h" #ifdef OS_LINUX #include // 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 ) diff --git a/cwObject.cpp b/cwObject.cpp index da4fe5e..8b36822 100644 --- a/cwObject.cpp +++ b/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); } diff --git a/cwString.cpp b/cwString.cpp new file mode 100644 index 0000000..2d68ac0 --- /dev/null +++ b/cwString.cpp @@ -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= 1 ) + sN += (ssN-1) * len(sep); + + if( sN == 0 ) + return nullptr; + + sN += 1; + + s = mem::alloc(sN); + + s[0] = 0; + for(unsigned i=0; i=1 && i 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; +} diff --git a/cwUtility.h b/cwUtility.h index 88d5d00..4b5445f 100644 --- a/cwUtility.h +++ b/cwUtility.h @@ -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 diff --git a/cwVectOps.h b/cwVectOps.h index c132838..043c67a 100644 --- a/cwVectOps.h +++ b/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 + unsigned phasor( T* y, unsigned n, T srate, T hz, unsigned init_idx=0 ) + { + for(unsigned i=init_idx; i + 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