229 lines
6.5 KiB
C++
229 lines
6.5 KiB
C++
//
|
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
|
// Creation Date: Thu Jun 6 20:41:53 PDT 2002
|
|
// Last Modified: Mon Feb 9 21:01:00 PST 2015 Updated for C++11.
|
|
// Filename: tools/midi2skini.cpp
|
|
// URL: https://github.com/craigsapp/midifile/blob/master/tools/midi2skini.cpp
|
|
// Syntax: C++11
|
|
// vim: ts=3
|
|
//
|
|
// Description: Converts a Standard MIDI file into the SKINI data format.
|
|
//
|
|
|
|
#include "MidiFile.h"
|
|
#include "Options.h"
|
|
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
using namespace smf;
|
|
|
|
|
|
void checkOptions (Options& opts, int argc, char** argv);
|
|
void example (void);
|
|
void printMidiAsSkini (MidiFile& midifile);
|
|
void processEvent (MidiEvent& event, double& tempo,
|
|
double& curtime);
|
|
void usage (const char* command);
|
|
|
|
// User interface variables:
|
|
Options options;
|
|
int track = -1; // track to extract from (starting from 0)
|
|
int debugQ = 0; // use with --debug option
|
|
int maxcount = 100000; // maxiumum number of notes expected
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
int main(int argc, char* argv[]) {
|
|
checkOptions(options, argc, argv);
|
|
MidiFile midifile;
|
|
midifile.read(options.getArg(1));
|
|
midifile.absoluteTicks();
|
|
midifile.joinTracks();
|
|
printMidiAsSkini(midifile);
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// printMidiAsSkini -- go thorough all events in the MIDI file
|
|
// and print the data. Input MIDI file is assumed to be in
|
|
// type 0 format (single track).
|
|
//
|
|
|
|
void printMidiAsSkini(MidiFile& midifile) {
|
|
int tpq = midifile.getTicksPerQuarterNote();
|
|
int ticks = 0; // absolute ticks of current event
|
|
double tempo = 120.0; // time units will be in seconds
|
|
double curtime = 0.0; // current time in seconds
|
|
int i;
|
|
|
|
for (i=0; i<midifile.getNumEvents(0); i++) {
|
|
int oldticks = ticks;
|
|
MidiEvent event = midifile.getEvent(0, i);
|
|
ticks = event.tick;
|
|
if (i>0) {
|
|
curtime += (ticks - oldticks) * 60.0 / tempo / tpq;
|
|
}
|
|
processEvent(event, tempo, curtime);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// processEvent -- check for tempo markings, and print MIDI event.
|
|
//
|
|
|
|
void processEvent(MidiEvent& event, double& tempo, double& curtime) {
|
|
if (((event[0] & 0xf0) == 0x80) || (((event[0] & 0xf0) == 0x90) &&
|
|
(event[2] == 0)) ) {
|
|
// note-off MIDI message
|
|
if (track >= 0 && track != event.track) { return; }
|
|
cout << "NoteOff\t\t=";
|
|
cout.width(9);
|
|
cout.setf(ios::left);
|
|
cout << curtime << "\t" << event.track << "\t"
|
|
<< (int)event[1] << "\t" << (int)event[2] << endl;
|
|
} else if ((event[0] & 0xf0) == 0x90) {
|
|
// note-on MIDI message
|
|
if (track >= 0 && track != event.track) { return; }
|
|
cout << "NoteOn\t\t=";
|
|
cout.width(9);
|
|
cout.setf(ios::left);
|
|
cout << curtime << "\t" << event.track << "\t"
|
|
<< (int)event[1] << "\t" << (int)event[2] << endl;
|
|
} else if ((event[0] & 0xf0) == 0xb0) {
|
|
// continuous controller MIDI message
|
|
if (track >= 0 && track != event.track) { return; }
|
|
if (event[1] == 7) {
|
|
cout << "Volume\t\t=";
|
|
cout.width(9);
|
|
cout.setf(ios::left);
|
|
cout << curtime << "\t" << event.track << "\t"
|
|
<< "\t" << (int)event[2] << endl;
|
|
} else {
|
|
cout << "ControlChange\t=";
|
|
cout.width(9);
|
|
cout.setf(ios::left);
|
|
cout << curtime << "\t" << event.track << "\t"
|
|
<< (int)event[1] << "\t" << (int)event[2] << endl;
|
|
}
|
|
} else if ((event.size() > 3) && (event[0] == 0xff) &&
|
|
(event[1] == 0x51)) {
|
|
// Tempo meta event
|
|
int microseconds = (unsigned int)event[3];
|
|
microseconds = microseconds << 8;
|
|
microseconds |= (unsigned int)event[4];
|
|
microseconds = microseconds << 8;
|
|
microseconds |= (unsigned int)event[5];
|
|
tempo = 1000000.0 / microseconds * 60.0;
|
|
cout << "// time:=" << curtime << " tempo: " << tempo << endl;
|
|
} else if ((event.size() > 3) && (event[0] == 0xff) &&
|
|
(event[1] == 0x03)) {
|
|
// Track name MIDI meta event
|
|
if (track >= 0 && track != event.track) { return; }
|
|
cout << "//";
|
|
cout << " time:=" << curtime;
|
|
cout << " track:" << event.track;
|
|
cout << " text: ";
|
|
for (int i=3; i<(int)event.size(); i++) {
|
|
cout << (char)event[i];
|
|
}
|
|
cout << endl;
|
|
} else {
|
|
// a MIDI event with an unknown mapping into SKINI, print as comment
|
|
if (track >= 0 && track != event.track) { return; }
|
|
cout << "//"
|
|
<< " time:=" << curtime
|
|
<< " track:" << event.track
|
|
<< " midi-data: ";
|
|
for (int i=0; i<(int)event.size(); i++) {
|
|
cout << (int)event[i] << " ";
|
|
}
|
|
cout << endl;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// checkOptions -- process command-line options.
|
|
//
|
|
|
|
void checkOptions(Options& opts, int argc, char* argv[]) {
|
|
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.define("t|track=i:-1", "which track to extract");
|
|
opts.define("max=i:100000", "maximum number of notes expected in input");
|
|
opts.define("debug=b", "debug mode to find errors in input file");
|
|
opts.process(argc, argv);
|
|
|
|
// handle basic options:
|
|
if (opts.getBoolean("author")) {
|
|
cout << "Written by Craig Stuart Sapp, "
|
|
<< "craig@ccrma.stanford.edu, 6 Jun 2002" << endl;
|
|
exit(0);
|
|
} else if (opts.getBoolean("version")) {
|
|
cout << argv[0] << ", version: 10 Jun 2002" << endl;
|
|
cout << "compiled: " << __DATE__ << endl;
|
|
exit(0);
|
|
} else if (opts.getBoolean("help")) {
|
|
usage(opts.getCommand().c_str());
|
|
exit(0);
|
|
} else if (opts.getBoolean("example")) {
|
|
example();
|
|
exit(0);
|
|
}
|
|
|
|
track = opts.getInteger("track");
|
|
debugQ = opts.getBoolean("debug");
|
|
maxcount = opts.getInteger("max");
|
|
|
|
if (opts.getArgCount() != 1) {
|
|
usage(opts.getCommand().c_str());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// example -- give example calls to the program.
|
|
//
|
|
|
|
void example(void) {
|
|
cout <<
|
|
"midi2skini file.mid == convert file.mid to the SKINI format\n"
|
|
"midi2skini file.mid >file.skini == save output SKINI data to file.skini\n"
|
|
"midi2skini -t1 file.mid == extract only track 1 from the tdata\n"
|
|
;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// usage --
|
|
//
|
|
|
|
void usage(const char* command) {
|
|
cout << "Usage: " << command << " [-t #|-m #] midifile\n";
|
|
cout << " -t # == extract only specified track (offset from 0).\n";
|
|
cout << " -m # == number of events to allocate for MIDI \n";
|
|
cout << endl;
|
|
}
|
|
|
|
|
|
|