// // 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/hiding/marking. // Last Modified: Thu Mar 24 04:22:03 PDT 2011 Fixes for 64-bit compiling. // Last Modified: Wed Mar 6 20:35:15 PST 2013 SetKey() was missing? // Last Modified: Wed Mar 6 20:35:15 PST 2013 Various changes. // Filename: ...sig/doc/examples/all/hplay/hplay.cpp // Syntax: C++ // // Description: Play **kern entries in a Humdrum file through MIDI. // #include #include #include #include #include "synthImprov.h" #include "HumdrumFile.h" #include "HumdrumStream.h" #include "Convert.h" using namespace std; /*----------------- 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 #define COLOR_MARKS 8 #define COLOR_COMMENT 9 double tempo = 120.0; // current metronome tempo SigTimer timer; // for playing the humdrum file EventBuffer eventBuffer; // for storing note offs when playing notes HumdrumStream streamer; // for inputting multiple Humdrum files/segments 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 noteonlyQ = 0; // hide/show stems and beaming in **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 vector trackmute; // used to mute/unmute tracks vector markers; // storage for markers created with space bar int markerindex = 0; // place to store first marker 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); int getKernNoteOnlyLen (char* buffer, const char* ptr); void printMarkLocation (HumdrumFile& infile, int line, int mindex); void printMarkerInfo (ostream& out, int mindex, int line, HumdrumFile& infile, int style); void printAllMarkers (ostream& out, vector& markers, HumdrumFile& infile); int hasLayout (HumdrumRecord& record); /*--------------------- 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(" , = slow down . = speed up p = pause/play "); psl(" _ = lower volume + = raise volume s = silence current notes "); psl(" < = slow down more > = speed up more space = toggle text output"); psl(" b = black bground w = white bground c = no coloring "); psl(" e = print spine info k = toggle kern-only display "); psl(" #l = transpose up # steps #L = transpose down # steps "); psl(" m = mute all spines #m = mute spine # "); psl(" t = increase tab size T = decrease tab size "); psl(" n = toggle notes only #o = set tempo to # "); psl(" #r = return to marker # R = list markers "); psl(" ^ = go to start of file space = insert marker "); psl(" x = clear screen $ = go to end of file "); psl(" #( = go back # measures #) = go forward # measures "); 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); eventBuffer.setPort(synth.getOutputPort()); if (colorQ) { colormessage(cout, COLOR_INIT, colormode, colorQ); colormessage(cout, COLOR_CLEAR_SCREEN, colormode, colorQ); //if (!options.getBoolean("Q")) { // print_commands(); //} //sleep(1); } trackmute.resize(1000); // maximum track in humdrum file assumed to be 1000 // space 1000-10 is used for a special purpose. std::fill(trackmute.begin(), trackmute.end(), 0); markers.resize(1001); std::fill(markers.begin(), markers.end(), 0); markerindex = 0; } ////////////////////////////// // // finishup -- This function is called just before exiting the program. // void finishup(void) { eventBuffer.off(); printAllMarkers(cout, markers, data); std::fill(markers.begin(), markers.end(), 0); 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()) { printAllMarkers(cout, markers, data); std::fill(markers.begin(), markers.end(), 0); 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 (remove later) case 'k': // 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) { std::fill(trackmute.begin(), trackmute.end(), !trackmute[(int)trackmute.size()-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': // toggle display of note only (supression // of beam and stem display // Also, don't display ![!]LO: lines. noteonlyQ = !noteonlyQ; if (noteonlyQ) { cout << "!! Notes only: supressing beams and stems in **kern data" << endl; } else { cout << "!! Displaying **kern data unmodified" << endl; } 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; } else if (number == 0) { cout << "!! Current tempo: " << tempo * tempoScale << endl; } 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': // return to a marker if (number == 0) { linenum = markers[0]; cout << "!! Going to line " << linenum << endl; eventBuffer.off(); timer.reset(); } else if (number < (int)markers.size()) { linenum = markers[number]; cout << "!! Going to line " << linenum << endl; eventBuffer.off(); timer.reset(); } break; case 'R': // Print a list of all markers printAllMarkers(cout, markers, data); 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 ' ': // mark the measure/beat/line of the music if ((number != 0) && (number < (int)markers.size())) { markerindex = number; } else { markerindex++; if (markerindex > (int)markers.size()-1) { markerindex = 1; } } printMarkLocation(data, linenum == 0 ? 0 : linenum-1, markerindex); 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; case '$': // go to the end of the file linenum = data.getNumLines() - 1; cout << "!! Going to end of file" << endl; break; } if (!isdigit(key)) { number = 0; } } /*------------------ end improvization algorithms -----------------------*/ ////////////////////////////// // // printAllMarkers -- // void printAllMarkers(ostream& out, vector& markers, HumdrumFile& infile) { int mcount = 0; for (int i=1; i<(int)markers.size(); i++) { if (markers[i] > 0) { mcount++; break; } } if (mcount == 0) { return; } colormessage(cout, COLOR_MARKS, colormode, colorQ); out << "**mark\t**line\t**abeat\t**bar\t**beat\n"; for (int i=1; i<(int)markers.size(); i++) { if (markers[i] > 0) { printMarkerInfo(out, i, markers[i], infile, 0); out << "\n"; } } out << "*-\t*-\t*-\t*-\t*-" << endl; colormessage(cout, COLOR_INIT, colormode, colorQ); } ////////////////////////////// // // printMarkerInfo -- // style = 0 : print without text labels. // style = 1 : print with text labels. // void printMarkerInfo(ostream& out, int mindex, int line, HumdrumFile& infile, int style) { double absbeat = infile.getAbsBeat(line); double measure = -10; double beat = infile[line].getBeat(); int i; int counter = 0; for (i=line; i>=0; i--) { if (data[i].isMeasure()) { counter++; if (sscanf(data[i][0], "=%lf", &measure)) { break; } if (counter >= 2) { // don't search more than two measures backwards break; } } } if (style == 0) { out << mindex << "\t" << line+1 << "\t" << absbeat; out << "\t" << measure << "\t" << beat; } else { out << "Mark:" << mindex << "\tLine:" << line+1 << "\tAbsbeat:" << absbeat; if (measure > -10) { out << "\tMeasure:" << measure; } if (beat >= 0) { out << "\tBeat:" << beat; } } } ////////////////////////////// // // printMarkLocation -- print where you are in the file. // void printMarkLocation(HumdrumFile& infile, int line, int mindex) { if (line < 0 || line > infile.getNumLines()-1) { return; } colormessage(cout, COLOR_COMMENT, colormode, colorQ); cout << "!! "; colormessage(cout, COLOR_MARKS, colormode, colorQ); printMarkerInfo(cout, mindex, line, infile, 1); colormessage(cout, COLOR_INIT, colormode, colorQ); cout << endl; markers[mindex] = line; markers[0] = line; } ////////////////////////////// // // 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 list; options.getArgList(list); streamer.setFileList(list); mine = options.getInteger("min"); if (mine < 0) { mine = 0; } shortenQ = !options.getBoolean("shorten"); shortenamount = options.getInteger("shorten"); colorQ = !options.getBoolean("color"); colormode = options.getString("color").c_str()[0]; if ((colormode != 'b') && (colormode != 'w')) { colormode = 'w'; } } ////////////////////////////// // // inputNewFile -- load in a new Humdrum file. // void inputNewFile(void) { data.clear(); linenum = 0; if (!streamer.read(data)) { finishup(); exit(0); } 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; if (data.getNumLines() == 0) { // ignore empty files. return; } 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 { int printline = 1; if (noteonlyQ) { printline = !hasLayout(record); } if (printline) { tabPrintLine(cout, record, tabsize); cout << endl; } } } ////////////////////////////// // // hasLayout -- returns if has layout records, such as starting with // !LO: or !!LO: // int hasLayout(HumdrumRecord& record) { int i; for (i=0; i= 0)) { track = record.getPrimaryTrack(lastkern); lastindex = lastkern; } else { lastindex = record.getFieldCount()-1; track = record.getPrimaryTrack(lastindex); } ptr = record[lastindex]; if (noteonlyQ && strcmp(record.getExInterp(lastindex), "**kern") == 0) { len = getKernNoteOnlyLen(buffer, ptr); ptr = buffer; } else { len = strlen(ptr); } if ((track > 0) && !suppressColor && trackmute[track]) { colormessage(cout, COLOR_MUTE, colormode, colorQ); } out << ptr; if ((track > 0) && !suppressColor && trackmute[track]) { colormessage(cout, COLOR_INIT, colormode, colorQ); } out << flush; return out; } ////////////////////////////// // // getKernNoteOnlyLen -- return the length of the **kern data excluding beaming // and steming information. // int getKernNoteOnlyLen(char* buffer, const char* ptr) { buffer[0] = '\0'; int counter = 0; int len = strlen(ptr); int i; for (i=0; i 127) { continue; } // skip tied notes if (strchr(buffer, '_') || strchr(buffer, ']')) { continue; } accentQ = strchr(buffer, '^') == NULL? 0 : 1; sforzandoQ = strchr(buffer, 'z') == NULL? 0 : 1; staccatoQ = strchr(buffer, '\'') == NULL? 0 : 1; 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.setKey(pitch); if (accentQ) { velocity *= 1.3; } if (sforzandoQ) { velocity *= 1.5; } if (velocity > 127) { velocity = 127; } note.setVelocity(velocity); 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; case COLOR_MARKS: out << ANSI_CYAN_TX; break; case COLOR_COMMENT: out << ANSI_GREEN_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; case COLOR_MARKS: out << ANSI_CYAN_TX; break; case COLOR_COMMENT: out << ANSI_GREEN_TX; break; default: return colormessage(out, COLOR_INIT, mode, status); } } out << flush; return out; }