307 lines
7.8 KiB
C++
307 lines
7.8 KiB
C++
//
|
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
|
// Creation Date: Sat Feb 24 20:20:18 PST 2018
|
|
// Last Modified: Sat Feb 24 20:20:21 PST 2018
|
|
// Filename: tools/redexpress.cpp
|
|
// URL: https://github.com/craigsapp/midifile/blob/master/tools/redexpress.cpp
|
|
// Syntax: C++11
|
|
// vim: ts=3
|
|
//
|
|
// Description: Adds expression information to notes extracted from
|
|
// Red Welte-Mignon piano rolls.
|
|
//
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// red Welte-Mignon tracker holes (T100):
|
|
//
|
|
// 10 expression on left side:
|
|
// 1: Mezzo-Forte-Off MIDI Key 14
|
|
// 2: Mezzo-Forte-On MIDI Key 15
|
|
// 3: Crescendo-Off MIDI Key 16
|
|
// 4: Crescendo-On MIDI Key 17
|
|
// 5: Forzando-Off MIDI Key 18
|
|
// 6: Forzando-On MIDI Key 19
|
|
// 7: Soft-Pedal-Off MIDI Key 20
|
|
// 8: Soft-Pedal-On MIDI Key 21
|
|
// 9: Motor-Off MIDI Key 22
|
|
// 10: Motor-On MIDI Key 23
|
|
// Then 80 notes from C1 to G7 (MIDI note 24 to 103):
|
|
// 11: C1 MIDI Key 24
|
|
// ...
|
|
// 50: D#4 MIDI Key 63
|
|
// Treble register (40 notes) from E4 to G7:
|
|
// 51: E4 MIDI Key 64
|
|
// ...
|
|
// 90: G7 MIDI Key 103
|
|
// Then 10 expression holes on the right side:
|
|
// 91: -10: Rewind MIDI Key 104
|
|
// 92: -9: Electric-Cutoff MIDI Key 105
|
|
// 93: -8: Sustain-Pedal-On MIDI Key 106
|
|
// 94: -7: Sustain-Pedal-Off MIDI Key 107
|
|
// 95: -6: Forzando-On MIDI Key 108
|
|
// 96: -5: Forzando-Off MIDI Key 109
|
|
// 97: -4: Crescendo-On MIDI Key 110
|
|
// 98: -3: Crescendo-Off MIDI Key 111
|
|
// 99: -2: Mezzo-Forte-On MIDI Key 112
|
|
// 100: -1: Mezzo-Forte-Off MIDI Key 113
|
|
//
|
|
///////////////////////////////////////////////////////////
|
|
|
|
#include "MidiFile.h"
|
|
#include "Options.h"
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
using namespace std;
|
|
using namespace smf;
|
|
|
|
|
|
void checkOptions (Options& opts, int argc, char** argv);
|
|
void example (void);
|
|
void usage (const char* command);
|
|
|
|
void addExpression_T100 (MidiFile& midifile);
|
|
int getRewindTime (MidiFile& midifile, int track);
|
|
void addSustainPedalling (MidiFile& midifile, int sourcetrack,
|
|
int targettrack);
|
|
void addSoftPedalling (MidiFile& midifile, int sourcetrack,
|
|
int targettrack);
|
|
|
|
#define TYPE_NONE 0
|
|
#define TYPE_T100 1
|
|
|
|
int Rolltype = TYPE_T100;
|
|
int RewindKey = 104;
|
|
int PedalOnKey = 106;
|
|
int PedalOffKey = 107;
|
|
int SoftOnKey = 21;
|
|
int SoftOffKey = 20;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
int main(int argc, char* argv[]) {
|
|
Options options;
|
|
checkOptions(options, argc, argv);
|
|
MidiFile midifile;
|
|
if (options.getArgCount() > 0) {
|
|
midifile.read(options.getArg(1));
|
|
} else {
|
|
midifile.read(cin);
|
|
}
|
|
addExpression_T100(midifile);
|
|
if (options.getArgCount() >= 2) {
|
|
midifile.write(options.getArg(2));
|
|
} else {
|
|
cout << midifile;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// addExpression_T100 --
|
|
//
|
|
|
|
void addExpression_T100(MidiFile& midifile) {
|
|
int tracks = midifile.getTrackCount();
|
|
if (tracks != 5) {
|
|
cerr << "This program expects a five-track MIDI file to process" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
// add a new track with some of the expression components (pedal)
|
|
midifile.addTrack();
|
|
|
|
int expressiontrack = 5;
|
|
int rewind = getRewindTime(midifile, 4);
|
|
MidiEvent me;
|
|
me.tick = rewind;
|
|
me.track = expressiontrack;
|
|
|
|
me.makeController(1, 7, 0); // mute for any post-rewind holes: bass
|
|
midifile.addEvent(me);
|
|
me.makeController(2, 7, 0); // mute for any post-rewind holes: treble
|
|
midifile.addEvent(me);
|
|
|
|
addSustainPedalling(midifile, 4, expressiontrack);
|
|
addSoftPedalling(midifile, 4, expressiontrack);
|
|
|
|
midifile.sortTracks();
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// addSustainPedalling --
|
|
//
|
|
|
|
void addSustainPedalling(MidiFile& midifile, int sourcetrack, int targettrack) {
|
|
int count = midifile.getEventCount(sourcetrack);
|
|
MidiEvent me;
|
|
me.track = targettrack;
|
|
for (int i=0; i<count; i++) {
|
|
if (!midifile[sourcetrack][i].isNoteOn()) {
|
|
continue;
|
|
}
|
|
int key = midifile[sourcetrack][i].getKeyNumber();
|
|
me.tick = midifile[sourcetrack][i].tick;
|
|
if (key == PedalOnKey) {
|
|
me.makeController(1, 64, 127);
|
|
midifile.addEvent(me);
|
|
me.makeController(2, 64, 127);
|
|
midifile.addEvent(me);
|
|
} else if (key == PedalOffKey) {
|
|
me.makeController(1, 64, 0);
|
|
midifile.addEvent(me);
|
|
me.makeController(2, 64, 0);
|
|
midifile.addEvent(me);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// addSoftPedalling --
|
|
//
|
|
|
|
void addSoftPedalling(MidiFile& midifile, int sourcetrack, int targettrack) {
|
|
int count = midifile.getEventCount(sourcetrack);
|
|
MidiEvent me;
|
|
me.track = targettrack;
|
|
for (int i=0; i<count; i++) {
|
|
if (!midifile[sourcetrack][i].isNoteOn()) {
|
|
continue;
|
|
}
|
|
int key = midifile[sourcetrack][i].getKeyNumber();
|
|
me.tick = midifile[sourcetrack][i].tick;
|
|
if (key == SoftOnKey) {
|
|
me.makeController(1, 67, 127);
|
|
midifile.addEvent(me);
|
|
me.makeController(2, 67, 127);
|
|
midifile.addEvent(me);
|
|
} else if (key == SoftOffKey) {
|
|
me.makeController(1, 67, 0);
|
|
midifile.addEvent(me);
|
|
me.makeController(2, 67, 0);
|
|
midifile.addEvent(me);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// getRewindTime -- Return the start time of the rewind hole.
|
|
// Any notes after this time should be muted (set master volume to 0).
|
|
//
|
|
|
|
int getRewindTime(MidiFile& midifile, int track) {
|
|
int count = midifile.getEventCount(track);
|
|
for (int i=count-1; i>=0; i--) {
|
|
if (!midifile[track][i].isNoteOn()) {
|
|
continue;
|
|
}
|
|
if (midifile[track][i].getKeyNumber() == RewindKey) {
|
|
return midifile[track][i].tick;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// checkOptions --
|
|
//
|
|
|
|
void checkOptions(Options& opts, int argc, char* argv[]) {
|
|
opts.define("t|type=s:redwelte", "type of roll to process");
|
|
opts.define("t100|T100=b", "red Welte-Mignon roll, 100 tracker holes");
|
|
|
|
opts.define("author=b", "author of program");
|
|
opts.define("version=b", "compilation info");
|
|
opts.define("example=b", "example usages");
|
|
opts.define("h|help=b", "short description");
|
|
opts.process(argc, argv);
|
|
|
|
// handle basic options:
|
|
if (opts.getBoolean("author")) {
|
|
cout << "Written by Craig Stuart Sapp, "
|
|
<< "craig@ccrma.stanford.edu, 24 February 2018" << endl;
|
|
exit(0);
|
|
} else if (opts.getBoolean("version")) {
|
|
cout << argv[0] << ", version: February 2018" << endl;
|
|
cout << "compiled: " << __DATE__ << endl;
|
|
exit(0);
|
|
} else if (opts.getBoolean("help")) {
|
|
usage(opts.getCommand().data());
|
|
exit(0);
|
|
} else if (opts.getBoolean("example")) {
|
|
example();
|
|
exit(0);
|
|
}
|
|
|
|
if (opts.getArgCount() > 2) {
|
|
usage(opts.getCommand().data());
|
|
exit(1);
|
|
}
|
|
|
|
Rolltype = TYPE_T100;
|
|
if (opts.getBoolean("type")) {
|
|
string typestring = opts.getString("type");
|
|
if (typestring == "redwelte") {
|
|
Rolltype = TYPE_T100;
|
|
} else if (typestring == "t100") {
|
|
Rolltype = TYPE_T100;
|
|
} else if (typestring == "T100") {
|
|
Rolltype = TYPE_T100;
|
|
}
|
|
} else if (opts.getBoolean("T100")) {
|
|
Rolltype = TYPE_T100;
|
|
}
|
|
|
|
if (Rolltype != TYPE_T100) {
|
|
cerr << "Unknown roll type" << endl;
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// example --
|
|
//
|
|
|
|
void example(void) {
|
|
// add examples here
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// usage --
|
|
//
|
|
|
|
void usage(const char* command) {
|
|
// add usage here
|
|
}
|
|
|
|
|
|
|