//| Copyright: (C) 2020-2024 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwTest.h" #include "cwMem.h" #include "cwFile.h" #include "cwText.h" #include "cwObject.h" #include "cwAudioFile.h" #include "cwUtility.h" #include "cwFileSys.h" #include "cwAudioFileOps.h" #include "cwVectOps.h" #include "cwMath.h" #include "cwDsp.h" #include "cwAudioTransforms.h" namespace cw { namespace dsp { namespace wnd_func { idLabelPair_t wndLabelArray[] = { { kHannWndId, "hann" }, { kHammingWndId, "hamming" }, { kTriangleWndId, "triangle" }, { kKaiserWndId, "kaiser" }, { kHannMatlabWndId, "hann_matlab" }, { kUnityWndId, "unity" }, { kInvalidWndId, "" } }; const char* wndIdToLabel( unsigned id ) { return cw::idToLabel( wndLabelArray, id, kInvalidWndId ); } unsigned wndLabelToId( const char* label ) { return cw::labelToId( wndLabelArray, label, kInvalidWndId ); } rc_t _test( const char* windowLabel, const double* wndV, unsigned maxWndN, unsigned wndN ) { rc_t rc = kOkRC; wnd_func::fobj_t* p = nullptr; unsigned wndId = wndLabelToId( windowLabel ); if((rc = create(p,wndId,maxWndN,wndN,3)) == kOkRC ) { vop::print(p->wndV, p->wndN, "%f ", windowLabel); printf("diff: %f\n", vop::sum_sq_diff( wndV, p->wndV, wndN)); destroy(p); } return rc; } rc_t test() { double hann_15[] = { 0.0, 0.04951557, 0.1882551 , 0.38873953, 0.61126047, 0.8117449, 0.95048443, 1.0, 0.95048443, 0.8117449, 0.61126047, 0.38873953, 0.1882551, 0.04951557, 0.0 }; double hann_16[] = { 0.0, 0.04322727, 0.1654347 , 0.3454915 , 0.55226423, 0.75, 0.9045085 , 0.9890738, 0.9890738, 0.9045085, 0.75, 0.55226423, 0.3454915, 0.1654347 , 0.04322727, 0.0 }; double hamm_15[] = { 0.08,0.12555432, 0.25319469, 0.43764037, 0.64235963, 0.82680531, 0.95444568, 1.0, 0.95444568, 0.82680531,0.64235963, 0.43764037, 0.25319469, 0.12555432, 0.08 }; double hamm_16[] = { 0.08,0.11976909, 0.23219992, 0.39785218, 0.58808309, 0.77, 0.91214782, 0.9899479, 0.9899479 , 0.91214782,0.77, 0.58808309, 0.39785218, 0.23219992, 0.11976909, 0.08}; double tri_15[] = { 0.0, 0.14285714, 0.28571429, 0.42857143, 0.57142857, 0.71428571, 0.85714286, 1.0, 0.85714286, 0.71428571,0.57142857, 0.42857143, 0.28571429, 0.14285714, 0.0 }; double tri_16[] = { 0.0, 0.13333333, 0.26666667, 0.4, 0.53333333, 0.66666667, 0.8, 0.93333333, 0.93333333, 0.8, 0.66666667, 0.53333333, 0.4, 0.26666667, 0.13333333, 0.0 }; double ones_5[] = { 1.0, 1.0, 1.0, 1.0, 1.0 }; rc_t rc = kOkRC; _test( "hann", hann_15, 15, 15); _test( "hann", hann_16, 16, 16); _test( "hamming", hamm_15, 15, 15); _test( "hamming", hamm_16, 16, 16); _test( "triangle", tri_15, 15, 15); _test( "triangle", tri_16, 16, 16); _test( "unity", ones_5, 5, 5); if( rc != kOkRC ) cwLogError(rc,"Window test failed."); return rc; } } namespace ola { rc_t test() { typedef float sample_t; rc_t rc = kOkRC; unsigned wndSmpCnt = 16; unsigned hopSmpCnt = 4; unsigned procSmpCnt = 2; unsigned wndTypeId = wnd_func::kUnityWndId; unsigned hopCnt = 8; unsigned oSmpCnt = hopCnt * hopSmpCnt; unsigned i,j,k; sample_t cV[] = { 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 4,4,4,4, 4,4,4,4, 4,4,4,4, 4,4,4,4 }; sample_t* x = mem::allocZ(wndSmpCnt); sample_t* y = mem::allocZ(oSmpCnt); vop::fill(x,wndSmpCnt,1.0); ola::fobj_t* p = nullptr; if((rc = ola::create(p, wndSmpCnt, hopSmpCnt, procSmpCnt, wndTypeId )) == kOkRC ) { // Each iteration represents a single audio cylce // which sources/sinks procSmpCnt samples for(i=0,j=0,k=0; k hopSmpCnt ) { j -= hopSmpCnt; ola::exec(p,x,p->wndSmpCnt); } const sample_t* op; // Get procSmpCnt samples from the output. if( (op=ola::execOut(p)) != NULL ) { assert( y + k + p->procSmpCnt <= y + oSmpCnt ); vop::copy(y+k, op, p->procSmpCnt); k += p->procSmpCnt; } } } vop::print(cV,oSmpCnt,"%f ","Correct "); vop::print(y, oSmpCnt,"%f ","Computed"); printf("diff:%f\n", vop::sum_sq_diff(cV,y,oSmpCnt)); ola::destroy(p); mem::release(x); mem::release(y); return rc; } } namespace shift_buf { rc_t test() { rc_t rc = kOkRC; typedef float sample_t; sample_t m[] = { 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13, 13,14,15,16,17,18,19, 19,20,21,22,23,24,25, 25,26,27,28,29,30,31, 31,32,33,34,35,36,37, 37,38,39,40,41,42,43, 43,44,45,46,47,48,49 }; unsigned procSmpCnt = 5; // count of samples to be fed to the shift buffer on each cycle unsigned hopSmpCnt = 6; // count of samples between shift buffer outputs unsigned maxWndSmpCnt= 7; unsigned wndSmpCnt = 7; // count of samples in each shift buffer output unsigned iSmpCnt = 49; // count of samples in the input test signal unsigned oColCnt = iSmpCnt / hopSmpCnt; unsigned oSmpCnt = oColCnt * wndSmpCnt; // count of samples in the output test signal unsigned i,j; shift_buf::fobj_t* p = nullptr; if((rc = shift_buf::create(p,procSmpCnt,maxWndSmpCnt,wndSmpCnt,hopSmpCnt)) == kOkRC ) { sample_t* x = mem::allocZ(iSmpCnt); sample_t* y = mem::allocZ(oSmpCnt); vop::seq(x,iSmpCnt,1.0f,1.0f); for(i=0,j=0; ioutV, wndSmpCnt ); vop::print(p->outV, wndSmpCnt, "%f "); vop::print(m + j, wndSmpCnt, "%f "); if( !vop::is_equal(p->outV, m+j, wndSmpCnt )) { rc = cwLogError(kTestFailRC,"shift_buf test failed."); goto errLabel; } } } mem::release(x); mem::release(y); shift_buf::destroy(p); } errLabel: return rc; } } namespace pv_anl { rc_t test() { rc_t rc = kOkRC; pv_anl::fobj_t* pva = nullptr; pv_syn::fobj_t* pvs = nullptr; unsigned procSmpCnt = 0; float srate = 0; float out_srate = 0; unsigned wndSmpCnt = 0; unsigned hopSmpCnt = 0; unsigned flags = kCalcHzPvaFl; unsigned wndTypeId = wnd_func::kHannWndId; if((rc = create( pva, procSmpCnt, srate, wndSmpCnt, wndSmpCnt, hopSmpCnt, flags )) != kOkRC ) { } if((rc = create( pvs, procSmpCnt, out_srate, wndSmpCnt, hopSmpCnt, wndTypeId )) != kOkRC ) { } destroy(pva); destroy(pvs); return rc; } } namespace wt_osc { typedef float sample_t; typedef float srate_t; rc_t test() { rc_t rc = kOkRC; srate_t srate = 8; sample_t aV[] = { 7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0 }; struct wt_str wt{ .tid = kLoopWtTId, .cyc_per_loop = 0, .aV = aV, .aN = 16, .rms = 0, .hz = 1, .srate = srate, .pad_smpN = 1, .posn_smp_idx = 0 }; struct obj_str obj; init(&obj,&wt); unsigned yN = (int)(srate*2); sample_t yV[yN]; unsigned actual = 0; process(&obj, yV, yN,actual); vop::print( yV, yN, "%f"); return rc; } } namespace wt_seq_osc { typedef float sample_t; typedef float srate_t; rc_t test() { rc_t rc = kOkRC; srate_t srate = 8; sample_t aV[] = { 7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0 }; struct wt_osc::wt_str wt{ .tid = wt_osc::kOneShotWtTId, .cyc_per_loop = 0, .aV = aV, .aN = 16, .rms = 0, .hz = 1, .srate = srate, .pad_smpN = 1, .posn_smp_idx = 0 }; struct wt_osc::wt_str wt0 = wt; struct wt_osc::wt_str wt1 = wt; struct wt_osc::wt_str wt2 = wt; wt1.tid = wt_osc::kLoopWtTId; wt1.posn_smp_idx = 16; wt2.tid = wt_osc::kLoopWtTId; wt2.posn_smp_idx = 32; struct wt_osc::wt_str wtA[] = { wt0, wt1, wt2 }; struct wt_seq_osc::wt_seq_str wt_seq{ .wtA = wtA, .wtN = 3 }; struct wt_seq_osc::obj_str obj; init(&obj,&wt_seq); unsigned yN = (int)(srate*10); sample_t yV[yN]; unsigned actual = 0; unsigned yi = 0; unsigned yDspSmpCnt = 8; while( yi < yN && is_init(&obj) ) { unsigned frmSmpN = std::min(yN-yi,yDspSmpCnt); process(&obj, yV+yi, frmSmpN, actual); assert( actual == frmSmpN ); yi += actual; } vop::print( yV, yi, "%5.3f",nullptr,8); return rc; } } namespace multi_ch_wt_seq_osc { typedef float sample_t; typedef float srate_t; rc_t test() { rc_t rc = kOkRC; unsigned chN = 2; srate_t srate = 8; sample_t a0V[] = { 7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0 }; sample_t a1V[] = { 17,10,11,12,13,14,15,16,17,10,11,12,13,14,15,16,17,10 }; sample_t a2V[] = { 27,20,21,22,23,24,25,26,27,20,21,22,23,24,25,26,27,20 }; struct wt_osc::wt_str wt{ .tid = wt_osc::kOneShotWtTId, .cyc_per_loop = 0, .aV = a0V, .aN = 16, .rms = 0, .hz = 1, .srate = srate, .pad_smpN = 1, .posn_smp_idx = 0 }; struct wt_osc::wt_str wt0 = wt; struct wt_osc::wt_str wt1 = wt; struct wt_osc::wt_str wt2 = wt; wt1.tid = wt_osc::kLoopWtTId; wt1.posn_smp_idx = 16; wt1.aV = a1V; wt2.tid = wt_osc::kLoopWtTId; wt2.posn_smp_idx = 32; wt2.aV = a2V; struct wt_osc::wt_str wtA[] = { wt0, wt1, wt2 }; struct wt_seq_osc::wt_seq_str wt_seq{ .wtA = wtA, .wtN = 3 }; struct wt_seq_osc::wt_seq_str chA[] = { wt_seq, wt_seq }; struct multi_ch_wt_seq_str mcs = { .chA = chA, .chN = chN }; struct obj_str obj; if((rc = create(&obj,chN)) != kOkRC ) goto errLabel; if((rc = setup(&obj,&mcs)) != kOkRC ) goto errLabel; else { unsigned yN = (int)(srate*10); unsigned actual = 0; unsigned yi = 0; unsigned yDspSmpCnt = 8; while( yi < yN && !is_done(&obj) ) { unsigned frmSmpN = std::min(yN-yi,yDspSmpCnt); sample_t yV[frmSmpN*chN]; if((rc = process(&obj, yV, chN, frmSmpN, actual)) != kOkRC ) goto errLabel; assert( actual == frmSmpN ); vop::print( yV, frmSmpN*chN, "%5.3f",nullptr,8); yi += actual; } } errLabel: destroy(&obj); return rc; } } rc_t test( const test::test_args_t& args ) { rc_t rc = kOkRC; if( textIsEqual(args.test_label,"wnd_func") ) { rc = wnd_func::test(); goto errLabel; } if( textIsEqual(args.test_label,"ola") ) { rc = ola::test(); goto errLabel; } if( textIsEqual(args.test_label,"shift_buf") ) { rc = shift_buf::test(); goto errLabel; } if( textIsEqual(args.test_label,"wt_osc") ) { rc = wt_osc::test(); goto errLabel; } if( textIsEqual(args.test_label,"wt_seq_osc") ) { rc = wt_seq_osc::test(); goto errLabel; } if( textIsEqual(args.test_label,"multi_ch_wt_seq_osc") ) { rc = multi_ch_wt_seq_osc::test(); goto errLabel; } rc = cwLogError(kInvalidArgRC,"Unknown test case module:%s test:%s.",args.module_label,args.test_label); errLabel: return rc; } } /* namespace spec_dist { rc_t _test_one( const object_t* arg, ) { } rc_t create( struct obj_str*& p, unsigned binN, bool bypassFl=false, T1 ceiling=30, T1 expo=2, T1 thresh=60, T1 uprSlope=0, T1 lwrSlope=2, T1 mix=0 ) rc_t test( const object_t* args ) { } } */ }