#include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" #include "cwText.h" #include "cwObject.h" #include "cwTime.h" #include "cwPresetSel.h" #include "cwFile.h" #include "cwFileSys.h" #include "cwMidi.h" #include "cwIo.h" #include "cwIoMidiRecordPlay.h" #include "cwVelTableTuner.h" #include "cwNumericConvert.h" namespace cw { namespace vtbl { ui::appIdMap_t mapA[] = { { kInvalidId, kVtDeviceSelectId, "vtDeviceSelectId" }, { kVtDeviceSelectId, kVtPianoDevId, "vtPianoDevId" }, { kVtDeviceSelectId, kVtSamplerDevId, "vtSamplerDevId" }, { kInvalidId, kVtTableSelectId, "vtTableSelectId" }, { kInvalidId, kVtPlayVelSeqBtnId, "vtPlayVelSeqBtnId" }, { kInvalidId, kVtPitchId, "vtPitchId" }, { kInvalidId, kVtPlayPitchSeqBtnId, "vtPlayPitchSeqBtnId" }, { kInvalidId, kVtVelocityId, "vtVelocityId" }, { kInvalidId, kVtMinPitchId, "vtMinPitchId" }, { kInvalidId, kVtMaxPitchId, "vtMaxPitchId" }, { kInvalidId, kVtIncPitchId, "vtIncPitchId" }, { kInvalidId, kVtApplyBtnId, "vtApplyBtnId" }, { kInvalidId, kVtSaveBtnId, "vtSaveBtnId" }, { kInvalidId, kVtDuplicateBtnId, "vtDuplicateBtnId" }, { kInvalidId, kVtNameStrId, "vtNameStrId" }, { kInvalidId, kVtStatusId, "vtStatusId" }, { kInvalidId, kVtEntry0, "vtEntry0" }, { kInvalidId, kVtEntry1, "vtEntry1" }, { kInvalidId, kVtEntry2, "vtEntry2" }, { kInvalidId, kVtEntry3, "vtEntry3" }, { kInvalidId, kVtEntry4, "vtEntry4" }, { kInvalidId, kVtEntry5, "vtEntry5" }, { kInvalidId, kVtEntry6, "vtEntry6" }, { kInvalidId, kVtEntry7, "vtEntry7" }, { kInvalidId, kVtEntry8, "vtEntry8" }, { kInvalidId, kVtEntry9, "vtEntry9" }, { kInvalidId, kVtEntry10, "vtEntry10" }, { kInvalidId, kVtEntry11, "vtEntry11" }, { kInvalidId, kVtEntry12, "vtEntry12" }, { kInvalidId, kVtEntry13, "vtEntry13" }, { kInvalidId, kVtEntry14, "vtEntry14" }, { kInvalidId, kVtEntry15, "vtEntry15" }, { kInvalidId, kVtEntry16, "vtEntry16" }, { kInvalidId, kVtEntry17, "vtEntry17" }, { kInvalidId, kVtEntry18, "vtEntry18" }, { kInvalidId, kVtEntry19, "vtEntry19" }, { kInvalidId, kVtEntry20, "vtEntry20" }, { kInvalidId, kVtEntry21, "vtEntry21" }, { kInvalidId, kVtEntry22, "vtEntry22" }, { kInvalidId, kVtEntry23, "vtEntry23" }, { kInvalidId, kVtEntry24, "vtEntry24" }, }; typedef struct tbl_str { bool enableFl; uint8_t* tableA; unsigned tableN; char* name; unsigned mrpDevIdx; unsigned appId; // id associated with UI select option for this table struct tbl_str* link; } tbl_t; enum { kStoppedStateId, kNoteOnStateId, // start a new now at 'nextTime' kNoteOffStateId, // turn off the current note on 'nextTime' }; enum { kVelSeqModeId, // sequence through current vel table on 'vseqPitch' kPitchSeqModeId // sequence through min-maxMIdiPitch on 'pseqVelocity' }; typedef struct vtbl_str { io::handle_t ioH; // midi_record_play::handle_t mrpH; // object_t* cfg; // char* cfg_fname; // char* cfg_backup_dir; // tbl_t* tableL; // array of tables unsigned nextTableAppId; tbl_t curTable; // current table being edited bool initUiFl; // True if the UI has been initialized (UI initialization happens once at startup) bool waitForStopFl; // The player was requested to stop but is waiting for the current note cycle to end unsigned state; // player state id (See ???StateId above) unsigned mode; // sequence across pitch or velocity (See k???ModeId) time::spec_t nextTime; // next note on/off time unsigned nextPitch; // next seq pitch (during vel sequencing) unsigned nextVelIdx; // next vel table index (during pitch sequencing) unsigned noteOnDurMs; // Note on duration unsigned noteOffDurMs; // Note off duration unsigned vseqPitch; // Sequence across this pitch during velocity sequencing unsigned pseqVelocity; // Sequence from min to max pitch on this velocity unsigned minPseqPitch; // during pitch sequencing unsigned maxPseqPitch; unsigned incPseqPitch; char* duplicateName; // table name to use with the duplicate } vtbl_t; typedef struct dev_map_str { const char* label; // device name (piano, sampler) unsigned appId; // device UI app id unsigned mrpDevIdx; // device MRP device index } dev_map_t; dev_map_t _devMapA[] = { { "piano", kVtPianoDevId, midi_record_play::kPiano_MRP_DevIdx }, { "sampler", kVtSamplerDevId, midi_record_play::kSampler_MRP_DevIdx } }; vtbl_t* _handleToPtr( handle_t h ) { return handleToPtr(h); } unsigned _dev_map_count() { return sizeof(_devMapA)/sizeof(_devMapA[0]); } const dev_map_t* _dev_map_from_label( const char* label ) { for(unsigned i=0; i<_dev_map_count(); ++i) if( strcmp(_devMapA[i].label,label) == 0 ) return _devMapA + i; return nullptr; } const dev_map_t* _dev_map_from_appId( unsigned appId ) { for(unsigned i=0; i<_dev_map_count(); ++i) if( _devMapA[i].appId == appId ) return _devMapA + i; return nullptr; } const dev_map_t* _dev_map_from_mrpDevIdx( unsigned mrpDevIdx ) { for(unsigned i=0; i<_dev_map_count(); ++i) if( _devMapA[i].mrpDevIdx == mrpDevIdx ) return _devMapA + i; return nullptr; } rc_t _destroy_tbl( tbl_t* t ) { mem::release(t->tableA); mem::release(t->name); mem::release(t); return kOkRC; } rc_t _destroy( vtbl_t* p ) { rc_t rc = kOkRC; tbl_t* t = p->tableL; while( t!=nullptr ) { tbl_t* t0 = t->link; _destroy_tbl(t); t = t0; } mem::release(p->cfg_fname); mem::release(p->cfg_backup_dir); mem::release(p->curTable.tableA); mem::release(p->curTable.name); mem::release(p->duplicateName); mem::release(p); return rc; } rc_t _set_statusv( vtbl_t* p, rc_t rc, const char* fmt, va_list vl ) { const int sN = 128; char s[sN]; vsnprintf(s,sN,fmt,vl); uiSendValue( p->ioH, uiFindElementUuId(p->ioH,kVtStatusId), s ); if( rc != kOkRC ) rc = cwLogError(rc,s); return rc; } rc_t _set_status( vtbl_t* p, rc_t rc, const char* fmt, ... ) { va_list vl; va_start(vl,fmt); rc = _set_statusv(p, rc, fmt, vl ); va_end(vl); return rc; } unsigned _table_count( vtbl_t* p ) { unsigned n = 0; for(tbl_t* t=p->tableL; t!=nullptr; t=t->link) ++n; return n; } const tbl_t* _table_from_name( vtbl_t* p, const char* name ) { for(tbl_t* t=p->tableL; t!=nullptr; t=t->link) if( textCompare(t->name,name) == 0 ) return t; return nullptr; } rc_t _insert_menu_option( vtbl_t* p, unsigned appId, const char* name ) { rc_t rc = kOkRC; unsigned uuId; unsigned selectUuId = io::uiFindElementUuId( p->ioH, kVtTableSelectId ); cwAssert( selectUuId != kInvalidId ); if((rc = uiCreateOption( p->ioH, uuId, selectUuId, nullptr, appId, kInvalidId, "optClass", name )) != kOkRC ) { cwLogError(rc,"Create table selection option failed for '%s'.",cwStringNullGuard(name)); goto errLabel; } errLabel: return rc; } rc_t _link_in_table( vtbl_t* p, tbl_t* t ) { rc_t rc = kOkRC; t->appId = p->nextTableAppId; // insert the table in the table selection menu if((rc = _insert_menu_option( p, t->appId, t->name )) != kOkRC ) goto errLabel; // link in the new table if( p->tableL == nullptr ) p->tableL = t; else { t->link = p->tableL; p->tableL = t; } p->nextTableAppId += 1; errLabel: if( rc != kOkRC ) rc = _set_status(p,rc,"Table insertion failed for '%s'.",cwStringNullGuard(t->name)); return rc; } rc_t _parseCfg( vtbl_t* p, const object_t* cfg ) { rc_t rc; const object_t* tables_node = nullptr; tbl_t* t = nullptr; if((rc = cfg->getv("note_on_ms",p->noteOnDurMs, "note_off_ms",p->noteOffDurMs, "vseq_pitch",p->vseqPitch, "pseq_velocity",p->pseqVelocity, "min_pitch",p->minPseqPitch, "max_pitch",p->maxPseqPitch, "incr_pitch",p->incPseqPitch, "tables",tables_node)) != kOkRC ) { rc = cwLogError(rc,"Velocity table mgr. cfg. file parsing error."); goto errLabel; } // for each table for(unsigned i=0; ichild_count(); ++i) { const object_t* tbl_hdr = nullptr; const char* tbl_name = nullptr; const char* tbl_device = nullptr; bool tbl_enableFl = false; const object_t* tbl_node = nullptr; const dev_map_t*devMap = nullptr; // get the tbl hdr node if((tbl_hdr = tables_node->child_ele(i)) == nullptr ) { rc = cwLogError(kSyntaxErrorRC,"The velocity table at index %i was not found.",i); goto errLabel; } // parse the table record if((rc = tbl_hdr->getv("name", tbl_name, "device", tbl_device, "enableFl",tbl_enableFl, "table", tbl_node)) != kOkRC ) { rc = cwLogError(rc,"Velocity table cfg. file parsing error."); goto errLabel; } if((devMap = _dev_map_from_label(tbl_device)) == nullptr ) { cwLogError(kInvalidArgRC,"The MIDI device '%s' is not valid.", cwStringNullGuard(tbl_device) ); goto errLabel; } t = mem::allocZ(); t->name = mem::duplStr(tbl_name); t->mrpDevIdx= devMap->mrpDevIdx; t->enableFl = tbl_enableFl; t->tableN = tbl_node->child_count(); t->tableA = mem::allocZ( t->tableN ); // parse the table velocity values for(unsigned j=0; jchild_count(); ++j) { uint8_t vel; if((rc = tbl_node->child_ele(j)->value(vel)) != kOkRC ) { rc = cwLogError(rc,"Parsing failed on velocity table '%s' index '%i'.", cwStringNullGuard(t->name), j); _destroy_tbl(t); goto errLabel; } t->tableA[j] = vel; } // link in the new table and assign it an app id _link_in_table(p, t ); // prevent the list from being corrupted should the // parsing of the next table fail t = nullptr; } errLabel: if( rc != kOkRC ) mem::release(t); return rc; } rc_t _backup( vtbl_t* p ) { rc_t rc; if((rc = filesys::makeDir(p->cfg_backup_dir)) != kOkRC ) { rc = cwLogError(rc,"The vel.table tuner backup directory '%s' could not be created.",cwStringNullGuard(p->cfg_backup_dir)); goto errLabel; } if((rc = file::backup(p->cfg_fname, p->cfg_backup_dir)) != kOkRC ) { rc = cwLogError(rc,"The vel.table tuner file backup failed.",cwStringNullGuard(p->cfg_backup_dir)); goto errLabel; } errLabel: return rc; } rc_t _save( vtbl_t* p ) { rc_t rc; object_t* cfg = nullptr; object_t* tables_node = nullptr; // backup the current vel table tuner file if((rc = _backup(p)) != kOkRC ) goto errLabel; cfg = newDictObject(nullptr); newPairObject("note_on_ms", p->noteOnDurMs, cfg); newPairObject("note_off_ms", p->noteOffDurMs, cfg); newPairObject("vseq_pitch", p->vseqPitch, cfg); newPairObject("pseq_velocity",p->pseqVelocity, cfg); newPairObject("min_pitch", p->minPseqPitch, cfg); newPairObject("max_pitch", p->maxPseqPitch, cfg); newPairObject("incr_pitch", p->incPseqPitch, cfg); tables_node = newPairObject("tables", newListObject(cfg), cfg ); for(tbl_t* t=p->tableL; t!=nullptr; t=t->link) { const dev_map_t* devMap = _dev_map_from_mrpDevIdx( t->mrpDevIdx ); cwAssert( devMap != nullptr ); object_t* tbl = newDictObject(tables_node); tables_node->append_child(tbl); newPairObject("name", t->name, tbl); newPairObject("device", devMap->label,tbl); newPairObject("enableFl", t->enableFl, tbl); object_t* table_node = newPairObject("table", newListObject(nullptr), tbl); for(unsigned i=0; itableN; ++i) newObject( (unsigned)t->tableA[i], table_node ); } if((rc = objectToFile(p->cfg_fname,cfg)) != kOkRC ) { rc = cwLogError(rc,"Velocity table tuner write failed."); goto errLabel; } errLabel: if(rc != kOkRC ) rc = cwLogError(rc,"The velocity table tuner save failed."); if( cfg != nullptr ) cfg->free(); return rc; } void _do_play(vtbl_t* p, unsigned mode ) { if( p->state == kStoppedStateId ) { p->mode = mode; p->state = kNoteOnStateId; p->nextPitch = p->minPseqPitch; p->nextVelIdx = 0; p->waitForStopFl = false; time::get(p->nextTime); time::advanceMs(p->nextTime,500); // turn on note in 500ms } else { p->waitForStopFl = true; } } cw::rc_t _play_vel_sequence(vtbl_t* p) { rc_t rc = kOkRC; _do_play(p,kVelSeqModeId); return rc; } cw::rc_t _play_pitch_sequence(vtbl_t* p) { rc_t rc = kOkRC; _do_play(p,kPitchSeqModeId); return rc; } cw::rc_t _apply(vtbl_t* p ) { rc_t rc = kOkRC; const dev_map_t* dm; if((dm = _dev_map_from_mrpDevIdx(p->curTable.mrpDevIdx)) == nullptr ) { rc = _set_status(p,kOpFailRC,"The current table has an invalid device index (%i).",p->curTable.mrpDevIdx ); goto errLabel; } if((rc = vel_table_set( p->mrpH, p->curTable.mrpDevIdx, p->curTable.tableA, p->curTable.tableN )) != kOkRC ) rc = _set_status(p,rc,"Velocity table apply failed."); else _set_status(p,kOkRC,"Velocity table applied to '%s'.", cwStringNullGuard(dm->label)); errLabel: return rc; } // Duplicate the source table 't' into the destination table 'dst_tbl'. // If newTablename is non-null then use it to name the 'dst_tbl' otherwise // name the table with the source table name. void _duplicateTable( vtbl_t* p, tbl_t& dst_tbl, const tbl_t* t, const char* newTableName=nullptr ) { // allocate a new velocity table if( dst_tbl.tableN != t->tableN ) { dst_tbl.tableA = mem::resize(dst_tbl.tableA,t->tableN); dst_tbl.tableN = t->tableN; } // copy the src velocities into the destination table for(unsigned i=0; itableN; ++i) dst_tbl.tableA[i] = t->tableA[i]; if( newTableName == nullptr ) newTableName = t->name; dst_tbl.name = mem::reallocStr(dst_tbl.name,newTableName); dst_tbl.mrpDevIdx = t->mrpDevIdx; dst_tbl.appId = t->appId; dst_tbl.link = nullptr; } void _form_versioned_name( const char* name, char* buf, unsigned bufN, int version ) { int i; // set 'j' to the index of the final '_' just prior to numeric suffix for(i=textLength(name)-1; i>=0; --i) if( !isdigit(name[i]) ) break; // if name is of the form ????_### then name+i is the end of the prefix if( i>0 && i < (int)textLength(name) && name[i]=='_' ) { snprintf(buf,bufN,"%.*s_%i",i,name,version); } else // otherwise append a sufix to name { snprintf(buf,bufN,"%s_%i",name,version); } } cw::rc_t _set_duplicate_name( vtbl_t* p, const char* name ) { rc_t rc = kOkRC; const tbl_t* t; unsigned i = 0; unsigned bufN = textLength(name) + 32; char buf[ bufN ]; strcpy(buf,name); // mutate 'name' until it is unique among other tables while((t = _table_from_name( p, buf )) != nullptr ) { _form_versioned_name(name,buf,bufN,i); if( i == 999 ) { rc = cwLogError(kInvalidOpRC,"The 'new' name could not be formed."); goto errLabel; } ++i; } // store the new name p->duplicateName = mem::reallocStr(p->duplicateName,buf); // update the UI uiSendValue(p->ioH, uiFindElementUuId( p->ioH, kVtNameStrId), p->duplicateName ); errLabel: return rc; } cw::rc_t _load( vtbl_t* p, unsigned tableOptionAppId ) { rc_t rc = kOkRC; tbl_t* t = p->tableL; const dev_map_t* dm = nullptr; // locate the selected table for(; t!=nullptr; t=t->link) if( t->appId == tableOptionAppId ) break; // verify that a table was found if( t == nullptr ) { cwLogError(kOpFailRC,"The table associated with appId %i was not found.",tableOptionAppId ); goto errLabel; } // duplicate the selected table into 'curTable' _duplicateTable(p,p->curTable,t); // update the UI with the new velocity table values for(unsigned i = 0; itableN; ++i) uiSendValue( p->ioH, uiFindElementUuId( p->ioH, kVtEntry0 + i), (unsigned)t->tableA[i] ); // set the device menu dm = _dev_map_from_mrpDevIdx(t->mrpDevIdx); // device labels were tested at parse time - so this function can't fail assert( dm != nullptr ); uiSendValue( p->ioH, uiFindElementUuId( p->ioH, kVtDeviceSelectId), dm->appId ); // set the table menu uiSendValue( p->ioH, io::uiFindElementUuId( p->ioH, kVtTableSelectId ), t->appId ); // Set the 'duplicate name' based on the new table _set_duplicate_name(p,t->name); _set_status(p,kOkRC,"'%s' loaded.",t->name); errLabel: return rc; } cw::rc_t _duplicate( vtbl_t* p ) { rc_t rc = kOkRC; // a name for the new table must have been given if( textLength(p->duplicateName) == 0 ) { rc = _set_status(p,kInvalidArgRC, "Enter a 'name' for the new table."); } else { // the name of the table must be unique if( _table_from_name(p,p->duplicateName) != nullptr ) rc = _set_status(p,kInvalidArgRC,"'%s' is not a unique table name.",p->duplicateName); else { // create a new table tbl_t* new_tbl = mem::allocZ(); // duplicate 'curTable' into the new table _duplicateTable(p, *new_tbl, &p->curTable, p->duplicateName); // link in the new table _link_in_table(p, new_tbl ); // load the new table _load(p,new_tbl->appId); } } return rc; } cw::rc_t _set_device( vtbl_t* p, unsigned devAppId ) { rc_t rc = kOkRC; const dev_map_t* dm = _dev_map_from_appId(devAppId); cwAssert( dm != nullptr ); p->curTable.mrpDevIdx = dm->mrpDevIdx; return rc; } cw::rc_t _set_pitch( vtbl_t* p, unsigned vseqPitch ) { rc_t rc = kOkRC; if( 0<= vseqPitch && vseqPitch < 128) p->vseqPitch = vseqPitch; else { rc = _set_status(p,kInvalidArgRC,"%i is not a valid MIDI pitch.",vseqPitch); } return rc; } cw::rc_t _validate_midi_value( vtbl_t* p, unsigned midiValue ) { if( 0 <= midiValue && midiValue < 128 ) return kOkRC; return _set_status(p,kInvalidArgRC,"%i is an invalid 8 bit MIDI value.",midiValue); } uint8_t _cast_int_to_8bits( vtbl_t* p, unsigned value ) { uint8_t v = 0; if( _validate_midi_value(p,value) == kOkRC ) { v = (uint8_t)value; } else { v = 127; } return v; } cw::rc_t _set_velocity( vtbl_t* p, unsigned midiVel ) { rc_t rc = kOkRC; if((rc = _validate_midi_value(p,midiVel)) == kOkRC ) { p->pseqVelocity = midiVel; } return rc; } cw::rc_t _set_min_pitch( vtbl_t* p, unsigned midiPitch ) { rc_t rc = kOkRC; if((rc = _validate_midi_value(p,midiPitch)) == kOkRC ) { p->minPseqPitch = midiPitch; } return rc; } cw::rc_t _set_max_pitch( vtbl_t* p, unsigned midiPitch ) { rc_t rc = kOkRC; if((rc = _validate_midi_value(p,midiPitch)) == kOkRC ) { p->maxPseqPitch = midiPitch; } return rc; } cw::rc_t _set_inc_pitch( vtbl_t* p, unsigned midiPitch ) { rc_t rc = kOkRC; if((rc = _validate_midi_value(p,midiPitch)) == kOkRC ) { p->incPseqPitch = midiPitch; } return rc; } cw::rc_t _set_table_entry( vtbl_t* p, unsigned table_idx, unsigned value ) { rc_t rc = kOkRC; if( table_idx >= p->curTable.tableN ) { rc = _set_status(p,kInvalidArgRC,"The table index %i is not valid.",table_idx); } else { if((rc = _validate_midi_value(p,value)) == kOkRC ) { p->curTable.tableA[ table_idx ] = value; _set_status(p,kOkRC,"The table index '%i' was set to the value '%i'.",table_idx,value); } } return rc; } } } unsigned cw::vtbl::get_ui_id_map_count() { return sizeof(mapA)/sizeof(mapA[0]); }; const cw::ui::appIdMap_t* cw::vtbl::get_ui_id_map( unsigned panelAppId ) { unsigned mapN = get_ui_id_map_count(); for(unsigned i=0; i(); if((rc = objectFromFile( cfg_fname, cfg )) != kOkRC ) { rc = cwLogError(rc,"The velocity table tuner cfg. file open failed on '%s'.",cwStringNullGuard(cfg_fname)); goto errLabel; } p->mrpH = mrpH; p->ioH = ioH; p->cfg_fname = mem::duplStr(cfg_fname); p->cfg_backup_dir = mem::duplStr(cfg_backup_dir); p->nextTableAppId = kLoadOptionBaseId; if((rc = _parseCfg(p, cfg )) != kOkRC ) { rc = cwLogError(rc,"Velocity table cfg. parsing failed."); goto errLabel; } hRef.set(p); errLabel: if( rc != kOkRC ) _destroy(p); if( cfg != nullptr ) cfg->free(); return rc; } cw::rc_t cw::vtbl::destroy( handle_t& hRef ) { rc_t rc = kOkRC; if( !hRef.isValid() ) return rc; vtbl_t* p = _handleToPtr(hRef); if((rc = _destroy(p)) != kOkRC ) goto errLabel; hRef.clear(); errLabel: return rc; } cw::rc_t cw::vtbl::on_ui_value( handle_t h, const io::ui_msg_t& m ) { rc_t rc = kOkRC; vtbl_t* p = _handleToPtr(h); switch( m.appId ) { case kVtTableSelectId: rc = vtbl::_load(p,m.value->u.u); break; case kVtDeviceSelectId: rc = vtbl::_set_device(p, m.value->u.u ); break; case kVtPianoDevId: rc = vtbl::_set_device(p,midi_record_play::kPiano_MRP_DevIdx ); break; case kVtSamplerDevId: rc = vtbl::_set_device(p,midi_record_play::kSampler_MRP_DevIdx ); break; case kVtPlayVelSeqBtnId: rc = vtbl::_play_vel_sequence(p); break; case kVtPitchId: rc = vtbl::_set_pitch(p,m.value->u.u); break; case kVtPlayPitchSeqBtnId: rc = vtbl::_play_pitch_sequence(p); break; case kVtVelocityId: rc = vtbl::_set_velocity(p,m.value->u.u); break; case kVtMinPitchId: rc = vtbl::_set_min_pitch(p,m.value->u.u); break; case kVtMaxPitchId: rc = vtbl::_set_max_pitch(p,m.value->u.u); break; case kVtIncPitchId: rc = vtbl::_set_inc_pitch(p,m.value->u.u); break; case kVtApplyBtnId: rc = vtbl::_apply(p); break; case kVtSaveBtnId: rc = vtbl::_save(p); break; case kVtDuplicateBtnId: rc = vtbl::_duplicate(p); break; case kVtNameStrId: rc = vtbl::_set_duplicate_name(p,m.value->u.s); break; default: if( kVtEntry0 <= m.appId && m.appId <= kVtEntry24 ) { rc = vtbl::_set_table_entry(p, m.appId - kVtEntry0, m.value->u.u ); break; } /* if( kLoadOptionBaseId <= m.appId && m.appId < _table_count(p) ) { printf("loader:%i\n", m.appId - kLoadOptionBaseId ); } */ } return rc; } cw::rc_t cw::vtbl::on_ui_echo( handle_t h, const io::ui_msg_t& m ) { rc_t rc = kOkRC; vtbl_t* p = _handleToPtr(h); switch( m.appId ) { case kVtTableSelectId: if( !p->initUiFl ) { // BUG BUG BUG: if multiple UI's are connected this is not the appropriate // response - echo should simply send the value, not change the state // of the vtbl if( _table_count(p) > 0 ) if(_load( p, kLoadOptionBaseId ) == kOkRC ) p->initUiFl = true; } break; case kVtPitchId: rc = io::uiSendValue(p->ioH, m.uuId, p->vseqPitch ); break; case kVtVelocityId: rc = io::uiSendValue(p->ioH, m.uuId, p->pseqVelocity ); break; case kVtMinPitchId: rc = io::uiSendValue(p->ioH, m.uuId, p->minPseqPitch ); break; case kVtMaxPitchId: rc = io::uiSendValue(p->ioH, m.uuId, p->maxPseqPitch ); break; case kVtIncPitchId: rc = io::uiSendValue(p->ioH, m.uuId, p->incPseqPitch ); break; } return rc; } cw::rc_t cw::vtbl::exec( handle_t h ) { rc_t rc = kOkRC; vtbl_t* p = _handleToPtr(h); time::spec_t t0; if( p->state != kStoppedStateId ) { time::get(t0); if( time::isGTE(t0,p->nextTime) ) { unsigned pitch = 0; unsigned vel = 0; switch( p->state ) { case kNoteOnStateId: { p->state = kNoteOffStateId; time::advanceMs(p->nextTime,p->noteOnDurMs); switch( p->mode ) { case kVelSeqModeId: pitch = p->vseqPitch; cwAssert( p->nextVelIdx < p->curTable.tableN ); vel = p->curTable.tableA[ p->nextVelIdx ]; break; case kPitchSeqModeId: vel = p->pseqVelocity; pitch = p->nextPitch; break; default: cwAssert(0); } } break; case kNoteOffStateId: { vel = 0; p->state = kNoteOnStateId; time::advanceMs(p->nextTime,p->noteOffDurMs); switch( p->mode ) { case kVelSeqModeId: { pitch = p->vseqPitch; if( p->nextVelIdx + 1 >= p->curTable.tableN || p->waitForStopFl ) p->state = kStoppedStateId; else p->nextVelIdx += 1; } break; case kPitchSeqModeId: { pitch = p->nextPitch; if( p->nextPitch + p->incPseqPitch > p->maxPseqPitch || p->waitForStopFl ) p->state = kStoppedStateId; else p->nextPitch += p->incPseqPitch; } break; default: cwAssert(0); } } break; default: cwAssert(0); } _set_status(p,kOkRC,"state:%i mode:%i wfs_fl:%i : dev:%i : pitch:%i vel:%i : nxt %i %i", p->state,p->mode,p->waitForStopFl, p->curTable.mrpDevIdx, pitch,vel, p->nextPitch,p->nextVelIdx); send_midi_msg( p->mrpH, p->curTable.mrpDevIdx, 0, midi::kNoteOnMdId, _cast_int_to_8bits(p,pitch), _cast_int_to_8bits(p,vel) ); } } return rc; }