Lyric Poems
A collection of rhyming poetry generated from songs from the last 50 years.
PDFs: Lyric Poems
Inspiration for this project initially came from my dad. After I told him about the guidelines and that we were going to be pulling data from random corpora, he sent me a link to a list of 10,000 fake band names. This had me thinking about possibilities that related to music. I'm part of The Cut, CMU's music magazine, so at first I thought it would be fun to generate a whole fake magazine article about a new band's first tour -- their name, their genre, a bio on each member, a generated graphic of each member (like a Bitmoji, from different head pieces), the set list of the tour, the cities that they were touring, and a mini concert review -- which I would try to include in the next issue as a joke. I realized while planning out ideas that 1) each page of the article would probably end up just being a repetitive list of words randomly chosen from each corpus, and 2) I probably wouldn't have enough time to execute all of my ideas well. Therefore, I decided I should pick one or a few of the ideas. When I was researching how to generate song titles to include in the set lists, one corpus I found also had 2.5 million song lyrics. So then I realized I could pull lyrics from those songs and make my own songs out of them.
What was especially interesting about the generative process here was that a lot of the lines make sense (meaningful sense), no matter where in the list of 2.5 million they came from. Even with a rhyme scheme. I like how this illustrates that artists have written about the same themes in their songs for decades: love, life, and loss. The combinations ended up abstract enough, however, that they resembled poetry more than songs. I ironically titled the chapter "Lyric Poetry" after an actual genre of poetry in which the poet specifically addresses emotions and feelings -- funny because, on the surface, some of these poems can seem very deep, beautiful, and emotional, but the fact that they were generated by a computer means that there is actually zero intent behind the words, making them emotionless on the deeper level.
Through the development I also discovered a bug with RiTa -- she has trouble rhyming the word "eight."
import processing.pdf.*; import rita.*; import cbl.quickdraw.*; boolean bExportingPDF; // See https://processing.org/reference/libraries/pdf/index.html int nPages; PImage imageArray[]; JSONArray jsonPages; int outputPageCount = 0; float inch = 72; String[] bandnames; String[] genres; String[] lyrics; String[] stanza; String[] titles; String[] colors = {"#5b97f7", "#a07ef7", "#ffb5ec"}; int numcircles = (int) random(8, 16); QuickDraw qd; int x = 160, y = 240; RiLexicon lexicon; Boolean pressed = true; int numstanzas; PFont caviar25; PFont pulsab70; PFont font1; PFont font2; Boolean on = true; int numcircles1 = 14; //======================================================= void setup() { // The book format is 6" x 9". // Each inch is 72 pixels (or points). // 6x72=432, 9*72=648 // USE THESE COMMANDS WHEN YOU'RE NOT EXPORTING PDF, // AND YOU JUST WANT SCREEN DISPLAY: //size(432, 648); //bExportingPDF = false; // // BUT: IMPORTANT: // WHEN IT IS TIME TO EXPORT THE PDF, // COMMENT OUT THE ABOVE size() COMMAND, AND // USE THE FOLLOWING TWO COMMANDS INSTEAD: size(432, 648, PDF, "poems25.pdf"); bExportingPDF = true; background(255); lexicon = new RiLexicon(); bandnames = loadStrings("bandnames1.txt"); genres = loadStrings("musicgenres.txt"); lyrics = loadStrings("SONGS.txt"); titles = loadStrings("songtitles.txt"); qd = new QuickDraw(this, "guitars.ndjson"); caviar25 = createFont("Arvo-Regular.ttf", 10); pulsab70 = createFont("Alpaca Scarlett Demo.ttf", 25); font1 = createFont("Arvo-Regular.ttf", 28); font2 = createFont("Alpaca Scarlett Demo.ttf", 50); } //======================================================= void draw() { if (bExportingPDF) { drawForPDFOutput(); } else { drawForScreenOutput(); } } //======================================================= void drawForPDFOutput() { // When finished drawing, quit and save the file if (outputPageCount >= nPages) { exit(); } else if (outputPageCount == 0) { title(); PGraphicsPDF pdf = (PGraphicsPDF) g; if (outputPageCount < (nPages-1)) { pdf.nextPage(); } } else { drawPage(outputPageCount); PGraphicsPDF pdf = (PGraphicsPDF) g; // Get the renderer if (outputPageCount < (nPages-1)) { pdf.nextPage(); // Tell it to go to the next page } } outputPageCount++; } //======================================================= void drawForScreenOutput() { int whichPageIndex = (int) map(mouseX, 0, width, 0, nPages); drawPage(whichPageIndex); println(whichPageIndex); } //======================================================= void drawPage(int whichPageIndex) { background(255); whichPageIndex = constrain(whichPageIndex, 0, nPages-1); fill(0); //if (pressed == true) { background(255); for (int e = 0; e < numcircles; e++) { noFill(); strokeWeight(random(.1,3.5)); int colorr = (int) random(170,255); int colorg = (int) random(170,220); int colorb = (int) random(220,245); int rad = (int) random(30, 200); int x = (int) random(0, width); int y = (int) random(0, height); //String col = random(colors); int p = (int) random(1, 8); for (int g = 0; g < p; g++) { stroke(colorr, colorg, colorb, 225-(g*40)); ellipse(x, y, rad, rad); rad*=.89; } } strokeWeight(5); pushMatrix(); stroke(180); int guitarIndex = (int) random(0, 80); //print("g", guitarIndex); qd.create(0,random(height/2-100, height/2+100), 140, 140, guitarIndex); popMatrix(); fill(255,180,80); textFont(pulsab70); drawText(); fill(0); textFont(caviar25); makeStanza(); } void drawText() { //textSize(70); textAlign(CENTER); int titleIndex = (int) random(titles.length); textAlign(LEFT); String title = titles[titleIndex]; String[] wordsInTitle = RiTa.tokenize(title); if (wordsInTitle.length > 3) { titleIndex+=1; } text(titles[titleIndex], 30, height/5); } void makeStanza() { textAlign(LEFT); numstanzas = (int) random(1, 8); for (int j = 0; j < numstanzas; j++) { for (int i = 0; i < 2; i++) { int lyricsIndex1 = (int) random(lyrics.length); String lyric1 = getLyric(lyrics, lyricsIndex1); text(lyric1, 30, height/5 + 25 + 60*j + (i*2)*13); String lyric2 = getRhymingLyric(lyrics, lyric1, lyricsIndex1); text(lyric2, 30, height/5 + 25 + 60*j + (i*2)*13+13); } } } String getLyric(String lyrics[], int lyricsIndex) { String lyric = lyrics[lyricsIndex]; if (lyric.length() < 4 ) { return getLyric(lyrics, lyricsIndex + 1); } if ((lyric.toLowerCase().contains("chorus")) || (lyric.toLowerCase().contains("verse")) || (lyric.toLowerCase().contains("[")) || (lyric.toLowerCase().contains("(")) || (lyric.toLowerCase().contains("nigga"))) { return getLyric(lyrics, (int) random(lyrics.length)); } char firstLetter = lyric.charAt(0); char lastLetter = lyric.charAt(lyric.length()-1); if (firstLetter == '\"') { return lyric.substring(1); } if (((firstLetter >= 'a' && firstLetter <= 'z') || (firstLetter >= 'A' && firstLetter <= 'Z')) && ((lastLetter >= 'a' && lastLetter <= 'z') || (lastLetter >= 'A' && lastLetter <= 'Z'))) { return lyric; } else { return getLyric(lyrics, lyricsIndex + 1); } } String getRhymingLyric(String[] lyrics, String lyric, int lyricsIndex) { String[] words = RiTa.tokenize(lyric); //words of previous lyric if (words.length < 3) { words = RiTa.tokenize(getLyric(lyrics, lyricsIndex+1)); } String lastWord = words[words.length-1]; //last word of previous lyric for (int i = (int) random(lyrics.length); i < lyrics.length; i++ ) { if (abs(lyricsIndex-i) <= 300) { continue; } String newLyric = lyrics[i]; if (newLyric.length() < 3) { continue; } if (newLyric.toLowerCase().equals(lyric.toLowerCase())) { return getRhymingLyric(lyrics, lyric, lyricsIndex); } String[] newWords = RiTa.tokenize(newLyric); //words of previous lyric if ((newWords.length < 3) || (newWords.length > 10)) { continue; } if (newWords[0].startsWith("\"")) { newWords[0] = newWords[0].substring(1); } String newLastWord = newWords[newWords.length-1]; //last word of previous lyric String lastWordLC = lastWord.toLowerCase(); String newLastWordLC = newLastWord.toLowerCase(); int count = 0; for (int n = 0; n < newWords.length; n++) { String word = newWords[n].toLowerCase(); if ((word.equals("chorus")) || (word.equals("nigga")) || (word.equals("christmas")) || (word.equals("santa"))) { count+=1; } } if (count > 0) { continue; } if (newLastWordLC.equals("eight")) { continue; } if (lastWordLC != newLastWordLC) { if (lyric.toLowerCase() != newLyric.toLowerCase()) { if (RiTa.isRhyme(lastWordLC, newLastWordLC, false)) { return newLyric; } else { continue; } } } } return getLyric(lyrics, (int)random(lyrics.length-1)); } void title() { background(255); //if (on) { for (int e = 0; e < numcircles1; e++) { noFill(); strokeWeight(random(.1,3.5)); int colorr = (int) random(170,255); int colorg = (int) random(170,220); int colorb = (int) random(220,245); int rad = (int) random(30, 200); int x = (int) random(0, width); int y = (int) random(0, height); //String col = random(colors); int p = (int) random(1, 8); for (int g = 0; g < p; g++) { stroke(colorr, colorg, colorb, 225-(g*40)); ellipse(x, y, rad, rad); rad*=.89; } } textAlign(CENTER); fill(255,180,80); textFont(font2); text("Lyric Poetry", width/2, height/3+50); fill(0); textFont(font1); text("by Casher", width/2, height/3+85); } |