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

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;
}