// // Programmer: Craig Stuart Sapp // Creation Date: 4 January 1998 // Last Modified: 4 January 1998 // Filename: loop.cpp // Syntax: Visual C++ 5.0; Synth Improv // // Description: This is a (rhythm) looping program. // #include "synthimprov.h" #include /*----------------- beginning of improvization algorithms ---------------*/ int beats; // number of beats in the loop int subdivisions; // number of subdivisions per beat int tempo; // number of beats per minute CTimer metronome; // keeps track of current beat and subdivision int* storage = NULL; // array for storing looped notes int size = 0; // size of loop array int clickTrack = 0; // click track, 1=on, 0=off /*--------------------- maintenance algorithms --------------------------*/ void description(void) { printtopline(); printstringline(" Rhythm loops -- Craig Stuart Sapp "); printstringline(" 4 January 1998"); printstringline(""); printstringline(" Press \"-\" to slow tempo, or \"=\" to speed tempo."); printstringline( " Here are the notes which can be played on the " "computer keyboard:"); printstringline(""); printcenterline("1 LOW_TOM 6 MUTE_CUICA "); printcenterline("2 LOW_MID_TOM 7 OPEN_CUICA "); printcenterline("3 HIGH_MID_TOM 8 MUTE_TRIANGLE "); printcenterline("4 HIGH_TOM 9 ACOUSTIC_BASS_DRUM"); printcenterline("5 HI_BONGO 0 REST "); printstringline(""); printstringline(" Additional commands:"); printstringline(" c = toggle click track z = clear (zero) loop"); printstringline(" s = save loop l = load loop"); printbottomline(); cout << endl; } void zero(void) { for (int i=0; i> beats; if (beats < 1 || beats > 100) { cout << "beats set to 1." << endl; beats = 1; } cout << "How many subdivisions per beat: "; cin >> subdivisions; if (subdivisions < 1 || subdivisions > 100) { cout << "subdivisions set to 1." << endl; subdivisions = 1; } cout << "How many beats per minute: "; cin >> tempo; if (tempo < 1 || tempo > 1000) { cout << "Tempo set to 120 beats per minute." << endl; tempo = 120; } size = beats * subdivisions; storage = new int[size]; zero(); metronome.setPeriod(60000.0/(tempo*subdivisions)); metronome.reset(); } void finishup(void) { } void saveLoop(const char* aFilename) { fstream output; output.open(aFilename, ios::out); if (!output) { cout << "Cannot write file: " << aFilename << endl; return; } output << "Beats: " << beats << endl; output << "Subdivisions: " << subdivisions << endl; output << "Tempo: " << tempo << endl; for (int i=0; i> buffer; // Beats: input >> beats; input >> buffer; // Subdivisions: input >> subdivisions; input >> buffer; // Tempo: input >> tempo; if (storage != NULL) delete [] storage; size = beats * subdivisions; storage = new int[size]; for (int i=0; i> storage[i]; } metronome.setPeriod(60000.0/(tempo * subdivisions)); metronome.reset(); } /*-------------------- main loop algorithms -----------------------------*/ 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(60000.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 if (storage[lastposition] != 0) { note_on(CH_10, storage[lastposition], 100); } if (clickTrack && (lastposition % subdivisions == 0)) { if (lastposition == 0) { note_on(CH_10, GM_CLAVES, 100); } else { note_on(CH_10, GM_CLAVES, 50); } } } } void mainloopalgorithms(void) { checkloop(); } /*-------------------- triggered algorithms -----------------------------*/ void keyboardchar(int key) { int note; // determine if this is a new note switch (key) { case '1': note = GM_LOW_TOM; break; case '2': note = GM_LOW_MID_TOM; break; case '3': note = GM_HIGH_MID_TOM; break; case '4': note = GM_HIGH_TOM; break; case '5': note = GM_HI_BONGO; break; case '6': note = GM_MUTE_CUICA; break; case '7': note = GM_OPEN_CUICA; break; case '8': note = GM_MUTE_TRIANGLE; break; case '9': note = GM_ACOUSTIC_BASS_DRUM; break; case '0': note = 0; break; case '-': tempo--; if (tempo < 1 ) tempo = 1; return; case '=': tempo++; if (tempo > 1000 ) tempo = 1000; return; case 's': saveLoop("loop.txt"); return; case 'l': loadLoop("loop.txt"); return; case 'c': clickTrack = !clickTrack; if (clickTrack) { cout << "Click track ON." << endl; } else { cout << "Click track OFF." << endl; } return; case 'z': zero(); return; default: // ignore the key return; } // determine where in the loop this note goes int current = metronome.expired(); if (current % 2 == 1) { current++; } else if (note != 0) { // this rhythm position already played, but play new as well note_on(CH_10, note, 80); } current /= 2; if (current >= size) current = 0; storage[current] = note; } /*------------------ end improvization algorithms -----------------------*/ /* some functions and variables provided by the support program program_change(channel, instrument); -------- sets the timbre for a channel control_change(channel, controller, value); - sends a continuous controller note_on(channel, keynumber, keyvelocity); --- plays a MIDI note note_off(channel, keynumber); --------------- same as note_on with 0 vel. raw_send(channel, command, data1, data2); --- send some midi command long t_time; -------------------------------- current time in milliseconds */