HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Song lyric generator using Markov Chains - Python

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
chainslyricgeneratorsongusingpythonmarkov

Problem

I have written a pop song generator which uses the Markovify library to produce lyrics based on (just for testing purposes) songs by Avril Lavigne.

In order to make the generator a bit more competent I have adapted some code which uses the nltk library to determine whether or not two words rhyme. I have used this to make the verses conform to an ABAC rhyme scheme.

It works, but is slow and sometimes gets stuck in a loop when the rhyme_finder function is called. I would greatly appreciate any suggestions as to how to streamline the programme, and or make it more efficient. I have linked the main programme app.py below, as well as the rhyme_finder function. But the Avril Lavigne lyrics, and the nltk.tokenize lyrics are on Pastebin.

The Markovify lib (https://github.com/jsvine/markovify), the Sylco syllable counter (https://github.com/eaydin/sylco), and some nltk dependencies (cmudict) are required to run.

Lyric Generator

```
import random
import markovify
import ast
from rhyme import rhyme_finder
from sylco import sylco

# Open and model lyrics
with open('lavigne_verse.txt') as f:
verse_text = f.read()
with open('lavigne_chorus.txt') as f:
chorus_text = f.read()
with open('lyrics_tokenize.txt') as f:
tokenized_text = f.read()

verse_model = markovify.NewlineText(verse_text, state_size=2)
chorus_model = markovify.NewlineText(chorus_text, state_size=2)

# Evaluate tokenized_text as a list
tokenized_text = ast.literal_eval(tokenized_text)

# Specify then remove punctuation
punc = set([',','.','"','?','!'])

def clean(str):
if str[-1] in punc:
return str[:-1]
return str

# Generate line that rhymes with stem of verse line 1
def match_rhyme(stem, verse_model):

# Check if rhymes exist
try:
ls = rhyme_finder(stem, tokenized_text)
except KeyError:
return None
if not ls:
return None

# If rhymes exist generate lines
for n in range(100):
while True:
rhyme_l

Solution

Some suggestions:

def unique(s):
    u = []
    for x in s:
        if x not in u:
            u.append(x)
        else:
            pass
    return u


may become much simpler:

def unique(s):
    return list(set(s))


but - as you use it only in one place

word_list_u = unique(word_list)


and in turn the word_list_u is used only in one place, too:

for (x, y) in word_list_u:


where word_list_u need not be a list.

So you may delete both the unique() definition and its usage, and write directly

for (x, y) in set(word_list):


as sets in Python inherently not allow duplicates.

punc = set([',','.','"','?','!'])

def clean(str):
    if str[-1] in punc:
        return str[:-1]
    return str


would be clearer with using the endswith() method and a tuple of punctuation symbols:

punc = tuple(',."?!')

def clean(str):
    if str.endswith(punc):
        return str[:-1]
    return str

Code Snippets

def unique(s):
    u = []
    for x in s:
        if x not in u:
            u.append(x)
        else:
            pass
    return u
def unique(s):
    return list(set(s))
word_list_u = unique(word_list)
for (x, y) in word_list_u:
for (x, y) in set(word_list):

Context

StackExchange Code Review Q#148653, answer score: 3

Revisions (0)

No revisions yet.