cwIoMidiRecordPlay.h/cpp : Added load() and seek() and MIDI event callback function

to support cwIoPresetSelApp.
This commit is contained in:
kevin 2021-11-01 10:13:48 -04:00
parent 421625d62b
commit 9bdc4fc6f1
2 changed files with 187 additions and 27 deletions

View File

@ -22,14 +22,15 @@ namespace cw
{ {
unsigned devIdx; unsigned devIdx;
unsigned portIdx; unsigned portIdx;
unsigned microsec;
unsigned id;
time::spec_t timestamp; time::spec_t timestamp;
uint8_t ch; uint8_t ch;
uint8_t status; uint8_t status;
uint8_t d0; uint8_t d0;
uint8_t d1; uint8_t d1;
unsigned microsec;
} am_midi_msg_t; } am_midi_msg_t;
typedef struct midi_record_play_str typedef struct midi_record_play_str
@ -56,6 +57,9 @@ namespace cw
bool pedalFl; bool pedalFl;
event_callback_t cb;
void* cb_arg;
} midi_record_play_t; } midi_record_play_t;
enum enum
@ -101,6 +105,36 @@ namespace cw
return rc; return rc;
} }
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 )
{
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, status + ch, d0, d1 );
if( p->cb )
p->cb( p->cb_arg, id, timestamp, ch, status, d0, d1 );
return kOkRC;
}
rc_t _transmit_msg( midi_record_play_t* p, const am_midi_msg_t* am )
{
return _event_callback( p, am->id, am->timestamp, am->ch, am->status, am->d0, am->d1 );
}
rc_t _transmit_ctl( midi_record_play_t* p, unsigned ch, unsigned ctlId, unsigned ctlVal )
{
time::spec_t ts = {0};
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 )
{
return _transmit_ctl( p, ch, pedalCtlId, pedalDownFl ? 127 : 0);
}
void _set_midi_msg_next_index( midi_record_play_t* p, unsigned next_idx ) void _set_midi_msg_next_index( midi_record_play_t* p, unsigned next_idx )
{ {
p->msgArrayInIdx = next_idx; p->msgArrayInIdx = next_idx;
@ -353,11 +387,13 @@ namespace cw
// TODO: should work for all channels // TODO: should work for all channels
// all notes off // all notes off
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, midi::kCtlMdId, 123, 0 ); _transmit_ctl( p, 0, 121, 0 ); // reset all controllers
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, midi::kCtlMdId, midi::kSustainCtlMdId, 0 ); _transmit_ctl( p, 0, 123, 0 ); // all notes off
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, midi::kCtlMdId, midi::kSostenutoCtlMdId, 0 ); _transmit_ctl( p, 0, 0, 0 ); // switch to bank 0
// soft pedal
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, midi::kCtlMdId, 67, 0 ); // send pgm change 0
time::spec_t ts = {0};
_event_callback( p, kInvalidId, ts, 0, midi::kPgmMdId, 0, 0 );
p->pedalFl = false; p->pedalFl = false;
@ -368,7 +404,7 @@ namespace cw
return rc; return rc;
} }
rc_t _midi_callback( midi_record_play_t* p, const io::midi_msg_t& m ) rc_t _midi_receive( midi_record_play_t* p, const io::midi_msg_t& m )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const midi::packet_t* pkt = m.pkt; const midi::packet_t* pkt = m.pkt;
@ -376,14 +412,16 @@ namespace cw
// for each midi msg // for each midi msg
for(unsigned j=0; j<pkt->msgCnt; ++j) for(unsigned j=0; j<pkt->msgCnt; ++j)
{ {
// if this is a sys-ex msg // if this is a sys-ex msg
if( pkt->msgArray == NULL ) if( pkt->msgArray == NULL )
{ {
// this is a sys ex msg use: pkt->sysExMsg[j]
} }
else // this is a triple else // this is a triple
{ {
//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 );
if( p->recordFl && p->startedFl ) if( p->recordFl && p->startedFl )
{ {
@ -403,6 +441,7 @@ namespace cw
if( midi::isChStatus(mm->status) ) if( midi::isChStatus(mm->status) )
{ {
am->id = p->msgArrayInIdx;
am->devIdx = pkt->devIdx; am->devIdx = pkt->devIdx;
am->portIdx = pkt->portIdx; am->portIdx = pkt->portIdx;
am->timestamp = mm->timeStamp; am->timestamp = mm->timeStamp;
@ -416,8 +455,10 @@ namespace cw
p->msgArrayInIdx += 1; p->msgArrayInIdx += 1;
if( p->thruFl ) if( p->thruFl )
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, am->status + am->ch, am->d0, am->d1 ); {
_transmit_msg( p, am );
//io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, am->status + am->ch, am->d0, am->d1 );
}
// send msg count // send msg count
//io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->msgArrayInIdx ); //io::uiSendValue( p->ioH, kInvalidId, uiFindElementUuId(p->ioH,kMsgCntId), p->msgArrayInIdx );
@ -441,7 +482,6 @@ namespace cw
return rc; return rc;
} }
rc_t _timer_callback(midi_record_play_t* p, io::timer_msg_t& m) rc_t _timer_callback(midi_record_play_t* p, io::timer_msg_t& m)
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -463,6 +503,8 @@ namespace cw
bool skipFl = false; 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( mm->status == midi::kCtlMdId && (mm->d0 == midi::kSustainCtlMdId || mm->d0 == midi::kSostenutoCtlMdId || mm->d0 == midi::kSoftPedalCtlMdId ) )
{ {
// if the pedal is down // if the pedal is down
@ -477,10 +519,12 @@ namespace cw
p->pedalFl = true; p->pedalFl = true;
} }
} }
*/
if( !skipFl ) if( !skipFl )
io::midiDeviceSend( p->ioH, p->midiOutDevIdx, p->midiOutPortIdx, mm->status + mm->ch, mm->d0, mm->d1 ); {
_transmit_msg( p, mm );
}
_set_midi_msg_next_play_index(p, p->msgArrayOutIdx+1 ); _set_midi_msg_next_play_index(p, p->msgArrayOutIdx+1 );
// if all MIDI messages have been played // if all MIDI messages have been played
@ -498,7 +542,7 @@ namespace cw
} }
} }
cw::rc_t cw::midi_record_play::create( handle_t& hRef, io::handle_t ioH, const object_t& cfg ) cw::rc_t cw::midi_record_play::create( handle_t& hRef, io::handle_t ioH, const object_t& cfg, event_callback_t cb, void* cb_arg )
{ {
midi_record_play_t* p = nullptr; midi_record_play_t* p = nullptr;
rc_t rc; rc_t rc;
@ -512,6 +556,8 @@ cw::rc_t cw::midi_record_play::create( handle_t& hRef, io::handle_t ioH, const o
goto errLabel; goto errLabel;
p->ioH = ioH; p->ioH = ioH;
p->cb = cb;
p->cb_arg = cb_arg;
if((p->midiOutDevIdx = io::midiDeviceIndex(p->ioH,p->midiOutDevLabel)) == kInvalidIdx ) if((p->midiOutDevIdx = io::midiDeviceIndex(p->ioH,p->midiOutDevLabel)) == kInvalidIdx )
{ {
@ -558,7 +604,7 @@ cw::rc_t cw::midi_record_play::destroy( handle_t& hRef )
return rc; return rc;
} }
cw::rc_t cw::midi_record_play::start( handle_t h ) cw::rc_t cw::midi_record_play::start( handle_t h, bool rewindFl )
{ {
midi_record_play_t* p = _handleToPtr(h); midi_record_play_t* p = _handleToPtr(h);
p->startedFl = true; p->startedFl = true;
@ -572,9 +618,18 @@ cw::rc_t cw::midi_record_play::start( handle_t h )
} }
else else
{ {
_set_midi_msg_next_play_index(p,0);
io::timerStart( p->ioH, io::timerIdToIndex(p->ioH, kMidiRecordPlayTimerId) );
time::get(p->play_time); time::get(p->play_time);
if( rewindFl )
_set_midi_msg_next_play_index(p,0);
else
{
// 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 );
}
io::timerStart( p->ioH, io::timerIdToIndex(p->ioH, kMidiRecordPlayTimerId) );
} }
return kOkRC; return kOkRC;
@ -641,6 +696,92 @@ cw::rc_t cw::midi_record_play::open( handle_t h, const char* fn )
return _midi_read(p,fn); return _midi_read(p,fn);
} }
cw::rc_t cw::midi_record_play::load( handle_t h, const midi_msg_t* msg, unsigned msg_count )
{
rc_t rc = kOkRC;
midi_record_play_t* p = _handleToPtr(h);
if( msg_count > p->msgArrayN )
{
mem::release(p->msgArray);
p->msgArray = mem::allocZ<am_midi_msg_t>( msg_count );
p->msgArrayN = msg_count;
}
for(unsigned i=0; i<msg_count; ++i)
{
p->msgArray[i].id = msg[i].id;
p->msgArray[i].timestamp = msg[i].timestamp;
p->msgArray[i].ch = msg[i].ch;
p->msgArray[i].status = msg[i].status;
p->msgArray[i].d0 = msg[i].d0;
p->msgArray[i].d1 = msg[i].d1;
p->msgArray[i].devIdx = p->midiOutDevIdx;
p->msgArray[i].portIdx = p->midiOutPortIdx;
p->msgArray[i].microsec = time::elapsedMicros(p->msgArray[0].timestamp,p->msgArray[i].timestamp);
}
p->msgArrayInIdx = msg_count;
p->msgArrayOutIdx = 0;
return rc;
}
cw::rc_t cw::midi_record_play::seek( handle_t h, time::spec_t seek_timestamp )
{
rc_t rc = kOkRC;
bool damp_down_fl = false; // TODO: track pedals on all channels
bool sost_down_fl = false;
bool soft_down_fl = false;
midi_record_play_t* p = _handleToPtr(h);
for(unsigned i=0; i<p->msgArrayInIdx; ++i)
{
am_midi_msg_t* mm = p->msgArray + i;
if( time::isLTE(seek_timestamp,mm->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 );
//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;
}
if( mm->status == midi::kCtlMdId )
{
switch( mm->d0 )
{
case midi::kSustainCtlMdId:
damp_down_fl = mm->d1 > 64;
break;
case midi::kSostenutoCtlMdId:
sost_down_fl = mm->d1 > 64;
break;
case midi::kSoftPedalCtlMdId:
soft_down_fl = mm->d1 > 64;
break;
default:
break;
}
}
}
return rc;
}
unsigned cw::midi_record_play::event_count( handle_t h ) unsigned cw::midi_record_play::event_count( handle_t h )
{ {
midi_record_play_t* p = _handleToPtr(h); midi_record_play_t* p = _handleToPtr(h);
@ -668,7 +809,7 @@ cw::rc_t cw::midi_record_play::exec( handle_t h, const io::msg_t& m )
case io::kMidiTId: case io::kMidiTId:
if( m.u.midi != nullptr ) if( m.u.midi != nullptr )
_midi_callback(p,*m.u.midi); _midi_receive(p,*m.u.midi);
break; break;
default: default:

View File

@ -8,10 +8,25 @@ namespace cw
{ {
typedef handle< struct midi_record_play_str > handle_t; typedef handle< struct midi_record_play_str > handle_t;
rc_t create( handle_t& hRef, io::handle_t ioH, const object_t& cfg ); typedef struct midi_msg_str
{
unsigned id;
time::spec_t timestamp;
uint8_t ch;
uint8_t status;
uint8_t d0;
uint8_t d1;
} midi_msg_t;
typedef void (*event_callback_t)( void* arg, unsigned id, const time::spec_t timestamp, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
rc_t create( handle_t& hRef, io::handle_t ioH, const object_t& cfg, event_callback_t cb=nullptr, void* cb_arg=nullptr );
rc_t destroy( handle_t& hRef ); rc_t destroy( handle_t& hRef );
rc_t start( handle_t h ); // Set rewindFl to play from start, otherwise play from current output location.
rc_t start( handle_t h, bool rewindFl=true );
rc_t stop( handle_t h ); rc_t stop( handle_t h );
bool is_started( handle_t h ); bool is_started( handle_t h );
@ -25,11 +40,15 @@ namespace cw
rc_t save( handle_t h, const char* fn ); rc_t save( handle_t h, const char* fn );
rc_t open( handle_t h, const char* fn ); rc_t open( handle_t h, const char* fn );
rc_t load( handle_t h, const midi_msg_t* msg, unsigned msg_count );
rc_t seek( handle_t h, time::spec_t timestamp );
unsigned event_count( handle_t h ); unsigned event_count( handle_t h );
unsigned event_index( handle_t h ); unsigned event_index( handle_t h );
rc_t exec( handle_t h, const io::msg_t& msg ); rc_t exec( handle_t h, const io::msg_t& msg );
// 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_file( const char* am_filename, const char* midi_filename );
rc_t am_to_midi_dir( const char* inDir ); rc_t am_to_midi_dir( const char* inDir );
rc_t am_to_midi_file( const object_t* cfg ); rc_t am_to_midi_file( const object_t* cfg );