cwAudioTransforms.h/cpp : Implemented wt_osc,wt_seq_osc,multi_ch_wt_seq_osc.

This commit is contained in:
kevin 2024-07-31 17:16:54 -04:00
parent 1b4ad4f45f
commit dfd1805ba1
2 changed files with 696 additions and 13 deletions

View File

@ -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;
}
}

View File

@ -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 );
@ -1234,7 +1234,446 @@ namespace cw
}
rc_t test( const cw::object_t* args );
//---------------------------------------------------------------------------------------------------------------------------------
// wt_osc
//
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