Goto: [ Program Documentation ]
//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: 22 Nov 1998
// Last Modified: Sun Jan 10 00:05:07 PST 1999
// Last Modified: Mon Jun 28 15:22:08 PDT 1999 (added sysex display capability)
// Last Modified: Fri Jul 2 18:10:43 PDT 1999 (added keyboard commands)
// Last Modified: Fri Nov 2 14:07:19 PST 2001 (added midi.open() before while)
// Last Modified: Sun Nov 20 02:09:20 PST 2005 (added cpu speed display)
// Filename: ...sig/doc/examples/improv/improv/cinmidi.cpp
// Syntax: C++; improv 2.2
//
#include "improv.h"
#include <ctype.h>
#include <string.h>
// Here is the syntax of a header comment:
#define HEADER_START ";; "
// abbreviated general MIDI instrument names for the patch change
// interpretation display. General MIDI instruments are standardized
// by the numbers 0-127. If your instruments are not General MIDI, or
// you are using a instrument bank other than bank 0, then the names
// do not have any meaning. These names are modified from the source:
// ftp://ftp.linpeople.org/pub/People/nathan/playmidi-2.4.tar.gz
//
const char *GMinstrument[128] = {
"acpiano", "britepno", "synpiano", "honkytonk", "epiano1", "epiano2",
"hrpschrd", "clavinet", "celeste", "glocken", "musicbox", "vibes",
"marimba", "xylophon", "tubebell", "santur", "homeorg", "percorg",
"rockorg", "churchorg", "reedorg", "accordn", "harmonica", "concrtna",
"nyguitar", "acguitar", "jazzgtr", "cleangtr", "mutegtr", "odguitar",
"distgtr", "gtrharm", "acbass", "fngrbass", "pickbass", "fretless",
"slapbas1", "slapbas2", "synbass1", "synbass2", "violin", "viola",
"cello", "contraba", "marcato", "pizzcato", "harp", "timpani",
"marcato", "slowstr", "synstr1", "synstr2", "choir", "doo",
"voices", "orchhit", "trumpet", "trombone", "tuba", "mutetrum",
"frenchorn", "hitbrass", "synbras1", "synbras2", "sprnosax", "altosax",
"tenorsax", "barisax", "oboe", "englhorn", "bassoon", "clarinet",
"piccolo", "flute", "recorder", "woodflut", "bottle", "shakazul",
"whistle", "ocarina", "sqrwave", "sawwave", "calliope", "chiflead",
"charang", "voxlead", "lead5th", "basslead", "fantasia", "warmpad",
"polysyn", "ghostie", "bowglass", "metalpad", "halopad", "sweeper",
"aurora", "soundtrk", "crystal", "atmosphr", "freshair", "unicorn",
"sweeper", "startrak", "sitar", "banjo", "shamisen", "koto",
"kalimba", "bagpipes", "fiddle", "shannai", "carillon", "agogo",
"steeldrum", "woodblock", "taiko", "toms", "syntom", "revcymb",
"fx-fret", "fx-blow", "seashore", "jungle", "telephone", "helicptr",
"applause", "ringwhsl"
};
// global variables for command-line options:
Options options; // for command-line processing
int absoluteQ = 0; // for -a command-line option (time display)
int interpretQ = 1; // for -i command-line option (interpret input)
int bytesQ = 1; // no longer used, set to 1
int style = 0; // for -d, -x, and -2 command-line options
int secondsQ = 0; // for -s command-line option
int keyboardQ = 1; // for -k command-line option
int activeSensingQ = 1; // for -A command-line option
int fileQ = 0; // for -o command-line option
int suppressOffQ = 0; // for -n command-line option
int filter[8] = {0}; // for -f command-line option
int cfilter[16] = {0}; // for -c command-line option
fstream outputfile; // for -o command-line option
#define MAX_KEY_BUFF (1024)
char inputBuffer[MAX_KEY_BUFF+1] = {0}; // for keyboard input buffer
int bufferIndex = 0; // for keyboard input buffer
// function declarations
void checkOptions(Options& opts);
void displayMessage(ostream& out, MidiMessage message,
int style);
void displayHeader(ostream& out);
void examineInputForCommand(const char* inputBuffer);
void example(void);
const char* getGmInst(int number);
char* getKey(int keynum);
void interpret(ostream& out, MidiMessage message);
void interpretSysex(ostream& out, MidiMessage message);
float makePitchBend(int lsb, int msb);
void printbyte(ostream& out, int value, int location,
int style);
void usage(const char* command);
void userCommandSend(const char* arguments);
MidiIO midi; // MIDI I/O interface
SigTimer sigtimer; // timing control
///////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
options.setOptions(argc, argv);
checkOptions(options);
displayHeader(cout);
if (fileQ) {
displayHeader(outputfile);
}
KeyboardInput keyboard; // for typing comments into output file
char keych; // character from keyboard
MidiMessage message;
int lastTime = -1;
midi.open();
while (1) {
while (midi.getCount() > 0) {
message = midi.extract();
// filter any specified message types
if (suppressOffQ && ((message.p0() & 0xf0) == 0x90) &&
(message.p2() == 0)) {
continue;
} else if (filter[(message.p0() >> 4) - 8]) {
continue;
} else if (cfilter[message.p0() & 0x0f]) {
continue;
}
// adjust message time to delta time if necessary
if (!absoluteQ) {
if (lastTime == -1) {
lastTime = message.time;
message.time = 0;
} else {
int temp = message.time;
message.time = message.time - lastTime;
lastTime = temp;
}
}
displayMessage(cout, message, style);
if (fileQ) {
displayMessage(outputfile, message, style);
}
}
if (keyboardQ && keyboard.hit()) {
keych = keyboard.getch();
switch (keych) {
case 27: // escape key
if (fileQ && bufferIndex != 0 && bufferIndex < MAX_KEY_BUFF) {
inputBuffer[bufferIndex] = '\0';
outputfile << inputBuffer;
}
keyboard.deinitialize();
exit(0);
break;
case 0x08: // backspace key
case 0x7f: // delete key
if (bufferIndex > 0) {
cout << "\b \b" << flush;
bufferIndex--;
}
break;
case 0x0a: // enter key only
#ifdef VISUAL
break;
#endif
case 13: // line feed
cout << endl;
if (bufferIndex < MAX_KEY_BUFF) {
inputBuffer[bufferIndex] = '\0';
if (fileQ) {
outputfile << inputBuffer << '\n';
}
examineInputForCommand(inputBuffer);
}
bufferIndex = 0;
break;
case 0x0c: // ^L key (redraw input)
cout << endl;
if (bufferIndex < MAX_KEY_BUFF) {
inputBuffer[bufferIndex] = '\0';
cout << inputBuffer << flush;
}
break;
default: // normal key
cout << keych << flush;
if (bufferIndex < MAX_KEY_BUFF) {
inputBuffer[bufferIndex++] = keych;
} else {
// buffer is WAY to long: kill it
bufferIndex = 0;
}
}
}
millisleep(1); // sleep for 1 millisec for multi-tasking courtesy
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// checkOptions -- process the command-line options
//
void checkOptions(Options& opts) {
opts.define("a|absolute=b"); // boolean for absoute time display
opts.define("i|interpret=b"); // boolean for interpreting messages
opts.define("p|port|inport=i:0"); // MIDI input port selection
opts.define("t|outport=i:0"); // MIDI output port selection
opts.define("l|list=b"); // boolean for displaying possible inputs
opts.define("d|dec|decimal=b"); // boolean for printing bytes as decimal
opts.define("x|hex|hexadecimal=b"); // boolean for printing bytes as hex
opts.define("2|bin|binary=b"); // boolean for printing bytes as binary
opts.define("o|output=s"); // for duplicating stdout to file
opts.define("n|nooffs=b"); // boolean for suppressing note-offs
opts.define("f|filter=s:"); // for filtering out various commands
opts.define("c|chan-filter=s:"); // for filtering out various channels
opts.define("u|user=s"); // for displaying in header info
opts.define("s|seconds=b"); // for displaying time info in seconds
opts.define("k|keyboard=b"); // for handling computer keyboard
opts.define("A|no-active-sensing=b");// for displaying active sensing 0xfe
opts.define("cpu=d:0.0"); // for setting the CPU speed
opts.define("author=b");
opts.define("version=b");
opts.define("example=b");
opts.define("h|help=b");
opts.process();
if (opts.getBoolean("author")) {
cout << "Written by Craig Stuart Sapp, "
"craig@ccrma.stanford.edu, November 1998" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << "cinmidi, version 1.2 (2 Jul 1999)\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.getBoolean("cpu")) {
sigtimer.setCpuSpeed((int64bits)(opts.getDouble("cpu") * 1000000.0));
}
if (opts.getBoolean("list")) {
// display the possible MIDI inputs to use with -p option
cout << "\nMidi Input ports: " << MidiInput::getNumPorts() << endl;
for (int i=0; i<MidiInput::getNumPorts(); i++) {
cout << "\t" << i << ": " << MidiInput::getName(i) << '\n';
}
cout << endl;
exit(0);
}
if (opts.getBoolean("absolute")) {
absoluteQ = 1;
}
if (opts.getBoolean("interpret")) {
interpretQ = 0;
}
if (opts.getBoolean("decimal")) {
style = 10;
} else if (opts.getBoolean("hexadecimal")) {
style = 16;
} else if (opts.getBoolean("binary")) {
style = 2;
} else {
style = 0;
}
if (style != 0) {
bytesQ = 1;
}
if (opts.getBoolean("nooffs")) {
suppressOffQ = 1;
}
if (opts.getBoolean("seconds")) {
secondsQ = 1;
}
if (opts.getBoolean("keyboard")) {
keyboardQ = 0;
}
const char *filterString = opts.getString("filter");
char current;
for (int i=0; i<(int)strlen(filterString); i++) {
current = filterString[i];
switch (tolower(current)) {
case '8': filter[0] = 1; break;
case '9': filter[1] = 1; break;
default:
if (!isdigit(current) && isxdigit(current)) {
if (current-'a'+2 >= 8 || current-'a'+2 < 0) {
cerr << "Error in filter string" << endl;
exit(1);
} else {
filter[current - 'a' + 2] = 1;
}
}
}
}
const char *cfilterString = opts.getString("chan-filter");
char stringc [2] = {0};
int cindex;
for (int j=0; j<(int)strlen(cfilterString); j++) {
if (isxdigit(cfilterString[j])) {
stringc[0] = cfilterString[j];
cindex = strtol(stringc, NULL, 16);
if (cindex < 0 || cindex > 15) {
cout << "Error in channel string: " << cfilterString << endl;
exit(1);
} else {
cfilter[cindex] = 1;
}
}
}
if (opts.getBoolean("output")) {
fileQ = 1;
outputfile.open(opts.getString("output"), ios::out);
if (!outputfile.is_open()) {
cout << "Error: cannot open file: "<< opts.getString("output") << endl;
exit(1);
}
}
midi.setInputPort(opts.getInteger("inport"));
midi.setOutputPort(opts.getInteger("outport"));
midi.openInput();
midi.openOutput();
if (opts.getArgCount() != 0) {
usage(opts.getCommand());
exit(1);
}
activeSensingQ = !opts.getBoolean("no-active-sensing");
}
//////////////////////////////
//
// displayHeader -- display data formatting information
//
void displayHeader(ostream& out) {
out << HEADER_START << "\n";
if (options.getBoolean("user")) {
out << HEADER_START << "Recorded by: ";
out << options.getString("user") << endl;
}
out << HEADER_START << "Style: ";
switch (style) {
case 10: out << "decimal\n"; break;
case 16: out << "hexadecimal\n"; break;
case 2: out << "binary\n"; break;
default: out << "default\n";
}
out << HEADER_START << "Timing: ";
if (absoluteQ) {
out << "absolute";
if (secondsQ) {
out << " seconds\n";
} else {
out << " milliseconds\n";
}
out << HEADER_START << "Message format: received-time, command-byte,"
" parameter-byte(s)\n";
} else {
out << "delta";
if (secondsQ) {
out << " seconds\n";
} else {
out << " milliseconds\n";
}
out << HEADER_START << "Message format: delta-time, MIDI command-byte, "
"MIDI parameter-byte(s)\n";
}
out << HEADER_START << "Format: asciimidi 1.0\n";
out << HEADER_START << "Command-line: "
<< options.getCommandLine() << '\n';
out << HEADER_START << "Input Port: " << options.getInteger("inport")
<< ": " << MidiInput::getName(options.getInteger("inport")) << '\n';
out << HEADER_START << "Cpu Speed: "
<< (double)sigtimer.getCpuSpeed() / 1000000.0 << " MHz" << '\n';
out << HEADER_START << '\n';
out << endl;
}
//////////////////////////////
//
// displayMessage -- displays the MIDI input message in the style
// specified on the command-line
//
void displayMessage(ostream& out, MidiMessage message, int style) {
if ((!activeSensingQ) && (message.p0() == 0xfe)) {
// don't display incoming active-sensing messages
return;
}
if (secondsQ) {
out << dec << message.time / 1000.0 << "\t";
} else {
out << dec << message.time << "\t";
}
if (bytesQ) {
printbyte(out, (int)message[0], 0, style);
if (message.p0() == 0xf0) {
int byteCount = midi.getSysexSize((int)message.p1());
uchar* sysexmessage = midi.getSysex((int)message.p1());
for (int k=1; k<byteCount-1; k++) {
out << " ";
printbyte(out, (int)sysexmessage[k], k, style);
}
out << " ";
printbyte(out, (int)sysexmessage[byteCount-1], 0, style);
} else { // not a system exclusive
int parameterCount = message.getParameterCount();
for (int i=0; i<parameterCount; i++) {
out << " ";
printbyte(out, (int)message[i+1], i+1, style);
}
}
}
if (bytesQ && interpretQ && (style == 2)) {
if (message.getParameterCount() <= 1) {
out << '\t';
}
out << "\t ; ";
} else if (bytesQ && interpretQ) {
if (message.getParameterCount() <= 1) {
out << '\t';
}
out << "\t\t; ";
}
if (interpretQ) {
interpret(out, message);
}
out << endl;
}
//////////////////////////////
//
// examineInputForCommand -- Look for a command embedded in user
// computer keyboard input.
//
void examineInputForCommand(const char* string) {
if (strncmp(HEADER_START, string, 3) != 0) {
// not a possible command string
return;
}
int length = strlen(string);
int i;
for (i=0; i<length+1; i++) {
if (string[i] == ':') {
goto command_found;
}
}
return;
// some commands are:
// ";; Send: bytes = send these MIDI bytes on MIDI output
// ";; Filter On: x or xx
// ";; Filter Off: x or xx
command_found:
char* buffer = new char[length + 1];
char* arguments = new char[length + 1];
strcpy(buffer, string);
char* command = strtok(buffer, ";: \t");
int j = 0;
while (command[j] != '\0') {
command[j] = tolower(command[j]);
j++;
}
if (strcmp(command, "send") == 0) {
strncpy(arguments, strtok(NULL, ""), length);
userCommandSend(arguments);
}
delete [] buffer;
delete [] arguments;
}
//////////////////////////////
//
// example -- shows various command-line option calls to program.
//
void example(void) {
cout <<
"# display MIDI input data in binary form:\n"
"cinmidi -2\n"
"\n"
"# don't display note-off messages:\n"
"cinmidi -n\n"
"\n"
"# don't display pitchbend data:\n"
"cinmidi -fe\n"
"\n"
"# display only pitchbend data:\n"
"cinmidi -f 89abcdf\n"
"\n"
"# display only note-on messages:\n"
"cinmidi -n -f 8abcdef\n"
"\n"
<< endl;
}
//////////////////////////////
//
// getGmInst -- the General MIDI instrument name.
//
const char* getGmInst(int number) {
return GMinstrument[number % 0x80];
}
//////////////////////////////
//
// getKey -- return the key name of the specified MIDI key number.
//
char* getKey(int keynum) {
static char name[16] = {0};
static char temp[16] = {0};
int octave = keynum / 12 - 1;
switch (keynum % 12) {
case 0: strcpy(name, "C"); break;
case 1: strcpy(name, "C#"); break;
case 2: strcpy(name, "D"); break;
case 3: strcpy(name, "Eb"); break;
case 4: strcpy(name, "E"); break;
case 5: strcpy(name, "F"); break;
case 6: strcpy(name, "F#"); break;
case 7: strcpy(name, "G"); break;
case 8: strcpy(name, "Ab"); break;
case 9: strcpy(name, "A"); break;
case 10: strcpy(name, "Bb"); break;
case 11: strcpy(name, "B"); break;
default: strcpy(name, "");
}
sprintf(temp, "%d", octave);
strcat(name, temp);
return name;
}
//////////////////////////////
//
// interpret -- convert a MIDI message to English.
//
void interpret(ostream& out, MidiMessage message) {
out << dec; // set output to decimal notation
switch (message.p0() & 0xf0) {
case 0x80: // NOTE OFF
out << "NOTEOFF chan: " << (int)(message.p0() & 0x0f) + 1
<< " key:" << getKey(message.p1()) << " vel: "
<< (int)message.p2();
break;
case 0x90: // NOTE ON
out << "NOTE chan:" << (int)(message.p0() & 0x0f) + 1
<< " key:" << getKey(message.p1())
<< " vel:" << (int)message.p2();
if (message.p2() == 0) {
out << " OFF";
}
break;
case 0xa0: // AFTERTOUCH
out << "AFTERTOUCH chan:" << (int)(message.p0() & 0x0f) + 1
<< " key:" << getKey(message.p1())
<< " val:" << (int)message.p2();
break;
case 0xb0:
out << "CONTROL chan:" << (int)(message.p0() & 0x0f) + 1
<< " #:" << (int)message.p1()
<< " val:" << (int)message.p2();
switch (message.p1()) {
case 7: out << " (volume)"; break;
case 64: out << " (sustain)"; break;
}
break;
case 0xc0: // PATCH CHANGE
out << "PATCH chan:" << (int)(message.p0() & 0x0f) + 1
<< " inst:" << (int)message.p1()
<< " (" << getGmInst(message.p1()) << ")";
break;
case 0xd0: // CHANNEL PRESSURE
out << "PRESSURE chan:" << (int)(message.p0() & 0x0f) + 1
<< " val:" << (int)message.p1();
break;
case 0xe0: // PITCH BEND
out << "PITCHBEND chan:" << (int)(message.p0() & 0x0f) + 1
<< " val:" << makePitchBend(message.p1(), message.p2());
break;
case 0xf0:
switch (message.p0()) {
// system common messages
case 0xf0: out << "SYSEX"; interpretSysex(out, message); break;
case 0xf1: out << "MIDI Time Code Quarter Frame"; break;
case 0xf2: out << "Song Position Pointer"; break;
case 0xf3: out << "Song Select"; break;
case 0xf4: out << "Undefined sys com msg"; break;
case 0xf5: out << "Undefined sys com msg"; break;
case 0xf6: out << "Tune Request"; break;
// system real time messages
case 0xf8: out << "Timing Clock"; break;
case 0xf9: out << "Undefined sys realtime msg"; break;
case 0xfa: out << "Start"; break;
case 0xfb: out << "Continue"; break;
case 0xfc: out << "Stop"; break;
case 0xfd: out << "Undefined"; break;
case 0xfe: out << "Active Sensing"; break;
case 0xff: out << "System Reset"; break;
}
break;
}
}
//////////////////////////////
//
// interpretSysex -- try to identify the system exclusive message
//
void interpretSysex(ostream& out, MidiMessage message) {
if (message.p0() != 0xf0) {
cout << "Error: interpretSysex needed a sysex message"
" but got: " << hex << (int)message.p0() << dec << endl;
exit(1);
}
uchar* sysex = midi.getSysex(message.p1());
int length = midi.getSysexSize(message.p1());
// check for Currently defined Universal System Exclusive Messages
// first the non-real time messages where sysex[1] == 0x7e
if (length >= 5 && sysex[1] == 0x7e) {
out << ": UNIV";
if (sysex[3] == 0x01) {
out << ": Sample Dump Header";
return;
} else if (sysex[3] == 0x02) {
out << ": Sample Data Packet";
return;
} else if (sysex[3] == 0x03) {
out << ": Sample Dump Request";
return;
} else if (sysex[3] == 0x04) {
out << ": MIDI Time Code";
switch (sysex[4]) {
case 0x00: out << ": Special"; break;
case 0x01: out << ": Punch In Points"; break;
case 0x02: out << ": Punch Out Points"; break;
case 0x03: out << ": Delete Punch In Point"; break;
case 0x04: out << ": Delete Punch Out Point"; break;
case 0x05: out << ": Event Start Point"; break;
case 0x06: out << ": Event Stop Point"; break;
case 0x07: out << ": Event Start Points + add. info"; break;
case 0x08: out << ": Event Stop Points + add. info"; break;
case 0x09: out << ": Delete Event Start Point"; break;
case 0x0a: out << ": Delete Event Stop Point"; break;
case 0x0b: out << ": Cue Points"; break;
case 0x0c: out << ": Cue Points with add. info"; break;
case 0x0d: out << ": Delete Cue Point"; break;
case 0x0e: out << ": Event Name in add. info"; break;
}
return;
} else if (sysex[3] == 0x05) {
out << ": Sample Dump Extensions";
switch (sysex[4]) {
case 0x01: out << ": Multiple Loop Points"; break;
case 0x02: out << ": Loop Points Request"; break;
}
return;
} else if (sysex[3] == 0x06) {
out << ": General Information";
switch (sysex[4]) {
case 0x01: out << ": Identity Request"; break;
case 0x02: out << ": Identity Reply"; break;
}
return;
} else if (sysex[3] == 0x07) {
out << ": File Dump";
switch (sysex[4]) {
case 0x01: out << ": Header"; break;
case 0x02: out << ": Data Packet"; break;
case 0x03: out << ": Request"; break;
}
return;
} else if (sysex[3] == 0x08) {
out << ": MIDI Tuning Standard";
switch (sysex[4]) {
case 0x01: out << ": Bulk Dump Request"; break;
case 0x02: out << ": Bulk Dump Reply"; break;
}
return;
} else if (sysex[3] == 0x09) {
out << ": General MIDI";
switch (sysex[4]) {
case 0x01: out << ": On"; break;
case 0x02: out << ": Off"; break;
}
return;
} else if (sysex[3] == 0x7b) {
out << ": End Of File";
return;
} else if (sysex[3] == 0x7c) {
out << ": Wait";
return;
} else if (sysex[3] == 0x7d) {
out << ": Cancel";
return;
} else if (sysex[3] == 0x7e) {
out << ": NAK (bad transfer)";
return;
} else if (sysex[3] == 0x7f) {
out << ": ACK (good transfer)";
return;
}
}
// check for Currently defined Universal System Exclusive Messages
// now the non-real time messages where sysex[1] == 0x7f
if (length >= 5 && sysex[1] == 0x7f) {
out << ": UNIV";
if (sysex[3] == 0x01) {
out << ": MIDI Time Code";
switch (sysex[4]) {
case 0x01: out << ": Full Message"; break;
case 0x02: out << ": User Bits"; break;
}
return;
} else if (sysex[3] == 0x02) {
out << ": MIDI Show Control";
switch (sysex[4]) {
case 0x00: out << ": Extensions"; break;
default: out << ": Commands"; break;
}
return;
} else if (sysex[3] == 0x03) {
out << ": Notation Info";
switch (sysex[4]) {
case 0x01: out << ": Bar Number"; break;
case 0x02: out << ": Time Sig. (Immediate)"; break;
case 0x42: out << ": Time Sig. (Delayed)"; break;
}
return;
} else if (sysex[3] == 0x04) {
out << ": Device Control";
switch (sysex[4]) {
case 0x01: out << ": Master Volume"; break;
case 0x02: out << ": Master Balance"; break;
}
return;
} else if (sysex[3] == 0x05) {
out << ": Real Time MTC Cueing";
switch (sysex[4]) {
case 0x00: out << ": Special"; break;
case 0x01: out << ": Punch In Points"; break;
case 0x02: out << ": Punch Out Points"; break;
case 0x03: out << ": Reserved"; break;
case 0x04: out << ": Reserved"; break;
case 0x05: out << ": Event Start Points"; break;
case 0x06: out << ": Event Stop Points"; break;
case 0x07: out << ": Event Start Points + add. info"; break;
case 0x08: out << ": Event Stop Points + add. info"; break;
case 0x09: out << ": Reserved"; break;
case 0x0a: out << ": Reserved"; break;
case 0x0b: out << ": Cue Points"; break;
case 0x0c: out << ": Cue Points with add. info"; break;
case 0x0d: out << ": Reserved"; break;
case 0x0e: out << ": Event Name in add. info"; break;
}
return;
} else if (sysex[3] == 0x06) {
out << ": MIDI Maching Control Commands";
return;
} else if (sysex[3] == 0x07) {
out << ": MIDI Maching Control Response";
return;
} else if (sysex[3] == 0x08) {
out << ": MIDI Tuning Standard";
switch (sysex[4]) {
case 0x02: out << ": Note Change";
}
return;
}
}
// If gotten to this point, the sysex starts with a
// manufacturer's ID number.
if (sysex[1] == 0x7f) {
out << ": MANF=non-commercial";
return;
}
if (sysex[1] >= 0x01 && sysex[1] <= 0x1f) {
out << ": MANF=American1";
return;
}
if (sysex[1] >= 0x20 && sysex[1] <= 0x3f) {
out << ": MANF=European1";
return;
}
if (sysex[1] >= 0x40 && sysex[1] <= 0x5f) {
out << ": MANF=Japanese1";
return;
}
if (sysex[1] >= 0x60 && sysex[1] <= 0x7c) {
out << ": MANF=other1";
return;
}
// must be a three byte sysex if get here
if (sysex[1] == 0) {
if (sysex[2] >= 0x20 && sysex[2] <= 0x3f) {
out << ": MANF=European3";
return;
}
if (sysex[2] >= 0x40 && sysex[2] <= 0x5f) {
out << ": MANF=Japanese3";
return;
}
if (sysex[2] >= 0x60 && sysex[2] <= 0x7F) {
out << ": MANF=Other3";
return;
}
else {
out << ": MANF=American3";
return;
}
}
}
//////////////////////////////
//
// makePitchBend -- create a number in range -1 to 1.
//
float makePitchBend(int lsb, int msb) {
int value = (msb << 7) | lsb;
float output = (float)((2.0 * value / 0x3fff) - 1.0);
if ((output < 0.0001) && (output > -0.0001)) {
return 0.0;
} else {
output = (float)(((int)(output * 1000.0))/1000.0);
return output;
}
}
//////////////////////////////
//
// printbyte -- display a byte according to specified style and location.
//
void printbyte(ostream& out, int value, int location, int style) {
int i;
switch (style) {
case 10:
if (value < 100) {
out << ' ';
}
if (value < 10) {
out << ' ';
}
out << dec << value;
break;
case 16:
if (value < 0x10) {
out << ' ';
}
out << hex << value;
break;
case 2:
for (i=0; i<8; i++) {
if (value & (1 << (7-i))) {
out << '1';
} else {
out << '0';
}
}
break;
default:
if (location == 0) {
out << "0x";
printbyte(out, value, 0, 16);
} else {
printbyte(out, value, location, 10);
}
}
}
//////////////////////////////
//
// usage -- how to run this program from the command-line.
//
void usage(const char* command) {
cout <<
"\n"
"Display/Record MIDI input data.\n"
"\n"
"Usage: " << command << " [-a][-i][-x|-d|-2] [-o output][-p port]\n"
"\n"
"Options:\n"
" -a = display MIDI timing in absolute time instead of delta time\n"
" -i = don't display interpretation of MIDI message as comments\n"
" -d = display MIDI bytes in decimal form\n"
" -x = display MIDI bytes in hexadecimal form\n"
" -2 = display MIDI bytes in binary form\n"
" -n = do not display note-off messages\n"
" -f string = filter out specified MIDI commands (e.g. \"bc\" will\n"
" filter out control change and patch change messages)\n"
" -c string = filter out specified MIDI channels, offset zero\n"
" (e.g., \"09AF\", filters channels 1, 10, 11, and 16)\n"
" -p port = use the specified MIDI input port\n"
" -l = list MIDI input ports and then exit\n"
" -o filename = send display to file as well as to screen\n"
" -u string = specify a user name for the header information\n"
" -s = display time values in seconds\n"
" -k = disable keyboard input\n"
" -A = don't display incoming active-sensing messages\n"
" --options = list all options, default values, and aliases\n"
"\n"
<< endl;
}
//////////////////////////////
//
// userCommandSend -- send some midi data out the MIDI out port.
// currently only allows decimal data.
//
void userCommandSend(const char* midibytes) {
char* buffer = new char[strlen(midibytes)+1];
strcpy(buffer, midibytes);
Array<uchar> midiinfo(0);
uchar inputdata;
char* data = strtok(buffer, " \t");
int number;
while (data != NULL && isdigit(data[0])) {
sscanf(data, "%d", &number);
inputdata = (uchar)number;
midiinfo.append(inputdata);
data = strtok(NULL, " \t");
}
midi.rawsend(midiinfo.getBase(), midiinfo.getSize());
}
// md5sum: d16ae16ba1368adc6c0f112b54b7ae86 cinmidi.cpp [20130206]