//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Apr 3 23:43:56 PDT 2013
// Last Modified: Thu Apr 4 01:39:09 PDT 2013
// Filename: ...sig/examples/all/midi2binasc.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/midi/midi2binasc.cpp
// Syntax: C++; museinfo
//
// Description: Converts a MIDI file into an ASCII format which can be
// converted back into a MIDI file with the binasc program:
// http://binasc.googlecode.com
//
#include "MidiFile.h"
#include "Options.h"
#ifndef OLDCPP
#include <iostream>
#include <iomanip>
using namespace std;
#else
#include <iostream.h>
#include <iomanip.h>
#endif
typedef unsigned char uchar;
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(MFEvent& 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.deltaTime();
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
MFEvent 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(MFEvent& event) {
// print the time:
cout << "v" << event.time << "\t";
// print the command byte in hex format (two digits):
int commandbyte = event.data[0];
printHexByte(commandbyte);
int i;
switch (commandbyte & 0xf0) {
case 0x90:
case 0x80:
for (i=1; i<event.data.getSize(); i++) {
cout << " ";
printDecByte(event.data[i]);
}
break;
default:
for (i=1; i<event.data.getSize(); i++) {
cout << " ";
printHexByte(event.data[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);
MFEvent event;
for (i=0; i<eventcount; i++) {
event = midifile.getEvent(track, i);
sum += getVlvSize(event.time);
sum += event.data.getSize();
}
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 ; MIDI file header chunk marker" << endl;
// print the number of bytes in the MIDI file to follow (always 6):
cout << "4'6 ; 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());
exit(0);
} else if (opts.getBoolean("example")) {
example();
exit(0);
}
debugQ = opts.getBoolean("debug");
if (opts.getArgCount() != 1) {
usage(opts.getCommand());
exit(1);
}
}
//////////////////////////////
//
// example --
//
void example(void) {
}
//////////////////////////////
//
// usage --
//
void usage(const char* command) {
cout << "Usage: " << command << " midifile" << endl;
}
// md5sum: a1a30253aa8cf823d34ba8acff2d05c0 midi2binasc.cpp [20130420]