2024-07-15 11:46:59 +08:00

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
}