Illsong

Being distressed at the prospect of singing a Hillsong song, I collected all the lyrics of 100 or so Hillsong songs, and put them through a Markov chain generator, to generate new songs in the style of the originals.  The parameters used are:

  • order: 10 – this is the length of memory
  • prior: 0.0001 – the bias towards using something unknown
  • corpus: the complete lyrics of each song make up the sample data, ie. each song is represented as a single string.  The results then are songs comprised of multiple lines, as a single string.

The results are about as coherent and approximately as Biblically sound as bulk of the songs that Hillsong writes.  Have I mentioned that I find the lyrics repetitive and incoherent?

Here are three selections. Notice the odd spelling mistake – that’s the Markov thing randomly choosing to make an error.  I made up titles for fun:

No end, this was not a new thing

There’s no end to Your embrace
Light of the world
Let the poor say, “I am rich”
Let the poke the Earth, let us sing
God, our hope is Yahweh, Yahweh
Forever You will never fail
Your name shout in all the earth
Will fade away
Still my soul
My heart within the darkness
Glory pierced the night sky
You give me breath and all I found was You
My God, I’ll only ever give You my heart
Distant shores and the end
The promise out
This was not a new thing
Until itSaviour, He can move the mountains with a whisper
And You calm my soul
Oh, now save
Our God is great and mighty
God in three persons, blessed Trinity

In the highest praise at the sound of faith

To the Lamb that was slain
Hosanna, Hosanna
Hosanna in the highest praise
What can separate us
Nothing can separate me now
You taught a way, You made a way
When You call, I won’t hide it, I won’t refuse
Each new day again I’ll choose
There is none like You
There is no one like You, God
Mountains bow down and the seas will roar
At the sound of faith
and the words to express the way of the Lord
Eternity’s King
Is coming again
Though all of the universe is at Your feet
Hide me now in the shadow of Your word, Your name
“I live to know You
I live to know You
Let go and throw my life has changed
When You took a crown of thorns
And Your blood was spilled
For my ransom
Everything I have, I give You praise in all of the earth rejoice
Let all the heavens
For You are holy, You are holy, You alone
Awake my soul the reason why I sing
All around the words to express
there’s nothing like
Your love You always

Nothing that’s true

For all Your sons and daughters
Who are walking in the east, beyond the heavens
And Your love transforms my soul, He is the Lord with all my hope is in You
Jesus Christ the Savior is born
He shall become a wonderful counselor, everlasting Lord
Late in time, beholding Your beauty
And in the palm of Your hands I belong, I’m a living stone
In this house I will grow
There is nothing that’s true

The script:

#! /usr/bin/python

from __future__ import division
import random


class Categorical(object):

    def __init__(self, support, prior):
        self.counts = {x: prior for x in support}
        self.total = sum(self.counts.itervalues())

    def observe(self, event, count=1):
        self.counts[event] += count
        self.total += count

    def sample(self, dice=random):
        sample = dice.uniform(0, self.total)
        for event, count in self.counts.iteritems():
            if sample <= count: return event sample -= count def __getitem__(self, event): return self.counts[event] / self.total class MarkovModel(object): def __init__(self, support, order, prior, boundary_symbol=None): self.support = set(support) self.support.add(boundary_symbol) self.order = order self.prior = prior self.boundary = boundary_symbol self.prefix = [self.boundary] * self.order self.postfix = [self.boundary] self.counts = {} def _categorical(self, context): if context not in self.counts: self.counts[context] = Categorical(self.support, self.prior) return self.counts[context] def _backoff(self, context): context = tuple(context) if len(context) > self.order:
            context = context[-self.order:]
        elif len(context) < self.order: context = (self.boundary,) * (self.order - len(context)) + context while context not in self.counts and len(context) > 0:
            context = context[1:]
        return context

    def observe(self, sequence, count=1):
        sequence = self.prefix + list(sequence) + self.postfix
        for i in range(self.order, len(sequence)):
            context = tuple(sequence[i - self.order:i])
            event = sequence[i]
            for j in range(len(context) + 1):
                self._categorical(context[j:]).observe(event, count)

    def sample(self, context):
        context = self._backoff(context)
        return self._categorical(context).sample()

    def generate(self):
        sequence = [self.sample(self.prefix)]
        while sequence[-1] != self.boundary:
            sequence.append(self.sample(sequence))
        return sequence[:-1]

    def __getitem__(self, condition):
        event = condition.start
        context = self._backoff(condition.stop)
        return self._categorial(context)[event]

class NameGenerator(object):
    def __init__(self, name_file, order=3, prior=.001):
        self.names = set()
        support = set()
        for name in name_file:
            name = name.strip()
            if len(name) > 0:
                self.names.add(name)
                support.update(name)
        self.model = MarkovModel(support, order, prior)
        for name in self.names:
            self.model.observe(name)

    def generate(self):
        while True:
            word = ''.join(self.model.generate())
            return word

def readsongs(fd):
    o=[]
    for line in fd:
        o.append(line)
        if line.strip()=='':
            yield ''.join(o)
            o=[]
    if len(o):
        yield ''.join(o)

if __name__=="__main__":
    import os,sys
    fd=open(sys.argv[1],'r')
    
    ng=NameGenerator(readsongs(fd), order=10,prior=0.0001)
    while True:
        r= ng.generate()
        if len(r)

You give that code a file with all the lyrics of your song (delimited by a blank line) and it will spit out a new Illsong – maybe a better one.  WordPress doesn’t like to publish files (what’s with that) so you’ll have to get your own illsong archive to see the wonderful results.  The code, by the way, is not my own … I can’t figure out where I plagiarised it from.

This entry was posted in Stuff and tagged , , , , . Bookmark the permalink.