// // Programmer: Craig Stuart Sapp // Creation Date: Thu Mar 8 16:27:39 PST 2001 // Last Modified: Thu Mar 8 16:27:45 PST 2001 // Filename: ...sig/doc/examples/sig/sigControl/midirec.cpp // Syntax: C++; improv 2.2 // #include "improv.h" #include "Convert.h" #include #include // Here is the syntax of a header comment: #define HEADER_START "!! " // 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 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 int starttimes[128] = {0}; int onvelocities[128] = {0}; int beatkey = -1; int measurekey = -1; int restkey = -1; int newlinekey = -1; int restartkey = -1; int flatkey = -1; int sharpkey = -1; int pausekey = -1; int counte = 0; int countstatus = 1; int intervaltime = -1; // function declarations void checkOptions (Options& opts); void displayMessage (ostream& out, MidiMessage message, int style, int duration = 0, int velocity = 0); void displayHeader (ostream& out); void examineInputForCommand (const char* inputBuffer); void example (void); 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 /////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { options.setOptions(argc, argv); checkOptions(options); int i; for (i=0; i<128; i++) { starttimes[i] = -1; } displayHeader(cout); if (fileQ) { displayHeader(outputfile); } KeyboardInput keyboard; // for typing comments into output file char keych; // character from keyboard MidiMessage message; int key = 0; int duration = 0; int velocity = 0; 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; } if (((message.p0() & 0xf0) == 0x90) || ((message.p0() & 0xf0) == 0x80)) { key = message.p1(); if ((message.p0() & 0xf0) == 0x90 && message.p2() > 0 && beatkey < 0) { beatkey = key; restkey = key - 1; sharpkey = key - 2; pausekey = key - 3; flatkey = key - 4; restartkey = key - 5; newlinekey = key - 6; measurekey = key - 7; } if (countstatus && key != beatkey) { countstatus = 0; } if (countstatus && (message.p0() & 0xf0) == 0x90 && (message.p2() > 0)) { counte++; } if (starttimes[key] >= 0) { // note was already sounding, so now going to print it out duration = message.time - starttimes[key]; velocity = onvelocities[key]; starttimes[key] = -1; displayMessage(cout, message, style, duration, velocity); if (fileQ) { displayMessage(outputfile, message, style, duration, velocity); } } else { // note was just turned on starttimes[key] = message.time; onvelocities[key] = message.p2(); } } else { 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) { // options are: 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("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("list")) { // display the possible MIDI inputs to use with -p option cout << "\nMidi Input ports: " << MidiInput::getNumPorts() << endl; for (int i=0; i= 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")); if (opts.getArgCount() != 0) { usage(opts.getCommand()); exit(1); } } ////////////////////////////// // // 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: midirec 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 << '\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, int duration, int velocity) { int key = message.p1(); if (((message.p0() & 0xf0) == 0x90) || ((message.p0() & 0xf0) == 0x80)) { char buffer[32] = {0}; if (secondsQ) { out << (message.time - duration)/1000.0 << "\t"; } else { out << message.time - duration << "\t"; } if (key == restkey) { out << "rest"; int ioitime = message.time - intervaltime; out << "\ti=" << ioitime << endl; intervaltime = message.time - duration; } else if (key == measurekey) { out << "measure" << endl; } else if (key == pausekey) { out << "pause" << endl; } else if (key == sharpkey) { out << "sharp" << endl; } else if (key == flatkey) { out << "flat" << endl; } else if (key == newlinekey) { out << "newline" << endl; } else if (key == restartkey) { out << "restart" << endl; } else if (key == pausekey) { out << "pause" << endl; } else if (key == beatkey) { out << "beat"; if (countstatus) { out << " " << counte << endl; } else { out << endl; } } else { int ioitime = message.time - intervaltime - duration; if (ioitime > 0x7fffffff) { ioitime = message.time - intervaltime; } out << Convert::base12ToKern(buffer, key); out << "\ti=" << ioitime << "\td=" << duration << "\tv=" << velocity << endl; intervaltime = message.time - duration; } 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= 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" " --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 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: fb155ac1460b0fc2b1f27a8c238d37f8 midirec.cpp [20050403]