227 lines
5.2 KiB
C++
227 lines
5.2 KiB
C++
//
|
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
|
// Creation Date: Thu Dec 2 12:45:43 PST 1999
|
|
// Last Modified: Fri Feb 20 20:34:39 PST 2015
|
|
// Filename: tools/midimixup.cpp
|
|
// URL: https://github.com/craigsapp/midifile/blob/master/tools/midimixup.cpp
|
|
// Syntax: C++11
|
|
// vim: ts=3
|
|
//
|
|
// Description: Reads a standard MIDI file, move the pitches around
|
|
// into a random order.
|
|
//
|
|
|
|
#include "MidiFile.h"
|
|
#include "Options.h"
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <random>
|
|
|
|
using namespace std;
|
|
using namespace smf;
|
|
|
|
|
|
void checkOptions (Options& opts);
|
|
void example (void);
|
|
void usage (const char* command);
|
|
void randomizeNotes (vector<MidiEvent*>& notes);
|
|
void reverseNotes (vector<MidiEvent*>& notes);
|
|
void swapNotes (vector<MidiEvent*>& notes, int index1, int index2);
|
|
|
|
class pairing {
|
|
public:
|
|
int index;
|
|
double value;
|
|
};
|
|
|
|
// User interface variables:
|
|
int reverseQ = 0; // used with -r option: reverse notes
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
int main(int argc, char** argv) {
|
|
Options options(argc, argv);
|
|
checkOptions(options);
|
|
|
|
MidiFile midifile;
|
|
if (options.getArgCount() == 2) {
|
|
midifile.read(options.getArg(1));
|
|
} else if (options.getArgCount() == 1) {
|
|
midifile.read(options.getArg(1));
|
|
} else {
|
|
cerr << "Need one optional MIDI file (or standard input), ";
|
|
cerr << "and one output" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
midifile.linkNotePairs();
|
|
vector<MidiEvent*> notes;
|
|
notes.reserve(123456);
|
|
int track, event;
|
|
for (track=0; track<midifile.getNumTracks(); track++) {
|
|
for (event=0; event<midifile.getNumEvents(track); event++) {
|
|
if (midifile[track][event].isNoteOn()) {
|
|
if (midifile[track][event].isLinked()) {
|
|
notes.push_back(&midifile[track][event]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (reverseQ) {
|
|
reverseNotes(notes);
|
|
} else {
|
|
randomizeNotes(notes);
|
|
}
|
|
|
|
if (options.getArgCount() == 2) {
|
|
midifile.write(options.getArg(2));
|
|
} else {
|
|
midifile.write(options.getArg(1));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// reverseNotes -- Reverse the order of the notes in the file.
|
|
//
|
|
|
|
void reverseNotes(vector<MidiEvent*>& notes) {
|
|
int count = (int)notes.size();
|
|
for (int i=0; i<count/2; i++) {
|
|
swapNotes(notes, i, count-1-i);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// randomizeNotes -- adjust an absolute MIDI time. If the time
|
|
// becomes negative, then the time will be set to zero.
|
|
//
|
|
|
|
void randomizeNotes(vector<MidiEvent*>& notes) {
|
|
random_device rd;
|
|
mt19937 md(rd());
|
|
uniform_real_distribution<double> dist(0, 100);
|
|
int count = (int)notes.size();
|
|
vector<pairing> neworder;
|
|
neworder.resize(notes.size());
|
|
int i;
|
|
for (i=0; i<(int)neworder.size(); i++) {
|
|
neworder[i].index = i;
|
|
neworder[i].value = dist(md);
|
|
}
|
|
sort(neworder.begin(), neworder.end(),
|
|
[](const pairing& a, const pairing& b) {
|
|
return a.value > b.value;
|
|
});
|
|
|
|
for (i=0; i<count; i++) {
|
|
swapNotes(notes, i, neworder[i].index);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// swapNotes -- move a note from one location to another.
|
|
//
|
|
|
|
void swapNotes(vector<MidiEvent*>& notes, int index1, int index2) {
|
|
MidiEvent* noteon1 = notes[index1];
|
|
MidiEvent* noteon2 = notes[index2];
|
|
MidiEvent* noteoff1 = notes[index1]->getLinkedEvent();
|
|
MidiEvent* noteoff2 = notes[index2]->getLinkedEvent();
|
|
if (noteon1 == NULL) { return; }
|
|
if (noteon2 == NULL) { return; }
|
|
if (noteoff1 == NULL) { return; }
|
|
if (noteoff2 == NULL) { return; }
|
|
int pitch1 = noteon1->getKeyNumber();
|
|
int pitch2 = noteon2->getKeyNumber();
|
|
if (pitch1 == pitch2) { return; }
|
|
if (pitch1 < 0) { return; }
|
|
if (pitch2 < 0) { return; }
|
|
|
|
noteon1->setKeyNumber(pitch2);
|
|
noteoff1->setKeyNumber(pitch2);
|
|
noteon2->setKeyNumber(pitch1);
|
|
noteoff2->setKeyNumber(pitch1);
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// checkOptions -- handle command-line options.
|
|
//
|
|
|
|
void checkOptions(Options& opts) {
|
|
opts.define("r|reverse=b", "Reverse the order of notes");
|
|
|
|
opts.define("author=b", "Author of the program");
|
|
opts.define("version=b", "Print version of the program");
|
|
opts.define("example=b", "Display example use of the program");
|
|
opts.define("help=b", "Dispay help for the program");
|
|
opts.process();
|
|
|
|
if (opts.getBoolean("author")) {
|
|
cout << "Written by Craig Stuart Sapp, "
|
|
<< "craig@ccrma.stanford.edu, 2 December 1999" << endl;
|
|
exit(0);
|
|
}
|
|
if (opts.getBoolean("version")) {
|
|
cout << "midimixup version 2.0" << endl;
|
|
cout << "compiled: " << __DATE__ << endl;
|
|
}
|
|
if (opts.getBoolean("help")) {
|
|
usage(opts.getCommand().c_str());
|
|
exit(0);
|
|
}
|
|
if (opts.getBoolean("example")) {
|
|
example();
|
|
exit(0);
|
|
}
|
|
|
|
reverseQ = opts.getBoolean("reverse");
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// example -- gives example calls to the midiplay program.
|
|
//
|
|
|
|
void example(void) {
|
|
cout <<
|
|
" \n"
|
|
<< endl;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// usage -- how to run the textmidi program on the command line.
|
|
//
|
|
|
|
void usage(const char* command) {
|
|
cout <<
|
|
" \n"
|
|
<< endl;
|
|
}
|
|
|
|
|
|
|