diff --git a/cwAudioTransforms.cpp b/cwAudioTransforms.cpp index d85a253..e2314d5 100644 --- a/cwAudioTransforms.cpp +++ b/cwAudioTransforms.cpp @@ -59,7 +59,7 @@ namespace cw return rc; } - rc_t test( const cw::object_t* args ) + 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 }; @@ -87,7 +87,7 @@ namespace cw namespace ola { - rc_t test( const cw::object_t* args ) + rc_t test() { typedef float sample_t; @@ -150,7 +150,7 @@ namespace cw namespace shift_buf { - rc_t test( const object_t* args ) + rc_t test() { rc_t rc = kOkRC; typedef float sample_t; @@ -223,7 +223,7 @@ namespace cw namespace pv_anl { - rc_t test( const object_t* args ) + rc_t test() { rc_t rc = kOkRC; pv_anl::fobj_t* pva = nullptr; @@ -253,12 +253,256 @@ namespace cw } - rc_t test( const cw::object_t* args ) + namespace wt_osc { - wnd_func::test(args); - ola::test(args); - shift_buf::test(args); - return kOkRC; + 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<sample_t,srate_t> 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<sample_t,srate_t> 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<sample_t,srate_t> 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<sample_t,srate_t> wt0 = wt; + struct wt_osc::wt_str<sample_t,srate_t> wt1 = wt; + struct wt_osc::wt_str<sample_t,srate_t> 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<sample_t,srate_t> wtA[] = { + wt0, + wt1, + wt2 + }; + + struct wt_seq_osc::wt_seq_str<sample_t,srate_t> wt_seq{ + .wtA = wtA, + .wtN = 3 + }; + + struct wt_seq_osc::obj_str<sample_t,srate_t> 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<sample_t,srate_t> 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<sample_t,srate_t> wt0 = wt; + struct wt_osc::wt_str<sample_t,srate_t> wt1 = wt; + struct wt_osc::wt_str<sample_t,srate_t> 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<sample_t,srate_t> wtA[] = { + wt0, + wt1, + wt2 + }; + + struct wt_seq_osc::wt_seq_str<sample_t,srate_t> wt_seq{ + .wtA = wtA, + .wtN = 3 + }; + + + struct wt_seq_osc::wt_seq_str<sample_t,srate_t> chA[] = { + wt_seq, + wt_seq + }; + + struct multi_ch_wt_seq_str<sample_t,srate_t> mcs = { + .chA = chA, + .chN = chN + }; + + + struct obj_str<sample_t,srate_t> 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; } } diff --git a/cwAudioTransforms.h b/cwAudioTransforms.h index b3d2614..52c78ee 100644 --- a/cwAudioTransforms.h +++ b/cwAudioTransforms.h @@ -779,9 +779,9 @@ namespace cw ifft::exec_polar( p->ft, magV, phsV ); - // convert double to float - T0 v[ p->ft->outN ]; - vop::copy( v, p->ft->outV, p->ft->outN ); + // convert double to float + T0 v[ p->ft->outN ]; + vop::copy( v, p->ft->outV, p->ft->outN ); ola::exec( p->ola, v, p->ft->outN ); @@ -1233,8 +1233,447 @@ namespace cw } + + //--------------------------------------------------------------------------------------------------------------------------------- + // wt_osc + // - rc_t test( const cw::object_t* args ); + namespace wt_osc + { + + typedef enum { + kInvalidWtTId, + kOneShotWtTId, + kLoopWtTId + } wt_tid_t; + + template< typename sample_t, typename srate_t > + struct wt_str + { + wt_tid_t tid; + unsigned cyc_per_loop; // count of cycles in the loop + sample_t* aV; // aV[ padN + aN + padN ] + unsigned aN; // Count of unique samples + double rms; + double hz; + srate_t srate; + unsigned pad_smpN; + unsigned posn_smp_idx; // The location of this sample in the original audio file. + }; + + + template< typename sample_t > + sample_t table_read_2( const sample_t* tab, double frac ) + { + + unsigned i0 = floor(frac); + unsigned i1 = i0 + 1; + double f = frac - int(frac); + + sample_t r = (sample_t)(tab[i0] + (tab[i1] - tab[i0]) * f); + + //intf("r:%f frac:%f i0:%i f:%f\n",r,frac,i0,f); + return r; + } + + template< typename sample_t > + sample_t hann_read( double x, double N ) + { + while( x > N) + x -= N; + + x = x - (N/2) ; + + return (sample_t)(0.5 + 0.5 * cos(2*M_PI * x / N)); + } + + template< typename sample_t, typename srate_t > + struct obj_str + { + const wt_str<sample_t,srate_t>* wt; + + double phs; // current fractional phase into wt->aV[] + double fsmp_per_wt; // + + }; + + template< typename sample_t, typename srate_t > + bool is_init(const struct obj_str<sample_t,srate_t>* p) + { return p->wt != nullptr; } + + template< typename sample_t, typename srate_t > + void init(struct obj_str<sample_t,srate_t>* p, struct wt_str<sample_t,srate_t>* wt) + { + if( wt == nullptr ) + p->wt = nullptr; + else + { + double fsmp_per_cyc = wt->srate/wt->hz; + p->fsmp_per_wt = fsmp_per_cyc * 2; // each wavetable contains 2 + + p->wt = wt; + p->phs = 0; + } + } + + template< typename sample_t, typename srate_t > + void _process_loop(struct obj_str<sample_t,srate_t>* p, sample_t* aV, unsigned aN, unsigned& actual_Ref) + { + double phs0 = p->phs; + double phs1 = phs0 + p->fsmp_per_wt/2; + unsigned smp_per_wt = (int)floor(p->fsmp_per_wt); // + + while(phs1 >= smp_per_wt) + phs1 -= smp_per_wt; + + for(unsigned i=0; i<aN; ++i) + { + sample_t s0 = table_read_2( p->wt->aV+p->wt->pad_smpN, phs0 ); + sample_t s1 = table_read_2( p->wt->aV+p->wt->pad_smpN, phs1 ); + + sample_t e0 = hann_read<sample_t>(phs0,p->fsmp_per_wt); + sample_t e1 = hann_read<sample_t>(phs1,p->fsmp_per_wt); + + aV[ i ] = e0*s0 + e1*s1; + + // advance the phases of the oscillators + phs0 += 1; + while(phs0 >= smp_per_wt) + phs0 -= smp_per_wt; + + phs1 += 1; + while(phs1 >= smp_per_wt) + phs1 -= smp_per_wt; + + } + + p->phs = phs0; + actual_Ref = aN; + } + + template< typename sample_t, typename srate_t > + void _process_one_shot(struct obj_str<sample_t,srate_t>* p, sample_t* aV, unsigned aN, unsigned& actual_Ref) + { + unsigned phs = (unsigned)p->phs; + unsigned i; + for(i=0; i<aN && phs<p->wt->aN; ++i,++phs) + aV[i] = p->wt->aV[ p->wt->pad_smpN + phs ]; + + p->phs = phs; + actual_Ref = i; + + } + + template< typename sample_t, typename srate_t > + void process(struct obj_str<sample_t,srate_t>* p, sample_t* aV, unsigned aN, unsigned& actual_Ref) + { + actual_Ref = 0; + switch( p->wt->tid ) + { + case wt_osc::kLoopWtTId: + _process_loop(p,aV,aN,actual_Ref); + break; + + case wt_osc::kOneShotWtTId: + _process_one_shot(p,aV,aN,actual_Ref); + break; + + default: + assert(0); + } + + } + + rc_t test(); + + } // wt_osc + + + namespace wt_seq_osc + { + + template< typename sample_t, typename srate_t > + struct wt_seq_str + { + struct wt_osc::wt_str<sample_t,srate_t>* wtA; + unsigned wtN; + }; + + template< typename sample_t, typename srate_t > + struct obj_str + { + struct wt_seq_osc::wt_seq_str<sample_t,srate_t>* wt_seq; + struct wt_osc::obj_str<sample_t,srate_t> osc0; + struct wt_osc::obj_str<sample_t,srate_t> osc1; + + unsigned wt_idx; // index of wt0 in wt_seq->wtA[] + + + unsigned mix_interval_smp; // osc0/osc1 crossfade interval in samples + unsigned mix_phs; // current crossfade phase (0 <= mix_phs <= mix_interval_smp) + }; + + template< typename sample_t, typename srate_t > + rc_t _update_wt( struct obj_str<sample_t,srate_t>* p, unsigned wt_idx ) + { + rc_t rc = kOkRC; + struct wt_osc::wt_str<sample_t,srate_t>* wt0 = nullptr; + struct wt_osc::wt_str<sample_t,srate_t>* wt1 = nullptr; + + p->mix_interval_smp = 0; + + if( wt_idx < p->wt_seq->wtN ) + wt0 = p->wt_seq->wtA + wt_idx; + + if( (wt_idx+1) < p->wt_seq->wtN ) + { + wt1 = p->wt_seq->wtA + (wt_idx+1); + + unsigned posn0_smp_idx = wt0->posn_smp_idx; + unsigned posn1_smp_idx = wt1->posn_smp_idx; + + if( posn1_smp_idx < posn0_smp_idx ) + { + rc = cwLogError(kInvalidStateRC,"The position of the wavetable at wt. seq index:%i must be greater than the position of the previous wt.",wt_idx+1); + + goto errLabel; + } + + p->mix_interval_smp = posn1_smp_idx - posn0_smp_idx; + } + + wt_osc::init(&p->osc0,wt0); + wt_osc::init(&p->osc1,wt1); + + p->wt_idx = wt_idx; + p->mix_phs = 0; + + errLabel: + return rc; + } + + + template< typename sample_t, typename srate_t > + bool is_init( const struct obj_str<sample_t,srate_t>* p ) + { + return is_init(&p->osc0); + } + + template< typename sample_t, typename srate_t > + rc_t init(struct obj_str<sample_t,srate_t>* p, struct wt_seq_osc::wt_seq_str<sample_t,srate_t>* wt_seq) + { + rc_t rc = kOkRC; + p->wt_seq = wt_seq; + p->wt_idx = 0; + + if((rc = _update_wt(p,0)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + + template< typename sample_t, typename srate_t > + rc_t process(struct obj_str<sample_t,srate_t>* p, sample_t* aV, unsigned aN, unsigned& actual_Ref) + { + actual_Ref = 0; + + rc_t rc = kOkRC; + unsigned actual; + bool atk_fl = p->wt_idx==0 && p->osc0.wt->tid == wt_osc::kOneShotWtTId; + + // if the osc is in the attack phase + if( atk_fl ) + { + // update aV[aN] from osc0 + wt_osc::process(&p->osc0,aV,aN,actual); + + actual_Ref = actual; + + // if all requested samples were generated we are done ... + if( actual >= aN ) + return rc; + + // otherwise all requested samples were not generated + // fill the rest of aV[] from the next one or two wave tables. + aN -= actual; + aV += actual; + + // initialize osc0 and osc1 + if((rc = _update_wt(p, 1)) != kOkRC ) + goto errLabel; + } + + wt_osc::process(&p->osc0,aV,aN,actual); + + // if the second oscillator is initialized + if( wt_osc::is_init(&p->osc1) ) + { + unsigned actual1 = 0; + sample_t tV[ aN ]; + // generate aN samples into tV[aN] + wt_osc::process(&p->osc1,tV,aN,actual1); + + assert( actual1 == actual ); + + + sample_t g = (sample_t)std::min(1.0,(double)p->mix_phs / p->mix_interval_smp); + + // mix the output of the second oscillator into the output signal + vop::scale_add(aV,aV,(1.0f-g),tV,g,actual1); + + p->mix_phs += actual; + + // if the osc0/osc1 xfade is complete ... + if( p->mix_phs >= p->mix_interval_smp ) + { + // ... then advance to the next set of wavetables + if((rc = _update_wt(p, p->wt_idx+1)) != kOkRC ) + goto errLabel; + } + + } + + actual_Ref += actual; + + errLabel: + return rc; + } + + rc_t test(); + + } // wt_seq_osc + + namespace multi_ch_wt_seq_osc + { + template< typename sample_t, typename srate_t > + struct multi_ch_wt_seq_str + { + struct wt_seq_osc::wt_seq_str<sample_t,srate_t>* chA; + unsigned chN; + }; + + template< typename sample_t, typename srate_t > + struct obj_str + { + const struct multi_ch_wt_seq_str<sample_t,srate_t>* mcs = nullptr; + struct wt_seq_osc::obj_str<sample_t,srate_t>* chA = nullptr; + unsigned chAllocN = 0; + unsigned chN = 0; + bool done_fl = true; + }; + + + template< typename sample_t, typename srate_t > + rc_t create(struct obj_str<sample_t,srate_t>* p, unsigned maxChN, const struct multi_ch_wt_seq_str<sample_t,srate_t>* mcs=nullptr ) + { + rc_t rc = kOkRC; + + destroy(p); + + p->chA = mem::allocZ< struct wt_seq_osc::obj_str<sample_t,srate_t> >(maxChN); + p->chAllocN = maxChN; + p->chN = 0; + p->done_fl = true; + + if( mcs != nullptr ) + setup(p,mcs); + + return rc; + } + + template< typename sample_t, typename srate_t > + rc_t destroy(struct obj_str<sample_t,srate_t>* p ) + { + rc_t rc = kOkRC; + + mem::release(p->chA); + p->chAllocN = 0; + p->chN = 0; + p->done_fl = true; + return rc; + } + + template< typename sample_t, typename srate_t > + rc_t setup( struct obj_str<sample_t,srate_t>* p, const struct multi_ch_wt_seq_str<sample_t,srate_t>* mcs ) + { + rc_t rc = kOkRC; + + if( mcs->chN > p->chAllocN ) + { + rc = cwLogError(kInvalidArgRC,"Invalid multi-ch-wt-osc channel count. (%i > %i)",mcs->chN,p->chAllocN); + goto errLabel; + } + + + p->mcs = mcs; + p->done_fl = false; + p->chN = mcs->chN; + for(unsigned i=0; i<mcs->chN; ++i) + if((rc = wt_seq_osc::init(p->chA+i,mcs->chA + i)) != kOkRC ) + goto errLabel; + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"multi-ch-wt-osc setup failed."); + + return rc; + } + + template< typename sample_t, typename srate_t > + rc_t is_done( struct obj_str<sample_t,srate_t>* p ) + { return p->done_fl; } + + template< typename sample_t, typename srate_t > + rc_t process( struct obj_str<sample_t,srate_t>* p, sample_t* aM, unsigned chN, unsigned frmN, unsigned& actual_Ref ) + { + rc_t rc = kOkRC; + unsigned actual = 0; + unsigned doneN = 0; + + for(unsigned i=0; i<p->chN; ++i) + { + unsigned actual0 = 0; + sample_t* aV = aM + (i*frmN); + + if( !wt_seq_osc::is_init(p->chA + i) ) + { + vop::zero(aV,frmN); + actual0 = frmN; + doneN += 1; + } + else + { + if((rc = wt_seq_osc::process(p->chA + i, aV, frmN, actual0 )) != kOkRC ) + goto errLabel; + } + + if( i!=0 && actual0 != actual ) + { + rc = cwLogError(kInvalidStateRC,"An inconsistent sample count was generated across channels (%i != !i).",actual0,actual); + goto errLabel; + } + + actual = actual0; + } + + actual_Ref = actual; + p->done_fl = doneN == p->chN; + + errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"multi-ch-wt-osc process failed."); + + return rc; + } + + rc_t test(); + + } //multi_ch_wt_seq_osc + + + rc_t test( const test::test_args_t& args ); } // dsp } // cw