//------------------------------------------------------------------------------ // "Ode to the rain" // // -- an ode to rain // -- This poem is a modern interpretation of the ancient Greek Pindaric ode, // a classic lyric poem performed with song and dance at Greek games. A chorus // would deliver the strophe on one side of stage, move to the other side // for the antistrophe, and then deliver the epode from center of stage. // // NOTE: need a pre-trained word vector model, e.g., from // https://chuck.stanford.edu/chai/data/glove/ // glove-wiki-gigaword-50-tsne-2.txt (400000 words x 2 dimensions) // // this file is based on code by Ge Wang: poem-i-feel.ck and poem-randomwalk.ck //------------------------------------------------------------------------------ //-----------------SETUP-------------------// // instantiate Word2Vec model; // pre-trained model to load me.dir() + "glove-wiki-gigaword-50-tsne-2.txt" => string filepath; // load pre-trained model (see URLs above for download) if( !model.load( filepath ) ) { <<< "cannot load model:", filepath >>>; me.exit(); } // conditions 3 => int MIN_WORDS_PER_LINE; 7 => int MAX_WORDS_PER_LINE; 9 => int LINES_STROPHE; 9 => int LINES_ANTISTROPHE; 17 => int LINES_EPODE; 3 => int NUM_STANZAS; // timing 300::ms => dur T_WORD_STROPHE; // duration per word 200::ms => dur T_WORD_ANTISTROPHE; // duration per word 100::ms => dur T_WORD_EPODE; // duration per word false => int shouldScaleTimeToWordLength; // longer words take longer? 250::ms => dur T_LINE_PAUSE; // pause after each line 750::ms => dur T_STROPHE_PAUSE; // pause after each stanza // sound ModalBar sound => Gain left => dac.left; sound => Gain right => dac.right; 4 => sound.preset; 36 => float pitch1; 48 => float pitch2; 60 => float pitch3; // starting pitches (in MIDI note numbers, octaves apart) [ 12.0, 24, 36, 48, 60, 72 ] @=> float pitches[]; // words and vectors "rain" => string cur_word; // starter words for strophe associated with rain ["rain", "drop", "pour", "water"] @=> string strophe_words[]; // starter words for antistrophe associated with sun ["sun", "rays" ,"meadow", "birds"] @=> string antistrophe_words[]; // starter words for epode associated rainbow and joy ["rainbow", "joy" ,"spring", "blossom", "life", "hope", "love", "summer", "ade rain"] @=> string epode_words[]; // word vector float word_vec[model.dim()]; // number of nearest words to retrieve for each word 15 => int K_NEAREST; // search results string words[K_NEAREST]; // ranges for each dimension (for sound mapping) float mins[0], maxs[0]; // get bounds for each dimension model.minMax( mins, maxs ); //----------------- MAKE LYRICAL AI POEM -------------------// // line break chout <= IO.newline(); chout.flush(); // --------- STROPHE ---------// // loop over lines in a strophe for( int n; n < LINES_STROPHE / 2; n++ ) { // set next word from strophe in order strophe_words[n] => cur_word; //include rain line for( int r; r < MIN_WORDS_PER_LINE; r++ ) { print( cur_word ); play( pitch1 ); wait(T_WORD_STROPHE); } // next line chout <= IO.newline(); chout.flush(); // pause at end of line T_LINE_PAUSE => now; // loop over words for( int w; w < MAX_WORDS_PER_LINE; w++ ) { // get word vector (for sonification) model.getVector( cur_word, word_vec ); // get similar words model.getSimilar( strophe_words[n], words.size(), words ); // choose one word from similar words randomly words[Math.random2(2,words.size()-1)] => cur_word; // print the word, with a space print( cur_word ); play( pitches[Math.random2(0,pitches.size()-2)] ); wait( T_WORD_STROPHE ); } // new line and pause chout <= IO.newline(); chout.flush(); T_LINE_PAUSE => now; } // new line and pause chout <= IO.newline(); chout.flush(); // --------- ANTISTROPHE ---------// for( int n; n < LINES_STROPHE / 2; n++ ) { // set next word from strophe in order antistrophe_words[n] => cur_word; //include sun line for( int r; r < MIN_WORDS_PER_LINE; r++ ) { print( cur_word ); play( pitch2 ); wait(T_WORD_ANTISTROPHE); } // new line and pause chout <= IO.newline(); chout.flush(); T_LINE_PAUSE => now; // loop over words for( int w; w < MAX_WORDS_PER_LINE; w++ ) { // get word vector (for sonification) model.getVector( cur_word, word_vec ); // get similar words model.getSimilar( antistrophe_words[n], words.size(), words ); // choose one word from similar words randomly words[Math.random2(0,words.size()-1)] => cur_word; // print the word, with a space print( cur_word ); play( pitches[Math.random2(0,pitches.size()-1)] ); wait( T_WORD_ANTISTROPHE ); } // new line and pause chout <= IO.newline(); chout.flush(); T_LINE_PAUSE => now; } // new line chout <= IO.newline(); chout.flush(); // --------- EPODE ---------// for( int n; n < LINES_STROPHE; n++ ) { // set next word from strophe in order epode_words[n] => cur_word; //include rainbow line for( int r; r < MIN_WORDS_PER_LINE; r++ ) { print( cur_word ); play( pitch3 ); wait(T_WORD_EPODE); } // new line and pause chout <= IO.newline(); chout.flush(); T_LINE_PAUSE => now; // loop over words for( int w; w < MAX_WORDS_PER_LINE; w++ ) { // get word vector (for sonification) model.getVector( cur_word, word_vec ); // get similar words model.getSimilar( epode_words[n], words.size(), words ); // choose one word from similar words randomly words[Math.random2(0,words.size()-1)] => cur_word; // print the word, with a space print( cur_word ); play( pitches[Math.random2(0,pitches.size()-1)] ); wait( T_WORD_EPODE ); // vector values control sound parameters //Math.remap( word_vec[0], mins[0], maxs[0], 24, 84 ) => Std.mtof => sound.freq; // use pow to expand/compress the dimensions to affect mapping sensitivity //Math.pow(Math.remap( word_vec[1], mins[1], maxs[1],0,1), 1.1) => sound.vibratoGain; } // new line and pause chout <= IO.newline(); chout.flush(); T_LINE_PAUSE => now; } //conclude with rain // dramatic pause T_LINE_PAUSE * 12 => now; chout <= IO.newline(); chout.flush(); "drop" => cur_word; for( int r; r < MIN_WORDS_PER_LINE; r++ ) { 1.0 => sound.gain; print( cur_word ); play( pitch1 ); wait(T_WORD_STROPHE * 2.5); } // print at the end chout <= IO.newline() <= IO.newline(); chout.flush(); // new line chout <= "\"Ode to the Rain\"" <= IO.newline(); chout <= "-- an AI take on the lyrical form of the Pindaric ode" <= IO.newline(); chout <= IO.newline(); chout.flush(); // new line chout.flush(); //----------------- HELPER FUNCTIONS -------------------// // print a word with space after fun void print( string word ) { // print it chout <= cur_word <= " "; chout.flush(); } // sonify fun void play( float pitch ) { // convert pitch to frequency and set it pitch => Std.mtof => sound.freq; // note on - velocity Math.random2f(.6,.8) => sound.noteOn; } // wait fun void wait( dur T ) { // let time pass, let sound...sound T => now; }