cwMidiState,cwSvgMidi,cwSvgScore, Makefile : Initial commit.
This commit is contained in:
parent
f3803fefd0
commit
a5d0116cd7
@ -67,8 +67,8 @@ libcwSRC += src/libcw/cwIoMidiRecordPlay.cpp src/libcw/cwIoAudioRecordPlay.cpp
|
|||||||
libcwHDR += src/libcw/cwIoPresetSelApp.h src/libcw/cwPianoScore.h src/libcw/cwPresetSel.h src/libcw/cwVelTableTuner.h
|
libcwHDR += src/libcw/cwIoPresetSelApp.h src/libcw/cwPianoScore.h src/libcw/cwPresetSel.h src/libcw/cwVelTableTuner.h
|
||||||
libcwSRC += src/libcw/cwIoPresetSelApp.cpp src/libcw/cwPianoScore.cpp src/libcw/cwPresetSel.cpp src/libcw/cwVelTableTuner.cpp
|
libcwSRC += src/libcw/cwIoPresetSelApp.cpp src/libcw/cwPianoScore.cpp src/libcw/cwPresetSel.cpp src/libcw/cwVelTableTuner.cpp
|
||||||
|
|
||||||
libcwHDR += src/libcw/cwCmInterface.h src/libcw/cwScoreFollower.h
|
libcwHDR += src/libcw/cwCmInterface.h src/libcw/cwScoreFollower.h src/libcw/cwMidiState.h src/libcw/cwSvgMidi.h src/libcw/cwSvgScore.h
|
||||||
libcwSRC += src/libcw/cwCmInterface.cpp src/libcw/cwScoreFollower.cpp
|
libcwSRC += src/libcw/cwCmInterface.cpp src/libcw/cwScoreFollower.cpp src/libcw/cwMidiState.cpp src/libcw/cwSvgMidi.cpp src/libcw/cwSvgScore.cpp
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
1245
cwMidiState.cpp
Normal file
1245
cwMidiState.cpp
Normal file
File diff suppressed because it is too large
Load Diff
167
cwMidiState.h
Normal file
167
cwMidiState.h
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
#ifndef cwMidiState_h
|
||||||
|
#define cwMidiState_h
|
||||||
|
|
||||||
|
namespace cw
|
||||||
|
{
|
||||||
|
namespace midi_state
|
||||||
|
{
|
||||||
|
// This object receives MIDI note and pedal (sustain,sostenuto,soft) messages
|
||||||
|
// and generates voice and pedal control information.
|
||||||
|
// Primarily it maintains the state of the voice associated with each
|
||||||
|
// MIDI channel and pitch and notifies the client of the changes to
|
||||||
|
// that state as various MIDI messages arrive.
|
||||||
|
// The primary features are:
|
||||||
|
//
|
||||||
|
// 1. Maintains note gate and pedal gate information to determine
|
||||||
|
// when a note is sounding. For example a note gate may be off
|
||||||
|
// but the voice may still be sounding due to the state of the
|
||||||
|
// sustain or sostenuto pedal.
|
||||||
|
//
|
||||||
|
// 2. Handles reporting 'half' pedal state on the sustain pedal.
|
||||||
|
//
|
||||||
|
// 3. Reports 're-attacks' when a note gate is on and receives
|
||||||
|
// another note-on messages.
|
||||||
|
//
|
||||||
|
// 4. Report 'no-change' state when a MIDI message is received
|
||||||
|
// but does not change the state of the voice or pedal.
|
||||||
|
// These messages often indicate a problem with the MIDI stream
|
||||||
|
// format.
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// 1. automatically decrease note-on velocity when soft pedal is down
|
||||||
|
//
|
||||||
|
// 2. handle legato and portmento controls
|
||||||
|
//
|
||||||
|
// 3. send a 'decay rate' based on the state of the pedals
|
||||||
|
// when when the note gate goes off (i.e. if the note is being
|
||||||
|
// held decrease the decay rate.
|
||||||
|
//
|
||||||
|
// 4. automatically send note-off / snd-off to all voices that
|
||||||
|
// are sounding on 'reset()'.
|
||||||
|
//
|
||||||
|
// 5. Allow sending arbitrary time markes (e.g. bars and sections)
|
||||||
|
// though this mechanism so that they can be ordered with the
|
||||||
|
// MIDI events
|
||||||
|
|
||||||
|
typedef handle<struct midi_state_str> handle_t;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kNoteOnFl = 0x0001, // note-on
|
||||||
|
kNoteOffFl = 0x0002, // note-off
|
||||||
|
kSoundOnFl = 0x0004, // note-on
|
||||||
|
kSoundOffFl = 0x0008, // note-off,sustain up,sostenuto up
|
||||||
|
kReattackFl = 0x0010, // note-on (when note gate is already on)
|
||||||
|
kSoftPedalFl = 0x0020, // soft pedal up/down (also accompanies note-on msg's when the pedal is down)
|
||||||
|
kUpPedalFl = 0x0040, // soft,sustain,sost pedal up
|
||||||
|
kHalfPedalFl = 0x0080, // sustain pedal entered half pedal range (also sent to all sounding notes)
|
||||||
|
kDownPedalFl = 0x0100, // soft,sustain,sost pedal down
|
||||||
|
kNoChangeFl = 0x0200, // a midi-msg arrived but did not cause a change in note or pedal state
|
||||||
|
kPedalEvtFl = 0x0400, // This is a pedal event
|
||||||
|
kNoteEvtFl = 0x0800, // This is a note event
|
||||||
|
kMarkerEvtFl = 0x1000 // This is a marker event (bar or section)
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct midi_msg_str
|
||||||
|
{
|
||||||
|
unsigned uid; // midi event unique id
|
||||||
|
uint8_t ch; // 0-15
|
||||||
|
uint8_t status; // status w/o channel
|
||||||
|
uint8_t d0; //
|
||||||
|
uint8_t d1; //
|
||||||
|
} midi_msg_t;
|
||||||
|
|
||||||
|
typedef struct marker_msg_str
|
||||||
|
{
|
||||||
|
unsigned uid;
|
||||||
|
unsigned typeId; // marker type id
|
||||||
|
unsigned value; // marker value
|
||||||
|
uint8_t ch;
|
||||||
|
uint8_t pad[3];
|
||||||
|
} marker_msg_t;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kMidiMsgTId,
|
||||||
|
kMarkerMsgTId
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct msg_str
|
||||||
|
{
|
||||||
|
unsigned typeId;
|
||||||
|
union {
|
||||||
|
midi_msg_t midi;
|
||||||
|
marker_msg_t marker;
|
||||||
|
} u;
|
||||||
|
} msg_t;
|
||||||
|
|
||||||
|
// Cached event record
|
||||||
|
typedef struct event_str
|
||||||
|
{
|
||||||
|
unsigned flags; // see flags above
|
||||||
|
double secs; // time of event
|
||||||
|
const msg_t* msg; // accompanying midi info
|
||||||
|
struct event_str* link; // null terminated list of following events
|
||||||
|
struct event_str* tlink; // time order link
|
||||||
|
} event_t;
|
||||||
|
|
||||||
|
const char* flag_to_label( unsigned flag );
|
||||||
|
|
||||||
|
|
||||||
|
// Returned string must be released via mem::release()
|
||||||
|
unsigned flags_to_string_max_string_length();
|
||||||
|
rc_t flags_to_string( unsigned flags, char* str, unsigned strCharCnt );
|
||||||
|
|
||||||
|
// Note that if the cache is not enabled then 'msg' is only valid during the callback and
|
||||||
|
// therefore should not be stored. If the cache is enabled then 'msg' is valid until
|
||||||
|
// the next call to 'reset()' or until the midi_state_t instance is destroyed.
|
||||||
|
typedef void (*callback_t)( void* arg, unsigned flags, double secs, const msg_t* msg );
|
||||||
|
|
||||||
|
rc_t create( handle_t& hRef,
|
||||||
|
callback_t cbFunc, // set to nullptr to disable callbacks
|
||||||
|
void* cbArg, // callback arg
|
||||||
|
bool cacheEnableFl, // enable/disable event caching
|
||||||
|
unsigned cacheBlockMsgN, // count of cache messages to pre-allocate in each block
|
||||||
|
unsigned pedalHalfMinMidiValue, // sustain half pedal lower value
|
||||||
|
unsigned pedalHalfMaxMidialue, // sustain pedal half pedal upper value
|
||||||
|
unsigned pedalUpMidiValue = 64 );// soft and sostenuto pedal down min value
|
||||||
|
|
||||||
|
rc_t create( handle_t& hRef,
|
||||||
|
callback_t cbFunc, // set to nullptr to disable callbacks
|
||||||
|
void* cbArg, // callback arg
|
||||||
|
bool cacheEnableFl, //
|
||||||
|
const object_t* cfg ); //
|
||||||
|
|
||||||
|
|
||||||
|
rc_t destroy( handle_t& hRef );
|
||||||
|
|
||||||
|
rc_t setMidiMsg( handle_t h, double sec, unsigned uid, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 );
|
||||||
|
rc_t setMarker( handle_t h, double sec, unsigned uid, uint8_t ch, unsigned typeId, unsigned value );
|
||||||
|
|
||||||
|
void reset( handle_t h );
|
||||||
|
|
||||||
|
// Get the first link in time, then use event.tlink to traverse the
|
||||||
|
// events in time.
|
||||||
|
const event_t* get_first_link( handle_t h );
|
||||||
|
|
||||||
|
// Return cached events.
|
||||||
|
// Note that the cached events are returned as a link list based on each channel note and pedal.
|
||||||
|
// This list is ordered in time and gives the recorded state of the voice/pedal.
|
||||||
|
const event_t* note_event_list( handle_t h, uint8_t ch, uint8_t pitch );
|
||||||
|
const event_t* pedal_event_list( handle_t h, uint8_t ch, unsigned pedal_index );
|
||||||
|
|
||||||
|
// Returns kInvalidIdx if 'midi_ctl_id' is not a valid MIDi pedal control id.
|
||||||
|
unsigned pedal_midi_ctl_id_to_index( unsigned midi_ctl_id );
|
||||||
|
unsigned pedal_index_to_midi_ctl_id( unsigned pedal_idx );
|
||||||
|
unsigned pedal_count( handle_t h );
|
||||||
|
|
||||||
|
void get_note_extents( handle_t h, uint8_t& minPitchRef, uint8_t& maxPitchRef, double& minSecRef, double& maxSecRef );
|
||||||
|
void get_pedal_extents( handle_t h, double& minSecRef, double& maxSecRef );
|
||||||
|
|
||||||
|
|
||||||
|
rc_t load_from_midi_file( handle_t h, const char* midi_fname );
|
||||||
|
|
||||||
|
rc_t test( const object_t* cfg );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
469
cwSvgMidi.cpp
Normal file
469
cwSvgMidi.cpp
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
#include "cwCommon.h"
|
||||||
|
#include "cwLog.h"
|
||||||
|
#include "cwCommonImpl.h"
|
||||||
|
#include "cwMem.h"
|
||||||
|
#include "cwObject.h"
|
||||||
|
#include "cwTime.h"
|
||||||
|
#include "cwMidi.h"
|
||||||
|
#include "cwMidiFile.h"
|
||||||
|
#include "cwPianoScore.h"
|
||||||
|
#include "cwSvg.h"
|
||||||
|
#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
|
||||||
|
|
||||||
|
namespace cw
|
||||||
|
{
|
||||||
|
namespace svg_midi
|
||||||
|
{
|
||||||
|
|
||||||
|
rc_t _write_svg_rect( svg::handle_t svgH, double secs0, double secs1, double y, const char* label, unsigned color )
|
||||||
|
{
|
||||||
|
rc_t rc;
|
||||||
|
double x = secs0 * PIX_PER_SEC;
|
||||||
|
double ww = secs1 * PIX_PER_SEC - x;
|
||||||
|
double hh = NOTE_HEIGHT;
|
||||||
|
|
||||||
|
if((rc = svg::rect( svgH, x, y*NOTE_HEIGHT, ww, hh, "fill", color, "rgb" )) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error creating SVG rect.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = svg::text( svgH, x, y*NOTE_HEIGHT+NOTE_HEIGHT, label )) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error writing SVG rect label.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _write_svg_vert_line( svg::handle_t svgH, double sec, unsigned color, unsigned minMidiPitch, unsigned maxMidiPitch )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
|
||||||
|
if((rc = line(svgH, sec*PIX_PER_SEC, 0, sec*PIX_PER_SEC, (maxMidiPitch-minMidiPitch)*NOTE_HEIGHT, "stroke", color, "rgb")) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error writing SVG line.");
|
||||||
|
goto errLabel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _write_svg_horz_line( svg::handle_t svgH, double sec0, double sec1, double y, unsigned color )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
|
||||||
|
if((rc = line(svgH, sec0*PIX_PER_SEC, y*NOTE_HEIGHT, sec1*PIX_PER_SEC, y*NOTE_HEIGHT, "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 )
|
||||||
|
{
|
||||||
|
cwAssert( e0!=nullptr && e1!=nullptr && e0->msg != nullptr && e1->msg !=nullptr);
|
||||||
|
|
||||||
|
const char* sciPitch = midi::midiToSciPitch( e0->msg->u.midi.d0 );
|
||||||
|
const unsigned labelCharN = 127;
|
||||||
|
char label[ labelCharN+1 ];
|
||||||
|
|
||||||
|
|
||||||
|
unsigned muid = e0->msg->u.midi.uid;
|
||||||
|
snprintf(label,labelCharN,"%s - %i",sciPitch,muid);
|
||||||
|
|
||||||
|
double y = -1.0 * (e0->msg->u.midi.d0 - minMidiPitch) + (maxMidiPitch - minMidiPitch);
|
||||||
|
|
||||||
|
_write_svg_rect( svgH, e0->secs, e1->secs, y, label, 0xafafaf );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
_write_svg_horz_line( svgH, e0->secs, e1->secs, y, 0xafafaf );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
for(; e!=nullptr; e=e->link)
|
||||||
|
{
|
||||||
|
if( cwIsFlag(e->flags,midi_state::kNoteOffFl) )
|
||||||
|
{
|
||||||
|
if( n0 == nullptr )
|
||||||
|
{
|
||||||
|
// consecutive note off msgs
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_write_note_rect( svgH, n0, e, minMidiPitch, maxMidiPitch );
|
||||||
|
}
|
||||||
|
|
||||||
|
n0 = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cwIsFlag(e->flags,midi_state::kNoteOnFl) )
|
||||||
|
{
|
||||||
|
// if note on without note-off
|
||||||
|
if( n0 != nullptr )
|
||||||
|
{
|
||||||
|
// TODO: check for reattack flag
|
||||||
|
_write_note_rect( svgH, n0, e, minMidiPitch, maxMidiPitch );
|
||||||
|
}
|
||||||
|
|
||||||
|
n0 = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( cwIsFlag(e->flags,midi_state::kSoundOnFl) )
|
||||||
|
{
|
||||||
|
if( s0 != nullptr )
|
||||||
|
{
|
||||||
|
// consecutive sound on msgs
|
||||||
|
_write_sound_line( svgH, s0, e, minMidiPitch, maxMidiPitch );
|
||||||
|
}
|
||||||
|
|
||||||
|
s0 = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cwIsFlag(e->flags,midi_state::kSoundOffFl) )
|
||||||
|
{
|
||||||
|
if( s0 == nullptr )
|
||||||
|
{
|
||||||
|
// consecutive off msgs
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_write_sound_line( svgH, s0, e, minMidiPitch, maxMidiPitch );
|
||||||
|
}
|
||||||
|
|
||||||
|
s0 = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
rc_t _write_svg_pedal( svg_midi_t* p, svg::handle_t svgH, const graphic_evt_t* ge, unsigned minMidiPitch, unsigned maxMidiPitch )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
const char* label = nullptr;
|
||||||
|
unsigned pedal_id = 0;
|
||||||
|
unsigned color;
|
||||||
|
|
||||||
|
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) )
|
||||||
|
{
|
||||||
|
case midi::kSustainCtlMdId:
|
||||||
|
label = "damp";
|
||||||
|
color = 0xf4a460;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case midi::kSostenutoCtlMdId:
|
||||||
|
label = "sost";
|
||||||
|
color = 0x7fffd4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case midi::kSoftPedalCtlMdId:
|
||||||
|
label = "soft";
|
||||||
|
color = 0x98fb98;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; e!=nullptr; e=e->link)
|
||||||
|
{
|
||||||
|
if( cwIsFlag(e->flags,midi_state::kNoChangeFl) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( cwIsFlag(e->flags,midi_state::kDownPedalFl) )
|
||||||
|
{
|
||||||
|
if( e0 != nullptr )
|
||||||
|
{
|
||||||
|
// two consecutive pedal downd - this shouldn't be possible
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e0 = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cwIsFlag(e->flags,midi_state::kUpPedalFl))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
_write_svg_rect( svgH, e0->secs, e->secs, y, label, color );
|
||||||
|
e0 = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
svg::handle_t svgH;
|
||||||
|
|
||||||
|
get_note_extents( msH, minMidiPitch, maxMidiPitch, minSec, maxSec );
|
||||||
|
|
||||||
|
// create the SVG file object
|
||||||
|
if((rc = svg::create(svgH)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"SVG file object create failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the note graphics
|
||||||
|
for(uint8_t i=0; i<midi::kMidiChCnt; ++i)
|
||||||
|
for(uint8_t j=0; j<midi::kMidiNoteCnt; ++j)
|
||||||
|
if((evt = note_event_list(msH,i,j)) != nullptr )
|
||||||
|
_write_svg_ch_note(svgH, evt, minMidiPitch, maxMidiPitch );
|
||||||
|
|
||||||
|
// create the pedal graphics
|
||||||
|
for(uint8_t i=0; i<midi::kMidiChCnt; ++i)
|
||||||
|
for(uint8_t j=0; j<midi_state::pedal_count(msH); ++j)
|
||||||
|
if((evt = pedal_event_list(msH,i,j)) != nullptr )
|
||||||
|
_write_svg_ch_pedal(svgH, evt, j, minMidiPitch, maxMidiPitch );
|
||||||
|
|
||||||
|
|
||||||
|
// write the SVG file
|
||||||
|
if((rc = svg::write( svgH, fname, nullptr, svg::kStandAloneFl )) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"SVG-MIDI file write failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
destroy(svgH);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
cw::rc_t cw::svg_midi::load_from_piano_score( handle_t h, const char* piano_score_fname )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
svg_midi_t* p = _handleToPtr(h);
|
||||||
|
score::handle_t scH;
|
||||||
|
|
||||||
|
|
||||||
|
if((rc = score::create( scH, piano_score_fname )) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"The piano score load failed.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned evtN = event_count(scH);
|
||||||
|
const score::event_t* evtA = base_event(scH);
|
||||||
|
time::spec_t timestamp;
|
||||||
|
|
||||||
|
for(unsigned i=0; i<evtN; ++i)
|
||||||
|
{
|
||||||
|
const score::event_t* e = evtA + i;
|
||||||
|
|
||||||
|
time::secondsToSpec(timestamp,e->sec);
|
||||||
|
|
||||||
|
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;
|
||||||
|
midi_state::handle_t msH;
|
||||||
|
|
||||||
|
|
||||||
|
// create the MIDI state object - with caching turned on
|
||||||
|
if((rc = midi_state::create( msH, nullptr, nullptr, true, midi_state_args )) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error creating the midi_state object.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the MIDI file
|
||||||
|
if((rc = midi_state::load_from_midi_file( msH, midi_fname)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error loading midi file into midi_state object.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the SVG file
|
||||||
|
if((rc = write(out_fname,msH)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error write the SVG-MIDI file.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error creating the SVG-MIDI 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 )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error loading the piano score file.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the SVG file
|
||||||
|
if((rc = write(h,out_fname)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error write the SVG-MIDI file.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
destroy(h);
|
||||||
|
|
||||||
|
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* 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 )
|
||||||
|
{
|
||||||
|
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 );
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
return rc;
|
||||||
|
}
|
18
cwSvgMidi.h
Normal file
18
cwSvgMidi.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef cwSvgMidi_h
|
||||||
|
#define cwSvgMidi_h
|
||||||
|
|
||||||
|
namespace cw
|
||||||
|
{
|
||||||
|
namespace svg_midi
|
||||||
|
{
|
||||||
|
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 test_midi_file( const object_t* cfg );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
126
cwSvgScore.cpp
Normal file
126
cwSvgScore.cpp
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#include "cwCommon.h"
|
||||||
|
#include "cwLog.h"
|
||||||
|
#include "cwCommonImpl.h"
|
||||||
|
#include "cwMem.h"
|
||||||
|
#include "cwObject.h"
|
||||||
|
#include "cwPianoScore.h"
|
||||||
|
#include "cwSvg.h"
|
||||||
|
#include "cwSvgScore.h"
|
||||||
|
|
||||||
|
namespace cw
|
||||||
|
{
|
||||||
|
namespace svg_score
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef struct svg_score_str
|
||||||
|
{
|
||||||
|
score::handle_t pianoScoreH;
|
||||||
|
|
||||||
|
} svg_score_t;
|
||||||
|
|
||||||
|
svg_score_t* _handleToPtr( handle_t h )
|
||||||
|
{
|
||||||
|
return handleToPtr<handle_t,svg_score_t>(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_t _destroy( svg_score_t* p )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
mem::release(p);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cw::rc_t cw::svg_score::create( handle_t& hRef )
|
||||||
|
{
|
||||||
|
rc_t rc;
|
||||||
|
if((rc = destroy(hRef)) != kOkRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
svg_score_t* p = mem::allocZ<svg_score_t>();
|
||||||
|
|
||||||
|
//errLabel:
|
||||||
|
if(rc != kOkRC )
|
||||||
|
_destroy(p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cw::rc_t cw::svg_score::destroy( handle_t& hRef )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
|
||||||
|
if( !hRef.isValid() )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
svg_score_t* p = _handleToPtr(hRef);
|
||||||
|
|
||||||
|
if((rc = _destroy(p)) != kOkRC )
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hRef.clear();
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cw::rc_t cw::svg_score::setPianoScore( handle_t h, score::handle_t pianoScH )
|
||||||
|
{
|
||||||
|
svg_score_t* p = _handleToPtr(h);
|
||||||
|
p->pianoScoreH = pianoScH;
|
||||||
|
return kOkRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
cw::rc_t cw::svg_score::write( handle_t h, const char* outFname, const char* cssFname )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cw::rc_t cw::svg_score::write( const object_t* cfg )
|
||||||
|
{
|
||||||
|
rc_t rc = kOkRC;
|
||||||
|
const char* piano_score_fname = nullptr;
|
||||||
|
const char* cm_score_fname = nullptr;
|
||||||
|
const char* css_fname = nullptr;
|
||||||
|
const char* out_fname = nullptr;
|
||||||
|
score::handle_t pianoScoreH;
|
||||||
|
svg_score::handle_t svgScoreH;
|
||||||
|
|
||||||
|
if((rc = cfg->getv("piano_score_fname",piano_score_fname,
|
||||||
|
"cm_score_fname",cm_score_fname,
|
||||||
|
"css_fname",css_fname,
|
||||||
|
"out_fname",out_fname)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error parsing arguments.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = create(svgScoreH)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error creating svg score.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = create(pianoScoreH,piano_score_fname)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error opening piano score.");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((rc = setPianoScore(svgScoreH,pianoScoreH)) != kOkRC )
|
||||||
|
{
|
||||||
|
rc = cwLogError(rc,"Error on setPianoScore().");
|
||||||
|
goto errLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
errLabel:
|
||||||
|
|
||||||
|
destroy(svgScoreH);
|
||||||
|
|
||||||
|
destroy(pianoScoreH);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
22
cwSvgScore.h
Normal file
22
cwSvgScore.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef cwSvgScore_h
|
||||||
|
#define cwSvgScore_h
|
||||||
|
|
||||||
|
namespace cw
|
||||||
|
{
|
||||||
|
namespace svg_score
|
||||||
|
{
|
||||||
|
typedef handle<struct svg_score_str> handle_t;
|
||||||
|
|
||||||
|
rc_t create( handle_t& hRef );
|
||||||
|
rc_t destroy( handle_t& hRef );
|
||||||
|
|
||||||
|
rc_t setPianoScore( handle_t h, score::handle_t pianoScH );
|
||||||
|
|
||||||
|
rc_t write( handle_t h, const char* outFname, const char* cssFname );
|
||||||
|
|
||||||
|
rc_t write( const object_t* cfg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user