cwIoMidiREcordPlay,cwIoPresetSelApp : Updated to include half-pedal experimentation procedure, and separate velocity tables for each MIDI device.

This commit is contained in:
kevin 2022-03-20 10:27:46 -04:00
parent cb31b0882a
commit 20f631afc5
4 changed files with 521 additions and 134 deletions

View File

@ -41,28 +41,65 @@ namespace cw
unsigned midiOutDevIdx;
unsigned midiOutPortIdx;
bool enableFl;
unsigned velTableN;
uint8_t* velTableArray;
bool pedalMapEnableFl;
unsigned pedalDownVelId;
unsigned pedalDownHalfVelId;
unsigned pedalDownVel;
unsigned pedalDownHalfVel;
} midi_device_t;
enum
{
kHalfPedalDone,
kWaitForBegin,
kWaitForNoteOn,
kWaitForNoteOff,
kWaitForPedalUp,
kWaitForPedalDown,
};
typedef struct midi_record_play_str
{
io::handle_t ioH;
am_midi_msg_t* msgArray; // msgArray[ msgArrayN ]
unsigned msgArrayN; // Count of messages allocated in msgArray.
unsigned msgArrayInIdx; // Next available space for incoming MIDI messages (also the current count of msgs in msgArray[])
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
am_midi_msg_t* iMsgArray; // msgArray[ msgArrayN ]
unsigned iMsgArrayN; // Count of messages allocated in msgArray.
unsigned iMsgArrayInIdx; // Next available space for incoming MIDI messages (also the current count of msgs in msgArray[])
midi_device_t* midiDevA;
unsigned midiDevN;
bool startedFl;
bool recordFl;
bool thruFl;
bool logInFl; // log incoming message when not in 'record' mode.
bool logOutFl; // log outgoing messages
bool halfPedalFl;
unsigned halfPedalState;
unsigned halfPedalNextUs;
unsigned halfPedalNoteDelayUs;
unsigned halfPedalNoteDurUs;
unsigned halfPedalUpDelayUs;
unsigned halfPedalDownDelayUs;
uint8_t halfPedalMidiPitch;
uint8_t halfPedalMidiNoteVel;
uint8_t halfPedalMidiPedalVel;
time::spec_t play_time;
time::spec_t start_time;
time::spec_t end_play_event_timestamp;
time::spec_t store_time;
bool pedalFl;
@ -91,10 +128,12 @@ namespace cw
{
mem::release(p->midiDevA[i].midiOutDevLabel);
mem::release(p->midiDevA[i].midiOutPortLabel);
mem::release(p->midiDevA[i].velTableArray);
}
mem::release(p->midiDevA);
mem::release(p->msgArray);
mem::release(p->iMsgArray);
mem::release(p);
return rc;
@ -107,12 +146,16 @@ namespace cw
if((rc = cfg.getv(
"max_midi_msg_count", p->msgArrayN,
"midi_timer_period_micro_sec", p->midi_timer_period_micro_sec,
"midi_device_list", midiDevL)) != kOkRC )
"midi_device_list", midiDevL,
"log_in_flag", p->logInFl,
"log_out_flag", p->logOutFl,
"half_pedal_flag", p->halfPedalFl)) != kOkRC )
{
rc = cwLogError(kSyntaxErrorRC,"MIDI record play configuration parse failed.");
goto errLabel;
}
p->iMsgArrayN = p->msgArrayN;
if( midiDevL->child_count() > 0 )
{
@ -126,6 +169,8 @@ namespace cw
const object_t* ele = midiDevL->child_ele(i);
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,
@ -136,14 +181,57 @@ namespace cw
goto errLabel;
}
if((rc = ele->getv_opt( "vel_table", velTable,
"pedal", pedalRecd)) != kOkRC )
{
rc = cwLogError(kSyntaxErrorRC,"MIDI record play device optional argument parsing failed.");
goto errLabel;
}
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<uint8_t>(p->midiDevA[i].velTableN);
for(unsigned j=0; j<p->midiDevA[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,
"down_vel", p->midiDevA[i].pedalDownVel,
"half_id", p->midiDevA[i].pedalDownHalfVelId,
"half_vel", p->midiDevA[i].pedalDownHalfVel )) != kOkRC )
{
rc = cwLogError(kSyntaxErrorRC,"An error occured while parsing the pedal record for MIDI device:'%s' port:'%s'.",midiOutDevLabel,midiOutPortLabel);
goto errLabel;
}
else
{
p->midiDevA[i].pedalMapEnableFl = true;
}
}
}
}
// allocate the MIDI msg buffer
p->msgArray = mem::allocZ<am_midi_msg_t>( p->msgArrayN );
p->iMsgArray = mem::allocZ<am_midi_msg_t>( p->iMsgArrayN );
errLabel:
return rc;
@ -151,54 +239,181 @@ namespace cw
rc_t _stop( midi_record_play_t* p );
rc_t _event_callback( midi_record_play_t* p, unsigned id, const time::spec_t timestamp, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
const am_midi_msg_t* _midi_store( midi_record_play_t* p, unsigned devIdx, unsigned portIdx, const time::spec_t& ts, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
{
am_midi_msg_t* am = nullptr;
// verify that space exists in the record buffer
if( p->iMsgArrayInIdx < p->iMsgArrayN )
{
// MAKE THIS ATOMIC
unsigned id = p->iMsgArrayInIdx;
++p->iMsgArrayInIdx;
am = p->iMsgArray + id;
am->id = id;
am->devIdx = devIdx;
am->portIdx = portIdx;
am->timestamp = ts;
am->ch = ch;
am->status = status;
am->d0 = d0;
am->d1 = d1;
}
return am;
}
rc_t _event_callback( midi_record_play_t* p, unsigned id, const time::spec_t timestamp, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1, bool log_fl=true )
{
rc_t rc = kOkRC;
if( !time::isZero(p->end_play_event_timestamp) && time::isGT(timestamp,p->end_play_event_timestamp))
{
rc = _stop(p);
printf("ZERO\n");
}
else
{
if( p->halfPedalFl )
{
if( status==midi::kCtlMdId && d0 == midi::kSustainCtlMdId && d1 != 0 )
d1 = p->halfPedalMidiPedalVel;
}
for(unsigned i=0; i<p->midiDevN; ++i)
if(p->midiDevA[i].enableFl )
io::midiDeviceSend( p->ioH, p->midiDevA[i].midiOutDevIdx, p->midiDevA[i].midiOutPortIdx, status + ch, d0, d1 );
{
uint8_t out_d1 = d1;
if( !p->halfPedalFl )
{
// map the note on velocity
if( status==midi::kNoteOnMdId and d1 != 0 and p->midiDevA[i].velTableArray != nullptr )
{
if( d1 >= p->midiDevA[i].velTableN )
cwLogError(kInvalidIdRC,"A MIDI note-on velocity (%i) outside the velocity table range was encountered.",d1);
else
out_d1 = p->midiDevA[i].velTableArray[ d1 ];
}
// map the pedal down velocity
if( status==midi::kCtlMdId && d0 == midi::kSustainCtlMdId && p->midiDevA[i].pedalMapEnableFl )
{
if( d1 == p->midiDevA[i].pedalDownVelId )
out_d1 = p->midiDevA[i].pedalDownVel;
else
if( d1 == p->midiDevA[i].pedalDownHalfVelId )
out_d1 = p->midiDevA[i].pedalDownHalfVel;
else
cwLogError(kInvalidIdRC,"Unexpected pedal down velocity (%i) during pedal velocity mapping.",d1);
}
}
io::midiDeviceSend( p->ioH, p->midiDevA[i].midiOutDevIdx, p->midiDevA[i].midiOutPortIdx, status + ch, d0, out_d1 );
}
if( p->cb )
p->cb( p->cb_arg, id, timestamp, ch, status, d0, d1 );
if( log_fl && p->logOutFl )
{
// Note: The device of outgoing messages is set to p->midiDevN + 1 to distinguish it from
// incoming messages.
_midi_store( p, p->midiDevN, 0, timestamp, ch, status, d0, d1 );
}
}
return rc;
}
rc_t _transmit_msg( midi_record_play_t* p, const am_midi_msg_t* am )
rc_t _transmit_msg( midi_record_play_t* p, const am_midi_msg_t* am, bool log_fl=true )
{
return _event_callback( p, am->id, am->timestamp, am->ch, am->status, am->d0, am->d1 );
return _event_callback( p, am->id, am->timestamp, am->ch, am->status, am->d0, am->d1, log_fl );
}
rc_t _transmit_ctl( midi_record_play_t* p, unsigned ch, unsigned ctlId, unsigned ctlVal )
rc_t _transmit_note( midi_record_play_t* p, unsigned ch, unsigned pitch, unsigned vel, unsigned microsecs )
{
time::spec_t ts = {0};
time::microsecondsToSpec( ts, microsecs );
return _event_callback( p, kInvalidId, ts, ch, midi::kNoteOnMdId, pitch, vel );
}
rc_t _transmit_ctl( midi_record_play_t* p, unsigned ch, unsigned ctlId, unsigned ctlVal, unsigned microsecs )
{
time::spec_t ts = {0};
time::microsecondsToSpec( ts, microsecs );
return _event_callback( p, kInvalidId, ts, ch, midi::kCtlMdId, ctlId, ctlVal );
}
rc_t _transmit_pedal( midi_record_play_t* p, unsigned ch, unsigned pedalCtlId, bool pedalDownFl )
rc_t _transmit_pedal( midi_record_play_t* p, unsigned ch, unsigned pedalCtlId, bool pedalDownFl, unsigned microsecs )
{
return _transmit_ctl( p, ch, pedalCtlId, pedalDownFl ? 127 : 0);
return _transmit_ctl( p, ch, pedalCtlId, pedalDownFl ? 127 : 0, microsecs);
}
void _half_pedal_update( midi_record_play_t* p, unsigned cur_time_us )
{
if( cur_time_us >= p->halfPedalNextUs )
{
unsigned midi_ch = 0;
switch( p->halfPedalState )
{
case kWaitForBegin:
printf("down: %i %i\n",cur_time_us/1000,p->halfPedalMidiPedalVel);
_transmit_ctl( p, midi_ch, midi::kSustainCtlMdId, p->halfPedalMidiPedalVel, cur_time_us);
p->halfPedalState = kWaitForNoteOn;
p->halfPedalNextUs += p->halfPedalNoteDelayUs;
break;
case kWaitForNoteOn:
printf("note: %i\n",cur_time_us/1000);
_transmit_note( p, midi_ch, p->halfPedalMidiPitch, p->halfPedalMidiNoteVel, cur_time_us );
p->halfPedalNextUs += p->halfPedalNoteDurUs;
p->halfPedalState = kWaitForNoteOff;
break;
case kWaitForNoteOff:
printf("off: %i\n",cur_time_us/1000);
_transmit_note( p, midi_ch, p->halfPedalMidiPitch, 0, cur_time_us );
p->halfPedalNextUs += p->halfPedalUpDelayUs;
p->halfPedalState = kWaitForPedalUp;
break;
case kWaitForPedalUp:
printf("up: %i\n",cur_time_us/1000);
_transmit_ctl( p, midi_ch, midi::kSustainCtlMdId, 0, cur_time_us);
p->halfPedalNextUs += p->halfPedalDownDelayUs;
p->halfPedalState = kWaitForPedalDown;
break;
case kWaitForPedalDown:
//printf("down: %i\n",cur_time_us/1000);
//_transmit_ctl( p, midi_ch, midi::kSustainCtlMdId, p->halfPedalMidiPedalVel, cur_time_us);
//_stop(p);
p->halfPedalState = kHalfPedalDone;
break;
case kHalfPedalDone:
break;
}
}
}
// Set the next location to store an incoming MIDI message
void _set_midi_msg_next_index( midi_record_play_t* p, unsigned next_idx )
{
p->msgArrayInIdx = next_idx;
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->midiMsgArrayInIdx );
p->iMsgArrayInIdx = next_idx;
}
// Set the next index of the next MIDI message to transmit
void _set_midi_msg_next_play_index(midi_record_play_t* p, unsigned next_idx)
{
p->msgArrayOutIdx = next_idx;
@ -255,6 +470,7 @@ namespace cw
return rc;
}
// Fill the play buffer from a previously store AM file.
rc_t _midi_read( midi_record_play_t* p, const char* fn )
{
rc_t rc = kOkRC;
@ -285,21 +501,24 @@ namespace cw
goto errLabel;
}
_set_midi_msg_next_index(p, n );
p->msgArrayInIdx = n;
cwLogInfo("Read %i from '%s'.",n,fn);
errLabel:
file::close(fH);
return rc;
}
// Write the record buffer to an AM file
rc_t _midi_write( midi_record_play_t* p, const char* fn )
{
rc_t rc = kOkRC;
file::handle_t fH;
if( p->msgArrayInIdx == 0 )
if( p->iMsgArrayInIdx == 0 )
{
cwLogWarning("Nothing to write.");
return rc;
@ -313,28 +532,75 @@ namespace cw
}
// write the file header
if((rc = write(fH,p->msgArrayInIdx)) != kOkRC )
if((rc = write(fH,p->iMsgArrayInIdx)) != kOkRC )
{
rc = cwLogError(kWriteFailRC,"Header write to '%s' failed.",cwStringNullGuard(fn));
goto errLabel;
}
// write the file data
if((rc = write(fH,p->msgArray,sizeof(am_midi_msg_t)*p->msgArrayInIdx)) != kOkRC )
if((rc = write(fH,p->iMsgArray,sizeof(am_midi_msg_t)*p->iMsgArrayInIdx)) != kOkRC )
{
rc = cwLogError(kWriteFailRC,"Data write to '%s' failed.",cwStringNullGuard(fn));
goto errLabel;
}
// update UI msg count
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->msgArrayInIdx );
errLabel:
file::close(fH);
cwLogInfo("Saved %i events to '%s'.", p->msgArrayInIdx, fn );
cwLogInfo("Saved %i events to '%s'.", p->iMsgArrayInIdx, fn );
return rc;
}
rc_t _write_csv( midi_record_play_t* p, const char* fn )
{
rc_t rc = kOkRC;
file::handle_t fH;
if( p->iMsgArrayInIdx == 0 )
{
cwLogWarning("Nothing to write.");
return rc;
}
// open the file
if((rc = file::open(fH,fn,file::kWriteFl)) != kOkRC )
{
rc = cwLogError(kOpenFailRC,"Unable to create the file '%s'.",cwStringNullGuard(fn));
goto errLabel;
}
file::printf(fH,"dev,port,microsec,id,sec,ch,status,d0,d1\n");
for(unsigned i=0; i<p->iMsgArrayInIdx; ++i)
{
const am_midi_msg_t* m = p->iMsgArray + i;
double secs = time::elapsedSecs( p->iMsgArray[0].timestamp, p->iMsgArray[i].timestamp );
char sciPitch[ midi::kMidiSciPitchCharCnt + 1 ];
if( m->status == midi::kNoteOnMdId )
midi::midiToSciPitch( m->d0, sciPitch, midi::kMidiSciPitchCharCnt );
else
strcpy(sciPitch,"");
if((rc = file::printf(fH, "%3i,%3i,%8i,%3i,%8.4f,%2i,0x%2x,%5s,%3i,%3i\n",
m->devIdx, m->portIdx, m->microsec, m->id, secs,
m->ch, m->status, sciPitch, m->d0, m->d1 )) != kOkRC )
{
rc = cwLogError(rc,"Write failed on line:%i", i+1 );
goto errLabel;
}
}
errLabel:
file::close(fH);
cwLogInfo("Saved %i events to '%s'.", p->iMsgArrayInIdx, fn );
return rc;
}
rc_t _midi_file_write( const char* fn, const am_midi_msg_t* msgArray, unsigned msgArrayCnt )
@ -424,13 +690,14 @@ namespace cw
time::spec_t t1;
time::get(t1);
// if we were recording
if( p->recordFl )
{
// set the 'microsec' value for each MIDI msg
for(unsigned i=0; i<p->msgArrayInIdx; ++i)
// set the 'microsec' value for each MIDI msg as an offset from the first message[]
for(unsigned i=0; i<p->iMsgArrayInIdx; ++i)
{
p->msgArray[i].microsec = time::elapsedMicros(p->msgArray[0].timestamp,p->msgArray[i].timestamp);
p->msgArray[i].microsec = time::elapsedMicros(p->iMsgArray[0].timestamp,p->iMsgArray[i].timestamp);
}
cwLogInfo("MIDI messages recorded: %i",p->msgArrayInIdx );
@ -440,27 +707,16 @@ namespace cw
{
io::timerStop( p->ioH, io::timerIdToIndex(p->ioH, kMidiRecordPlayTimerId) );
// TODO: should work for all channels
// TODO:
// BUG BUG BUG: should work for all channels
// all notes off
_transmit_ctl( p, 0, 121, 0 ); // reset all controllers
_transmit_ctl( p, 0, 123, 0 ); // all notes off
_transmit_ctl( p, 0, 0, 0 ); // switch to bank 0
//_transmit_pedal( p, 0, midi::kSustainCtlMdId, false );
//_transmit_pedal( p, 0, midi::kSostenutoCtlMdId, false );
//_transmit_pedal( p, 0, midi::kSoftPedalCtlMdId, false );
// send pgm change 0
//time::spec_t ts = {0};
//_event_callback( p, kInvalidId, ts, 0, midi::kPgmMdId, 0, 0 );
p->pedalFl = false;
_transmit_ctl( p, 0, 121, 0, 0 ); // reset all controllers
_transmit_ctl( p, 0, 123, 0, 0 ); // all notes off
_transmit_ctl( p, 0, 0, 0, 0 ); // switch to bank 0
}
//cwLogInfo("Runtime: %5.2f seconds.", time::elapsedMs(p->start_time,t1)/1000.0 );
return rc;
}
@ -480,65 +736,42 @@ namespace cw
{
//if( !midi::isPedal(pkt->msgArray[j].status,pkt->msgArray[j].d0) )
// printf("0x%x 0x%x 0x%x\n", pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1 );
//printf("IN: 0x%x 0x%x 0x%x\n", pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1 );
if( p->recordFl && p->startedFl )
if( (p->recordFl || p->logInFl) && p->startedFl )
{
// verify that space exists in the record buffer
if( p->msgArrayInIdx >= p->msgArrayN )
if( p->iMsgArrayInIdx >= p->iMsgArrayN )
{
_stop(p);
rc = cwLogError(kBufTooSmallRC,"MIDI message record buffer is full. % messages.",p->msgArrayN);
rc = cwLogError(kBufTooSmallRC,"MIDI message record buffer is full. % messages.",p->iMsgArrayN);
goto errLabel;
}
else
{
// copy the msg into the record buffer
am_midi_msg_t* am = p->msgArray + p->msgArrayInIdx;
midi::msg_t* mm = pkt->msgArray + j;
if( midi::isChStatus(mm->status) )
{
const am_midi_msg_t* am = _midi_store( p, pkt->devIdx, pkt->portIdx, mm->timeStamp, mm->status & 0x0f, mm->status & 0xf0, mm->d0, mm->d1 );
am->id = p->msgArrayInIdx;
am->devIdx = pkt->devIdx;
am->portIdx = pkt->portIdx;
am->timestamp = mm->timeStamp;
am->ch = mm->status & 0x0f;
am->status = mm->status & 0xf0;
am->d0 = mm->d0;
am->d1 = mm->d1;
if( p->thruFl && am != nullptr )
_transmit_msg( p, am, false );
//printf("st:0x%x ch:%i d0:0x%x d1:0x%x\n",am->status,am->ch,am->d0,am->d1);
p->msgArrayInIdx += 1;
if( p->thruFl )
_transmit_msg( p, am );
// send msg count
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->msgArrayInIdx );
}
}
}
}
/*
if( pkt->msgArray == NULL )
printf("io midi cb: 0x%x ",pkt->sysExMsg[j]);
else
{
if( !_midi_filter(pkt->msgArray + j) )
printf("io midi cb: %ld %ld 0x%x %i %i\n", pkt->msgArray[j].timeStamp.tv_sec, pkt->msgArray[j].timeStamp.tv_nsec, pkt->msgArray[j].status, pkt->msgArray[j].d0, pkt->msgArray[j].d1);
}
*/
}
errLabel:
return rc;
}
rc_t _timer_callback(midi_record_play_t* p, io::timer_msg_t& m)
{
rc_t rc = kOkRC;
@ -551,6 +784,9 @@ namespace cw
unsigned cur_time_us = time::elapsedMicros(p->play_time,t);
if( p->halfPedalFl )
_half_pedal_update( p, cur_time_us );
else
while( p->msgArray[ p->msgArrayOutIdx ].microsec <= cur_time_us )
{
@ -558,30 +794,8 @@ namespace cw
//_print_midi_msg(mm);
bool skipFl = false;
/*
// if this is a pedal message
if( mm->status == midi::kCtlMdId && (mm->d0 == midi::kSustainCtlMdId || mm->d0 == midi::kSostenutoCtlMdId || mm->d0 == midi::kSoftPedalCtlMdId ) )
{
// if the pedal is down
if( p->pedalFl )
{
skipFl = mm->d1 > 64;
p->pedalFl = false;
}
else
{
skipFl = mm->d1 <= 64;
p->pedalFl = true;
}
}
*/
if( !skipFl )
{
_transmit_msg( p, mm );
}
_set_midi_msg_next_play_index(p, p->msgArrayOutIdx+1 );
// if all MIDI messages have been played
@ -615,6 +829,16 @@ cw::rc_t cw::midi_record_play::create( handle_t& hRef, io::handle_t ioH, const o
p->ioH = ioH;
p->cb = cb;
p->cb_arg = cb_arg;
p->halfPedalState = kHalfPedalDone;
p->halfPedalNextUs = 0;
p->halfPedalNoteDelayUs = 100 * 1000;
p->halfPedalNoteDurUs = 1000 * 1000;
p->halfPedalUpDelayUs = 1000 * 1000;
p->halfPedalDownDelayUs = 1000 * 1000;
p->halfPedalMidiPitch = 64;
p->halfPedalMidiNoteVel = 64;
p->halfPedalMidiPedalVel = 127;
for( unsigned i=0; i<
p->midiDevN; ++i)
@ -673,7 +897,6 @@ cw::rc_t cw::midi_record_play::start( handle_t h, bool rewindFl, const time::spe
{
midi_record_play_t* p = _handleToPtr(h);
p->startedFl = true;
p->pedalFl = false;
// set the end play time
if( end_play_event_timestamp == nullptr or time::isZero(*end_play_event_timestamp) )
@ -683,11 +906,12 @@ cw::rc_t cw::midi_record_play::start( handle_t h, bool rewindFl, const time::spe
time::get(p->start_time);
if( p->recordFl )
if( p->recordFl || p->logInFl or p->logOutFl )
{
_set_midi_msg_next_index(p, 0 );
}
else
if( !p->recordFl )
{
time::get(p->play_time);
@ -700,6 +924,12 @@ cw::rc_t cw::midi_record_play::start( handle_t h, bool rewindFl, const time::spe
time::subtractMicros(p->play_time, p->msgArray[ p->msgArrayOutIdx ].microsec );
}
if( p->halfPedalFl )
{
p->halfPedalNextUs = 0;
p->halfPedalState = kWaitForBegin;
}
io::timerStart( p->ioH, io::timerIdToIndex(p->ioH, kMidiRecordPlayTimerId) );
}
@ -761,6 +991,12 @@ cw::rc_t cw::midi_record_play::save( handle_t h, const char* fn )
return _midi_write(p,fn);
}
cw::rc_t cw::midi_record_play::save_csv( handle_t h, const char* fn )
{
midi_record_play_t* p = _handleToPtr(h);
return _write_csv(p,fn);
}
cw::rc_t cw::midi_record_play::open( handle_t h, const char* fn )
{
midi_record_play_t* p = _handleToPtr(h);
@ -790,6 +1026,7 @@ cw::rc_t cw::midi_record_play::load( handle_t h, const midi_msg_t* msg, unsigned
p->msgArray[i].devIdx = kInvalidIdx;
p->msgArray[i].portIdx = kInvalidIdx;
p->msgArray[i].microsec = time::elapsedMicros(p->msgArray[0].timestamp,p->msgArray[i].timestamp);
}
p->msgArrayInIdx = msg_count;
@ -815,13 +1052,12 @@ cw::rc_t cw::midi_record_play::seek( handle_t h, time::spec_t seek_timestamp )
{
p->msgArrayOutIdx = i;
_transmit_pedal( p, mm->ch, midi::kSustainCtlMdId, damp_down_fl );
_transmit_pedal( p, mm->ch, midi::kSostenutoCtlMdId, sost_down_fl );
_transmit_pedal( p, mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl );
_transmit_pedal( p, mm->ch, midi::kSustainCtlMdId, damp_down_fl, 0 );
_transmit_pedal( p, mm->ch, midi::kSostenutoCtlMdId, sost_down_fl, 0 );
_transmit_pedal( p, mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl, 0 );
printf("PEDAL: %s.\n", damp_down_fl ? "Down" : "Up");
//io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, mm->status + mm->ch, midi::kSustainCtlMdId, damp_down_fl ? 127 : 0 );
//io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, mm->status + mm->ch, midi::kSostenutoCtlMdId, sost_down_fl ? 127 : 0 );
//io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, mm->status + mm->ch, midi::kSoftPedalCtlMdId, soft_down_fl ? 127 : 0 );
break;
}
@ -862,7 +1098,7 @@ unsigned cw::midi_record_play::event_count( handle_t h )
unsigned cw::midi_record_play::event_index( handle_t h )
{
midi_record_play_t* p = _handleToPtr(h);
return p->recordFl ? p->msgArrayInIdx : p->msgArrayOutIdx;
return p->recordFl ? p->iMsgArrayInIdx : p->msgArrayOutIdx;
}
@ -923,6 +1159,18 @@ void cw::midi_record_play::enable_device( handle_t h, unsigned devIdx, bool enab
}
}
void cw::midi_record_play::half_pedal_params( handle_t h, unsigned noteDelayMs, unsigned pitch, unsigned vel, unsigned pedal_vel, unsigned noteDurMs, unsigned downDelayMs )
{
midi_record_play_t* p = _handleToPtr(h);
p->halfPedalNoteDelayUs = noteDelayMs * 1000;
p->halfPedalNoteDurUs = noteDurMs * 1000;
p->halfPedalDownDelayUs = downDelayMs * 1000;
p->halfPedalMidiPitch = pitch;
p->halfPedalMidiNoteVel = vel;
p->halfPedalMidiPedalVel= pedal_vel;
}
cw::rc_t cw::midi_record_play::am_to_midi_file( const char* am_filename, const char* midi_filename )
{
rc_t rc = kOkRC;

View File

@ -39,6 +39,8 @@ namespace cw
bool thru_state( handle_t h );
rc_t save( handle_t h, const char* fn );
rc_t save_csv( handle_t h, const char* fn );
rc_t open( handle_t h, const char* fn );
// Load the playback buffer with messages to output.
@ -54,6 +56,8 @@ namespace cw
bool is_device_enabled( handle_t h, unsigned devIdx );
void enable_device( handle_t h, unsigned devIdx, bool enableFl );
void half_pedal_params( handle_t h, unsigned noteDelayMs, unsigned pitch, unsigned vel, unsigned pedal_vel, unsigned noteDurMs, unsigned downDelayMs );
// Convert an audio-midi file to a MIDI file
rc_t am_to_midi_file( const char* am_filename, const char* midi_filename );
rc_t am_to_midi_dir( const char* inDir );

View File

@ -69,6 +69,13 @@ namespace cw
kStatusId,
kHalfPedalPedalVel,
kHalfPedalDelayMs,
kHalfPedalPitch,
kHalfPedalVel,
kHalfPedalDurMs,
kHalfPedalDnDelayMs,
kLogId,
kFragListId,
@ -140,6 +147,15 @@ namespace cw
{ kPanelDivId, kInsertLocId, "insertLocId" },
{ kPanelDivId, kInsertBtnId, "insertBtnId" },
{ kPanelDivId, kDeleteBtnId, "deleteBtnId" },
{ kPanelDivId, kHalfPedalPedalVel, "halfPedalPedalVelId" },
{ kPanelDivId, kHalfPedalDelayMs, "halfPedalDelayMsId" },
{ kPanelDivId, kHalfPedalPitch, "halfPedalPitchId" },
{ kPanelDivId, kHalfPedalVel, "halfPedalVelId" },
{ kPanelDivId, kHalfPedalDurMs, "halfPedalDurMsId" },
{ kPanelDivId, kHalfPedalDnDelayMs, "halfPedalDnDelayMsId" },
{ kPanelDivId, kStatusId, "statusId" },
{ kPanelDivId, kLogId, "logId" },
@ -214,6 +230,15 @@ namespace cw
bool printMidiFl;
unsigned hpDelayMs;
unsigned hpPedalVel;
unsigned hpPitch;
unsigned hpVel;
unsigned hpDurMs;
unsigned hpDnDelayMs;
} app_t;
rc_t _parseCfg(app_t* app, const object_t* cfg, const object_t*& params_cfgRef )
@ -362,13 +387,20 @@ namespace cw
void _midi_play_callback( void* arg, unsigned id, const time::spec_t timestamp, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 )
{
app_t* app = (app_t*)arg;
if( id != kInvalidId )
{
if( app->printMidiFl )
{
const unsigned buf_byte_cnt = 256;
char buf[ buf_byte_cnt ];
event_to_string( app->scoreH, id, buf, buf_byte_cnt );
// if this event is not in the score
if( id == kInvalidId )
{
// TODO: print this out in the same format as event_to_string()
snprintf(buf,buf_byte_cnt,"ch:%i status:0x%02x d0:%i d1:%i",ch,status,d0,d1);
}
else
score::event_to_string( app->scoreH, id, buf, buf_byte_cnt );
printf("%s\n",buf);
}
@ -381,9 +413,6 @@ namespace cw
if( f != nullptr )
_do_select_frag( app, f->guiUuId );
}
}
}
loc_map_t* _find_loc( app_t* app, unsigned loc )
@ -424,6 +453,9 @@ namespace cw
goto errLabel;
}
midi_record_play::half_pedal_params( app->mrpH, app->hpDelayMs, app->hpPitch, app->hpVel, app->hpPedalVel, app->hpDurMs, app->hpDnDelayMs );
if((begMap = _find_loc(app,begLoc)) == nullptr )
{
rc = cwLogError(kInvalidArgRC,"The begin play location is not valid.");
@ -971,6 +1003,7 @@ namespace cw
m[i].d1 = e->d1;
m[i].id = e->uid;
app->locMap[i].loc = e->loc;
app->locMap[i].timestamp = m[i].timestamp;
@ -987,6 +1020,8 @@ namespace cw
goto errLabel;
}
cwLogInfo("%i MIDI events loaded.", midiEventN );
mem::free(m);
}
@ -1031,7 +1066,6 @@ namespace cw
return rc;
}
rc_t _on_ui_start( app_t* app )
{
return _do_play(app, app->beg_play_loc, app->end_play_loc );
@ -1257,6 +1291,77 @@ namespace cw
return rc;
}
rc_t _on_ui_half_pedal_value( app_t* app, unsigned appId, unsigned uuId, unsigned value )
{
switch( appId )
{
case kHalfPedalDelayMs:
app->hpDelayMs = value;
break;
case kHalfPedalPedalVel:
app->hpPedalVel = value;
break;
case kHalfPedalPitch:
app->hpPitch = value;
break;
case kHalfPedalVel:
app->hpVel = value;
break;
case kHalfPedalDurMs:
app->hpDurMs = value;
break;
case kHalfPedalDnDelayMs:
app->hpDnDelayMs = value;
break;
default:
{ assert(0); }
}
return kOkRC;
}
rc_t _on_echo_half_pedal( app_t* app, unsigned appId, unsigned uuId )
{
switch( appId )
{
case kHalfPedalDelayMs:
io::uiSendValue( app->ioH, uuId, app->hpDelayMs );
break;
case kHalfPedalPedalVel:
io::uiSendValue( app->ioH, uuId, app->hpPedalVel );
break;
case kHalfPedalPitch:
io::uiSendValue( app->ioH, uuId, app->hpPitch );
break;
case kHalfPedalVel:
io::uiSendValue( app->ioH, uuId, app->hpVel );
break;
case kHalfPedalDurMs:
io::uiSendValue( app->ioH, uuId, app->hpDurMs );
break;
case kHalfPedalDnDelayMs:
io::uiSendValue( app->ioH, uuId, app->hpDnDelayMs );
break;
default:
{ assert(0); }
}
return kOkRC;
}
rc_t _onUiInit(app_t* app, const io::ui_msg_t& m )
{
rc_t rc = kOkRC;
@ -1299,7 +1404,8 @@ namespace cw
//preset_sel::report( app->psH );
//io_flow::apply_preset( app->ioFlowH, 2000.0, app->tmp==0 ? "a" : "b");
//app->tmp = !app->tmp;
io_flow::print(app->ioFlowH);
//io_flow::print(app->ioFlowH);
midi_record_play::save_csv(app->mrpH,"/home/kevin/temp/mrp_1.csv");
break;
case kSaveBtnId:
@ -1371,6 +1477,15 @@ namespace cw
_on_ui_delete_btn(app);
break;
case kHalfPedalPedalVel:
case kHalfPedalDelayMs:
case kHalfPedalPitch:
case kHalfPedalVel:
case kHalfPedalDurMs:
case kHalfPedalDnDelayMs:
_on_ui_half_pedal_value( app, m.appId, m.uuId, m.value->u.u );
break;
case kFragInGainId:
_on_ui_frag_value( app, m.uuId, m.value->u.d);
break;
@ -1496,6 +1611,15 @@ namespace cw
_on_echo_master_value( app, preset_sel::kMasterSyncDelayMsVarId, m.uuId );
break;
case kHalfPedalPedalVel:
case kHalfPedalDelayMs:
case kHalfPedalPitch:
case kHalfPedalVel:
case kHalfPedalDurMs:
case kHalfPedalDnDelayMs:
_on_echo_half_pedal( app, m.appId, m.uuId );
break;
}
return rc;
@ -1614,8 +1738,9 @@ namespace cw
cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_proc_dict )
{
rc_t rc;
app_t app = { };
app_t app = { .hpDelayMs=250, .hpPedalVel=127, .hpPitch=64, .hpVel=64, .hpDurMs=500, .hpDnDelayMs=1000 };
const object_t* params_cfg = nullptr;
// Parse the configuration
@ -1662,6 +1787,7 @@ cw::rc_t cw::preset_sel_app::main( const object_t* cfg, const object_t* flow_pro
goto errLabel;
}
// execute the io framework
while( !isShuttingDown(app.ioH))
{

View File

@ -49,6 +49,15 @@
button:{ name: deleteBtnId, title:"Delete", enable: false },
},
row: {
number:{ name: halfPedalDelayMsId, title:"DelayMs:", min:0, max:5000, step:1, decpl:0 },
number:{ name: halfPedalPedalVelId, title:"PVel:", min:0, max:127, step:1, decpl:0 },
number:{ name: halfPedalPitchId, title:"Pitch:", min:0, max:127, step:1, decpl:0 },
number:{ name: halfPedalVelId, title:"Vel:", min:0, max:127, step:1, decpl:0 },
number:{ name: halfPedalDurMsId, title:"DurMs:", min:0, max:5000, step:1, decpl:0 },
number:{ name: halfPedalDnDelayMsId, title:"DownMs:", min:0, max:5000, step:1, decpl:0 },
},
row: {
str_disp:{ name: statusId, title:"Status:", value: "" },
}