244 lines
5.4 KiB
C++
244 lines
5.4 KiB
C++
//
|
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
|
// Creation Date: Tue Feb 10 21:57:42 PST 2015
|
|
// Last Modified: Tue Feb 10 21:57:45 PST 2015
|
|
// Filename: tools/shutak.cpp
|
|
// URL: https://github.com/craigsapp/midifile/blob/master/tools/shutak.cpp
|
|
// Syntax: C++11
|
|
// vim: ts=3
|
|
//
|
|
// Description: Convert lines of MIDI note numbers into MIDI files.
|
|
// Multiple lines will be placed in multiple tracks.
|
|
// Each note has the duration of one second (quarter notes
|
|
// at MM60).
|
|
//
|
|
// Each row of input is a set of numbers for MIDI pitches that are
|
|
// played one per second:
|
|
// 77 74 81 69 81 62
|
|
//
|
|
// If there are more than one row, each row is played in parallel
|
|
// with the other:
|
|
// 66 81 63 79 64 62
|
|
// 75 68 66 76 81 66
|
|
// 63 64 75 68 77 73
|
|
// In this case the first three notes that are played together are:
|
|
// (66, 75, 63)
|
|
//
|
|
|
|
#include "Options.h"
|
|
#include "MidiFile.h"
|
|
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
using namespace std;
|
|
using namespace smf;
|
|
|
|
|
|
void checkOptions (Options& opts);
|
|
void createMidiFile (const char* filename,
|
|
vector<vector<int> >& sequence);
|
|
void processFile (const string& filename);
|
|
void getData (vector<vector<int> >& sequence,
|
|
const string& filename);
|
|
void example (void);
|
|
void usage (const string& command);
|
|
|
|
// User interface variables:
|
|
Options options; // for command-line processing
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
int main(int argc, char** argv) {
|
|
options.setOptions(argc, argv);
|
|
checkOptions(options);
|
|
string line;
|
|
for (int i=1; i<=options.getArgCount(); i++) {
|
|
processFile(options.getArg(i));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// processFile -- Convert file into a MIDI file.
|
|
//
|
|
|
|
void processFile(const string& filename) {
|
|
char buffer[1024] = {0};
|
|
strncpy(buffer, filename.c_str(),1000);
|
|
char* ptr = strrchr(buffer, '.');
|
|
if (ptr != NULL) {
|
|
*ptr = '\0';
|
|
}
|
|
strcat(buffer, ".mid");
|
|
|
|
vector<vector<int> > sequence;
|
|
getData(sequence, filename);
|
|
|
|
createMidiFile(buffer, sequence);
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// createMidiFile --
|
|
//
|
|
|
|
void createMidiFile(const char* filename, vector<vector<int> >& sequence) {
|
|
MidiFile midifile;
|
|
midifile.absoluteTicks();
|
|
midifile.addTrack(1);
|
|
int tpq = 120;
|
|
double beat = 0.0;
|
|
midifile.setTicksPerQuarterNote(tpq);
|
|
|
|
|
|
MidiEvent tempo;
|
|
tempo.setMetaTempo(60.0);
|
|
tempo.track = 0;
|
|
tempo.tick = 0;
|
|
midifile.addEvent(tempo);
|
|
|
|
int maxlen = 0;
|
|
int i, j;
|
|
for (i=0; i<(int)sequence.size(); i++) {
|
|
if ((int)sequence[i].size() > maxlen) {
|
|
maxlen = (int)sequence[i].size();
|
|
}
|
|
}
|
|
|
|
vector<int> notelist;
|
|
MidiEvent noteon(0x90, 0, 64);
|
|
MidiEvent noteoff(0x80, 0, 64);
|
|
noteon.track = 1;
|
|
noteoff.track = 1;
|
|
|
|
for (i=0; i<maxlen; i++) {
|
|
notelist.clear();
|
|
for (j=0; j<(int)sequence.size(); j++) {
|
|
if (i<(int)sequence[j].size()) {
|
|
notelist.push_back(sequence[j][i]);
|
|
}
|
|
}
|
|
for (j=0; j<(int)notelist.size(); j++) {
|
|
noteon[1] = 0x7f & notelist[j];
|
|
noteoff[1] = 0x7f & notelist[j];
|
|
noteon.tick = (int)(beat * tpq + 0.5);
|
|
noteoff.tick = (int)(beat * tpq + 1 * tpq + 0.5);
|
|
midifile.addEvent(noteon);
|
|
midifile.addEvent(noteoff);
|
|
}
|
|
beat += 1.0;
|
|
}
|
|
|
|
midifile.sortTracks();
|
|
midifile.write(filename);
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// getData -- Extract MIDI key numbers from input file.
|
|
//
|
|
|
|
void getData(vector<vector<int> >& sequence, const string& filename) {
|
|
ifstream infile;
|
|
infile.open(filename.c_str());
|
|
string line;
|
|
int key;
|
|
stringstream sss;
|
|
sequence.clear();
|
|
vector<int> list;
|
|
while (getline(infile, line)) {
|
|
cout << "LINE = " << line << "\n";
|
|
sss.clear();
|
|
list.clear();
|
|
sss << line;
|
|
while (sss >> key) {
|
|
list.push_back(key);
|
|
}
|
|
if (list.size() == 0) {
|
|
continue;
|
|
}
|
|
sequence.push_back(list);
|
|
}
|
|
infile.close();
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// checkOptions --
|
|
//
|
|
|
|
void checkOptions(Options& opts) {
|
|
opts.define("author=b", "author of the program");
|
|
opts.define("version=b", "version of the program");
|
|
opts.define("example=b", "example useage of the program");
|
|
opts.define("h|help=b", "list of options for the program");
|
|
opts.process();
|
|
|
|
if (opts.getBoolean("author")) {
|
|
cout << "Written by Craig Stuart Sapp, "
|
|
<< "craig@ccrma.stanford.edu, February 2015" << endl;
|
|
exit(0);
|
|
} else if (opts.getBoolean("version")) {
|
|
cout << "shutak, version 1.0 (10 Feb 2015)\n"
|
|
<< "compiled: " << __DATE__ << endl;
|
|
exit(0);
|
|
} else if (opts.getBoolean("help")) {
|
|
usage(opts.getCommand());
|
|
exit(0);
|
|
} else if (opts.getBoolean("example")) {
|
|
example();
|
|
exit(0);
|
|
}
|
|
|
|
if (opts.getArgCount() == 0) {
|
|
cerr << "Error: One or more input file is required." << endl;
|
|
exit(1);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// example -- shows various command-line option calls to program.
|
|
//
|
|
|
|
void example(void) {
|
|
cout <<
|
|
"\n"
|
|
<< endl;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// usage -- how to run this program from the command-line.
|
|
//
|
|
|
|
void usage(const string& command) {
|
|
cout <<
|
|
"\n"
|
|
<< endl;
|
|
}
|
|
|
|
|
|
|