diff --git a/cwSvgMidi.cpp b/cwSvgMidi.cpp index 069c522..9a68c7e 100644 --- a/cwSvgMidi.cpp +++ b/cwSvgMidi.cpp @@ -3,6 +3,7 @@ #include "cwCommonImpl.h" #include "cwMem.h" #include "cwObject.h" +#include "cwText.h" #include "cwTime.h" #include "cwMidi.h" #include "cwMidiFile.h" @@ -11,19 +12,21 @@ #include "cwMidiState.h" #include "cwSvgMidi.h" -#define PEDAL_COUNT 3 -#define SUST_PEDAL_IDX 0 -#define SOST_PEDAL_IDX 1 -#define SOFT_PEDAL_IDX 2 - #define PIX_PER_SEC 100.0 #define NOTE_HEIGHT 15.0 +#define TIME_GRID_SECS 5.0 +#define PITCH_LABEL_INTERVAL_SECS 10 namespace cw { namespace svg_midi { + enum { + kBarTypeId, + kSectionTypeId + }; + rc_t _write_svg_rect( svg::handle_t svgH, double secs0, double secs1, double y, const char* label, unsigned color ) { rc_t rc; @@ -76,9 +79,24 @@ namespace cw errLabel: return rc; } + + rc_t _write_svg_line( svg::handle_t svgH, double sec0, double y0, double sec1, double y1, unsigned color ) + { + rc_t rc = kOkRC; + + if((rc = line(svgH, sec0*PIX_PER_SEC, y0, sec1*PIX_PER_SEC, y1, "stroke", color, "rgb")) != kOkRC ) + { + rc = cwLogError(rc,"Error writing SVG line."); + goto errLabel; + + } + + errLabel: + return rc; + } - void _write_note_rect( svg::handle_t svgH, const midi_state::event_t* e0, const midi_state::event_t* e1, unsigned minMidiPitch, unsigned maxMidiPitch ) + const midi_state::event_t* _write_note_rect( svg::handle_t svgH, const midi_state::event_t* e0, const midi_state::event_t* e1, const midi_state::event_t* t0, unsigned minMidiPitch, unsigned maxMidiPitch ) { cwAssert( e0!=nullptr && e1!=nullptr && e0->msg != nullptr && e1->msg !=nullptr); @@ -88,17 +106,23 @@ namespace cw unsigned muid = e0->msg->u.midi.uid; - snprintf(label,labelCharN,"%s - %i",sciPitch,muid); + if( t0!=nullptr && e0->secs - t0->secs < PITCH_LABEL_INTERVAL_SECS ) + snprintf(label,labelCharN,"%i",muid); + else + { + snprintf(label,labelCharN,"%s - %i",sciPitch,muid); + t0 = e1; + } double y = -1.0 * (e0->msg->u.midi.d0 - minMidiPitch) + (maxMidiPitch - minMidiPitch); _write_svg_rect( svgH, e0->secs, e1->secs, y, label, 0xafafaf ); - + + return t0; } void _write_sound_line( svg::handle_t svgH, const midi_state::event_t* e0, const midi_state::event_t* e1, unsigned minMidiPitch, unsigned maxMidiPitch ) { - cwAssert( e0!=nullptr && e1!=nullptr && e0->msg != nullptr && e1->msg !=nullptr); double y = -1.0 * (e0->msg->u.midi.d0 - minMidiPitch) + (maxMidiPitch - minMidiPitch); @@ -106,15 +130,32 @@ namespace cw _write_svg_horz_line( svgH, e0->secs, e1->secs, y, 0xafafaf ); } + void _write_marker( svg::handle_t svgH, const midi_state::event_t* e, unsigned minMidiPitch, unsigned maxMidiPitch ) + { + unsigned color = e->msg->u.marker.typeId == kBarTypeId ? 0x0000ff : 0xff0000; + _write_svg_vert_line( svgH, e->secs, color, minMidiPitch, maxMidiPitch ); + unsigned labelCharN = 127; + char label[labelCharN+1]; + snprintf(label,labelCharN,"%i",e->msg->u.marker.value); + + svg::text( svgH, e->secs*PIX_PER_SEC, -20, label ); + + } void _write_svg_ch_note( svg::handle_t svgH, const midi_state::event_t* e0, unsigned minMidiPitch, unsigned maxMidiPitch ) { const midi_state::event_t* e = e0; const midi_state::event_t* n0 = nullptr; const midi_state::event_t* s0 = nullptr; + const midi_state::event_t* t0 = nullptr; for(; e!=nullptr; e=e->link) { + if( cwIsFlag(e->flags,midi_state::kMarkerEvtFl) ) + { + _write_marker( svgH, e, minMidiPitch, maxMidiPitch ); + } + if( cwIsFlag(e->flags,midi_state::kNoteOffFl) ) { if( n0 == nullptr ) @@ -123,7 +164,7 @@ namespace cw } else { - _write_note_rect( svgH, n0, e, minMidiPitch, maxMidiPitch ); + t0 = _write_note_rect( svgH, n0, e, t0, minMidiPitch, maxMidiPitch ); } n0 = nullptr; @@ -135,7 +176,7 @@ namespace cw if( n0 != nullptr ) { // TODO: check for reattack flag - _write_note_rect( svgH, n0, e, minMidiPitch, maxMidiPitch ); + t0 = _write_note_rect( svgH, n0, e, t0, minMidiPitch, maxMidiPitch ); } n0 = e; @@ -166,71 +207,18 @@ namespace cw s0 = nullptr; } - } - + } } - /* - rc_t _write_svg_pedal( svg_midi_t* p, svg::handle_t svgH, const graphic_evt_t* ge, unsigned minMidiPitch, unsigned maxMidiPitch ) + void _write_svg_ch_pedal( svg::handle_t svgH, const midi_state::event_t* e, unsigned pedal_idx, unsigned minMidiPitch, unsigned maxMidiPitch, unsigned pedalCnt ) { - rc_t rc = kOkRC; - const char* label = nullptr; - unsigned pedal_id = 0; - unsigned color; + const midi_state::event_t* e0 = nullptr; + const midi_state::event_t* e1 = nullptr; + unsigned color = 0; + const char* label = nullptr; + unsigned midiCtlId = midi_state::pedal_index_to_midi_ctl_id(pedal_idx); - switch( ge->beg_evt->d0 & 0xf0 ) - { - case midi::kSustainCtlMdId: - label = "damp"; - pedal_id = 0; - color = 0xf4a460; - break; - - case midi::kSostenutoCtlMdId: - label = "sost"; - pedal_id = 1; - color = 0x7fffd4; - break; - - case midi::kSoftPedalCtlMdId: - label = "soft"; - pedal_id = 2; - color = 0x98fb98; - break; - } - - - double y = (maxMidiPitch - minMidiPitch) + 1 + pedal_id; - - if((rc = _write_svg_rect( p, svgH, ge, y, label, color )) != kOkRC ) - { - rc = cwLogError(rc,"Error writing SVG pedal rect."); - goto errLabel; - } - - if((rc = _write_svg_line( p, svgH, ge->beg_evt->time, color, minMidiPitch, maxMidiPitch )) != kOkRC ) - { - rc = cwLogError(rc,"Error writing SVG pedal begin line."); - goto errLabel; - } - - if((rc = _write_svg_line( p, svgH, ge->end_evt->time, color, minMidiPitch, maxMidiPitch )) != kOkRC ) - { - rc = cwLogError(rc,"Error writing SVG pedal end line."); - goto errLabel; - } - - errLabel: - return rc; - } - */ - - void _write_svg_ch_pedal( svg::handle_t svgH, const midi_state::event_t* e, unsigned pedal_idx, unsigned minMidiPitch, unsigned maxMidiPitch ) - { - const midi_state::event_t* e0 = nullptr; - unsigned color = 0; - const char* label = nullptr; - switch( midi_state::pedal_index_to_midi_ctl_id(pedal_idx) ) + switch( midiCtlId ) { case midi::kSustainCtlMdId: label = "damp"; @@ -252,61 +240,133 @@ namespace cw for(; e!=nullptr; e=e->link) { - if( cwIsFlag(e->flags,midi_state::kNoChangeFl) ) - continue; - if( cwIsFlag(e->flags,midi_state::kDownPedalFl) ) - { - if( e0 != nullptr ) + if( !cwIsFlag(e->flags,midi_state::kNoChangeFl) ) + { + if( cwIsFlag(e->flags,midi_state::kDownPedalFl) ) { - // two consecutive pedal downd - this shouldn't be possible + if( e0 != nullptr ) + { + // two consecutive pedal downd - this shouldn't be possible + } + else + { + e0 = e; + } } - else + + if( cwIsFlag(e->flags,midi_state::kUpPedalFl)) { - e0 = e; + if( e0 == nullptr ) + { + // two consecutive pedal up's + } + else + { + + double y = (maxMidiPitch - minMidiPitch) + 1 + pedal_idx; + + _write_svg_rect( svgH, e0->secs, e->secs, y, label, color ); + + if( midiCtlId == midi::kSustainCtlMdId ) + _write_svg_vert_line( svgH, e->secs, color, minMidiPitch, maxMidiPitch ); + + e0 = nullptr; + } } } - if( cwIsFlag(e->flags,midi_state::kUpPedalFl)) + if( e1 != nullptr ) { - if( e0 == nullptr ) - { - // two consecutive pedal up's - } - else - { - - // two consecutve pedal ups - this shouldn't be possible - double y = (maxMidiPitch - minMidiPitch) + 1 + pedal_idx; + unsigned yOffs = ((maxMidiPitch - minMidiPitch) + pedalCnt) * NOTE_HEIGHT; + _write_svg_line( svgH, e1->secs, yOffs + e1->msg->u.midi.d1, e->secs, yOffs + e->msg->u.midi.d1, color ); + } + e1 = e; + + + } + } - _write_svg_rect( svgH, e0->secs, e->secs, y, label, color ); - e0 = nullptr; + rc_t _load_from_piano_score( midi_state::handle_t msH, const char* piano_score_fname ) + { + rc_t rc = kOkRC; + score::handle_t scH; + unsigned n = 0; + + if((rc = score::create( scH, piano_score_fname )) != kOkRC ) + { + rc = cwLogError(rc,"The piano score load failed."); + goto errLabel; + } + else + { + const score::event_t* e = base_event(scH); + + for(; e!=nullptr; e=e->link) + { + uint8_t ch = 0; + + if( e->bar != 0 ) + if((rc = setMarker(msH, e->sec, e->uid, ch, kBarTypeId, e->bar )) != kOkRC ) + { + rc = cwLogError(rc,"Error setting bar marker."); + goto errLabel; + } + + if( e->section != 0 ) + if((rc = setMarker(msH, e->sec, e->uid, ch, kSectionTypeId, e->section )) != kOkRC ) + { + rc = cwLogError(rc,"Error setting section marker."); + goto errLabel; + } + + if( e->status != 0 ) + { + if( e->status < 255 && e->d0 < 128 && e->d1 < 128 ) + { + uint8_t status = (uint8_t)e->status & 0xf0; + uint8_t ch = (uint8_t)e->status & 0x0f; + uint8_t d0 = (uint8_t)e->d0; + uint8_t d1 = (uint8_t)e->d1; + + //printf("%i : %i %i : %i %x %i %i\n", n, e->uid, e->loc, ch, status, d0, d1 ); + + if((rc = setMidiMsg(msH, e->sec, e->uid, ch, status, d0, d1 ) ) != kOkRC ) + { + rc = cwLogError(rc,"Error on MIDI event insertion."); + goto errLabel; + } + ++n; + } } } } + + errLabel: + destroy(scH); + + return rc; } - - } } - - - cw::rc_t cw::svg_midi::write( const char* fname, midi_state::handle_t msH ) { - rc_t rc = kOkRC; - uint8_t minMidiPitch = midi::kMidiNoteCnt; - uint8_t maxMidiPitch = 0; - const midi_state::event_t* evt = nullptr; - - double minSec = 0.0; - double maxSec = 0.0; + rc_t rc = kOkRC; + uint8_t minMidiPitch = midi::kMidiNoteCnt; + uint8_t maxMidiPitch = 0; + unsigned pedal_cnt = midi_state::pedal_count( msH ); + double minSec = 0.0; + double maxSec = 0.0; - svg::handle_t svgH; + const midi_state::event_t* evt = nullptr; + svg::handle_t svgH; + get_note_extents( msH, minMidiPitch, maxMidiPitch, minSec, maxSec ); + printf("pitch - min:%i max:%i sec - min:%f max:%f\n",minMidiPitch,maxMidiPitch,minSec,maxSec); + // create the SVG file object if((rc = svg::create(svgH)) != kOkRC ) { @@ -314,6 +374,10 @@ cw::rc_t cw::svg_midi::write( const char* fname, midi_state::handle_t msH ) goto errLabel; } + // create the time grid + for(double sec = 0.0; sec<=maxSec; sec+=TIME_GRID_SECS) + _write_svg_vert_line(svgH, sec, 0xefefef, minMidiPitch, maxMidiPitch ); + // create the note graphics for(uint8_t i=0; isec); - - if( e->bar != 0 ) - _setEvent(p, kBarTypeId, e->bar, timestamp, i, 0,0,0 ); - - if( e->section != 0 ) - _setEvent(p, kSectionTypeId, e->section, timestamp, i, 0,0,0 ); - - if( e->status != 0 ) - _setEvent(p, kMidiTypeId, 0, timestamp, i, 0,0,0 ); - } - } - - errLabel: - destroy(scH); - - return rc; -} -*/ - cw::rc_t cw::svg_midi::midi_to_svg_file( const char* midi_fname, const char* out_fname, const object_t* midi_state_args ) { rc_t rc = kOkRC; @@ -389,7 +410,7 @@ cw::rc_t cw::svg_midi::midi_to_svg_file( const char* midi_fname, const char* out // create the MIDI state object - with caching turned on - if((rc = midi_state::create( msH, nullptr, nullptr, true, midi_state_args )) != kOkRC ) + if((rc = midi_state::create( msH, nullptr, nullptr, midi_state_args )) != kOkRC ) { rc = cwLogError(rc,"Error creating the midi_state object."); goto errLabel; @@ -413,56 +434,75 @@ cw::rc_t cw::svg_midi::midi_to_svg_file( const char* midi_fname, const char* out destroy(msH); return rc; } -/* -cw::rc_t cw::svg_midi::piano_score_to_svg_file( const char* piano_score_fname, const char* out_fname, unsigned midiMsgCacheCnt, unsigned pedalUpMidiValue ) -{ - rc_t rc = kOkRC; - handle_t h; - // create the SVG-MIDI object - if((rc = create(h,midiMsgCacheCnt,pedalUpMidiValue)) != kOkRC ) +cw::rc_t cw::svg_midi::piano_score_to_svg_file( const char* piano_score_fname, const char* out_fname, const object_t* midi_state_args ) +{ + rc_t rc = kOkRC; + midi_state::handle_t msH; + + + // create the MIDI state object - with caching turned on + if((rc = midi_state::create( msH, nullptr, nullptr, midi_state_args )) != kOkRC ) { - rc = cwLogError(rc,"Error creating the SVG-MIDI object."); + rc = cwLogError(rc,"Error creating the midi_state object."); goto errLabel; } - // load the MIDI file msg events into the svg-midi cache - if((rc = load_from_piano_score(h, piano_score_fname)) != kOkRC ) + // load the MIDI file + if((rc = _load_from_piano_score( msH, piano_score_fname)) != kOkRC ) { - rc = cwLogError(rc,"Error loading the piano score file."); + rc = cwLogError(rc,"Error loading midi file into midi_state object."); goto errLabel; } // write the SVG file - if((rc = write(h,out_fname)) != kOkRC ) + if((rc = write(out_fname,msH)) != kOkRC ) { rc = cwLogError(rc,"Error write the SVG-MIDI file."); goto errLabel; } errLabel: - destroy(h); - + destroy(msH); return rc; } -*/ + cw::rc_t cw::svg_midi::test_midi_file( const object_t* cfg ) { rc_t rc; - const char* midi_fname = nullptr; + const char* src_file_type = nullptr; + const char* src_fname = nullptr; const char* out_fname = nullptr; const object_t* midi_state_args = nullptr; - if((rc = cfg->getv( "midi_fname", midi_fname, - "out_fname", out_fname, - "midi_state_args",midi_state_args)) != kOkRC ) + if((rc = cfg->getv( + "src_file_type", src_file_type, + "src_fname", src_fname, + "out_fname", out_fname, + "midi_state_args",midi_state_args)) != kOkRC ) { rc = cwLogError(rc,"Error parsing svg_midi::test_midi_file() arguments."); goto errLabel; } - rc = midi_to_svg_file( midi_fname, out_fname, midi_state_args ); + if( textCompare( src_file_type, "midi" ) == 0 ) + { + rc = midi_to_svg_file( src_fname, out_fname, midi_state_args ); + } + else + if( textCompare( src_file_type, "piano_score" ) == 0 ) + { + rc = piano_score_to_svg_file( src_fname, out_fname, midi_state_args ); + } + else + { + rc = cwLogError(kInvalidArgRC,"Invalid file type:'%s'.",cwStringNullGuard(src_file_type)); + goto errLabel; + } + + if( rc != kOkRC ) + cwLogError(rc,"The SVG file create failed."); errLabel: return rc; diff --git a/cwSvgMidi.h b/cwSvgMidi.h index 2ba1b0a..e4fdae3 100644 --- a/cwSvgMidi.h +++ b/cwSvgMidi.h @@ -7,8 +7,8 @@ namespace cw { rc_t write( const char* fname, midi_state::handle_t msH ); - rc_t midi_to_svg_file( const char* midi_fname, const char* out_fname, const object_t* midi_state_args ); - //rc_t piano_score_to_svg_file( const char* piano_score_fname, const char* out_fname, const object_t* cfg ); + rc_t midi_to_svg_file( const char* midi_fname, const char* out_fname, const object_t* midi_state_args ); + rc_t piano_score_to_svg_file( const char* piano_score_fname, const char* out_fname, const object_t* midi_state_args ); rc_t test_midi_file( const object_t* cfg );