From 5b28630b6d0df0470883eb98be6518214e6756d1 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 27 Jun 2023 17:11:23 -0400 Subject: [PATCH 1/8] cwFile.h : Fix comment. --- cwFile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cwFile.h b/cwFile.h index b608292..35382c0 100644 --- a/cwFile.h +++ b/cwFile.h @@ -160,7 +160,7 @@ namespace cw // // On the first call to this function *bufPtrPtr must be set to nullptr and // *bufByteCntPtr must be set to 0. - // Following the last call to this function call cmMemPtrFree(bufPtrptr) + // Following the last call to this function call mem::release(bufPtrptr) // to be sure the line buffer is fully released. Note this step is not // neccessary if the last call does not return kOkFileRC. rc_t getLineAuto( handle_t h, char** bufPtrPtr, unsigned* bufByteCntPtr ); From 92df31e5912e209269d375a4d34eafdce40934e6 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 27 Jun 2023 17:24:12 -0400 Subject: [PATCH 2/8] cwIo.h/cpp : Add timerSetNextTime(). --- cwIo.cpp | 43 +++++++++++++++++++++++++++++++++++++------ cwIo.h | 8 +++++++- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/cwIo.cpp b/cwIo.cpp index 97eefd2..339f883 100644 --- a/cwIo.cpp +++ b/cwIo.cpp @@ -58,6 +58,7 @@ namespace cw unsigned index; unsigned periodMicroSec; bool asyncFl; + time::spec_t nextTime; } timer_t; typedef struct serialPort_str @@ -281,7 +282,8 @@ namespace cw t0 = t1; } } - + + //---------------------------------------------------------------------------------------------------------- // // Timer @@ -290,8 +292,17 @@ namespace cw { timer_t* t = (timer_t*)arg; - sleepUs( t->periodMicroSec ); + time::spec_t t0; + time::get(t0); + int usec = time::diffMicros(t0,t->nextTime); + + if( usec > 0 ) + sleepUs(usec); + + time::advanceMicros(t->nextTime,t->periodMicroSec); + + if( t->startedFl && !t->deletedFl ) { rc_t rc = kOkRC; @@ -315,7 +326,7 @@ namespace cw rc_t rc = kOkRC; timer_t* t = nullptr; unsigned timer_idx = kInvalidIdx; - + // look for a deleted timer for(unsigned i=0; itimerN; ++i) if( p->timerA[i].deletedFl ) @@ -352,6 +363,9 @@ namespace cw t->asyncFl = asyncFl; t->periodMicroSec = periodMicroSec; + + time::get(t->nextTime); + if((rc = thread_mach::add(p->threadMachH,_timerThreadCb,t)) != kOkRC ) { rc = cwLogError(rc,"Timer thread assignment failed."); @@ -380,7 +394,6 @@ namespace cw rc = kInvalidIdRC; else p->timerA[ timerIdx ].startedFl = startFl; - return rc; } @@ -1340,7 +1353,7 @@ namespace cw void _audioDevSync( io_t* p, audioDev_t** syncDevA, unsigned syncDevN ) { - for(unsigned i=0; iclockLink) if(audio::device::execute( p->audioH, ad->devIdx ) != kOkRC ) cwLogWarning("Synced audio device '%s' execution failed.",ad->label); @@ -1381,7 +1394,7 @@ namespace cw // trigger any devices synced to the calling device _audioDevSync( p, iSyncDevListA, inPktCnt ); - _audioDevSync( p, oSyncDevListA, outPktCnt ); + _audioDevSync( p, oSyncDevListA, outPktCnt ); } @@ -2516,7 +2529,25 @@ cw::rc_t cw::io::timerSetPeriodMicroSec( handle_t h, unsigned timerIdx, unsig if( t == nullptr ) rc = kInvalidIdRC; else + { p->timerA[ timerIdx ].periodMicroSec = periodMicroSec; + } + + return rc; +} + +cw::rc_t cw::io::timerSetNextTime( handle_t h, unsigned timerIdx, const time::spec_t& time ) +{ + rc_t rc = kOkRC; + io_t* p = _handleToPtr(h); + timer_t* t = _timerIndexToPtr(p, timerIdx); + + if( t == nullptr ) + rc = kInvalidIdRC; + else + { + p->timerA[ timerIdx ].nextTime = time; + } return rc; } diff --git a/cwIo.h b/cwIo.h index e0ebae7..8462664 100644 --- a/cwIo.h +++ b/cwIo.h @@ -179,7 +179,7 @@ namespace cw // // Timer // - + rc_t timerCreate( handle_t h, const char* label, unsigned id, unsigned periodMicroSec, bool asyncFl ); rc_t timerDestroy( handle_t h, unsigned timerIdx ); @@ -190,6 +190,12 @@ namespace cw unsigned timerId( handle_t h, unsigned timerIdx ); unsigned timerPeriodMicroSec( handle_t h, unsigned timerIdx ); rc_t timerSetPeriodMicroSec( handle_t h, unsigned timerIdx, unsigned periodMicroSec ); + + // Set an explicit next time to trigger. The 'time' argument will over ride the next periodic callback. + // Note that the most reliable way to use this function is by calling it in the timer callback + // so that it will deterine the time of the following callback. + rc_t timerSetNextTime( handle_t h, unsigned timerIdx, const time::spec_t& time ); + rc_t timerStart( handle_t h, unsigned timerIdx ); rc_t timerStop( handle_t h, unsigned timerIdx ); From 0eea3aeb42afb708f6a556899be33a1d437bb2e8 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 27 Jun 2023 17:26:32 -0400 Subject: [PATCH 3/8] cwScoreFollower.h/cpp : Added ability to enable/disable the score follower via the cfg. arg. --- cwScoreFollower.cpp | 14 +++++++++++++- cwScoreFollower.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cwScoreFollower.cpp b/cwScoreFollower.cpp index 5424995..c70402a 100644 --- a/cwScoreFollower.cpp +++ b/cwScoreFollower.cpp @@ -39,6 +39,7 @@ namespace cw { typedef struct score_follower_str { + bool enableFl; double srate; unsigned search_area_locN; unsigned key_wnd_locN; @@ -74,7 +75,8 @@ namespace cw const char* cm_score_csv_fname; - if((rc = cfg->getv("cm_score_csv_fname", cm_score_csv_fname, + if((rc = cfg->getv("enable_flag", p->enableFl, + "cm_score_csv_fname", cm_score_csv_fname, "search_area_locN", p->search_area_locN, "key_wnd_locN", p->key_wnd_locN )) != kOkRC ) { @@ -324,6 +326,12 @@ cw::rc_t cw::score_follower::destroy( handle_t& hRef ) return rc; } +bool cw::score_follower::is_enabled( handle_t h ) +{ + score_follower_t* p = _handleToPtr(h); + return p->enableFl; +} + cw::rc_t cw::score_follower::reset( handle_t h, unsigned cwLocId ) { rc_t rc = kOkRC; @@ -363,6 +371,10 @@ cw::rc_t cw::score_follower::exec( handle_t h, double sec, unsigned smpIdx, uns unsigned scLocIdx = cmInvalidIdx; unsigned pre_match_id_curN = p->match_id_curN; + if( !p->enableFl ) + return cwLogError(kInvalidStateRC,"The score follower is not enabled."); + + newMatchFlRef = false; // Note: pass p->perf_idx as 'muid' to the score follower diff --git a/cwScoreFollower.h b/cwScoreFollower.h index 1a7e8b6..ff3e8ef 100644 --- a/cwScoreFollower.h +++ b/cwScoreFollower.h @@ -11,6 +11,8 @@ namespace cw rc_t destroy( handle_t& hRef ); + bool is_enabled( handle_t h ); + // Set the starting search location and calls clear_match_id_array(). rc_t reset( handle_t h, unsigned loc ); From e8c9e38d8181d1edc41a4212eb29b3baf800f99b Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 27 Jun 2023 17:27:15 -0400 Subject: [PATCH 4/8] cwTime.cpp : Fixed some misuses of '1e' notation. --- cwTime.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/cwTime.cpp b/cwTime.cpp index 6188643..7061c29 100644 --- a/cwTime.cpp +++ b/cwTime.cpp @@ -226,12 +226,12 @@ void cw::time::advanceMicros( spec_t& ts, unsigned us ) ts.tv_sec += sec; } - ts.tv_nsec += us * 1000000000; // convert microseconds to nanoseconds + ts.tv_nsec += us * 1000; // convert microseconds to nanoseconds // stip off whole seconds from tv_nsec - while( ts.tv_nsec > 1e9 ) + while( ts.tv_nsec > 1000000000 ) { - ts.tv_nsec -= 1e9; + ts.tv_nsec -= 1000000000; ts.tv_sec +=1; } @@ -253,9 +253,9 @@ void cw::time::advanceMs( spec_t& ts, unsigned ms ) ts.tv_nsec += ms * 1000000; // convert millisconds to nanoseconds // stip off whole seconds from tv_nsec - while( ts.tv_nsec > 1e9 ) + while( ts.tv_nsec > 1000000000 ) { - ts.tv_nsec -= 1e9; + ts.tv_nsec -= 1000000000; ts.tv_sec +=1; } } @@ -279,10 +279,10 @@ double cw::time::specToSeconds( const spec_t& t ) { spec_t ts = t; double sec = ts.tv_sec; - while( ts.tv_nsec >= 1e9 ) + while( ts.tv_nsec >= 1000000000 ) { sec += 1.0; - ts.tv_nsec -= 1e9; + ts.tv_nsec -= 1000000000; } return sec + ((double)ts.tv_nsec)/1e9; @@ -364,7 +364,18 @@ cw::rc_t cw::time::test() subtractMicros( t0, 500000 ); // subtract .5 seconds printf("%li %li\n",t0.tv_sec,t0.tv_nsec); + + time::get(t0); + //time::get(t1); + t1 = t0; + + advanceMicros(t1,5000); + int usec = time::elapsedMicros(t0,t1); + + printf("usec:%i\n",usec); + + return kOkRC; From c78f695c31e4d8f3f09cef442264b6dc4884c00d Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 27 Jun 2023 17:27:58 -0400 Subject: [PATCH 5/8] cwVelTableTuner.h/cpp : Added get_vel_table(). --- cwVelTableTuner.cpp | 20 ++++++++++++++++++++ cwVelTableTuner.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/cwVelTableTuner.cpp b/cwVelTableTuner.cpp index 9bb3e30..950c1d9 100644 --- a/cwVelTableTuner.cpp +++ b/cwVelTableTuner.cpp @@ -1110,3 +1110,23 @@ cw::rc_t cw::vtbl::exec( handle_t h ) } return rc; } + +const uint8_t* cw::vtbl::get_vel_table( handle_t h, const char* label, unsigned& velTblN_Ref ) +{ + vtbl_t* p = _handleToPtr(h); + + const tbl_t* t= nullptr; + + velTblN_Ref = 0; + + if((t = _table_from_name( p, label )) == nullptr ) + { + cwLogError(kInvalidArgRC,"The velocity table named:'%s' could not be found.",cwStringNullGuard(label)); + return nullptr; + } + + velTblN_Ref = t->tableN; + + return t->tableA; + +} diff --git a/cwVelTableTuner.h b/cwVelTableTuner.h index cac3427..9cfb9eb 100644 --- a/cwVelTableTuner.h +++ b/cwVelTableTuner.h @@ -80,5 +80,8 @@ namespace cw // Update the state of the player rc_t exec( handle_t h ); + + const uint8_t* get_vel_table( handle_t h, const char* label, unsigned& velTblN_Ref ); + } } From 0622345a8b064cff0660e036105b81a86d1a3b09 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 27 Jun 2023 17:31:34 -0400 Subject: [PATCH 6/8] cwAudioDeviceFile.cpp : Comment out debugging code. --- cwAudioDeviceFile.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cwAudioDeviceFile.cpp b/cwAudioDeviceFile.cpp index 7f24671..78e0797 100644 --- a/cwAudioDeviceFile.cpp +++ b/cwAudioDeviceFile.cpp @@ -473,7 +473,8 @@ namespace cw rc_t _write_cache_output( dev_t* d ) { rc_t rc = kOkRC; - + //sample_t s; + assert( d->oCacheEnd != nullptr ); if( d->oCacheEnd->frameIdx + d->framesPerCycle > d->oCacheEnd->frameN ) @@ -485,6 +486,10 @@ namespace cw memcpy(d->oCacheEnd->buf + d->oCacheEnd->frameIdx * d->oChCnt, d->oPktAudioBuf, d->framesPerCycle * d->oChCnt * sizeof(sample_t)); d->oCacheEnd->frameIdx += d->framesPerCycle; + //s = vop::sum(d->oPktAudioBuf,d->framesPerCycle * d->oChCnt); + + //printf("w:%i %i %f\n",d->oCacheEnd->frameIdx,d->framesPerCycle * d->oChCnt,s); + errLabel: return rc; } From 1a3aeaaf0cd268f54ae4726f7a06836f327cc85a Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 27 Jun 2023 17:44:23 -0400 Subject: [PATCH 7/8] cwIoMidiRecordPlay.h/cpp : Internal timer now uses timerSetNextTime() rather than periodic callback. The internal MIDI devices now have labels. --- cwIoMidiRecordPlay.cpp | 244 +++++++++++++++++------------------------ cwIoMidiRecordPlay.h | 8 ++ 2 files changed, 109 insertions(+), 143 deletions(-) diff --git a/cwIoMidiRecordPlay.cpp b/cwIoMidiRecordPlay.cpp index 96efe65..3ea399c 100644 --- a/cwIoMidiRecordPlay.cpp +++ b/cwIoMidiRecordPlay.cpp @@ -54,12 +54,25 @@ namespace cw } am_midi_msg_t; + typedef struct vel_tbl_str + { + uint8_t* velTblA; + unsigned velTblN; + bool enableFl; + bool defaultFl; + char* name; + char* device; + } vel_tbl_t; + typedef struct midi_device_str { + char* label; + char* midiOutDevLabel; char* midiOutPortLabel; unsigned midiOutDevIdx; unsigned midiOutPortIdx; + bool enableFl; unsigned velTableN; uint8_t* velTableArray; @@ -104,7 +117,6 @@ namespace cw kWaitForPedalDown, }; - typedef struct midi_record_play_str { io::handle_t ioH; @@ -114,6 +126,7 @@ namespace cw unsigned msgArrayInIdx; // Next available space for loaded MIDI messages (also the current count of msgs in msgArray[]) unsigned msgArrayOutIdx; // Next message to transmit in msgArray[] unsigned midi_timer_period_micro_sec; // Timer period in microseconds + unsigned midi_timer_index; // unsigned all_off_delay_ms; // Wait this long before turning all notes off after the last note-on has played am_midi_msg_t* iMsgArray; // msgArray[ msgArrayN ] @@ -151,7 +164,6 @@ namespace cw time::spec_t start_time; time::spec_t end_play_event_timestamp; time::spec_t all_off_timestamp; - time::spec_t store_time; event_callback_t cb; void* cb_arg; @@ -169,13 +181,13 @@ namespace cw rc_t _destroy( midi_record_play_t* p ) { rc_t rc = kOkRC; - unsigned timerIdx; - if((timerIdx = io::timerLabelToIndex( p->ioH, TIMER_LABEL )) != kInvalidIdx ) - io::timerDestroy( p->ioH, timerIdx); + if(p->midi_timer_index != kInvalidIdx ) + io::timerDestroy( p->ioH, p->midi_timer_index); for(unsigned i=0; imidiDevN; ++i) { + mem::release(p->midiDevA[i].label); mem::release(p->midiDevA[i].midiOutDevLabel); mem::release(p->midiDevA[i].midiOutPortLabel); mem::release(p->midiDevA[i].velTableArray); @@ -184,11 +196,21 @@ namespace cw mem::release(p->midiDevA); mem::release(p->msgArray); mem::release(p->iMsgArray); + printf("P:%p\n",p); mem::release(p); return rc; } + unsigned _label_to_device_index( midi_record_play_t* p, const char* label ) + { + for(unsigned i=0; imidiDevN; ++i) + if( textCompare(p->midiDevA[i].label,label) == 0 ) + return i; + + return kInvalidIdx; + } + void _xmit_midi( midi_record_play_t* p, unsigned devIdx, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 ) { if( !p->supressMidiXmitFl ) @@ -351,8 +373,7 @@ namespace cw } } - _set_chord_note_count(m0,m,chordNoteCnt); - + _set_chord_note_count(m0,m,chordNoteCnt); } rc_t _parseCfg(midi_record_play_t* p, const object_t& cfg ) @@ -386,13 +407,14 @@ namespace cw for(unsigned i=0; imidiDevN; ++i) { const object_t* ele = midiDevL->child_ele(i); + const char* label = nullptr; const char* midiOutDevLabel = nullptr; const char* midiOutPortLabel = nullptr; - const object_t* velTable = nullptr; const object_t* pedalRecd = nullptr; bool enableFl = false; - if((rc = ele->getv( "midi_out_device", midiOutDevLabel, + if((rc = ele->getv( "label", label, + "midi_out_device", midiOutDevLabel, "midi_out_port", midiOutPortLabel, "enableFl", enableFl)) != kOkRC ) { @@ -402,7 +424,6 @@ namespace cw if((rc = ele->getv_opt( - "vel_table", velTable, "pedal", pedalRecd, "force_damper_down_fl",p->midiDevA[i].force_damper_down_fl, "force_damper_down_threshold",p->midiDevA[i].force_damper_down_threshold, @@ -424,29 +445,12 @@ namespace cw p->midiDevA[i].damper_dead_band_enable_fl, p->midiDevA[i].damper_dead_band_min_value, p->midiDevA[i].damper_dead_band_max_value ); - + + p->midiDevA[i].label = mem::duplStr( label ); p->midiDevA[i].midiOutDevLabel = mem::duplStr( midiOutDevLabel); p->midiDevA[i].midiOutPortLabel = mem::duplStr( midiOutPortLabel); p->midiDevA[i].enableFl = enableFl; - /* - if( velTable != nullptr ) - { - p->midiDevA[i].velTableN = velTable->child_count(); - p->midiDevA[i].velTableArray = mem::allocZ(p->midiDevA[i].velTableN); - - - for(unsigned j=0; jmidiDevA[i].velTableN; ++j) - { - if((rc = velTable->child_ele(j)->value( p->midiDevA[i].velTableArray[j] )) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"An error occured while parsing the velocity table for MIDI device:'%s' port:'%s'.",midiOutDevLabel,midiOutPortLabel); - goto errLabel; - } - } - } - */ - if( pedalRecd != nullptr ) { if((rc = pedalRecd->getv( "down_id", p->midiDevA[i].pedalDownVelId, @@ -564,7 +568,7 @@ namespace cw { // verify that the vel. is legal given the table if( d1 >= p->midiDevA[i].velTableN ) - cwLogError(kInvalidIdRC,"A MIDI note-on velocity (%i) outside the velocity table range was encountered.",d1); + cwLogError(kInvalidIdRC,"A MIDI note-on velocity (%i) outside the velocity table (%i>%i) range was encountered on device:%s.",d1,(p->midiDevA[i].velTableN)-1,cwStringNullGuard(p->midiDevA[i].label)); else out_d1 = p->midiDevA[i].velTableArray[ d1 ]; @@ -1253,9 +1257,11 @@ namespace cw } else - { + { io::timerStop( p->ioH, io::timerIdToIndex(p->ioH, kMidiRecordPlayTimerId) ); + + // TODO: // BUG BUG BUG: should work for all channels @@ -1347,7 +1353,6 @@ namespace cw else while( p->msgArray[ p->msgArrayOutIdx ].microsec <= cur_time_us ) { - am_midi_msg_t* mm = p->msgArray + p->msgArrayOutIdx; @@ -1364,105 +1369,23 @@ namespace cw break; } } + + if( p->msgArrayOutIdx < p->msgArrayInIdx ) + { + // the next callback should happen at now() + (next_msg_us - cur_us) + time::advanceMicros(t, p->msgArray[ p->msgArrayOutIdx ].microsec - cur_time_us ); + io::timerSetNextTime( p->ioH, p->midi_timer_index,t); + } } return rc; } - rc_t _loadVelTable( midi_record_play_t* p, const object_t* cfg, midi_device_t* dev, unsigned devIdx ) - { - rc_t rc = kOkRC; - const char* refDevLabel = nullptr; - const object_t* tables = nullptr; - unsigned i = 0; - - // get the textual name the device - switch( devIdx ) - { - case kSampler_MRP_DevIdx: refDevLabel="sampler"; break; - case kPiano_MRP_DevIdx: refDevLabel="piano"; break; - } - - // verify that the device has a label - if( refDevLabel == nullptr ) - { - rc = cwLogError(kInvalidStateRC,"The MIDI record-play unit device '%i' does not have a device label.",devIdx); - goto errLabel; - } - - // get the 'tables' node - if((rc = cfg->getv("tables",tables)) != kOkRC ) - { - rc = cwLogError(rc,"The 'tables' field could not be read from the vel. table cfg. object."); - goto errLabel; - } - - // for each table - for(i=0; ichild_count(); ++i) - { - const object_t* tbl = tables->child_ele(i); - const object_t* array = nullptr; - bool enableFl = false; - bool defaultFl = false; - const char* deviceLabel = nullptr; - - // read the table record - if((rc = tbl->getv("table",array, - "enableFl",enableFl, - "defaultFl",defaultFl, - "device",deviceLabel)) != kOkRC ) - { - rc = cwLogError(rc,"Parsing failed on the velocity table at index '%i'.",i); - goto errLabel; - } - - // if this table is enabled and the 'default' for this device - if( enableFl && defaultFl && textCompare(refDevLabel,deviceLabel) == 0 ) - { - // allocate the actual velocity table - dev->velTableN = array->child_count(); - dev->velTableArray = mem::allocZ(dev->velTableN); - - // for each velocity entry - for(unsigned j=0; jvelTableN; ++j) - { - // read the velocity value - unsigned v; - if((rc = array->child_ele(j)->value(v)) != kOkRC ) - { - rc = cwLogError(rc,"Parsing failed on the velocity at index '%i'.",j); - goto errLabel; - } - - // validate the velocity range - if( 0 <= v && v <= 127 ) - dev->velTableArray[j] = (uint8_t)v; - else - { - rc = cwLogError(kInvalidArgRC,"A velocity table entry outside the range 0-127 was encountered at index %i.",j); - goto errLabel; - } - } - - break; - } - } - - // if no enabled and default table was found for this device - if( i >= tables->child_count() ) - { - cwLogError(kInvalidStateRC,"A velocity table was not found for MIDI device index '%i'.",devIdx ); - goto errLabel; - } - - errLabel: - - return rc; - } } } - + + cw::rc_t cw::midi_record_play::create( handle_t& hRef, io::handle_t ioH, const object_t& cfg, @@ -1480,20 +1403,16 @@ cw::rc_t cw::midi_record_play::create( handle_t& hRef, p = mem::allocZ(); + printf("P0:%p\n",p); + p->ioH = ioH; // p->ioH is used in _destory() so initialize it here // parse the cfg if((rc = _parseCfg(p,cfg)) != kOkRC ) goto errLabel; - // parse the vel table cfg - if(velTableFname != nullptr ) - if((rc = objectFromFile(velTableFname,velTblCfg)) != kOkRC ) - { - rc = cwLogError(rc,"Parsing the velocity table cfg. failed on '%s'.",cwStringNullGuard(velTableFname)); - goto errLabel; - } - + + p->midi_timer_index = kInvalidIdx; p->cb = cb; p->cb_arg = cb_arg; p->halfPedalState = kHalfPedalDone; @@ -1526,16 +1445,9 @@ cw::rc_t cw::midi_record_play::create( handle_t& hRef, goto errLabel; } - if( velTblCfg != nullptr ) - if((rc = _loadVelTable( p, velTblCfg, dev, i )) != kOkRC ) - { - rc = cwLogError(rc,"MIDI record-play velocity table setup failed."); - goto errLabel; - } - printf("%s %s : %i %i\n",dev->midiOutDevLabel, dev->midiOutPortLabel, dev->midiOutDevIdx, dev->midiOutPortIdx ); } - + // create the MIDI playback timer if((rc = timerCreate( p->ioH, TIMER_LABEL, kMidiRecordPlayTimerId, p->midi_timer_period_micro_sec, asyncFl)) != kOkRC ) { @@ -1543,6 +1455,12 @@ cw::rc_t cw::midi_record_play::create( handle_t& hRef, goto errLabel; } + if((p->midi_timer_index = io::timerLabelToIndex( p->ioH, TIMER_LABEL )) == kInvalidIdx ) + { + cwLogError(rc,"Audio-MIDI timer index access failed."); + goto errLabel; + } + _midi_state_clear(p); errLabel: @@ -1610,9 +1528,13 @@ cw::rc_t cw::midi_record_play::start( handle_t h, bool rewindFl, const time::spe _set_midi_msg_next_play_index(p,0); else { + + io::timerSetNextTime( p->ioH, p->midi_timer_index,p->play_time); + // Set the begin play time back by the time offset of the current output event. // This will cause that event to be played back immediately. time::subtractMicros(p->play_time, p->msgArray[ p->msgArrayOutIdx ].microsec ); + } if( p->halfPedalFl ) @@ -1726,8 +1648,6 @@ cw::rc_t cw::midi_record_play::save_synced_csv( handle_t h, const char* fn, cons } p->iMsgArray[i].loc = syncA[j].loc; - - } } @@ -1836,6 +1756,18 @@ cw::rc_t cw::midi_record_play::load( handle_t h, const midi_msg_t* msg, unsigned return rc; } +unsigned cw::midi_record_play::label_to_device_index( handle_t h, const char* devLabel ) +{ + midi_record_play_t* p = _handleToPtr(h); + return _label_to_device_index( p, devLabel ); +} + +const char* cw::midi_record_play::device_index_to_label( handle_t h, unsigned devIdx ) +{ + midi_record_play_t* p = _handleToPtr(h); + return 0 <= devIdx && devIdx < p->midiDevN ? p->midiDevA[devIdx].label : nullptr; +} + cw::rc_t cw::midi_record_play::seek( handle_t h, time::spec_t seek_timestamp ) { rc_t rc = kOkRC; @@ -1845,14 +1777,14 @@ cw::rc_t cw::midi_record_play::seek( handle_t h, time::spec_t seek_timestamp ) _midi_state_clear(p); // supress MIDI transmission during the seek - p->supressMidiXmitFl = true; + p->supressMidiXmitFl = true; // iterate throught the MIDI msg array for(unsigned i=0; imsgArrayInIdx; ++i) { am_midi_msg_t* mm = p->msgArray + i; - // if this msg is prior to or equal to the seek target + // if this msg is at or after the seek target if( time::isLTE(seek_timestamp,mm->timestamp) ) { p->msgArrayOutIdx = i; @@ -2097,6 +2029,31 @@ unsigned cw::midi_record_play::dev_count( handle_t h ) return p->midiDevN; } +cw::rc_t cw::midi_record_play::vel_table_disable( handle_t h, unsigned devIdx ) +{ + rc_t rc = kOkRC; + midi_record_play_t* p = _handleToPtr(h); + + if( devIdx != kInvalidIdx ) + { + if( devIdx > p->midiDevN ) + { + rc = cwLogError(kInvalidArgRC,"The device index '%i'is invalid.",devIdx); + goto errLabel; + } + + p->midiDevA[devIdx].velTableArray = nullptr; + } + else + { + for(unsigned i=0; imidiDevN; ++i) + p->midiDevA[i].velTableArray = nullptr; + } + + errLabel: + return rc; +} + unsigned cw::midi_record_play::vel_table_count( handle_t h, unsigned devIdx ) { midi_record_play_t* p = _handleToPtr(h); @@ -2126,3 +2083,4 @@ cw::rc_t cw::midi_record_play::vel_table_set( handle_t h, unsigned devIdx, const return kOkRC; } + diff --git a/cwIoMidiRecordPlay.h b/cwIoMidiRecordPlay.h index c704f4e..df4bb54 100644 --- a/cwIoMidiRecordPlay.h +++ b/cwIoMidiRecordPlay.h @@ -83,6 +83,11 @@ namespace cw rc_t exec( handle_t h, const io::msg_t& msg ); + unsigned label_to_device_index( handle_t h, const char* devLabel ); + const char* device_index_to_label( handle_t h, unsigned devIdx ); + + + unsigned device_count( handle_t h ); bool is_device_enabled( handle_t h, unsigned devIdx ); void enable_device( handle_t h, unsigned devIdx, bool enableFl ); @@ -96,6 +101,9 @@ namespace cw rc_t am_to_midi_file( const object_t* cfg ); unsigned dev_count( handle_t h ); + + // Set devIdx to kInvalididx to disable vel tables on all devices + rc_t vel_table_disable( handle_t h, unsigned devIdx=kInvalidIdx ); unsigned vel_table_count( handle_t h, unsigned devIdx ); const uint8_t* vel_table( handle_t h, unsigned devIdx ); From 4c8ac89fd7984909d000a23158c3992cbaf4edfd Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 27 Jun 2023 17:53:04 -0400 Subject: [PATCH 8/8] cwIoPresetSelApp.cpp, ui.cfg : Added _set_vel_table() to load vel. tables from _do_load(). Velocity tables are now set in the cfg. 'perfDirL' array. Allow score follower to be disabled. --- cwIoPresetSelApp.cpp | 421 ++++++++++++++++++++--------------------- html/preset_sel/ui.cfg | 15 +- 2 files changed, 208 insertions(+), 228 deletions(-) diff --git a/cwIoPresetSelApp.cpp b/cwIoPresetSelApp.cpp index 1a1d258..bf86630 100644 --- a/cwIoPresetSelApp.cpp +++ b/cwIoPresetSelApp.cpp @@ -238,27 +238,36 @@ namespace cw unsigned presetId; } ui_blob_t; + + typedef struct vel_tbl_str + { + const char* name; + const char* device; + } vel_tbl_t; + typedef struct perf_recording_str { - char* fname; // perf recording - char* label; // menu label - unsigned id; // menu appId - unsigned uuId; // menu uuid + char* fname; // perf recording + char* label; // menu label + unsigned id; // menu appId + unsigned uuId; // menu uuid + vel_tbl_t* vel_tblA; // vel_tblA[ velTblN ] + unsigned vel_tblN; // struct perf_recording_str* link; } perf_recording_t; typedef struct app_str { - io::handle_t ioH; + io::handle_t ioH; + + // path components for reading/writing the preset assignments + const char* record_dir; + const char* record_fn; + const char* record_fn_ext; + const char* record_backup_dir; - const char* record_dir; - const char* record_fn; - const char* record_fn_ext; - const char* record_backup_dir; const char* scoreFn; - const object_t* perfL; const object_t* perfDirL; - unsigned perfMenuCnt; const char* velTableFname; const char* velTableBackupDir; const object_t* midi_play_record_cfg; @@ -271,47 +280,43 @@ namespace cw unsigned in_audio_begin_loc; double in_audio_offset_sec; - cm::handle_t cmCtxH; - score_follower::handle_t sfH; - midi_record_play::handle_t mrpH; - unsigned scoreFollowMaxLocId; + cm::handle_t cmCtxH; + score_follower::handle_t sfH; + midi_record_play::handle_t mrpH; score::handle_t scoreH; - loc_map_t* locMap; + loc_map_t* locMap; unsigned locMapN; - unsigned insertLoc; // last valid insert location id received from the GUI + unsigned insertLoc; // last valid insert location id received from the GUI - unsigned minLoc; - unsigned maxLoc; + unsigned minLoc; // min/max locations of the currently loaded performance + unsigned maxLoc; - time::spec_t beg_play_timestamp; - time::spec_t end_play_timestamp; - - unsigned beg_play_loc; - unsigned end_play_loc; + unsigned beg_play_loc; // beg/end play loc's from the UI + unsigned end_play_loc; - preset_sel::handle_t psH; - const preset_sel::frag_t* psNextFrag; - time::spec_t psLoadT0; + preset_sel::handle_t psH; + const preset_sel::frag_t* psNextFrag; + time::spec_t psLoadT0; - vtbl::handle_t vtH; - io_flow::handle_t ioFlowH; + vtbl::handle_t vtH; + io_flow::handle_t ioFlowH; double crossFadeSrate; unsigned crossFadeCnt; - bool printMidiFl; + bool printMidiFl; - bool seqActiveFl; // true if the sequence is currently active (set by 'Play Seq' btn) - unsigned seqStartedFl; // set by the first seq idle callback - unsigned seqFragId; // - unsigned seqPresetIdx; // + bool seqActiveFl; // true if the sequence is currently active (set by 'Play Seq' btn) + unsigned seqStartedFl; // set by the first seq idle callback + unsigned seqFragId; // + unsigned seqPresetIdx; // - bool useLiveMidiFl; // use incoming MIDI to drive program (otherwise use score file) - bool trackMidiFl; // apply presets based on MIDI location (otherwise respond only to direct manipulation of fragment control) + bool useLiveMidiFl; // use incoming MIDI to drive program (otherwise use score file) + bool trackMidiFl; // apply presets based on MIDI location (otherwise respond only to direct manipulation of fragment control) - bool enableRecordFl; // enable recording of incoming MIDI + bool enableRecordFl; // enable recording of incoming MIDI char* midiRecordDir; const char* midiRecordFolder; char* midiLoadFname; @@ -385,19 +390,18 @@ namespace cw const char* flow_proc_dict_fn = nullptr; const char* midi_record_dir; - if((rc = cfg->getv( "params", params_cfgRef, + if((rc = cfg->getv( "params", params_cfgRef, "flow", app->flow_cfg)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"Preset Select App 'params' cfg record not found."); goto errLabel; } - if((rc = params_cfgRef->getv( "record_dir", app->record_dir, + if((rc = params_cfgRef->getv( "record_dir", app->record_dir, "record_fn", app->record_fn, "record_fn_ext", app->record_fn_ext, "score_fn", app->scoreFn, "perfDirL", app->perfDirL, - "perfL", app->perfL, "flow_proc_dict_fn", flow_proc_dict_fn, "midi_play_record", app->midi_play_record_cfg, "vel_table_fname", app->velTableFname, @@ -485,59 +489,12 @@ namespace cw return rc; } - rc_t _load_perf_selection_menu( app_t* app ) - { - rc_t rc = kOkRC; - unsigned selectUuId = kInvalidId; - - // verify that a performance list was given - if( app->perfL == nullptr || app->perfL->child_count()==0) - { - rc = cwLogError(rc,"The performance list is missing or empty."); - goto errLabel; - } - - // get the peformance menu UI uuid - if((selectUuId = io::uiFindElementUuId( app->ioH, kPerfSelId )) == kInvalidId ) - { - rc = cwLogError(rc,"The performance list base UI element does not exist."); - goto errLabel; - } - - for(unsigned i=0; iperfL->child_count(); ++i) - { - const object_t* pair = nullptr; - unsigned uuId = kInvalidId; - - // get and validate the option label - if((pair = app->perfL->child_ele(i)) == nullptr || !pair->is_pair() || pair->pair_label()==nullptr) - { - rc = cwLogError(kSyntaxErrorRC,"The performance list contains a syntax error near element index %i.",i); - goto errLabel; - } - - // create an option entry in the selection ui - if((rc = uiCreateOption( app->ioH, uuId, selectUuId, nullptr, kPerfOptionBaseId+i, kInvalidId, "optClass", pair->pair_label() )) != kOkRC ) - { - rc = cwLogError(kSyntaxErrorRC,"The performance list contains a syntax error near element index %i.",i); - goto errLabel; - } - - } - - errLabel: - - app->perfMenuCnt = app->perfL->child_count(); - - return rc; - } - rc_t _load_perf_recording_menu( app_t* app ) { rc_t rc = kOkRC; perf_recording_t* prp = nullptr; unsigned id = 0; - unsigned selectUuId = kInvalidId; + unsigned selectUuId = kInvalidId; // get the peformance menu UI uuid if((selectUuId = io::uiFindElementUuId( app->ioH, kPerfSelId )) == kInvalidId ) @@ -547,7 +504,7 @@ namespace cw } // for each performance recording - for(prp=app->perfRecordingBeg; prp!=nullptr; prp=prp->link) + for(prp = app->perfRecordingBeg; prp!=nullptr; prp=prp->link) { // create an option entry in the selection ui if((rc = uiCreateOption( app->ioH, prp->uuId, selectUuId, nullptr, kPerfOptionBaseId+id, kInvalidId, "optClass", prp->label )) != kOkRC ) @@ -558,8 +515,6 @@ namespace cw prp->id = id; id += 1; - - } errLabel: @@ -567,12 +522,43 @@ namespace cw return rc; } - rc_t _parse_perf_recording_dir( app_t* app, const char* dir, const char* fname ) + rc_t _parse_perf_recording_vel_tbl( app_t* app, const object_t* velTblCfg, vel_tbl_t*& velTblA_Ref, unsigned& velTblN_Ref ) { - rc_t rc = kOkRC; + rc_t rc = kOkRC; + + velTblA_Ref = nullptr; + velTblN_Ref = 0; + + unsigned velTblN = velTblCfg->child_count(); + vel_tbl_t* velTblA = nullptr; + + if( velTblN > 0 ) + { + velTblA = mem::allocZ(velTblN); + + for(unsigned i = 0; ichild_ele(i)->getv("name",velTblA[i].name, + "device",velTblA[i].device)) != kOkRC ) + { + rc = cwLogError(rc,"The vel table at index '%i' parse failed.",i); + goto errLabel; + } + } + } + + velTblA_Ref = velTblA; + velTblN_Ref = velTblN; + errLabel: + return rc; + } + + rc_t _parse_perf_recording_dir( app_t* app, const char* dir, const char* fname, const object_t* velTblCfg ) + { + rc_t rc = kOkRC; filesys::dirEntry_t* deA = nullptr; - unsigned deN = 0; - char* path = nullptr; + unsigned deN = 0; + char* path = nullptr; // get the directory entries based on 'dir' if((deA = filesys::dirEntries( dir, filesys::kDirFsFl, &deN )) == nullptr ) @@ -585,7 +571,7 @@ namespace cw cwLogWarning("The performance recording directory '%s' was found to be empty.",cwStringNullGuard(dir)); // for each directory entry - for(unsigned i=0; iperfRecording list prp = mem::allocZ(); prp->fname = mem::duplStr(path); prp->label = mem::duplStr(deA[i].name); + if((rc = _parse_perf_recording_vel_tbl(app, velTblCfg, prp->vel_tblA, prp->vel_tblN )) != kOkRC ) + { + rc = cwLogError(rc,"Parse failed on vel table at directory entry index '%i'.",i); + // don't goto errLabel because the perf_recording record would be leaked + } + if( app->perfRecordingEnd == nullptr ) { app->perfRecordingBeg = prp; @@ -640,9 +632,10 @@ namespace cw // for each performance directory for(unsigned i=0; iperfDirL->child_count(); ++i) { - const object_t* d; - const char* dir=nullptr; - const char* fname=nullptr; + const object_t* d = nullptr;; + const char* dir = nullptr; + const char* fname = nullptr; + const object_t* velTblCfg = nullptr; // get the directory dict. from the cfg file if((d = app->perfDirL->child_ele(i)) == nullptr || !d->is_dict() ) @@ -652,14 +645,16 @@ namespace cw } // get the directory - if((rc = d->getv("dir",dir,"fname",fname)) != kOkRC ) + if((rc = d->getv("dir",dir, + "fname",fname, + "vel_table", velTblCfg)) != kOkRC ) { rc = cwLogError(rc ,"Error parsing the performance directory entry at index '%i'.",i); goto errLabel; } // create the performance records from this directory - if((rc = _parse_perf_recording_dir(app,dir,fname)) != kOkRC ) + if((rc = _parse_perf_recording_dir(app,dir,fname,velTblCfg)) != kOkRC ) { rc = cwLogError(rc ,"Error creating the performance directory entry at index '%i'.",i); goto errLabel; @@ -727,6 +722,7 @@ namespace cw perf_recording_t* tmp = prp->link; mem::release(prp->fname); mem::release(prp->label); + mem::release(prp->vel_tblA); mem::release(prp); prp = tmp; } @@ -904,16 +900,10 @@ namespace cw maxLocId = matchLocA[i]; printf("%i ",matchLocA[i]); } - - // notice if the end of the score was reached - if( maxLocId > app->scoreFollowMaxLocId ) - { - loc = maxLocId; - app->scoreFollowMaxLocId = maxLocId; - printf(" set"); - } - printf("\n"); + + + loc = maxLocId; clear_match_id_array(app->sfH); } @@ -960,7 +950,8 @@ namespace cw double sec = time::specToSeconds(timestamp); // call the score follower - loc = _get_loc_from_score_follower( app, sec, id, status, d0, d1 ); + if( score_follower::is_enabled(app->sfH) ) + loc = _get_loc_from_score_follower( app, sec, id, status, d0, d1 ); // TODO: ZERO SHOULD BE A VALID LOC VALUE - MAKE -1 THE INVALID LOC VALUE @@ -1098,8 +1089,6 @@ namespace cw score_follower::reset(app->sfH,app->sfResetLoc); - app->scoreFollowMaxLocId = 0; - cwLogInfo("SF reset loc: %i",app->sfResetLoc); return rc; } @@ -1160,13 +1149,10 @@ namespace cw goto errLabel; } - app->beg_play_timestamp = begMap->timestamp; - app->end_play_timestamp = endMap->timestamp; - - if( !time::isZero(app->beg_play_timestamp) ) + if( !time::isZero(begMap->timestamp) ) { // seek the player to the requested loc - if((rc = midi_record_play::seek( app->mrpH, app->beg_play_timestamp )) != kOkRC ) + if((rc = midi_record_play::seek( app->mrpH, begMap->timestamp )) != kOkRC ) { rc = cwLogError(rc,"MIDI seek failed."); goto errLabel; @@ -1185,11 +1171,8 @@ namespace cw midi_record_play::clear(app->mrpH); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kMidiSaveBtnId ), false ); } - - // reset the score follower - //if((rc = score_follower::reset(app->sfH,score_loc)) != kOkRC ) if((rc = _do_sf_reset(app,score_loc)) != kOkRC ) { rc = cwLogError(rc,"Score follower reset failed."); @@ -1208,7 +1191,7 @@ namespace cw { unsigned evt_cnt = 0; - if((rc = midi_record_play::start(app->mrpH,rewindFl,&app->end_play_timestamp)) != kOkRC ) + if((rc = midi_record_play::start(app->mrpH,rewindFl,&endMap->timestamp)) != kOkRC ) { rc = cwLogError(rc,"MIDI start failed."); goto errLabel; @@ -1335,7 +1318,7 @@ namespace cw //io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kCurMidiEvtCntId), midi_record_play::event_index(app->mrpH) ); //io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), midi_record_play::event_count(app->mrpH) ); - io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), app->maxLoc ); + io::uiSendValue( app->ioH, uiFindElementUuId(app->ioH,kTotalMidiEvtCntId), app->maxLoc-app->minLoc ); } // Update the UI with the value from the the fragment data record. @@ -1638,8 +1621,6 @@ namespace cw unsigned fragListUuId = io::uiFindElementUuId( app->ioH, kFragListId ); unsigned fragChanId = fragId; //endLoc; // use the frag. endLoc as the channel id unsigned fragPanelUuId = kInvalidId; - //unsigned fragBegLocUuId = kInvalidId; - //unsigned fragEndLocUuId = kInvalidId; unsigned fragPresetRowUuId = kInvalidId; unsigned presetN = preset_sel::preset_count( app->psH ); unsigned fragBegLoc = 0; @@ -1653,13 +1634,9 @@ namespace cw // get the uuid's of the new fragment panel and the endloc number display fragPanelUuId = io::uiFindElementUuId( app->ioH, fragListUuId, kFragPanelId, fragChanId ); - //fragBegLocUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragBegLocId, fragChanId ); - //fragEndLocUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragEndLocId, fragChanId ); fragPresetRowUuId = io::uiFindElementUuId( app->ioH, fragPanelUuId, kFragPresetRowId, fragChanId ); assert( fragPanelUuId != kInvalidId ); - //assert( fragBegLocUuId != kInvalidId ); - //assert( fragEndLocUuId != kInvalidId ); assert( fragPresetRowUuId != kInvalidId ); // Make the fragment panel clickable @@ -1726,7 +1703,6 @@ namespace cw } // Settting psNextFrag to a non-null value causes the - // _fragment_restore_ui() to be called from the io::exec() callback app->psNextFrag = preset_sel::get_fragment_base(app->psH); _set_status(app,"Loaded fragment file."); @@ -1766,18 +1742,11 @@ namespace cw // if all the fragment UI's have been created if( app->psNextFrag == nullptr ) { - // enable the start/stop buttons - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), true ); - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), true ); - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ), true ); - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kEnaRecordCheckId ), true ); - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kTrackMidiCheckId ), true ); - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), true ); - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetBtnId ), true ); - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetLocNumbId ), true ); + + // the fragments are loaded enable the 'load' menu io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPerfSelId ), true ); - - _set_status(app,"Fragment restore complete: elapsed secs:%f\n",time::elapsedSecs(app->psLoadT0)); + + cwLogInfo("Fragment restore complete: elapsed secs:%f",time::elapsedSecs(app->psLoadT0)); } } @@ -1954,8 +1923,6 @@ namespace cw midiEventCntRef = midiEventN; - //uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kSfResetLocNumbId), app->minLoc, app->maxLoc, 1, 0, app->minLoc ); - cwLogInfo("%i MIDI events loaded from score. Loc Min:%i Max:%i", midiEventN , app->minLoc, app->maxLoc); } @@ -1965,7 +1932,56 @@ namespace cw return rc; } - rc_t _do_load( app_t* app, const char* perf_fn ) + rc_t _set_vel_table( app_t* app, const vel_tbl_t* vtA, unsigned vtN ) + { + rc_t rc; + const uint8_t* tableA = nullptr; + unsigned tableN = 0; + unsigned midiDevIdx = kInvalidIdx; + unsigned assignN = 0; + + if((rc = vel_table_disable(app->mrpH)) != kOkRC ) + { + rc = cwLogError(rc,"Velocity table disable failed."); + goto errLabel; + } + + + for(unsigned i=0; imrpH, vtA[i].device)) == kInvalidIdx ) + { + rc = cwLogError(kInvalidArgRC,"The MIDI device '%s' could not be found.",cwStringNullGuard(vtA[i].device)); + goto errLabel; + } + + if((tableA = get_vel_table( app->vtH, vtA[i].name, tableN )) == nullptr ) + { + rc = cwLogError(kInvalidArgRC,"The MIDI velocity table '%s' could not be found.",cwStringNullGuard(vtA[i].name)); + goto errLabel; + } + + if((rc = vel_table_set(app->mrpH, midiDevIdx, tableA, tableN )) != kOkRC ) + { + rc = cwLogError(rc,"Velocity table device:%s name:%s assignment failed.",cwStringNullGuard(vtA[i].device), cwStringNullGuard(vtA[i].name)); + goto errLabel; + } + + assignN += 1; + } + + if( assignN == 0 ) + cwLogWarning("All velocity tables disabled."); + + errLabel: + + if( rc != kOkRC ) + rc = cwLogError(rc,"Velocity table assignment failed."); + + return rc; + } + + rc_t _do_load( app_t* app, const char* perf_fn, const vel_tbl_t* vtA=nullptr, unsigned vtN=0 ) { rc_t rc = kOkRC; unsigned midiEventN = 0; @@ -1973,73 +1989,56 @@ namespace cw cwLogInfo("Loading"); _set_status(app,"Loading..."); - // create the score - if( perf_fn != nullptr ) - if((rc= score::create( app->scoreH, perf_fn )) != kOkRC ) - { - cwLogError(rc,"Score create failed on '%s'.",app->scoreFn); - goto errLabel; - } - - if((rc = _load_midi_player(app, midiEventN )) != kOkRC ) + // load the performance or score + if((rc= score::create( app->scoreH, perf_fn )) != kOkRC ) { - cwLogError(rc,"MIDI player load failed."); + cwLogError(rc,"Score create failed on '%s'.",app->scoreFn); goto errLabel; } - - // reset the timestamp tracker - track_loc_reset( app->psH ); - // set the range of the global play location controls - //if( firstLoadFl ) - if( true ) + // load the midi player, create locMap[], set app->min/maxLoc + if((rc = _load_midi_player(app, midiEventN )) != kOkRC ) { - unsigned begPlayLocUuId = io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId); - unsigned endPlayLocUuId = io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId); - - //if( app->end_play_loc == 0 ) - app->end_play_loc = app->maxLoc; - - //if( !(app->minLoc <= app->beg_play_loc && app->beg_play_loc <= app->maxLoc) ) - app->beg_play_loc = app->minLoc; - - io::uiSetNumbRange( app->ioH, begPlayLocUuId, app->minLoc, app->maxLoc, 1, 0, app->beg_play_loc ); - io::uiSetNumbRange( app->ioH, endPlayLocUuId, app->minLoc, app->maxLoc, 1, 0, app->end_play_loc ); - - io::uiSendValue( app->ioH, begPlayLocUuId, app->beg_play_loc); - io::uiSendValue( app->ioH, endPlayLocUuId, app->end_play_loc); - - // enable the insert 'End Loc' number box since the score is loaded - io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true ); - - uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kSfResetLocNumbId), app->minLoc, app->maxLoc, 1, 0, app->beg_play_loc ); - + rc = cwLogError(rc,"MIDI player load failed."); + goto errLabel; } - /* + // assign the vel. table + if((rc = _set_vel_table(app, vtA, vtN )) != kOkRC ) + { + rc = cwLogError(rc,"Velocity table assignment failed."); + goto errLabel; + } - // update the current event and event count - _update_event_ui(app); - - // enable the start/stop buttons + // A performance is loaded so enable the UI io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), true ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), true ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLiveCheckId ), true ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kEnaRecordCheckId ), true ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kTrackMidiCheckId ), true ); - - // restore the fragment records - if( firstLoadFl ) - if((rc = _restore_fragment_data( app )) != kOkRC ) - { - rc = cwLogError(rc,"Restore failed."); - goto errLabel; - } - - //io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kLoadBtnId ), true ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSaveBtnId ), true ); + io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), true ); - */ + // set the UI begin/end play to the locations of the newly loaded performance + app->end_play_loc = app->maxLoc; + app->beg_play_loc = app->minLoc; + + // Update the master range of the play beg/end number widgets + io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, app->beg_play_loc ); + io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), app->minLoc, app->maxLoc, 1, 0, app->end_play_loc ); + + io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kBegPlayLocNumbId ), true ); + io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kEndPlayLocNumbId ), true ); + + io::uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kBegPlayLocNumbId), app->beg_play_loc); + io::uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kEndPlayLocNumbId), app->end_play_loc); + + // set the range of the SF reset number + io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetBtnId ), true ); + io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetLocNumbId ), true ); + io::uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kSfResetLocNumbId), app->minLoc, app->maxLoc, 1, 0, app->beg_play_loc ); + io::uiSendValue( app->ioH, io::uiFindElementUuId(app->ioH, kSfResetLocNumbId), app->minLoc); + cwLogInfo("'%s' loaded.",perf_fn); errLabel: @@ -2084,14 +2083,12 @@ namespace cw } // load the requested performance - if((rc = _do_load(app,prp->fname)) != kOkRC ) + if((rc = _do_load(app,prp->fname,prp->vel_tblA, prp->vel_tblN)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"The performance load failed."); goto errLabel; } - //_do_sf_reset(app,app->beg_play_loc); - errLabel: return rc; } @@ -2709,6 +2706,7 @@ namespace cw _update_event_ui(app); + /* // disable start and stop buttons until a score is loaded io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStartBtnId ), false ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kStopBtnId ), false ); @@ -2719,6 +2717,8 @@ namespace cw io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetBtnId ), false ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kSfResetLocNumbId ), false ); io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kPerfSelId ), false ); + io::uiSetEnable( app->ioH, io::uiFindElementUuId( app->ioH, kInsertLocId ), false ); + */ io::uiSendValue( app->ioH, io::uiFindElementUuId( app->ioH, kMidiLoadFnameId), app->midiRecordDir); @@ -2730,13 +2730,6 @@ namespace cw if((rc = _fragment_load_data(app)) != kOkRC ) rc = cwLogError(rc,"Preset data restore failed."); - if( app->sfH.isValid() ) - { - cw_loc_range(app->sfH,app->minLoc,app->maxLoc); - printf("SF Loc range:%i %i\n",app->minLoc,app->maxLoc); - uiSetNumbRange( app->ioH, io::uiFindElementUuId(app->ioH, kSfResetLocNumbId), app->minLoc, app->maxLoc, 1, 0, app->minLoc ); - } - _on_live_midi_fl(app,app->useLiveMidiFl); return rc; @@ -2779,10 +2772,6 @@ namespace cw _on_ui_save(app); break; - case kLoadBtnId: - _do_load(app,app->scoreFn); - break; - case kPerfSelId: _on_perf_select(app,m.value->u.u); break; @@ -3344,7 +3333,7 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, int argc, const char* ar } // create the MIDI record-play object - if((rc = midi_record_play::create(app.mrpH,app.ioH,*app.midi_play_record_cfg,app.velTableFname,_midi_play_callback,&app)) != kOkRC ) + if((rc = midi_record_play::create(app.mrpH,app.ioH,*app.midi_play_record_cfg,nullptr,_midi_play_callback,&app)) != kOkRC ) { rc = cwLogError(rc,"MIDI record-play object create failed."); goto errLabel; @@ -3357,14 +3346,6 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, int argc, const char* ar goto errLabel; } - // create the performance selection menu - /* - if((rc= _load_perf_selection_menu(&app)) != kOkRC ) - { - rc = cwLogError(rc,"The performance list UI create failed."); - goto errLabel; - } - */ // create the performance selection menu if((rc= _load_perf_dir_selection_menu(&app)) != kOkRC ) { diff --git a/html/preset_sel/ui.cfg b/html/preset_sel/ui.cfg index 68638ea..758d625 100644 --- a/html/preset_sel/ui.cfg +++ b/html/preset_sel/ui.cfg @@ -12,7 +12,6 @@ button:{ name: ioReportBtnId, title:"IO Report" }, button:{ name: netPrintBtnId, title:"Print Network" } button:{ name: reportBtnId, title:"App Report" }, - //button:{ name: loadBtnId, title:"Load" }, button:{ name: saveBtnId, title:"Save" }, select:{ name: perfSelId, title:"Load",children: {} }, @@ -33,16 +32,16 @@ }, row: { - button:{ name: startBtnId, title:"Start" }, - button:{ name: stopBtnId, title:"Stop" }, - number:{ name: begLocNumbId, title:"Loc:", min:0, max:100000, step:1, decpl:0 }, - number:{ name: endLocNumbId, title:"End:", min:0, max:100000, step:1, decpl:0 }, - check: { name: liveCheckId, title:"Live" }, - check: { name: trackMidiCheckId, title:"Track MIDI" }, + button:{ name: startBtnId, title:"Start", enable: false }, + button:{ name: stopBtnId, title:"Stop", enable: false }, + number:{ name: begLocNumbId, title:"Loc:", min:0, max:100000, step:1, decpl:0, enable: false }, + number:{ name: endLocNumbId, title:"End:", min:0, max:100000, step:1, decpl:0, enable: false }, + check: { name: liveCheckId, title:"Live", enable: false }, + check: { name: trackMidiCheckId, title:"Track MIDI", enable: false }, }, row: { - check:{ name: midiThruCheckId, title:"MIDI Thru" }, + check:{ name: midiThruCheckId, title:"MIDI Thru", enable: false }, numb_disp: { name: curMidiEvtCntId, title:"Current Loc:" }, numb_disp: { name: totalMidiEvtCntId, title:"Max Loc:" }, },