// // Programmer: Leland Stanford, Jr. // Creation Date: Wed May 12 00:18:31 PDT 1999 // Last Modified: Wed May 12 01:47:06 PDT 1999 // Filename: .../improv/examples/synthImprov/markov1/markov1.cpp // Syntax: C++; Visual C++ 6.0; synthImprov // // Description: Generate/create markov transition tables for playing a // melody. // #include "synthImprov.h" #include #ifndef OLDCPP #include #include using namespace std; #else #include #include #endif #ifdef VISUAL double drand48(void) { return (double)rand()/RAND_MAX; } #endif /*----------------- beginning of improvization algorithms ---------------*/ #define MARKOV_SIZE 12 double markov[MARKOV_SIZE][MARKOV_SIZE] = { {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}, {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}}; int markovRecord = 0; // true if recording markov chain data double tempo = 120; // the tempo of the performed melody int markovNote = 0; // the markov note for performance int octave = 4; // the octave of the markov note for performance Voice markovVoice; // for keeping track of note-offs int lastMarkovNote = 0; // for counting markov notes int newMarkovNote = 0; // for counting markov notes int i, j; // for counting SigTimer metronome; // for determing the rhythm int constant = 0; // for constant/non constant rhythm // function declarations: void processNoteForMarkov(void); void performNoteFromMarkov(void); void setNextEventTime(double tempo, SigTimer& metronome); int chooseNoteFromMarkovArray(int markovNote); void zeroMarkovTable(void); void generateMarkovTable(void); /*--------------------- maintenance algorithms --------------------------*/ ////////////////////////////// // // description -- this function is called by the improv interface // whenever a capital "D" is pressed on the computer keyboard. // Put a description of the program and how to use it here. // void description(void) { cout << "Markov -- play/record markov melodies\n" " keyboard commands: \n" " , = slow down tempo\n" " . = speed up tempo\n" " p = print markov table\n" " 1-7 = set the octave to play markov note\n" " ' ' = switch between recording and playing a markov melody\n" << endl; } ////////////////////////////// // // initialization -- this function is called by the improv // intervace once at the start of the program. Put items // here which need to be initialized at the beginning of // the program. // void initialization(void) { markovVoice.pc(0); markovVoice.setChannel(0); metronome.setTempo(60); } ////////////////////////////// // // finishup -- this function is called by the improv interface // whenever the program is exited. Put items here which // need to be taken care of when the program is finished. // void finishup(void) { } /*-------------------- main loop algorithms -----------------------------*/ ////////////////////////////// // // mainloopalgorithms -- this function is called by the improv interface // continuously while the program is running. The global variable t_time // which stores the current time is set just before this function is // called and remains constant while in this functions. // void mainloopalgorithms(void) { if (markovRecord) { processNoteForMarkov(); } else if (metronome.expired()) { performNoteFromMarkov(); setNextEventTime(tempo, metronome); } } /*-------------------- triggered algorithms -----------------------------*/ /////////////////////////////// // // keyboardchar -- this function is called by the improv interface // whenever a key is pressed on the computer keyboard. // Put commands here which will be executed when a key is // pressed on the computer keyboard. // void keyboardchar(int key) { if (isdigit(key)) { octave = key - '0'; if (octave < 1) { octave = 1; } if (octave > 7) { octave = 7; } return; } switch (key) { case ',': // slow down the tempo which will be tempo /= 1.05; // activated at the next note to be played if (tempo < 20) { tempo = 20; } cout << "Tempo is: " << tempo << endl; break; case '.': // speed up the tempo which will be tempo *= 1.05; // activated at the next note to be played if (tempo > 500) { tempo = 500; } cout << "Tempo is: " << tempo << endl; break; case 'p': // print the markov table cout << "Markov transition table:" << endl; for (i=0; i 0) { message = synth.extractNote(); if (message.p2() != 0) { lastMarkovNote = newMarkovNote; newMarkovNote = message.p1() % 12; if (newMarkovNote == (21%12) && message.p1() == 21) { newMarkovNote = -1; } else if (lastMarkovNote >= 0) { markov[lastMarkovNote][newMarkovNote] += 1; } } } } ///////////////////////////// // // performNoteFromMarkov -- generate a note from the last played note. // void performNoteFromMarkov(void) { MidiMessage message; int newnote = 0; if (synth.getNoteCount() != 0) { while (synth.getNoteCount() > 0) { message = synth.extractNote(); if (message.p2() != 0) { newnote = message.p1() % 12; } } newnote = chooseNoteFromMarkovArray(newnote); } else { newnote = chooseNoteFromMarkovArray(markovNote); } cout << "Last note: " << markovNote << "\tNew note: " << newnote << endl; markovVoice.play(newnote + octave * 12, rand()%20 + 60); markovNote = newnote; } ///////////////////////////// // // chooseNoteFromMarkovArray -- get the next markov note, returning // B if any under-valuing the probabilities. // int chooseNoteFromMarkovArray(int markovNote) { markovNote = markovNote % 12; double targetSum = drand48(); double sum = 0.0; int targetNote = 0; while (targetNote < MARKOV_SIZE && sum+markov[markovNote][targetNote] < targetSum) { sum += markov[markovNote][targetNote]; targetNote++; } return targetNote; } ///////////////////////////// // // setNextEventTime -- choose the duration of the current note. // void setNextEventTime(double tempo, SigTimer& metronome) { int random = (int)(drand48() * 100); if (constant) { metronome.setTempo(tempo*2); } else if (random < 10) { metronome.setTempo(tempo/4.0); } else if (random < 20) { metronome.setTempo(tempo/4.0*2); } else if (random < 30) { metronome.setTempo(tempo/4.0*3); } else if (random < 40) { metronome.setTempo(tempo/4.0*4); } else if (random < 50) { metronome.setTempo(tempo/4.0*6); } else if (random < 60) { metronome.setTempo(tempo/4.0*8); } else if (random < 70) { metronome.setTempo(tempo/4.0*12); } else if (random < 80) { metronome.setTempo(tempo/4.0*16); } else if (random < 90) { metronome.setTempo(tempo/4.0*4.5); } else { metronome.setTempo(tempo/4.0*0.5); } metronome.reset(); } ////////////////////////////// // // zeroMarkovTable -- replace all values with zeros. // void zeroMarkovTable(void) { int i, j; for (i=0; i