Goto: [ Program Documentation ]
// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: 4 January 1998 // Last Modified: Fri Jan 9 18:30:17 GMT-0800 1998 // Filename: ...sig/doc/examples/improv/synthImprov/loop2/loop2.cpp // Syntax: C++; synthImprov 2.0 // // Description: This is a (rhythm) looping program. // #include "synthImprov.h" #ifndef OLDCPP #include <fstream> using namespace std; #else #include <fstream.h> #endif /*----------------- beginning of improvization algorithms ---------------*/ #define TRIGVEL 100 int beats; // number of beats in the loop int subdivisions; // number of subdivisions per beat int tempo; // number of beats per minute SigTimer metronome; // keeps track of curnt beat and subdivision int instrument[10]; // instruments, midi keynumbers int size = 0; // size of loop array = beats * subdivisions char* storage[10] = {NULL}; // array for storing looped notes, store vel int silenceQ[10] = {0}; // whether or not to have track sound int silenceHitQ = 0; // whether silence shift key was hit int unsilenceHitQ = 0; // whether silence shift key was hit int clickTrack = 0; // click track, 1=on, 0=off int defaultinst[10] = {0, GM_LOW_TOM, GM_LOW_MID_TOM, GM_HIGH_MID_TOM, GM_HIGH_TOM, GM_HI_BONGO, GM_MUTE_CUICA, GM_OPEN_CUICA, GM_MUTE_TRIANGLE, GM_ACOUSTIC_BASS_DRUM}; /*--------------------- maintenance algorithms --------------------------*/ void description(void) { printboxtop(); psl(" Rhythm loops 2 -- Craig Stuart Sapp <craig@ccrma.stanford.edu>"); psl(" 9 January 1998 -- version 22 November 1998"); printintermediateline(); psl(" Attack Trigger row: 1 2 3 4 5 6 7 8 9 "); psl(" Silence Trigger row: q w e r t y u i o "); psl(" Clear Notes row: a s d f g h j k l"); psl(" "); psl(" Commands:"); psl(" z = clear all notes"); psl(" x = change beat pattern"); psl(" c = toggle click track"); psl(" - = slow down tempo = = speed up tempo"); psl(" v = load loop 1 , = save loop 1"); psl(" b = load loop 2 . = save loop 2"); psl(" n = load loop 3 / = save loop 3"); psl(" 0 = silence all notes on all tracks at current beat location"); psl(" "); psl(" Two-key commands:"); psl(" p + number = silence a rhythm track"); psl(" ; + number = unsilence a rhythm track"); psl(""); printboxbottom(); cout << endl; } void zero(void) { for (int inst=0; inst<10; inst++) { for (int i=0; i<size; i++) { storage[inst][i] = 0; } } } void initialization(void) { int inst; cout << "How many beats in the loop: "; echoKeysOn(); cin >> beats; echoKeysOff(); if (beats < 1 || beats > 100) { cout << "beats set to 1." << endl; beats = 1; } cout << "How many subdivisions per beat: "; echoKeysOn(); cin >> subdivisions; echoKeysOff(); if (subdivisions < 1 || subdivisions > 100) { cout << "subdivisions set to 1." << endl; subdivisions = 1; } cout << "How many beats per minute: "; echoKeysOn(); cin >> tempo; echoKeysOff(); if (tempo < 1 || tempo > 1000) { cout << "Tempo set to 120 beats per minute." << endl; tempo = 120; } size = beats * subdivisions; for (inst=0; inst<10; inst++) { if (storage[inst] != NULL) delete [] storage[inst]; storage[inst] = new char[size]; } zero(); metronome.setPeriod(30000.0/(tempo*subdivisions)); metronome.reset(); for (inst=1; inst<10; inst++) { if (instrument[inst] == 0) { instrument[inst] = defaultinst[inst]; } } } void finishup(void) { for (int inst=0; inst<10; inst++) { delete [] storage[inst]; } } void saveLoop(const char* aFilename) { fstream output; output.open(aFilename, ios::out); if (!output) { cout << "Cannot write file: " << aFilename << endl; return; } output << "Comment: Loop2 " << endl << endl; output << "Beats: " << beats << endl; output << "Subdivisions: " << subdivisions << endl; output << "Tempo: " << tempo << endl << endl; for (int inst=1; inst<10; inst++) { output << "Instrument: " << inst << " " << instrument[inst]; for (int i=0; i<size; i++) { if (i % subdivisions == 0) output << endl; output << setw(4) << (int)storage[inst][i]; } output << endl << endl; } output << endl; } void loadLoop(const char* aFilename) { int inst, i, temp; int instSetQ = 0; fstream input; #ifndef OLDCPP input.open(aFilename, ios::in); #else input.open(aFilename, ios::in | ios::nocreate); #endif if (!input) { cout << "Cannot read file: " << aFilename << endl; return; } static char buffer[1000]; while (!input.eof()) { // get command input >> buffer; switch (tolower(buffer[0])) { case 'b': // beats per loop if (instSetQ) { cerr << "Error: beat command after instrument loop" << endl; return; } input >> beats; break; case 'c': // comment // get rest of line input.getline(buffer, 1000, '\n'); cout << buffer << endl; break; case 's': // subdivisions per beat if (instSetQ) { cerr << "Error: subdivision command after instrument loop" << endl; return; } input >> subdivisions; break; case 't': // tempo input >> tempo; break; case 'i': // instrument track if (instSetQ == 0) { instSetQ = 1; for (i=0; i<10; i++) { if (storage[i] != NULL) delete [] storage[i]; size = beats * subdivisions; storage[i] = new char[size]; } } // first read the instrument number input >> inst; if (inst < 0 || inst >= 10) { cerr << "Error: invalid instrument number: " << inst << endl; return; } // then read the rhythm instrument to assign to that instrument input >> instrument[inst]; // now read the attack velocities for the instrument for (i=0; i<size; i++) { input >> temp; storage[inst][i] = (char)temp; } break; default: ; // ignore the command } } // set the metronome metronome.setPeriod(30000.0/(tempo * subdivisions)); metronome.reset(); } /*-------------------- main loop algorithms -----------------------------*/ void playTracks(int position); void checkloop(void) { static int lastposition = -1; int current = metronome.expired(); if (current >= size*2) { metronome.update(size*2); // tempo updated only at barlines metronome.setPeriod(30000.0/(tempo*subdivisions)); current -= size*2; } if (current/2 != lastposition) { lastposition = current/2; if (lastposition >= size) { cerr << "Error: out of bounds in array: " << lastposition << endl; exit(1); } else { playTracks(lastposition); } if (clickTrack && (lastposition % subdivisions == 0)) { if (lastposition == 0) { synth.play(9, GM_CLAVES, 100); } else { synth.play(9, GM_CLAVES, 50); } } } } void playTracks(int position) { static int inst; for (inst=1; inst<10; inst++) { if (storage[inst][position] != 0 && silenceQ[inst] != 1) { synth.play(9, instrument[inst], storage[inst][position]); } } } void mainloopalgorithms(void) { checkloop(); } /*-------------------- triggered algorithms -----------------------------*/ void processPercussion(int inst); void restPercussion(int inst); void clearPercussion(int inst); void keyboardchar(int key) { if (silenceHitQ) { int track = key - '0'; silenceHitQ = 0; if (track >=0 && track < 10) { silenceQ[track] = 1; return; } } else if (unsilenceHitQ) { int track = key - '0'; unsilenceHitQ = 0; if (track >=0 && track < 10) { silenceQ[track] = 0; return; } } // determine if this is a new note switch (key) { case '1': processPercussion(1); break; case '2': processPercussion(2); break; case '3': processPercussion(3); break; case '4': processPercussion(4); break; case '5': processPercussion(5); break; case '6': processPercussion(6); break; case '7': processPercussion(7); break; case '8': processPercussion(8); break; case '9': processPercussion(9); break; case 'q': restPercussion(1); break; case 'w': restPercussion(2); break; case 'e': restPercussion(3); break; case 'r': restPercussion(4); break; case 't': restPercussion(5); break; case 'y': restPercussion(6); break; case 'u': restPercussion(7); break; case 'i': restPercussion(8); break; case 'o': restPercussion(9); break; case '0': restPercussion(1); restPercussion(2); restPercussion(3); restPercussion(4); restPercussion(5); restPercussion(6); restPercussion(7); restPercussion(8); restPercussion(9); return; case 'a': clearPercussion(1); break; case 's': clearPercussion(2); break; case 'd': clearPercussion(3); break; case 'f': clearPercussion(4); break; case 'g': clearPercussion(5); break; case 'h': clearPercussion(6); break; case 'j': clearPercussion(7); break; case 'k': clearPercussion(8); break; case 'l': clearPercussion(9); break; case '-': tempo--; if (tempo < 1 ) tempo = 1; cout << "\rTempo = " << tempo << " "; cout.flush(); return; case '=': tempo++; if (tempo > 1000 ) tempo = 1000; cout << "\rTempo = " << tempo << " "; cout.flush(); return; case 'v': loadLoop("loop1.txt"); return; case 'b': loadLoop("loop2.txt"); return; case 'n': loadLoop("loop3.txt"); return; case 'x': initialization(); return; case ',': saveLoop("loop1.txt"); return; case '.': saveLoop("loop2.txt"); return; case '/': saveLoop("loop3.txt"); return; case 'c': clickTrack = !clickTrack; if (clickTrack) { cout << "Click track ON." << endl; } else { cout << "Click track OFF." << endl; } return; case 'z': zero(); return; case 'p': silenceHitQ = 1; return; case ';': unsilenceHitQ = 1; return; default: // ignore the key or play the computer keyboard synth keys: charsynth(key); return; } } void restPercussion(int inst) { // determine where in the loop this rest goes int current = metronome.expired(); if (current % 2 == 1) { current++; } else if (storage[inst][current] != 0) { // this rhythm position already played, but turn off before finished synth.play(9, instrument[inst], 0); } current /= 2; if (current >= size) current = 0; storage[inst][current] = 0; } void clearPercussion(int inst) { for (int i=0; i<size; i++) { storage[inst][i] = 0; } } void processPercussion(int inst) { if (silenceQ[inst] == 1) { synth.play(9, instrument[inst], TRIGVEL); // don't store in loop since paused return; } // determine where in the loop this note goes int current = metronome.expired(); if (current % 2 == 1) { current++; } else { // this rhythm position already played, but play new as well synth.play(9, instrument[inst], TRIGVEL); } current /= 2; if (current >= size) current = 0; storage[inst][current] = TRIGVEL; } /*------------------ end improvization algorithms -----------------------*/ // md5sum: e5016c0509c904fbe8c441d9323850d4 loop2.cpp [20050403]