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]