From 3de88dfc32c3ca8b4a68cac69c6ef8957de39188 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 31 Jul 2024 17:15:10 -0400 Subject: [PATCH] cwWaveTableBank.h/cpp : Removed dead code. Added instr_pitch_velocities(). Implemented get_wave_table(). --- cwWaveTableBank.cpp | 1318 ++++++------------------------------------- cwWaveTableBank.h | 108 +--- 2 files changed, 198 insertions(+), 1228 deletions(-) diff --git a/cwWaveTableBank.cpp b/cwWaveTableBank.cpp index 485c2bd..04db5c5 100644 --- a/cwWaveTableBank.cpp +++ b/cwWaveTableBank.cpp @@ -12,1084 +12,11 @@ #include "cwVectOps.h" #include "cwDspTypes.h" #include "cwDsp.h" +#include "cwAudioTransforms.h" #include "cwWaveTableBank.h" #include "cwMidi.h" #include "cwTest.h" -#ifdef NOT_DEF -namespace cw -{ - namespace wt_bank - { - - typedef struct instr_str - { - char* label; - unsigned id; - struct instr_str* link; - } instr_t; - - typedef struct af_str - { - object_t* cfg; - - const char* audio_fname; - - wt_t* wtA; - unsigned wtN; - - ch_t* chA; - unsigned chN; - - seg_t* segA; - unsigned segN; - - struct af_str* link; - } af_t; - - - typedef struct wt_map_str - { - wt_t** map; // bankM[pitch(128)][vel(128)] - } wt_map_t; - - typedef struct wt_bank_str - { - af_t* afList; // One af_t record per cfg file found in the source directory given to create() - - wt_map_t* wtMapA; // wtMapA[ wtMapN ] one wt_map_t record per instr_t record - unsigned wtMapN; // wtMapN is the same as the length of instrList - - unsigned next_instr_id; // Current length of instrList - instr_t* instrList; // List of instruments - - } wt_bank_t; - - wt_bank_t* _handleToPtr( handle_t h ) - { return handleToPtr(h); } - - void _report_map( const wt_map_t* map ) - { - for(unsigned i=0; imap[(j*midi::kMidiNoteCnt) + i]; - - cwLogPrint("%i:%1i ",j,wt != nullptr); - } - cwLogPrint("\n"); - } - - } - - void _report( wt_bank_t* p ) - { - for(af_t* af = p->afList; af!=nullptr; af=af->link) - { - cwLogInfo("%s",af->audio_fname); - - for(unsigned i=0; iwtN; ++i) - { - const wt_t* wt = af->wtA + i; - - cwLogInfo(" pitch:%i vel:%i ", wt->pitch, wt->vel ); - - for(unsigned j=0; jchN; ++j) - { - const ch_t* ch = wt->chA + j; - - cwLogInfo(" ch:%i",ch->ch_idx); - - for(unsigned k=0; ksegN; ++k) - { - const seg_t* seg = ch->segA + k; - - cwLogInfo(" type:%i smpN:%i cost:%f", seg->tid, seg->aN, seg->cost); - - } - } - } - } - - if( p->wtMapN > 0 ) - _report_map( p->wtMapA ); - } - - rc_t _destroy( wt_bank_t* p ) - { - rc_t rc = kOkRC; - af_t* af = p->afList; - while( af != nullptr ) - { - af_t* af0 = af->link; - - for(unsigned i=0; isegN; ++i) - if(af->segA != nullptr ) - mem::release(af->segA[i].aV); - - af->cfg->free(); - mem::release(af->wtA); - mem::release(af->chA); - mem::release(af->segA); - mem::release(af); - - af = af0; - } - - - for(unsigned i=0; iwtMapN; ++i) - mem::release(p->wtMapA[i].map); - - instr_t* instr=p->instrList; - while( instr!=nullptr ) - { - instr_t* i0 = instr->link; - mem::release(instr->label); - mem::release(instr); - instr=i0; - } - - mem::release(p->wtMapA); - - - mem::release(p); - return rc; - } - - void _load_segment_audio( seg_t& seg, - const sample_t* const * audio_ch_buf, - unsigned audio_ch_bufN, - unsigned padSmpN, - unsigned ch_idx, - seg_tid_t tid, - unsigned bsi, - unsigned esi ) - { - assert( ch_idx < audio_ch_bufN ); - - seg.aN = esi - bsi; - seg.aV = mem::alloc( padSmpN + seg.aN + padSmpN ); - seg.padN = padSmpN; - - // audio vector layout - // aV[ padSmpN + aN + padSmpN ] - - vop::copy( seg.aV, audio_ch_buf[ ch_idx ] + esi - padSmpN, padSmpN ); - vop::copy( seg.aV + padSmpN, audio_ch_buf[ ch_idx ] + bsi, seg.aN ); - vop::copy( seg.aV + padSmpN + seg.aN, audio_ch_buf[ ch_idx ] + bsi, padSmpN ); - - } - - instr_t* _find_instr( wt_bank_t* p, const char* instr_label ) - { - instr_t* instr = p->instrList; - for(; instr != nullptr; instr=instr->link ) - if( textIsEqual(instr->label,instr_label) ) - return instr; - - return nullptr; - } - - unsigned _find_or_add_instr_id( wt_bank_t* p, const char* instr_label ) - { - instr_t* instr; - if((instr = _find_instr(p,instr_label)) == nullptr ) - { - instr = mem::allocZ(); - instr->id = p->next_instr_id++; - instr->label = mem::duplStr(instr_label); - - instr->link = p->instrList; - p->instrList = instr; - } - - return instr->id; - } - - // If 'count_fl' is set then p->wtN,p->chN and p->segN are set but no - // data is stored. - rc_t _parse_cfg( wt_bank_t* p, - af_t* af, - const object_t* wtL, - const sample_t* const* audio_ch_buf = nullptr, - unsigned audio_ch_bufN = 0, - unsigned padSmpN = 0, - srate_t srate = 0) - { - rc_t rc = kOkRC; - unsigned wtN = wtL->child_count(); - unsigned ch_idx = 0; - unsigned seg_idx = 0; - const char* instr_label = nullptr; - - // count_Fl true if counting wt/ch/seg records otherwise building wt/ch/seg data structures - bool count_fl = audio_ch_buf == nullptr; - - // for each wt cfg record - for(unsigned i=0; iwtA[i]; - - af->wtN += count_fl ? 1 : 0; - - // get the ith wt cfg - if((wt = wtL->child_ele(i)) == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"Unexpected missing wave table record at index %i.",i); - goto errLabel; - } - - // parse th eith wt cfg - if((rc = wt->getv("instr",instr_label, - "pitch",wr.pitch, - "vel",wr.vel, - "chL",chL)) != kOkRC ) - { - rc = cwLogError(rc,"Error parsing the wave table record at index %i.",i); - goto errLabel; - } - - if( wr.pitch >= midi::kMidiNoteCnt ) - { - rc = cwLogError(kSyntaxErrorRC,"The MIDI pitch value %i is invalid on the WT record %s vel:%i.",wr.pitch,cwStringNullGuard(instr_label),wr.vel); - goto errLabel; - } - - if( wr.vel >= midi::kMidiVelCnt ) - { - rc = cwLogError(kSyntaxErrorRC,"The MIDI velocity value %i is invalid on the WT record %s pitch:%i.",wr.vel,cwStringNullGuard(instr_label),wr.pitch); - goto errLabel; - } - - // count or store the wt recd - wr.instr_id = count_fl ? kInvalidId : _find_or_add_instr_id(p,instr_label); - wr.chN = chL->child_count(); - wr.chA = count_fl ? nullptr : af->chA + ch_idx; - wr.srate = srate; - ch_idx += wr.chN; - - // for each channel cfg on this wt cfg - for(unsigned j=0; jchN += count_fl ? 1 : 0; - - // get the jth ch cfg - if((ch = chL->child_ele(j)) == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"Unexpected missing channel record at wt index %i ch index:%i.",i,j); - goto errLabel; - } - - // parse the ch cfg - if((rc = ch->getv("ch_idx", cr.ch_idx, - "segL", segL)) != kOkRC ) - { - rc = cwLogError(rc,"Error parsing the channel record at wt index %i ch index:%i.",i,j); - goto errLabel; - } - - // count or store the ch cfg - cr.segN = segL->child_count(); - cr.segA = count_fl ? nullptr : af->segA + seg_idx; - seg_idx += cr.segN; - - // for each seg cfg on this ch cfg - for(unsigned k=0; ksegN += count_fl ? 1 : 0; - - // get the kth seg cfg - if((seg = segL->child_ele(k)) == nullptr ) - { - rc = cwLogError(kSyntaxErrorRC,"Unexpected missing segment record at wt index %i ch index:%i seg index:%i.",i,j,k); - goto errLabel; - } - - // parse the kth seg cfg - if((rc = seg->getv("cost",sr.cost, - "cyc_per_loop",sr.cyc_per_loop, - "bsi",bsi, - "esi",esi)) != kOkRC ) - { - rc = cwLogError(rc,"Error parsing the segment record at wt index %i ch index:%i seg index:%i.",i,j,k); - goto errLabel; - } - - // set the type of this seg - sr.tid = k==0 ? kAttackTId : kLoopTId; - - // if storing then load the audio into the seg - if( !count_fl ) - { - if( cr.ch_idx >= audio_ch_bufN ) - { - rc = cwLogError(kSyntaxErrorRC,"The invalid audio channel index %i was encountered on wt index %i ch index:%i seg index:%i.",cr.ch_idx,i,j,k); - goto errLabel; - } - - _load_segment_audio(sr,audio_ch_buf,audio_ch_bufN, padSmpN, cr.ch_idx, sr.tid, bsi, esi ); - } - } - } - } - - assert( ch_idx == af->chN); - assert( seg_idx == af->segN ); - - errLabel: - return rc; - } - - rc_t _load_af_wt_array( wt_bank_t* p, af_t* af, const object_t* wtL, unsigned padN ) - { - rc_t rc = kOkRC; - audiofile::handle_t afH; - audiofile::info_t af_info; - sample_t* smpV = nullptr; - - if((rc = open( afH, af->audio_fname, &af_info )) != kOkRC ) - { - rc = cwLogError(rc,"Audio sample file open failed."); - goto errLabel; - } - else - { - sample_t* buf[ af_info.chCnt ]; - unsigned actualFrmN = 0; - unsigned smpN = af_info.frameCnt * af_info.chCnt; - - // allocate memory to hold the entire audio file - smpV = mem::alloc( smpN ); - - // create the channel buffer - for(unsigned i=0; iaudio_fname)); - goto errLabel; - } - - // Parse the cfg and fill the associated af. - if((rc = _parse_cfg( p, af,wtL, buf, af_info.chCnt, padN, af_info.srate)) != kOkRC ) - goto errLabel; - - } - - errLabel: - - mem::release(smpV); - close(afH); - - return rc; - - } - - rc_t _load_cfg_file( wt_bank_t* p, const char* cfg_fname, unsigned padN ) - { - rc_t rc = kOkRC; - const object_t* wtL = nullptr; - - af_t* af = mem::allocZ(); - - af->link = p->afList; - p->afList = af; - - - if((rc = objectFromFile(cfg_fname,af->cfg)) != kOkRC ) - { - rc = cwLogError(rc,"Unable to open the wavetable file '%s'.",cwStringNullGuard(cfg_fname)); - goto errLabel; - } - - if((rc = af->cfg->getv("audio_fname",af->audio_fname, - "wt",wtL)) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The wave table header parse failed."); - goto errLabel; - } - - // Determine the count of wt,ch, and seg records required by this af_t record, - // and fill in af->wtN, af->chN and af->segN - if((rc = _parse_cfg( p, af, wtL )) != kOkRC ) - goto errLabel; - - cwLogInfo("wtN:%i chN:%i segN:%i audio:%s",af->wtN,af->chN,af->segN,af->audio_fname); - - af->wtA = mem::allocZ(af->wtN); - af->chA = mem::allocZ(af->chN); - af->segA = mem::allocZ(af->segN); - - // Parse the wt list and load the af. - if((rc = _load_af_wt_array( p, af, wtL, padN )) != kOkRC ) - goto errLabel; - - errLabel: - if( rc != kOkRC ) - rc = cwLogError(rc,"Wave table load failed on '%s'.",cwStringNullGuard(cfg_fname)); - - return rc; - } - - void _load_wt_map( wt_bank_t* p, unsigned instr_id, wt_t**& wt_map ) - { - for(af_t* af=p->afList; af!=nullptr; af=af->link) - for(unsigned i=0; iwtN; i++) - if( af->wtA[i].instr_id == instr_id ) - { - unsigned idx = af->wtA[i].vel * midi::kMidiNoteCnt + af->wtA[i].pitch; - - assert( idx < midi::kMidiNoteCnt * midi::kMidiVelCnt ); - - if( wt_map[idx] != nullptr ) - cwLogWarning("Multiple wt records map to instr:%i pitch:%i vel:%i. Only one will be preserved",instr_id,af->wtA[i].pitch,af->wtA[i].vel); - - wt_map[idx] = af->wtA + i; - } - } - - rc_t _create_wt_map_array( wt_bank_t* p ) - { - rc_t rc = kOkRC; - p->wtMapN = p->next_instr_id; - p->wtMapA = mem::allocZ(p->wtMapN); - - for(unsigned i=0; iwtMapN; ++i) - { - p->wtMapA[i].map = mem::allocZ(midi::kMidiNoteCnt * midi::kMidiVelCnt ); - - _load_wt_map( p, i, p->wtMapA[i].map ); - - } - - return rc; - } - - - const wt_t* _get_wave_table( wt_bank_t* p, unsigned instr_idx, unsigned pitch, unsigned vel ) - { - rc_t rc = kOkRC; - - if( instr_idx >= p->wtMapN ) - { - rc = cwLogError(kInvalidArgRC,"Invalid instr_idx %i",instr_idx); - goto errLabel; - } - - if( pitch >= midi::kMidiNoteCnt ) - { - rc = cwLogError(kInvalidArgRC,"Invalid MIDI pitch %i",pitch); - goto errLabel; - } - - if( vel >= midi::kMidiVelCnt ) - { - rc = cwLogError(kInvalidArgRC,"Invalid MIDI pitch %i",pitch); - goto errLabel; - } - - return p->wtMapA[ instr_idx ].map[ vel * midi::kMidiNoteCnt + pitch ]; - - errLabel: - cwLogError(rc,"Wave table lookup failed."); - return nullptr; - } - - typedef struct seg_osc_str - { - seg_tid_t tid; - const seg_t* seg; - unsigned id; - double xphase; - double loop_frac_smpN; // length of the loop including fractional part - unsigned cur_loopN; // count of loops - sample_t* envV; - unsigned envN; - unsigned delay_smp_idx; - unsigned cur_smp_idx; - unsigned ch_idx; - } seg_osc_t; - - void _seg_osc_setup( seg_osc_t* osc, - unsigned id, - unsigned ch_idx, - const seg_t* seg, - double smp_per_cyc, - sample_t* envV, - unsigned envN, - unsigned delay_smp_idx, - seg_tid_t tid = kInvalidTId ) - { - osc->seg = seg; - osc->id = id; - osc->tid = tid==kInvalidTId ? seg->tid : tid; - osc->loop_frac_smpN = osc->tid==kAttackTId ? seg->aN : smp_per_cyc * seg->cyc_per_loop; - osc->xphase = 0; - osc->envV = envV; - osc->envN = envN; - osc->delay_smp_idx = delay_smp_idx; - osc->cur_smp_idx = 0; - osc->ch_idx = ch_idx; - } - - void _seg_osc_update( seg_osc_t* osc, sample_t* yV, unsigned yN, unsigned& actual_yN_ref ) - { - unsigned yi = 0; - const float* xV = osc->seg->aV + osc->seg->padN; - - actual_yN_ref = 0; - - for(yi=0; yicur_smp_idx) - if( osc->cur_smp_idx >= osc->delay_smp_idx ) - { - - double xfi = std::floor(osc->xphase); - double frac = osc->xphase - xfi; - int xi = (int)xfi; - - yV[yi] += xV[xi] + (xV[xi+1] - xV[xi]) * frac * osc->envV[xi]; - - /* - float f = frac; - float f_1 = f - 1.0; - float f_2 = f - 2.0; - float f1 = f + 1.0; - - yV[yi] += osc->envV[xi] * ((-f)*f_1*f_2*xV[xi-1]/6.0f + f1*f_1*f_2*xV[xi]/2.0f - f1*f*f_2*xV[xi+1]/2.0f + f1*f*f_1*xV[xi+2]/6.0f); - */ - - //if( osc->seg->tid==kLoopTId && osc->ch_idx==0 && osc->cur_loopN < 4 ) - // printf("%i,%i,%i,%i,%f,%f,%f,%f\n",osc->id,osc->cur_smp_idx,yi,xi,frac,xV[xi],osc->envV[xi],yV[yi]); - - osc->xphase += 1.0f; // osc->seg->tid == kLoopTId ? 0.5f : 1.0f; - - // if the end of the wave table is encountered - if( osc->xphase >= osc->loop_frac_smpN ) - { - if( osc->tid != kLoopTId) - goto errLabel; - - osc->xphase = osc->xphase - osc->loop_frac_smpN; - osc->cur_loopN += 1; - } - } - - - errLabel: - actual_yN_ref = yi; - - } - - - void _seg_osc_update_0( seg_osc_t* osc, sample_t* yV, unsigned yN, unsigned& actual_yN_ref ) - { - unsigned yi = 0; - const float* xV = osc->seg->aV + osc->seg->padN; - double xphs = osc->xphase; - double xfi = std::floor(osc->xphase); - double frac = xphs - xfi; - int xi = (int)xfi; - - actual_yN_ref = 0; - - for(yi=0; yicur_smp_idx) - if( osc->cur_smp_idx >= osc->delay_smp_idx ) - { - - //yV[yi] = xV[xi] + (xV[xi+1] - xV[xi]) * frac; - - float f = frac; - float f_1 = f - 1.0; - float f_2 = f - 2.0; - float f1 = f + 1.0; - - yV[yi] += osc->envV[xi] * ((-f)*f_1*f_2*xV[xi-1]/6.0f + f1*f_1*f_2*xV[xi]/2.0f - f1*f*f_2*xV[xi+1]/2.0f + f1*f*f_1*xV[xi+2]/6.0f); - - //if( loop_fl && ch_idx==0 && seg_loopN_ref < 4 ) - // printf("%i,%i,%f,%f,%f\n",yi,xi,frac,xV[xi],yV[yi]); - - xi += 1; - - - // if the end of the wave table is encountered - if( frac+xi >= osc->loop_frac_smpN ) - { - if( osc->tid != kLoopTId) - goto errLabel; - - xphs = (frac+xi) - osc->loop_frac_smpN; - xi = (unsigned)std::floor(xphs); - frac = xphs - xi; - osc->cur_loopN += 1; - } - } - - - errLabel: - actual_yN_ref = yi; - osc->xphase = frac + xi; - - } - - - rc_t _gen_note( const wt_t* wt, srate_t srate, sample_t** outChV, unsigned y_frm_cnt ) - { - rc_t rc = kOkRC; - const unsigned frmPerUpdate = 64; - - double hz = midi_to_hz( wt->pitch ); - double smp_per_cyc = srate / hz; - - // for each audio output channel - for(unsigned ch_idx=0; ch_idxchN; ++ch_idx) - { - seg_osc_t a_osc, b_osc, l0_osc, l1_osc; - seg_osc_t* cur_osc0 = &a_osc; - seg_osc_t* cur_osc1 = nullptr; - seg_osc_t* cur_oscb = nullptr; - - unsigned a_envN = wt->chA[ch_idx].segA[0].aN; - sample_t a_envV[ a_envN ]; - vop::ones(a_envV,a_envN); - - unsigned l_envN = wt->chA[ch_idx].segA[1].aN; - sample_t l_envV[ l_envN ]; - dsp::hann(l_envV,l_envN); - - unsigned b_envN = wt->chA[ch_idx].segA[1].aN; - sample_t b_envV[ b_envN ]; - vop::zero(b_envV,b_envN); - vop::copy(b_envV, l_envV+l_envN/2, l_envN/2); - - - _seg_osc_setup( &a_osc, 0, ch_idx, wt->chA[ch_idx].segA, smp_per_cyc, a_envV, a_envN, 0 ); - _seg_osc_setup( &b_osc, 1, ch_idx, wt->chA[ch_idx].segA + 1, smp_per_cyc, b_envV, b_envN, 0, kAttackTId ); - _seg_osc_setup( &l0_osc, 2, ch_idx, wt->chA[ch_idx].segA + 1, smp_per_cyc, l_envV, l_envN, 0 ); - _seg_osc_setup( &l1_osc, 3, ch_idx, wt->chA[ch_idx].segA + 1, smp_per_cyc, l_envV, l_envN, l_envN/2 ); - - sample_t* yV = outChV[ch_idx]; - unsigned y_frm_idx = 0; - - // while the output channel is not full - while( y_frm_idx < y_frm_cnt ) - { - unsigned y_upd_cnt = 0; - - // while the current frame update has not generated frmPerUpdate samples - while( y_upd_cnt < frmPerUpdate && y_frm_idx < y_frm_cnt ) - { - unsigned y_actual_upd_cnt = 0; - - unsigned n = std::min(frmPerUpdate, std::min(frmPerUpdate-y_upd_cnt, y_frm_cnt-y_frm_idx)); - - if( cur_oscb != nullptr ) - { - _seg_osc_update( cur_oscb, yV + y_frm_idx, n, y_actual_upd_cnt); - if( y_actual_upd_cnt != n ) - cur_oscb = nullptr; - } - - _seg_osc_update( cur_osc0, yV + y_frm_idx, n, y_actual_upd_cnt); - - if( cur_osc1 != nullptr ) - _seg_osc_update( cur_osc1, yV + y_frm_idx, n, y_actual_upd_cnt); - - y_upd_cnt += y_actual_upd_cnt; - y_frm_idx += y_actual_upd_cnt; - - // if the segment ran out of samples ... - if( y_actual_upd_cnt < n ) - { - // (only attack segments run out of samples - because they do not loop) - assert( cur_osc0->tid == kAttackTId ); - - cur_osc0 = &l0_osc; - //cur_osc1 = &l1_osc; - //cur_oscb = &b_osc; - } - } - } - - } - return rc; - } - - /* - void _gen_osc_update(const sample_t* xV, unsigned xN, double loop_frac_smpN, double& xPhs_ref, sample_t* yV, unsigned yN, unsigned& actual_yN_ref, bool loop_fl, unsigned& seg_loopN_ref, unsigned ch_idx ) - { - unsigned yi = 0; - double xphs = xPhs_ref; - double xfi = std::floor(xphs); - double frac = xphs - xfi; - int xi = (int)xfi; - - actual_yN_ref = 0; - - for(yi=0; yi= loop_frac_smpN ) - { - if( !loop_fl) - goto errLabel; - - xphs = (frac+xi) - loop_frac_smpN; - xi = (unsigned)std::floor(xphs); - frac = xphs - xi; - seg_loopN_ref += 1; - } - } - - - errLabel: - actual_yN_ref = yi; - xPhs_ref = frac + xi; - } - - rc_t _gen_note_0( const wt_t* wt, srate_t srate, sample_t** outChV, unsigned y_frm_cnt ) - { - rc_t rc = kOkRC; - const unsigned frmPerUpdate = 64; - - double hz = midi_to_hz( wt->pitch ); - double smp_per_cyc = srate / hz; - - // for each audio output channel - for(unsigned ch_idx=0; ch_idxchN; ++ch_idx) - { - sample_t* yV = outChV[ch_idx]; - unsigned y_frm_idx = 0; - const seg_t* seg = wt->chA[ch_idx].segA; - unsigned seg_idx = 0; - unsigned seg_smp_cnt = seg->aN; - double seg_phase = 0; - double seg_frac_smpN = seg_smp_cnt; - unsigned seg_loop_cnt = 0; - - // while the output channel is not full - while( y_frm_idx < y_frm_cnt ) - { - unsigned y_upd_cnt = 0; - - // while the current frame update has not generated frmPerUpdate samples - while( y_upd_cnt < frmPerUpdate && y_frm_idx < y_frm_cnt ) - { - unsigned y_actual_upd_cnt = 0; - - // TODO: handle case where y_frm_cnt is not an even multiple of frmPerUpdate - - // attempt to generate frmPerUpdate samples into yV[ y_frm_idx:y_frm_idx + frmPerUpdate ] - _gen_osc_update(seg->aV + seg->padN, seg_smp_cnt, seg_frac_smpN, seg_phase, yV + y_frm_idx, frmPerUpdate, y_actual_upd_cnt, seg->tid==kLoopTId, seg_loop_cnt, ch_idx ); - - - y_upd_cnt += y_actual_upd_cnt; - y_frm_idx += y_actual_upd_cnt; - - // if the segment ran out of samples ... - if( y_actual_upd_cnt < frmPerUpdate ) - { - // (only attack segments run out of samples - because they do not loop) - assert( seg->tid == kAttackTId ); - - // ...then advance to the next segment - seg_idx += 1; - if( seg_idx >= wt->chA[ch_idx].segN ) - { - // done - goto errLabel; - } - - seg = wt->chA[ch_idx].segA + seg_idx; - seg_phase = 0; - seg_smp_cnt = seg->aN; - seg_frac_smpN = smp_per_cyc * seg->cyc_per_loop; - seg_loop_cnt = 0; - } - } - } - } - errLabel: - return rc; - } - */ - - } -} - -cw::rc_t cw::wt_bank::create( handle_t& hRef, const char* dir, unsigned padN ) -{ - rc_t rc = kOkRC; - filesys::dirEntry_t* de = nullptr; - unsigned deN = 0; - wt_bank_t* p = nullptr; - - if((rc = destroy(hRef)) != kOkRC ) - return rc; - - p = mem::allocZ(); - - // get the filenames in the directory 'dir'. - if((de = filesys::dirEntries( dir, filesys::kFileFsFl | filesys::kFullPathFsFl, &deN )) == nullptr ) - { - rc = cwLogError(kOpFailRC,"Read failed on directory: %s", cwStringNullGuard(dir)); - goto errLabel; - } - - // for each filename create an 'af_t' record and load it. - for(unsigned i=0; inext_instr_id; -} - -unsigned cw::wt_bank::instr_index( handle_t h, const char* instr_label ) -{ - wt_bank_t* p = _handleToPtr(h); - instr_t* instr; - - if((instr = _find_instr(p,instr_label)) != nullptr ) - return instr->id; - - return kInvalidIdx; -} - - -const cw::wt_bank::wt_t* cw::wt_bank::get_wave_table( handle_t h, unsigned instr_idx, unsigned pitch, unsigned vel ) -{ - wt_bank_t* p = _handleToPtr(h); - - return _get_wave_table(p,instr_idx, pitch, vel); -} - -cw::rc_t cw::wt_bank::gen_notes( handle_t h, unsigned instr_idx, const unsigned* pitchA, const unsigned* velA, unsigned noteN, double note_dur_sec, const char* out_fname, double inter_note_gap_sec ) -{ - rc_t rc = kOkRC; - wt_bank_t* p = _handleToPtr(h); - const wt_t* wtA[ noteN ]; - srate_t srate = 0; - unsigned outFrmN = 0; - unsigned noteSmpN = 0; - unsigned gapSmpN = 0; - unsigned yFrmIdx = 0; - unsigned chN = 0; - sample_t* outV = nullptr; - - // Examine the wave table and determine the srate,audio ch. count, and output signal size. - for(unsigned i=0; israte; - chN = wtA[i]->chN; - - noteSmpN = (unsigned)(srate * note_dur_sec); - gapSmpN = (unsigned)(srate * inter_note_gap_sec); - - } - else - { - assert( srate == wtA[i]->srate ); - assert( chN == wtA[i]->chN ); - } - - printf("pitch:%i vel:%i s/cyc:%f\n", wtA[i]->pitch, wtA[i]->vel, srate/midi_to_hz(wtA[i]->pitch) ); - - for(unsigned j=0; jchN; ++j) - { - printf(" ch:%i\n",wtA[i]->chA[j].ch_idx); - - for(unsigned k=0; kchA[j].segN; ++k) - { - printf(" %i aN:%i padN:%i\n", - wtA[i]->chA[j].segA[k].tid, - wtA[i]->chA[j].segA[k].aN, - wtA[i]->chA[j].segA[k].padN ); - } - } - - outFrmN += noteSmpN + gapSmpN; - } - - - if( outFrmN==0 || chN == 0 ) - { - rc = cwLogError(kInvalidArgRC,"The sample rate:%f, output audio signal length (%i), or channel count (%i) is 0.",srate,outFrmN,chN); - goto errLabel; - } - else - { - sample_t* outChV[ chN ]; - - outV = mem::allocZ(outFrmN*chN); - - // for each note - for(unsigned i=0; i outFrmN ? outFrmN-yFrmIdx : noteSmpN; - - // load the output audio channel vector - for(unsigned ch_idx=0; ch_idxgetv("wtb_cfg_fname",cfg_fname)) != kOkRC ) - goto errLabel; - - if((rc0 = create(h,cfg_fname,padN)) != kOkRC ) - goto errLabel; - - - assert( sizeof(pitchA)/sizeof(pitchA[0]) == sizeof(velA)/sizeof(velA[0]) ); - - gen_notes(h,instr_idx,pitchA,velA,sizeof(pitchA)/sizeof(pitchA[0]),note_dur_sec,"~/temp/temp.wav"); - - //report(h); - -errLabel: - if((rc1 = destroy(h)) != kOkRC ) - { - rc1 = cwLogError(rc1,"Wave table bank destroy failed."); - } - - return rcSelect(rc0,rc1); -} -#endif - - - namespace cw { namespace wt_bank @@ -1097,6 +24,7 @@ namespace cw typedef struct vel_str { unsigned vel; + unsigned bsi; multi_ch_wt_seq_t mc_seq; } vel_t; @@ -1109,17 +37,21 @@ namespace cw typedef struct instr_str { - char* label; - pitch_t* pitchA; - unsigned pitchN; - struct instr_str* link; + char* label; + pitch_t* pitchA; + unsigned pitchN; + + multi_ch_wt_seq_t** pvM; // pgM[128x128] pgmM[pitch][vel] // each row holds the vel's for a given pitch + struct instr_str* link; } instr_t; typedef struct wt_bank_str { unsigned allocAudioBytesN; unsigned padSmpN; - instr_t* instrList; + + unsigned instrN; + instr_t** instrA; } wt_bank_t; typedef struct audio_buf_str @@ -1221,6 +153,7 @@ namespace cw mem::release(pr->velA); } mem::release(instr->pitchA); + mem::release(instr->pvM); mem::release(instr); } @@ -1228,22 +161,79 @@ namespace cw { rc_t rc = kOkRC; - instr_t* instr = p->instrList; - while( instr != nullptr ) - { - instr_t* instr0 = instr->link; - _destroy_instr(instr); - instr = instr0; - } + for(unsigned i=0; iinstrN; ++i ) + _destroy_instr(p->instrA[i]); mem::release(p); return rc; } + + void _alloc_wt( wt_bank_t* p, + wt_t* wt, + wt_tid_t tid, + srate_t srate, + const sample_t* aV, + unsigned posn_smp_idx, + unsigned aN, + double hz, + double rms) + { + wt->tid = tid; + wt->aN = aN; + wt->hz = hz; + wt->rms = rms; + wt->cyc_per_loop = 1; + wt->srate = srate; + wt->pad_smpN = p->padSmpN; + wt->posn_smp_idx = posn_smp_idx; + + unsigned allocSmpCnt = p->padSmpN + wt->aN + p->padSmpN; + p->allocAudioBytesN += allocSmpCnt * sizeof(sample_t); + + // allocate the wavetable audio buffer + wt->aV = mem::allocZ( allocSmpCnt ); + + // fill the wavetable from the audio file + vop::copy(wt->aV+p->padSmpN, aV + posn_smp_idx, wt->aN); + + // fill the wavetable prefix + vop::copy(wt->aV, wt->aV + p->padSmpN + (wt->aN-p->padSmpN), p->padSmpN ); + + // fill the wavetable suffix + vop::copy(wt->aV + p->padSmpN + wt->aN, wt->aV + p->padSmpN, p->padSmpN ); + + } + + rc_t _create_instr_pv_map( instr_t* instr ) + { + rc_t rc = kOkRC; + instr->pvM = mem::allocZ(midi::kMidiVelCnt * midi::kMidiNoteCnt ); + for(unsigned i=0; ipitchN; ++i) + for(unsigned j=0; jpitchA[i].velN; ++j) + { + unsigned pitch = instr->pitchA[i].midi_pitch; + unsigned vel = instr->pitchA[i].velA[j].vel; + + if( pitch >= midi::kMidiNoteCnt ) + rc = cwLogError(kInvalidArgRC,"An invalid pitch value (%i) was encounted.",pitch); + + if( vel >= midi::kMidiVelCnt ) + rc = cwLogError(kInvalidArgRC,"An invalid velocity value (%i) was encountered.",vel); + + instr->pvM[(vel*midi::kMidiNoteCnt) + pitch ] = &instr->pitchA[i].velA[j].mc_seq; + + } + + if( rc != kOkRC ) + rc = cwLogError(rc,"Pitch-velocy map creation failed on instrument:'%s'.",cwStringNullGuard(instr->label)); + + return rc; + } } } -cw::rc_t cw::wt_bank::create( handle_t& hRef, unsigned padSmpN ) +cw::rc_t cw::wt_bank::create( handle_t& hRef, unsigned padSmpN, const char* instr_json_fname ) { rc_t rc = kOkRC; @@ -1255,8 +245,18 @@ cw::rc_t cw::wt_bank::create( handle_t& hRef, unsigned padSmpN ) hRef.set(p); + if( instr_json_fname != nullptr ) + if((rc = load(hRef,instr_json_fname)) != kOkRC ) + { + hRef.clear(); + } + + if(rc != kOkRC ) + { + rc = cwLogError(rc,"WT Bank create failed."); _destroy(p); + } return rc; } @@ -1356,6 +356,7 @@ cw::rc_t cw::wt_bank::load( handle_t h, const char* instr_json_fname ) } if((rc = velR->getv("vel",vel->vel, + "bsi",vel->bsi, "chL",chL)) != kOkRC ) { rc = cwLogError(rc,"Instrument file syntax error while reading the velocity record at index %i from the pitch record for MIDI pitch:%i.",j,pitch->midi_pitch); @@ -1372,54 +373,44 @@ cw::rc_t cw::wt_bank::load( handle_t h, const char* instr_json_fname ) const object_t* wtL = chL->child_ele(ch_idx); wt_seq_t* wts = vel->mc_seq.chA + ch_idx; - wts->wtN = wtL->child_count(); + wts->wtN = wtL->child_count() + 1; wts->wtA = mem::allocZ( wts->wtN ); - for(unsigned wti=0; wtiwtN; ++wti) + for(unsigned wti=0; wtiwtN-1; ++wti) { const object_t* wtR = wtL->child_ele(wti); - wt_t* wt = wts->wtA + wti; + wt_t* wt = wts->wtA + wti + 1; unsigned wtbi = kInvalidIdx; unsigned wtei = kInvalidIdx; - unsigned allocSmpCnt = 0; + double rms = 0; if((rc = wtR->getv("wtbi",wtbi, "wtei",wtei, - "rms",wt->rms, + "rms",rms, "est_hz",wt->hz)) != kOkRC ) { rc = cwLogError(rc,"Instrument file syntax error in wavetable record at index %i, channel index:%i, velocity index:%i midi pitch:%i.",wti,ch_idx,j,pitch->midi_pitch); goto errLabel; } - - wt->cyc_per_loop = 1; - wt->hz = hz; - wt->aN = wtei-wtbi; + // if this is the first looping wave table then insert the attack wave table before it + if( wti==0 ) + { + _alloc_wt(p,wts->wtA,dsp::wt_osc::kOneShotWtTId,srate,abuf.ch_buf[ch_idx], vel->bsi, wtbi-vel->bsi,hz,0); + } - allocSmpCnt = p->padSmpN+wt->aN+p->padSmpN; - p->allocAudioBytesN += allocSmpCnt * sizeof(sample_t); - - // allocate the wavetable audio buffer - wt->aV = mem::allocZ( allocSmpCnt ); - - // fill the wavetable from the audio file - vop::copy(wt->aV+p->padSmpN, abuf.ch_buf[ch_idx] + wtbi, wt->aN); - - // fill the wavetable prefix - vop::copy(wt->aV, wt->aV + p->padSmpN + (wt->aN-p->padSmpN), p->padSmpN ); - - // fill the wavetable suffix - vop::copy(wt->aV + p->padSmpN + wt->aN, wt->aV + p->padSmpN, p->padSmpN ); - + _alloc_wt(p,wt,dsp::wt_osc::kLoopWtTId,srate,abuf.ch_buf[ch_idx],wtbi,wtei-wtbi,hz,rms); } } } } - instr->link = p->instrList; - p->instrList = instr; - + if((rc = _create_instr_pv_map( instr )) != kOkRC ) + goto errLabel; + + p->instrA = mem::resizeZ( p->instrA, p->instrN+1 ); + p->instrA[ p->instrN++ ] = instr; + errLabel: if(rc != kOkRC ) { @@ -1429,8 +420,9 @@ errLabel: } _audio_buf_free(abuf); - - f->free(); + + if( f != nullptr ) + f->free(); return rc; } @@ -1438,8 +430,10 @@ errLabel: void cw::wt_bank::report( handle_t h ) { wt_bank_t* p = _handleToPtr(h); - for(instr_t* instr = p->instrList; instr!=nullptr; instr=instr->link) + + for(unsigned instr_idx=0; instr_idxinstrN; ++instr_idx) { + instr_t* instr = p->instrA[instr_idx]; cwLogPrint("%s \n",instr->label); for(unsigned i=0; ipitchN; ++i) { @@ -1456,6 +450,8 @@ void cw::wt_bank::report( handle_t h ) for(unsigned wti=0; fl; ++wti) { + fl = false; + const char* indent = " "; for(unsigned ch_idx=0; ch_idxmc_seq.chN; ++ch_idx) if( wti < vel->mc_seq.chA[ch_idx].wtN ) @@ -1468,7 +464,9 @@ void cw::wt_bank::report( handle_t h ) } if( fl ) - cwLogPrint("\n"); + { + cwLogPrint("\n"); + } } } @@ -1481,36 +479,88 @@ void cw::wt_bank::report( handle_t h ) unsigned cw::wt_bank::instr_count( handle_t h ) { wt_bank_t* p = _handleToPtr(h); - - unsigned n = 0; - for(instr_t* instr=p->instrList; instr!=nullptr; instr=instr->link) - ++n; - - return n; + return p->instrN; } unsigned cw::wt_bank::instr_index( handle_t h, const char* instr_label ) { wt_bank_t* p = _handleToPtr(h); - unsigned i = 0; - for(instr_t* instr=p->instrList; instr!=nullptr; instr=instr->link,++i) - if( textIsEqual(instr->label,instr_label) ) + for(unsigned i=0; iinstrN; ++i) + if( textIsEqual(p->instrA[i]->label,instr_label) ) return i; return kInvalidIdx; } -const cw::wt_bank::wt_t* cw::wt_bank::get_wave_table( handle_t h, unsigned instr_idx, unsigned pitch, unsigned vel ) +cw::rc_t cw::wt_bank::instr_pitch_velocities( handle_t h, unsigned instr_idx, unsigned pitch, unsigned* velA, unsigned velCnt, unsigned& velCnt_Ref ) { - return nullptr; -} + rc_t rc = kOkRC; + wt_bank_t* p = _handleToPtr(h); -cw::rc_t cw::wt_bank::gen_notes( handle_t h, unsigned instr_idx, const unsigned* pitchA, const unsigned* velA, unsigned noteN, double dur_secs, const char* out_fname, double inter_note_gap_secs ) -{ - rc_t rc = kOkRC; + velCnt_Ref = 0; + + if( instr_idx > p->instrN || pitch >= midi::kMidiNoteCnt ) + { + rc = cwLogError(kInvalidArgRC,"Invalid wave table request : instr:%i (instrN:%i) pitch:%i.",instr_idx,p->instrN,pitch); + goto errLabel; + } + + for(unsigned i=0; iinstrA[instr_idx]->pitchN; ++i) + if( p->instrA[instr_idx]->pitchA[i].midi_pitch == pitch ) + { + if( p->instrA[instr_idx]->pitchA[i].velN > velCnt ) + { + rc = cwLogError(kBufTooSmallRC,"The velocity buffer is too small (%i>%i) for instr:%i pitch:%i.",p->instrA[instr_idx]->pitchA[i].velN,velCnt,instr_idx,pitch); + goto errLabel; + } + + for(unsigned j=0; jinstrA[instr_idx]->pitchA[i].velN; ++j) + velA[j] = p->instrA[instr_idx]->pitchA[i].velA[j].vel; + + velCnt_Ref = p->instrA[instr_idx]->pitchA[i].velN; + break; + } + + if( velCnt_Ref==0) + { + rc = cwLogError(kInvalidArgRC,"No sampled wave tables exist for instr:%i pitch:%i.",instr_idx,pitch); + goto errLabel; + } + +errLabel: return rc; } + + + +cw::rc_t cw::wt_bank::get_wave_table( handle_t h, unsigned instr_idx, unsigned pitch, unsigned vel, cw::wt_bank::multi_ch_wt_seq_t const * & mcs_Ref ) +{ + rc_t rc = kOkRC; + wt_bank_t* p = _handleToPtr(h); + + mcs_Ref = nullptr; + + if( instr_idx > p->instrN || pitch >= midi::kMidiNoteCnt || vel >= midi::kMidiVelCnt ) + { + rc = cwLogError(kInvalidArgRC,"Invalid wave table request : instr:%i (instrN:%i) pitch:%i vel:%i.",instr_idx,p->instrN,pitch,vel); + goto errLabel; + } + + + mcs_Ref = p->instrA[instr_idx]->pvM[(vel*midi::kMidiNoteCnt) + pitch ]; + + if( mcs_Ref == nullptr ) + { + rc = cwLogError(kInvalidStateRC,"The wave table request : instr:%i (instrN:%i) pitch:%i vel:%i is not populated.",instr_idx,p->instrN,pitch,vel); + goto errLabel; + } + +errLabel: + + return rc; +} + cw::rc_t cw::wt_bank::test( const test::test_args_t& args ) { @@ -1541,7 +591,7 @@ cw::rc_t cw::wt_bank::test( const test::test_args_t& args ) //assert( sizeof(pitchA)/sizeof(pitchA[0]) == sizeof(velA)/sizeof(velA[0]) ); //gen_notes(h,instr_idx,pitchA,velA,sizeof(pitchA)/sizeof(pitchA[0]),note_dur_sec,"~/temp/temp.wav"); - + report(h); errLabel: diff --git a/cwWaveTableBank.h b/cwWaveTableBank.h index 990db22..e68106d 100644 --- a/cwWaveTableBank.h +++ b/cwWaveTableBank.h @@ -1,72 +1,6 @@ #ifndef cwWaveTableBank_h #define cwWaveTableBank_h -#ifdef NOT_DEF -namespace cw -{ - namespace wt_bank - { - typedef handle handle_t; - typedef dsp::sample_t sample_t; - typedef dsp::srate_t srate_t; - - typedef enum { - kAttackTId, - kLoopTId, - kInvalidTId - } seg_tid_t; - - typedef struct seg_str - { - seg_tid_t tid; - double cost; - unsigned cyc_per_loop; // count of cycles in the loop - sample_t* aV; // aV[ padN + aN + padN ] - unsigned aN; // Count of unique samples - unsigned padN; // Count of pre/post repeat samples - } seg_t; - - typedef struct ch_str - { - unsigned ch_idx; - unsigned segN; - seg_t* segA; // segV[ segN ] - } ch_t; - - typedef struct wt_str - { - unsigned instr_id; - srate_t srate; - unsigned pitch; - unsigned vel; - - unsigned chN; - ch_t* chA; // chA[ chN ] - } wt_t; - - - - - rc_t create( handle_t& hRef, const char* dir, unsigned padN ); - - rc_t destroy( handle_t& hRef ); - - void report( handle_t h ); - - unsigned instr_count( handle_t h ); - - unsigned instr_index( handle_t h, const char* instr_label ); - - const wt_t* get_wave_table( handle_t h, unsigned instr_idx, unsigned pitch, unsigned vel ); - - rc_t gen_notes( handle_t h, unsigned instr_idx, const unsigned* pitchA, const unsigned* velA, unsigned noteN, double dur_secs, const char* out_fname, double inter_note_gap_secs=0.1 ); - - rc_t test( const test::test_args_t& args ); - - - } -} -#endif namespace cw { @@ -74,31 +8,16 @@ namespace cw { typedef handle handle_t; - typedef dsp::sample_t sample_t; - typedef dsp::srate_t srate_t; - - typedef struct wt_str - { - 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; - } wt_t; - - typedef struct wt_seq_str - { - wt_t* wtA; - unsigned wtN; - } wt_seq_t; - - typedef struct multi_ch_wt_seq_str - { - unsigned chN; - wt_seq_t* chA; - } multi_ch_wt_seq_t; + typedef dsp::sample_t sample_t; + typedef dsp::srate_t srate_t; - rc_t create( handle_t& hRef, unsigned padSmpN ); + typedef dsp::wt_osc::wt_tid_t wt_tid_t; + typedef struct dsp::wt_osc::wt_str wt_t; + typedef struct dsp::wt_seq_osc::wt_seq_str wt_seq_t; + typedef struct dsp::multi_ch_wt_seq_osc::multi_ch_wt_seq_str multi_ch_wt_seq_t; + + + rc_t create( handle_t& hRef, unsigned padSmpN, const char* instr_json_fname=nullptr ); rc_t destroy( handle_t& hRef ); void report( handle_t h ); @@ -108,11 +27,12 @@ namespace cw unsigned instr_count( handle_t h ); unsigned instr_index( handle_t h, const char* instr_label ); - - const wt_t* get_wave_table( handle_t h, unsigned instr_idx, unsigned pitch, unsigned vel ); - - rc_t gen_notes( handle_t h, unsigned instr_idx, const unsigned* pitchA, const unsigned* velA, unsigned noteN, double dur_secs, const char* out_fname, double inter_note_gap_secs=0.1 ); + // Return the actual measured velocities, not the interpolated velocities, for a given instr/pitch. + rc_t instr_pitch_velocities( handle_t h, unsigned instr_idx, unsigned pitch, unsigned* velA, unsigned velCnt, unsigned& velCnt_Ref ); + + rc_t get_wave_table( handle_t h, unsigned instr_idx, unsigned pitch, unsigned vel, multi_ch_wt_seq_t const* & mcs_Ref ); + rc_t test( const test::test_args_t& args ); }