// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: Thu Feb 18 10:12:41 PST 1999 // Last Modified: Thu Jul 8 15:12:29 PDT 1999 // Filename: ...sig/doc/examples/all/perform/perform.cpp // Syntax: C++; Improv 2.1 // // Description: The original header from the C program is in the comment // below. // // This version of the program with work in both // Linux (gnu C++) and Windows 95/NT/98 (Microsoft // Visual C++) without alterations. // /************************************ PERFORM: the perform command. Takes input from stdin and translates it into MIDI track-ready data which it then sends to a MIDI device. Playback is interactive, as the user can change tempi, search for strings in the file, pause the playback, or goto individual measures using key presses. Revision history: Created: Summer 1993 - Kyle Dawkins Altered: June 10th, 1994 - KLD - Ported to Borland C++ 3.0 from Microsoft's Quick C. - Removed memory management code Altered: June 13th, 1994 - KLD - Rewrote command-line parser - Added help screen for interactive mode Altered: February 1999 - Craig Sapp - Ported to Linux operating system using Gnu C 2.7.2 or later, with Intel Pentium 75 MHz CPU or better. - Ported to Windows 95/NT operating system using Microsoft Visual C++ ver. 5/6, with Intel Pentium 75 MHz CPU or better. - Modified command line options slightly for new operating systems(-p, -l added -i removed). Altered: July 1999 - Craig Sapp - Changed internal data structure for performance data. - Converted to C++ program flow control style. ************************************/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include "Performance.h" #include "Options.h" #include "KeyboardInput.h" #include "LineDisplay.h" typedef unsigned char uchar; typedef unsigned long ulong; // defines #define DEFAULT_TEMPO 80 /* initial tempo if none specified */ #define MAX_TEMPO 999 /* fastest that a quarter note can go */ #define MIN_TEMPO 4 /* slowest that a quarter note can go */ #define FORWARD_SEARCH 1 #define BACKWARD_SEARCH -1 // defines for computer keyboard input modes #define BUFF_STATUS_NOT_USED 0 #define BUFF_STATUS_SEARCH_BACKWARD 1 #define BUFF_STATUS_SEARCH_FORWARD 2 // function declarations void checkOptions(Options& opts); void eraseline(int count); void example(void); void help(void); void help_screen (void); int keyboardchar(int key); void runloop(Performance& data); void processBufferStatus(int keybufferstatus); // global variables Performance data; // where the MIDI data resides KeyboardInput keyboard; // the computer keyboard interface. Options options; // the command-line interface. LineDisplay display; // for displaying and erasing lines. int measure_flag = 0; // for returning to a measure #define KEY_BUFFER_SIZE 1024 char keybuffer[KEY_BUFFER_SIZE] = {0}; // computer keyboard commands int keybufferindex = 0; // location in computer keyboard buffer int keybufferstatus = BUFF_STATUS_NOT_USED; // global variables associated with command-line options double default_tempo = DEFAULT_TEMPO; // for command line tempo setting int default_vel = 64; // velocity of note if none given int echo = 1; // true of echoing text or not. istream* input = NULL; // for input datafile to perform. /////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { options.setOptions(argc, argv); checkOptions(options); if (options.getArgCount() == 0) { data.read(cin); runloop(data); } else { // perform each command-line input file in sequence for (int i=1; i<=options.getArgCount(); i++) { data.read(options.getArg(i)); runloop(data); // pause between performances according to "wait" option if (i < options.getArgCount()) { millisleep((float)(1000.0 * options.getDouble("wait"))); } } } return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // runloop -- the main interpreter of computer keyboard input and // performance realization. // void runloop(Performance& data) { int quitFlag = 0; while (!quitFlag && !data.eof()) { // determine if it is time to play another note data.perform(); // check the computer keyboard for input if (keyboard.hit()) { quitFlag = keyboardchar(keyboard.getch()); } // give unnecessary cpu time to other processes on computer millisleep(1); } cout << endl; if (!quitFlag) { data.stop(); return; } } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // checkOptions -- check the command line options for any arguments. // Options are: // -t <double> sets the amount by which the tempo is multiplied // -g turns OFF global command echo // -i <num> sets the ioaddress of the card (deactivated) // -v <int> sets the default velocity for keys with unspecified velocities // -\? or -h prints the help screen // New options: // -p <num> set the MIDI output to port <num> (replaces -i functionality) // -l lists possible MIDI output ports and then exits immediately // --help same as -h option. // --wait <num> wait <num> seconds after each file to perform. // // void checkOptions(Options& opts) { opts.define("tempo-scale|t=d:1.0"); opts.define("tempo=d:80"); opts.define("global-echo|g=b"); opts.define("ioaddress|i=i:330 hex"); opts.define("velocity|v=i:64"); opts.define("port|p=i:0"); opts.define("list-ports|l=b"); opts.define("help|?|h=b"); opts.define("wait|w=d:1.0 seconds"); opts.define("author:b"); opts.define("version:b"); opts.define("example:b"); opts.process(); if (opts.getBoolean("author")) { cout << "Initial author: Kyle Dawkins, 1993-94\n" << "Revised by: Craig Stuart Sapp (craig@ccrma.stanford.edu) 1999" << endl; exit(0); } if (opts.getBoolean("version")) { cout << "Humdrum tool: perform created 1993, version 8 July 1999" << endl; cout << "compiled: " << __DATE__ << endl; exit(0); } if (opts.getBoolean("help")) { help(); exit(0); } if (opts.getBoolean("example")) { example(); exit(0); } if (opts.getBoolean("list-ports")) { int count = data.getNumPorts(); for (int i=0; i<count; i++) { data.setPort(i); cout << "\t" << i << ":\t" << data.getName() << endl; } exit(0); } else { data.setPort(opts.getInteger("port")); cout << "opening MIDI output port " << opts.getInteger("port") << ":\t" << data.getName() << endl; data.open(); } default_tempo = opts.getDouble("tempo") * opts.getDouble("tempo-scale"); if (default_tempo > MAX_TEMPO) { default_tempo = MAX_TEMPO; cerr << "Warning: reset tempo to: " << MAX_TEMPO << endl; } else if (default_tempo < MIN_TEMPO) { default_tempo = MIN_TEMPO; cerr << "Warning: reset tempo to: " << MIN_TEMPO << endl; } default_vel = opts.getInteger("velocity"); if (default_vel > 127) { cerr << "Warning: reset default velocity to: " << 127 << endl; default_vel = 127; } else if (default_vel < 1) { cerr << "Warning: reset default velocity to: " << 1 << endl; default_vel = 1; } if (opts.getBoolean("global-echo")) { echo = 0; } else { echo = 1; } if (opts.getBoolean("ioaddress")) { cout << "Warning: the -i option is disabled, use the -p option" << endl; } } ////////////////////////////// // // example -- give an example useage of this program // void example(void) { cout << "No examples yet" << endl; } ////////////////////////////// // // help -- prints out a help page for the perform command including // invocation information and options. // void help(void) { cout << "\n" "PERFORM : Play Humdrum **MIDI files\n" "\n" "This command provides an interactive, stream-oriented MIDI-sequencer for\n" "proof-listening and auditioning of **MIDI-format inputs.\n" "\n" "Some Interactive commands: <cr> : return to beginning\n" "<ESC> : terminate perform p : panic reset\n" "<space> : pause; suspend playback < : reduce tempo\n" " /<string>: search forward for string > : increase tempo\n" " ?<string>: search backwards for string <num>- : back <num> measures\n" " <num><cr>: go to measure <num> <num>+ : forward <num> measures\n" "\n" "Syntax:\n" "\n" " perform [-g] [-i hex] [-t n.n] [-v n] [inputfile]\n" "\n" "Options:\n" "\n" " -g : suppress echoing of performance text to standard output\n" " -p n : use port n for MIDI output\n" " -l : list possible MIDI output ports, then exit\n" " -t n.n : set initial tempo to n.n times the default tempo\n" " -v n : set default MIDI key-velocity value: 1 (slow) to 127 (fast)\n" << endl; } ////////////////////////////// // // help_screen -- prints out all of the interactive key commands that // are available during the perform process. // void help_screen(void) { cout << "\n" "Key Function \n" "--- -------- \n" "<Esc> or q Quit \n" "<space> Pause playback \n" "f Flag measure \n" "p Panic \n" "P Power panic \n" "< or , Slower tempo \n" "> or . Faster tempo \n" "<enter> Restart from beginning of input file\n" "- Go to previous barline \n" "+ Go to next barline \n" "<number><enter> Go to measure <number> \n" "<number>- Go back <number> measures \n" "<number>+ Go forward <number> measures \n" "/<string> Search forward for occurrence of <string>\n" "?<string> Search backward for occurrence of <string>\n" "h Display this help screen \n" << endl; } ////////////////////////////// // // keyboardchar -- handle input from the computer keyboard. // int keyboardchar(int key) { int quitflag = 0; switch (key) { case 'p': // panic data.tacet(); break; case 'P': // super panic data.silence(); break; case 'F': // flag a measure case 'f': // flag a measure data.displayClear(); measure_flag = data.getMeasure(); cout << "Measure " << measure_flag << " flagged." << endl; break; case ' ': // halt case 'h': // halt case 'H': // halt data.pause(); data.displayClear(); if (tolower(key) == 'h') { help_screen(); } cout << "Press space to continue..." << flush; break; case '0': // bar number case '1': // bar number case '2': // bar number case '3': // bar number case '4': // bar number case '5': // bar number case '6': // bar number case '7': // bar number case '8': // bar number case '9': // bar number cout << "+++ Command Not ready" << endl; break; case '-': // relative back new bar case '_': // relative back new bar data.backBar(1); break; case '+': // relative forward new bar case '=': // relative forward new bar data.forwardBar(1); break; case 0x0d: // return character processBufferStatus(keybufferstatus); break; case '.': // speed up tempo case '>': // speed up tempo { double current_tempo = data.getTempo() + 6; if (current_tempo > MAX_TEMPO) { current_tempo = MAX_TEMPO; } else if (current_tempo < MIN_TEMPO) { current_tempo = MIN_TEMPO; } data.setTempo(current_tempo); } break; case ',': // slow down tempo case '<': // slow down tempo { double current_tempo = data.getTempo() - 6; if (current_tempo > MAX_TEMPO) { current_tempo = MAX_TEMPO; } else if (current_tempo < MIN_TEMPO) { current_tempo = MIN_TEMPO; } data.setTempo(current_tempo); } break; case '/': // search forward keybufferstatus = BUFF_STATUS_SEARCH_FORWARD; break; case '?': // search backward keybufferstatus = BUFF_STATUS_SEARCH_BACKWARD; break; case 0x08: // backspace cout << "Not yet ready" << endl; break; case 0x1b: // quit (Escape) case 'q': // quit case 'Q': // quit data.stop(); quitflag = 1; break; default: break; } return quitflag; } ////////////////////////////// // // processBufferStatus -- figure out what to do with the // keyboard buffer // void processBufferStatus(int keybufferstatus) { switch (keybufferstatus) { case BUFF_STATUS_SEARCH_BACKWARD: break; case BUFF_STATUS_SEARCH_FORWARD: break; case BUFF_STATUS_NOT_USED: break; default: cout << "Unknown keyboard buffer status flag" << endl; exit(1); } } // md5sum: 0380f88b1461df552e30b38784a54f80 perform.cpp [20050403]