Etude #1, journey to writing a poem using AI and Chuck.

Pkmazarakis
6 min readJan 21, 2023

--

CS 470. A 400 level CS course titled Music and AI… I came in expecting to get absolutely destroyed by an old professor heaving complex mathematical equations consisting of symbols I have never even seen before. However, to my pleasant surprise, it is exactly what I wanted for my last year at Stanford, a class all about experimentation and building.

To be honest, the beginning of this project sucked. Plain and simple. Learning a new language after 8 years of grinding python and JS was not a fun journey to embark on. However, as I looked through all the example code and was able to start testing and listening to unique combinations of sounds that “I” was making, made it all worth it.

The middle of the assignment was where the fun truly came in. This is when I got to thinking about how I wanted to structure my poems. The first one I wanted support in the idea so I used insipiration from the I feel poem and made my own version from it. I asked three of my friends to give me adjectives describing me. Of course they memed and said ridiculous things but they ended up being quite funny. Then I structured them to show in the poem what they said about me and what other things would describe me using the word2vec. Then I attached simple timing and pitches and strings to give personality to the words displayed.

I knew for the second one I would want to control the pitch of the noises with the values of the coordinates that the words of the poems held in space. So I asked chatGPT to make a poem about me making a poem about making poems and then filtered it in to Chuck line by line and extracted the y value coordinate of each word in word2vec space and played a sound based on that value.

I very much enjoyed this as a creative stepping stone into this class and am SO pumped to continue exploring for the rest of the quarter.

Thanks!

//------------------------------------------------------------------------------
// name: poem-i-feel.ck
// desc: a sus poem made with chAI and word2vec
// sorting: part of chAI (ChucK for AI)
//
// "i feel"
// -- a stream of unconsciousness poem generator
//
// 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)
//
// author: Ge Wang
// date: Spring 2023
//------------------------------------------------------------------------------

// random seed (if set, sequence can be reproduce)
// Math.srandom( 515 );

// 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();
}

["twangy", "untuned", "intrepid", "languid", "zoalastic",
"foamenting", "bombastic", "persistent", "sensitive",
"imprisoned by my own mind", "ill", "candid", "transparent"] @=> string friendseeme[];

["confused", "happy", "sad", "complex", "yet simple", "spaghetti strapped dress", "bubbly",
"melancholy", "surface shallow", "kitty pool", "mariana's trench", "hung"] @=> string friend2seeme[];

["happy", "sentimental", "bubbly", "smart", "dumb", "self-aware?", "confused", "purpose",
"emotional", "tiger", "optimistic", "bouncy"] @=> string friend3seeme[];

// conditions
3 => int LINES_PER_STANZA;
3 => int NUM_SECTIONS; // here each section is a fixed stanzas format

// number of nearest words to retrieve for each word
// higher this number the higher the variance per word
10 => int K_NEAREST;

// timing
400::ms => dur T_WORD; // duration per word
1::second => dur WAIT_2; // duration per word

T_WORD => dur T_LINE_PAUSE; // a little pause after each line
T_WORD * 2 => dur T_STANZA_PAUSE; // pause after each stanza

// sound
BandedWG crystal => Gain left => dac.left;
crystal => Gain right => dac.right;
// feedback delay for echos



// current word
string feeling;
// word vector
float vec[model.dim()];
// search results
string words[K_NEAREST];

string wordsSaid[0];

["friend1", "friend2", "friend3"] @=> string friends[];
[friendseeme, friend2seeme, friend3seeme] @=> string friendswords[][];


// line break
say("who"); play(40); wait(WAIT_2);
say("am"); play(43); wait(WAIT_2);
say("I"); play(40); wait(WAIT_2);
say("?"); play(60); wait(WAIT_2);
wait(WAIT_2);
say("lets"); play(40); wait(T_WORD);
say("ask"); play(43); wait(T_WORD);
say("my"); play(40); wait(T_WORD);
say("friends"); play(60); wait(T_WORD); wait(WAIT_2);


// loop over stanzas
chout <= IO.newline() <= IO.newline(); chout.flush();

stanza( feeling, LINES_PER_STANZA, 50, 65 );

stanza( feeling, LINES_PER_STANZA, 48, 63 );

stanza( feeling, LINES_PER_STANZA, 36, Math.random2(33,36)*2 );

endStanza();
chout <= IO.newline() <= IO.newline(); chout.flush();
// pause at end of line
T_STANZA_PAUSE => now;
chout.flush();

// print a stanza from a starter feeling word
fun void stanza( string feeling, int numLines, int pitch1, int pitch2 )
{
// loop over lines in a stanza
for( int n; n < numLines; n++ )
{
// a line
friendswords[n][Math.random2(0,friendswords[n].size()-1)] => feeling;
say(feeling);
wordsSaid << feeling;
play(pitch1);say(friends[n]); play(pitch1); wait();
play(pitch1);say("says"); play(pitch1); wait();
play(pitch1);say("I"); wait();
play(pitch1);say("am"); play(pitch1); wait();
say(feeling); play(pitch2); wait();
endl(); wait();
stanza2();
// get similar words
// choose one at random

}
// next line!
// pause at end of line
T_LINE_PAUSE => now;
}

fun void stanza2()
{
say("Does"); play(40); wait(WAIT_2);
say("this"); play(43); wait(WAIT_2);
say("mean"); play(40); wait(WAIT_2);
say("I'm"); play(43); wait(WAIT_2);
say("also"); play(40); wait(WAIT_2);
model.getSimilar( wordsSaid[0], words.size(), words );
words[Math.random2(0,words.size()-1)] => string useWord;
say(useWord); play( 60); wait();
chout <= IO.newline(); chout.flush();
chout <= IO.newline(); chout.flush();



}

fun void endStanza()
{
// stanza break; no new sound for now
say("Friends"); play(40); wait(WAIT_2);
say("Are"); play(43); wait(WAIT_2);
say("Me"); play(46); wait(WAIT_2);
say("And"); play(49); wait(WAIT_2);
say("Me"); play(46); wait(WAIT_2);
say("Is"); play(43); wait(WAIT_2);
say("Friend"); play(40); wait(WAIT_2);

}

// say a word with space after
fun void say( string word )
{
say( word, " " );
}

// say a word
fun void say( string word, string append )
{
// print it
chout <= word <= append; chout.flush();
}

// sonify
fun void play( int pitch )
{
play( pitch, Math.random2f(.8,1) );
}

// sonify
fun void play( int pitch, float velocity )
{
// convert pitch to frequency and set it
pitch => Std.mtof => crystal.freq;
// note on
velocity => crystal.noteOn;
}

// wait
fun void wait()
{
wait( T_WORD );
}

// wait
fun void wait( dur T )
{
// let time pass, let sound...sound
T => now;
}

// new line with timing
fun void endl()
{
endl( T_WORD );
}

// new line with timing
fun void endl( dur T )
{
// new line
chout <= IO.newline(); chout.flush();
// let time pass
T => now;
}
// default file
ConsoleInput in;
// tokenizer
StringTokenizer tok;
// 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();
}

BandedWG crystal => Gain left => dac.left;
crystal => Gain right => dac.right;
me.sourceDir() + "chatgpt.txt" => string filename;

// look at command line
if( me.args() > 0 ) me.arg(0) => filename;

// instantiate
FileIO fio;

// open a file
fio.open( filename, FileIO.READ );



200::ms => dur T_WORD;
// ensure it's ok
if( !fio.good() )
{
cherr <= "can't open file: " <= filename <= " for reading..."
<= IO.newline();
me.exit();
}

// loop until end
while( fio.more() )
{
tok.set( fio.readLine() );
while( tok.more() )
{
float vector[model.dim()];
tok.next() => string nextWord;
model.getVector(nextWord, vector);

(vector[1]*10) $ int => int roundedVector;
if(roundedVector < 0) {
play(-roundedVector);
say(nextWord);wait(T_WORD);
} else if (roundedVector == 0) {
play(10);
say(nextWord);wait(T_WORD);
}else {
play(roundedVector);
say(nextWord);wait(T_WORD);
}

}
chout <= IO.newline(); chout.flush();

}

// say a word with space after
fun void say( string word )
{
say( word, " " );
}
fun void say( string word, string append )
{
// print it

chout <= word <= append; chout.flush();
}

fun void play( int pitch )
{
play( pitch, Math.random2f(.8,1) );
}

// sonify
fun void play( int pitch, float velocity )
{
// convert pitch to frequency and set it
pitch => Std.mtof => crystal.freq;
// note on
velocity => crystal.noteOn;
}

fun void wait( dur T )
{
// let time pass, let sound...sound
T => now;
}
Platon Mazarakis at Stanford,
With code and words in hand,
Creates a program grand,
Using ChatGPT and Chuck as his band.

He writes a poem about writing poems,
Of verse and rhyme and meter,
A digital ode to the creative process,
A symphony of thought, a true meter.

The lines of code flow like a river,
As he crafts his verse and meter,
A poet in the world of technology,
A master of both worlds, a true winner.

So here's to Platon Mazarakis,
A student at Stanford true,
Who writes a poem about writing poems,
And creates something new.

For the first code I used inspiration from the I feel poem given to us.

For the second code I used the documentation for read-line

Unlisted

--

--