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

322 lines
7.2 KiB
C++

//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Apr 3 23:43:56 PDT 2013
// Last Modified: Mon Feb 9 21:26:32 PST 2015 Updated for C++11.
// Filename: tools/midi2binasc.cpp
// URL: https://github.com/craigsapp/midifile/blob/master/tools/midi2binasc.cpp
// Syntax: C++11
// vim: ts=3
//
// Description: Converts a MIDI file into an ASCII format which can be
// converted back into a MIDI file with the binasc program:
// https://github.com/craigsapp/binasc
//
#include "MidiFile.h"
#include "Options.h"
#include <iostream>
#include <iomanip>
using namespace std;
using namespace smf;
void convertMidiFile (MidiFile& midifile);
void printMidiHeader (MidiFile& midifile);
void checkOptions (Options& opts, int argc, char* argv[]);
void printTrack (MidiFile& midifile, int track);
int getVlvSize (int value);
int getTrackByteCount (MidiFile& midifile, int track);
void printDecByte (int value);
void printMidiEvent (MidiEvent& event);
void printHexByte (int value);
void usage (const char* command);
void example (void);
// User interface variables:
Options options;
int debugQ = 0; // use with --debug option
int type0Q = 0; // force MIDI file to type 0 (single track)
//////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
checkOptions(options, argc, argv);
MidiFile midifile(options.getArg(1));
convertMidiFile(midifile);
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// convertMidiFile --
//
void convertMidiFile(MidiFile& midifile) {
if (type0Q) {
midifile.joinTracks();
}
midifile.deltaTicks();
printMidiHeader(midifile);
int trackcount = midifile.getTrackCount();
for (int i=0; i<trackcount; i++) {
printTrack(midifile, i);
}
}
//////////////////////////////
//
// printTrack --
//
void printTrack(MidiFile& midifile, int track) {
cout << endl;
// first print track header
cout << "+M +T +r +k\t\t\t; Track chunk marker" << endl;
// print number of bytes in track
int trackbytes = getTrackByteCount(midifile, track);
cout << "4'" << trackbytes << "\t\t\t\t; number of bytes to follow in track"
<< endl;
cout << endl;
// print the list of events in the track
MidiEvent event;
int eventcount = midifile.getEventCount(track);
for (int i=0; i<eventcount; i++) {
event = midifile.getEvent(track, i);
printMidiEvent(event);
}
}
//////////////////////////////
//
// printMidiEvent -- print a time/MIDI message pair.
//
void printMidiEvent(MidiEvent& event) {
// print the time:
cout << "v" << event.tick << "\t";
// print the command byte in hex format (two digits):
int commandbyte = event[0];
printHexByte(commandbyte);
int i;
switch (commandbyte & 0xf0) {
case 0x90:
case 0x80:
for (i=1; i<(int)event.size(); i++) {
cout << " ";
printDecByte(event[i]);
}
break;
default:
for (i=1; i<(int)event.size(); i++) {
cout << " ";
printHexByte(event[i]);
}
}
cout << endl;
}
//////////////////////////////
//
// printDecByte --
//
void printDecByte(int value) {
cout << "'" << value;
}
//////////////////////////////
//
// printHexByte --
//
void printHexByte(int value) {
if (value < 16) {
cout << "0";
} else if (value > 255) {
cerr << "ERROR: value is too large: " << value << endl;
exit(1);
}
cout << hex << value << dec;
}
//////////////////////////////
//
// getTrackByteCount -- number of by the track after the track header.
// Might need to keep track of End-of-track meta message.
//
int getTrackByteCount(MidiFile& midifile, int track) {
int sum = 0;
int i;
int eventcount = midifile.getEventCount(track);
MidiEvent event;
for (i=0; i<eventcount; i++) {
event = midifile.getEvent(track, i);
sum += getVlvSize(event.tick);
sum += (int)event.size();
}
return sum;
}
//////////////////////////////
//
// getVlvSize -- return the number of bytes in the VLV format for the
// integer.
//
int getVlvSize(int value) {
if (value < 0x80) {
return 1;
} if (value < 0x4000) {
return 2;
} else if (value < 0x200000) {
return 3;
} else if (value < 0x10000000) {
return 4;
} else {
return 5;
}
}
//////////////////////////////
//
// printMidiHeader --
//
// Example header:
//
// +M +T +h +d ; MIDI file header chunk marker
// 4'6 ; bytes in header to follow
// 2'0 ; format: Type-0 (single track)
// 2'1 ; track count: 1 track
// '-25 '40 ; divisions: SMPTE: 25 frames/sec 40 subframes/frame
// ; in other words: 25 * 40 = 1000 ticks per second.
//
void printMidiHeader(MidiFile& midifile) {
// print MIDI file header marker
cout << "+M +T +h +d\t\t\t; MIDI file header chunk marker" << endl;
// print the number of bytes in the MIDI file to follow (always 6):
cout << "4'6\t\t\t\t; bytes in header to follow" << endl;
// print the format (0 = single track, 1 = multitrack)
// The MidiFile class does not exactly keep track of this value.
// It will presume that a single track file is a type-0 MIDI file
// (type-1 MIDI files can theoretically have a single track, but not
// usually).
if (midifile.getTrackCount() == 0) {
cout << "2'0\t\t\t\t; format: Type-0 (single track)";
} else {
cout << "2'1\t\t\t\t; format: Type-1 (multi-track)";
}
cout << endl;
// print track count
cout << "2'" << midifile.getTrackCount();
cout << "\t\t\t\t; track count: ";
cout << midifile.getTrackCount() << " track";
if (midifile.getTrackCount() != 1) {
cout << "s";
}
cout << endl;
// print the ticks per quarter note. The ticks per quarter note
// can be SMPTE or regular. Assuming regular at the moment.
int ticks = midifile.getTicksPerQuarterNote();
cout << "2'" << ticks << "\t\t\t\t; ticks per quarter note" << endl;
}
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("debug=b", "debug mode to find errors in input file");
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, 22 Jan 2002" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 12 Nov 2003" << 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);
}
debugQ = opts.getBoolean("debug");
if (opts.getArgCount() != 1) {
usage(opts.getCommand().c_str());
exit(1);
}
}
//////////////////////////////
//
// example --
//
void example(void) {
// add example usage here
}
//////////////////////////////
//
// usage --
//
void usage(const char* command) {
cout << "Usage: " << command << " midifile" << endl;
}