// // Programmer: Craig Stuart Sapp // Creation Date: Thu May 6 17:15:41 PDT 2000 // Last Modified: Thu Jun 29 00:11:48 PDT 2000 (updated to museinfo 1.1) // Last Modified: Thu Jun 29 00:11:48 PDT 2000 (added shorten options) // Last Modified: Wed May 27 16:19:29 EDT 2009 (added more keyboard commands) // Last Modified: Thu Jun 4 10:24:13 EDT 2009 (added colorizing) // Last Modified: Fri Jun 12 18:25:00 PDT 2009 (added muting) // Filename: ...sig/doc/examples/all/hplay/hplay.cpp // Syntax: C++ // // Description: Play **kern entries in a Humdrum file through MIDI. // #include "synthImprov.h" #include "HumdrumFile.h" #include "Convert.h" #include // include string stream class which can have various names: #ifndef OLDCPP #include #define SSTREAM stringstream #define CSTRING str().c_str() #else #ifdef VISUAL #include #else #include #endif #define SSTREAM strstream #define CSTRING str() #endif /*----------------- beginning of improvization algorithms ---------------*/ // colorization messages: #define COLOR_INVALID -1 #define COLOR_RESET 0 #define COLOR_INIT 1 #define COLOR_BARLINE 2 #define COLOR_CLEAR_SCREEN 3 #define COLOR_MUTE 4 #define COLOR_OVERFILL 5 #define COLOR_INTERPRETATION 6 #define COLOR_TANDEM 7 double tempo = 120.0; // current metronome tempo SigTimer timer; // for playing the humdrum file EventBuffer eventBuffer; // for storing note offs when playing notes HumdrumFile data; // humdrum file to play int linenum = 0; // for keeping track of current line in file double tempoScale = 1.0; // for global adjustment of tempo int velocity = 64; // default velocity to play MIDI notes int echoTextQ = 1; // boolean for displaying input file int fileNumber = 1; // current file number being played int pauseQ = 0; // pause or unpause the music. int colorQ = 0; // colorize the display int hideQ = 0; // hide/show non-kern spines int colormode = 'b'; // w = white background; b = black background int tabsize = 12; // used with 't' and 'T' real-time commands int transpose = 0; // used with 'l' and 'L' real-time commands Array trackmute; // used to mute/unmute tracks int mine = 30; // used with the -m option int shortenQ = 0; // used with the -s option int shortenamount = 30; // used with the -s option // non-synthImprov function declarations: void checkOptions (void); void inputNewFile (void); void playdata (HumdrumFile& data, int& linenum, SigTimer& timer); void printInputLine (HumdrumFile& infile, int line); void processNotes (HumdrumRecord& record); int getMeasureLine (HumdrumFile& data, int number); ostream& colormessage (ostream& out, int messagetype, int mode, int status); ostream& tabPrintLine (ostream& out, HumdrumRecord& record, int tabsize, int sMessage = COLOR_INVALID); int getKernTrack (int number, HumdrumFile& infile); int goBackMeasures (HumdrumFile& data, int startline, int target); int goForwardMeasures (HumdrumFile& data, int startline, int target); void printExclusiveInterpLine(int linenum, HumdrumFile& infile); /*--------------------- maintenance algorithms --------------------------*/ ////////////////////////////// // // description -- This function is called with the user presses // the letter 'D' on the computer keyboard. // void description(void) { printboxtop(); psl(" HUMPLAY -- by Craig Stuart Sapp, 4 May 2000"); psl(" _ = lower volume + = raise volume s = silence current notes"); psl(" , = slow down . = speed up p = toggle MIDI output"); psl(" < = slow down more > = speed up more space = toggle text output"); printboxbottom(); } ////////////////////////////// // // initialization -- This function is called at the start of the program. // void initialization(void) { checkOptions(); timer.setPeriod(500); timer.reset(); eventIdler.setPeriod(0); eventBuffer.setPollPeriod(10); if (colorQ) { colormessage(cout, COLOR_INIT, colormode, colorQ); colormessage(cout, COLOR_CLEAR_SCREEN, colormode, colorQ); print_commands(); sleep(1); } trackmute.setSize(1000); // maximum track in humdrum file assumed to be 1000 // space 1000-10 is used for a special purpose. trackmute.allowGrowth(0); trackmute.setAll(0); } ////////////////////////////// // // finishup -- This function is called just before exiting the program. // void finishup(void) { eventBuffer.off(); colormessage(cout, COLOR_RESET, colormode, colorQ); } /*-------------------- main loop algorithms -----------------------------*/ ////////////////////////////// // // mainloopalgorithms -- This function is called continuously while // the programming is running. It checks to determine whether another // line of the Humdrum file is ready to be played. // void mainloopalgorithms(void) { eventBuffer.checkPoll(); if (pauseQ) { return; } if (timer.expired()) { playdata(data, linenum, timer); if (linenum >= data.getNumLines()) { inputNewFile(); } } } /*-------------------- triggered algorithms -----------------------------*/ ////////////////////////////// // // keyboardchar -- When the user presses a key on the computer keyboard, // it is sent to this program. Certain capital letter keys are // used by the interface (such as "?", "M", "D", "X", "Y", "G"), and // will not be sent to this function. // void keyboardchar(int key) { static int lastkeytime = 0; static int number = 0; if (t_time - lastkeytime > 5000) { // reset the number value if more than 5 seconds has elapsed // since the last key press. number = 0; } lastkeytime = t_time; if (isdigit(key)) { number = number * 10 + (key - '0'); return; } switch (key) { // case 'a': break; case 'b': // set color mode to black colorQ = 1; // turn on colorization automatically colormode = 'b'; colormessage(cout, COLOR_INIT, colormode, colorQ); cout << "!! CHANGING TO BLACK BACKGROUND" << endl; break; case 'c': // toggle colorization colorQ = !colorQ; if (colorQ) { colormessage(cout, COLOR_INIT, colormode, colorQ); cout << "!! COLORIZATION TURNED ON" << endl; } else { colormessage(cout, COLOR_RESET, colormode, !colorQ); cout << "!! COLORIZATION TURNED OFF" << endl; } break; // case 'd': break; case 'e': // print exclusive interpretation info for spines printExclusiveInterpLine(linenum, data); break; // case 'f': break; // case 'g': break; case 'h': // hide/unhide non-kern spine hideQ = !hideQ; if (hideQ) { cout << "!! Hiding non-kern spines" << endl; } else { cout << "!! Showing all spines" << endl; } break; // case 'i': break; // case 'j': break; // case 'k': break; case 'l': // transpose up specified number of semitones if (number < 100) { transpose = number; cout << "!! Transposing " << transpose << " steps up" << endl; } break; case 'L': // transpose down specified number of semitones if (number < 100) { transpose = -number; cout << "!! Transposing " << -transpose << " steps down" << endl; } break; case 'm': // mute or unmute all tracks if (number == 0) { trackmute.setAll(!trackmute[trackmute.getSize()-1]); if (trackmute[0]) { cout << "!! All spines are muted" << endl; } else { cout << "!! All spines are unmuted" << endl; } } else { int tracknum = getKernTrack(number, data); trackmute[tracknum] = !trackmute[tracknum]; if (trackmute[tracknum]) { cout << "!! **kern spine " << number << " is muted" << endl; } else { cout << "!! **kern spine " << number << " is unmuted" << endl; } } break; break; // case 'n': break; case 'o': // set the tempo to a particular value if (number > 20 && number < 601) { cout << "!! TEMPO SET TO " << number << endl; tempo = number; tempoScale = 1.0; } break; case 'p': // toggle music pausing eventBuffer.off(); timer.reset(); pauseQ = !pauseQ; if (pauseQ) { cout << "!! Paused" << endl; } break; case 'q': // toggle display of file while playing echoTextQ = !echoTextQ; if (echoTextQ) { cout << "!! FILE DISPLAY TURNED ON" << endl; } else { cout << "!! FILE DISPLAY TURNED OFF" << endl; } break; // case 'r': break; case 's': // silence notes eventBuffer.off(); break; case 't': // increase tab size tabsize++; // cout << "!! tabsize = " << tabsize << endl; break; case 'T': // decrease tab size tabsize--; if (tabsize < 3) { tabsize = 3; } // cout << "!! tabsize = " << tabsize << endl; break; // case 'u': break; // case 'v': break; case 'w': // set color mode to white colorQ = 1; // turn on colorization automatically colormode = 'w'; colormessage(cout, COLOR_INIT, colormode, colorQ); cout << "!! CHANGING TO WHITE BACKGROUND" << endl; break; case 'x': // clear the screen colormessage(cout, COLOR_CLEAR_SCREEN, colormode, 1); printInputLine(data, linenum-1); break; // case 'y': break; // case 'z': break; case ',': // slow down tempo tempoScale *= 0.97; cout << "!! TEMPO SET TO " << (int)(tempo * tempoScale) << endl; break; case '<': tempoScale *= 0.93; cout << "!! TEMPO SET TO " << (int)(tempo * tempoScale) << endl; break; case '.': // speed up tempo tempoScale *= 1.03; cout << "!! TEMPO SET TO " << (int)(tempo * tempoScale) << endl; break; case '>': tempoScale *= 1.07; cout << "!! TEMPO SET TO " << (int)(tempo * tempoScale) << endl; break; case '=': { int newline = 0; if (number == 0) { newline = 0; } else { newline = getMeasureLine(data, number); } if (newline >= 0) { cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << " =" << number << endl; linenum = newline; eventBuffer.off(); timer.reset(); } } break; case '(': { int newline = goBackMeasures(data, linenum, number); cout << "!! back " << number << " measure" << (number==1? "":"s") << endl; linenum = newline; eventBuffer.off(); timer.reset(); } break; case ')': { int newline = goForwardMeasures(data, linenum, number); cout << "!! forward " << number << " measure" << (number==1? "":"s") << endl; linenum = newline; eventBuffer.off(); timer.reset(); } break; case '+': // louder velocity++; if (velocity > 127) { velocity = 127; } cout << "!! velocity = " << velocity << endl; break; case '_': // sofer velocity--; if (velocity < 1) { velocity = 1; } cout << "!! velocity = " << velocity << endl; break; case '^': // go to the start of the file linenum = 0; cout << "!! Going to start of file" << endl; break; } if (!isdigit(key)) { number = 0; } } /*------------------ end improvization algorithms -----------------------*/ ////////////////////////////// // // printExclusiveInterpLine -- // void printExclusiveInterpLine(int linenum, HumdrumFile& infile) { int dataline = -1; int i; for (i=0; i=0; i--) { if (data[i].isMeasure()) { counter++; if (counter > target) { break; } } } return i; } ////////////////////////////// // // goForwardMeasures -- // int goForwardMeasures(HumdrumFile& data, int startline, int target) { if (target < 0) { target = 1; } if (startline > data.getNumLines()-1) { return data.getNumLines()-1; } int i; int counter = 0; for (i=startline; i target) { break; } } } return i; } ////////////////////////////// // // getMeasureLine -- return the line number on which the measure // number is found. Return -1 if measure is not found. // int getMeasureLine(HumdrumFile& data, int number) { int i; int testnum = -1; for (i=0; i count) { finishup(); exit(0); } data.read(options.getArg(fileNumber)); data.analyzeRhythm("4"); if (fileNumber > 1) { millisleep((float)(1000 * options.getDouble("pause"))); } fileNumber++; } ////////////////////////////// // // playdata -- play the next line of the humdrum file, update the // line number and the time for the next events to be read // from the file. // void playdata(HumdrumFile& data, int& linenum, SigTimer& timer) { double duration = 0; // duration of the current line; int type = data[linenum].getType(); while (linenum < data.getNumLines() && duration == 0.0) { duration = data[linenum].getDuration(); if (type == E_humrec_data) { processNotes(data[linenum]); } else if (type == E_humrec_interpretation) { if (strncmp(data[linenum][0], "*MM", 3) == 0) { tempo = atoi(&data[linenum][0][3]); } } if (echoTextQ) { printInputLine(data, linenum); } if (duration > 0.0) { timer.setPeriod(60000 / tempo / tempoScale * duration); timer.reset(); } linenum++; if (linenum < data.getNumLines()) { type = data[linenum].getType(); } } } ////////////////////////////// // // printInputLine -- print the current line of the file, // omitting the duration field at the end of the line // void printInputLine(HumdrumFile& infile, int line) { if ((line < 0) || (line >= infile.getNumLines())) { return; } HumdrumRecord& record = infile[line]; if (record.isMeasure()) { colormessage(cout, COLOR_BARLINE, colormode, colorQ); tabPrintLine(cout, record, tabsize, COLOR_BARLINE); colormessage(cout, COLOR_INIT, colormode, colorQ); cout << endl; } else if (record.isTandem()) { colormessage(cout, COLOR_TANDEM, colormode, colorQ); tabPrintLine(cout, record, tabsize, COLOR_TANDEM); colormessage(cout, COLOR_INIT, colormode, colorQ); cout << endl; } else if (record.isInterpretation()) { colormessage(cout, COLOR_INTERPRETATION, colormode, colorQ); tabPrintLine(cout, record, tabsize, COLOR_INTERPRETATION); colormessage(cout, COLOR_INIT, colormode, colorQ); cout << endl; } else { tabPrintLine(cout, record, tabsize); cout << endl; } } ////////////////////////////// // // tabPrintLine -- print a line with tabs changed to spaces // default value sMessage = COLOR_INVALID // ostream& tabPrintLine(ostream& out, HumdrumRecord& record, int tabsize, int sMessage) { int i, j; int len; int track; int suppressColor = record.isMeasure() || record.isInterpretation(); // count the **kern spines (used with hideQ varaible): int kerncount = 0; int lastkern = -1; for (i=0; i= 0)) { track = record.getPrimaryTrack(lastkern); lastindex = lastkern; } else { lastindex = record.getFieldCount()-1; track = record.getPrimaryTrack(lastindex); } if ((track > 0) && !suppressColor && trackmute[track]) { colormessage(cout, COLOR_MUTE, colormode, colorQ); } out << record[lastindex]; if ((track > 0) && !suppressColor && trackmute[track]) { colormessage(cout, COLOR_INIT, colormode, colorQ); } out << flush; return out; } ////////////////////////////// // // processNotes -- play all kern notes in the current record and // return the shortest note duration. Don't play notes // if they are in a track which is muted. // void processNotes(HumdrumRecord& record) { NoteEvent note; int pitch = 0; double duration = 0.0; int staccatoQ = 0; int accentQ = 0; int sforzandoQ = 0; int i, j; int notecount = 0; char buffer[128] = {0}; for (i=0; i 127) { continue; } // skip tied notes if (strchr(buffer, '_') || strchr(buffer, ']')) { continue; } accentQ = (int)strchr(buffer, '^'); sforzandoQ = (int)strchr(buffer, 'z'); staccatoQ = (int)strchr(buffer, '\''); note.setChannel(0); note.setKey(pitch); note.setOnTime(t_time); duration = duration * 60000 / tempo / tempoScale; if (shortenQ) { duration -= shortenamount; if (duration < mine) { duration = mine; } } note.setDur((int)duration); if (staccatoQ) { note.setDur((int)(0.5 * note.getDur())); } note.setVelocity(velocity); if (accentQ) { note.setVelocity((int)(note.getVelocity() * 1.3)); } if (sforzandoQ) { note.setVelocity((int)(note.getVelocity() * 1.5)); } note.activate(); note.action(eventBuffer); eventBuffer.insert(note); } } } } ////////////////////////////// // // colormessage -- // ostream& colormessage(ostream& out, int messagetype, int mode, int status) { if (messagetype == COLOR_INVALID) { return out; } #define ANSI_RESET "\033[0m" /* go back to the original colors */ #define ANSI_UNDERLINE_ON "\033[4m" #define ANSI_UNDERLINE_OFF "\033[24m" #define ANSI_CLEAR_SCREEN "\033[2J\033[H" /* and move to the top */ #define ANSI_BLACK "\033[37;40m" /* black background; white text */ #define ANSI_WHITE "\033[30;107m" /* white background; black text */ #define ANSI_BLACK_TX "\033[30m" #define ANSI_RED_TX "\033[31m" #define ANSI_GREEN_TX "\033[32m" #define ANSI_YELLOW_TX "\033[33m" #define ANSI_BLUE_TX "\033[34m" #define ANSI_MAGENTA_TX "\033[35m" #define ANSI_CYAN_TX "\033[36m" #define ANSI_WHITE_TX "\033[37m" #define ANSI_HI_BLACK_TX "\033[90m" #define ANSI_HI_RED_TX "\033[91m" #define ANSI_HI_GREEN_TX "\033[92m" #define ANSI_HI_YELLOW_TX "\033[93m" #define ANSI_HI_BLUE_TX "\033[94m" #define ANSI_HI_MAGENTA_TX "\033[95m" #define ANSI_HI_CYAN_TX "\033[96m" #define ANSI_HI_WHITE_TX "\033[97m" #define ANSI_GRAY_BG "\033[47m" if (status == 0) { return out; // don't do any coloring } if (mode == 'b') { switch (messagetype) { case COLOR_RESET: out << ANSI_RESET; break; case COLOR_INIT: out << ANSI_BLACK; break; case COLOR_BARLINE: out << ANSI_YELLOW_TX; break; case COLOR_CLEAR_SCREEN: out << ANSI_CLEAR_SCREEN; break; case COLOR_MUTE: out << ANSI_BLUE_TX; break; case COLOR_OVERFILL: out << ANSI_HI_BLUE_TX; break; case COLOR_INTERPRETATION:out << ANSI_HI_RED_TX; break; case COLOR_TANDEM: out << ANSI_MAGENTA_TX; break; default: return colormessage(out, COLOR_INIT, mode, status); } } else if (mode == 'w') { switch (messagetype) { case COLOR_RESET: out << ANSI_RESET; break; case COLOR_INIT: out << ANSI_WHITE; break; case COLOR_BARLINE: out << ANSI_GRAY_BG; break; case COLOR_CLEAR_SCREEN: out << ANSI_CLEAR_SCREEN; break; case COLOR_MUTE: out << ANSI_YELLOW_TX; break; case COLOR_OVERFILL: out << ANSI_HI_CYAN_TX; break; case COLOR_INTERPRETATION:out << ANSI_HI_RED_TX; break; case COLOR_TANDEM: out << ANSI_MAGENTA_TX; break; default: return colormessage(out, COLOR_INIT, mode, status); } } out << flush; return out; } // md5sum: cea522f0a889b56a2e92d7b301ffc6a6 humplay.cpp [20090615]