# 17. Transforming Fibonacci Numbers into Music.

## Fibonacci Sequence The Fibonacci sequence or numbers - named after Leonardo of Pisa, also known as Fibonacci - have fascinated not only mathematicians for centuries. Fibonacci formulated an exercise about the rabbits and their reproduction: In the beginning there is one pair of rabbits (male and female), born out of the blue sky. It takes one month until they can mate. At the end of the second month the female gives birth to a new pair of rabbits. It works the same way with every newly born pair of rabbits, i.e. that it takes one month until the female one can mate and after another month she gives birth to a new pair of rabbits. Now let's suppose that every female rabbit will bring forth another pair of rabbits every month after the end of the first month. Be aware of the fact that these "mathematical" rabbits are immortal. So the population for the the generations look like this:

1, 1, 2, 3, 5, 8, 13, 21, ...

We can easily see that each new number is the sum of the previous two.

We get to music very soon, and feel free to skip the mathematics, but one thing is also worth mentioning. The Fibonacci numbers are strongly related to the Golden Ratio $\varphi$:

$$\varphi = {\frac {1 + \sqrt{5}} {2}}$$

because the quotient of last and the previous to last number in this seqence is getting closer and closer to $\varphi$:

$$\lim_{n\to\infty} { F_n \over F_{n-1}} = \varphi$$

(\$F_n stands for the n-th Fibonacci number)

The Fibonacci numbers, often presented in conjunction with the golden ratio, are a popular theme in culture. The Fibonacci numbers have been used in the visual art and architecture. The have been used in music very often. My favorite example is the song Lateralus by Tool. The text is rhythmically grouped in Fibonacci numbers. If you look at the following lines , you can count the syllables and you will get 1, 1, 2, 3, 5, 8, 5, 3, 13, 8, 5, 3:

Black
then
white are
all I see
in my infancy
Red and yellow then came to be,
reaching out to me
Lets me see

As below, so above and beyond, I imagine
Drawn beyond the lines of reason
Push the envelope,
watch it bend

We will create a piano score for Fibonacci numbers in this chapter. There is no unique way to do this. We will create both a PDF score our "composition" and a midi file, so that you can listen to the result. We will use LilyPond to create the score:

## LilyPond

What is LilyPond? On the websitelypond.org they write the following:

LilyPond is a music engraving program, devoted to producing the highest-quality sheet music possible. It brings the aesthetics of traditionally engraved music to computer printouts. LilyPond is free software and part of the GNU Project.

You can learn more about Lilypond in the chapter Musical Scores with Python of our Python tutorial. The following page give you also a great impression how LilyPond works: 'Compiling' Music

%%writefile simple_example.ly
\version "2.14.1"
\include "english.ly"

\score {
\new Staff {
\key d \major
\numericTimeSignature
\time 2/4
<cs' d'' b''>16 <cs' d'' b''>8.
%% Here: the tie on the D's looks funny
%% Too tall? Left-hand endpoint is not aligned with the B tie?
~
<cs' d'' b''>8 [ <b d'' a''> ]
}
}


### OUTPUT:

Overwriting simple_example.ly

!lilypond  simple_example.ly


### OUTPUT:

GNU LilyPond 2.20.0
Processing simple_example.ly'
Parsing...
Interpreting music...
Preprocessing graphical objects...
Finding the ideal number of pages...
Fitting music on 1 page...
Drawing systems...
Layout output to /tmp/lilypond-R5QDyE'...
Converting to simple_example.pdf'...
Deleting /tmp/lilypond-R5QDyE'...
Success: compilation successfully completed


You can see the result by looking at the pdf file simple_example.pdf. The original file from which the PDF was created is simple_example.ly

## Fibonacci Score

### The Fibonacci Function

To create our Fibonacci score we will use the following Fibonacci function. You can find further explanation - most probably not necessary for the understanding of this chapter - concerning the Fibonacci function in our chapter Recursive Functions and additionally in our chapters Memoization with Decorators and Generators and Iterators.

class FibonacciLike:

def __init__(self, i1=0, i2=1):
self.memo = {0:i1, 1:i2}

def __call__(self, n):
if n not in self.memo:
self.memo[n] = self.__call__(n-1) + self.__call__(n-2)
return self.memo[n]

fib = FibonacciLike()


Furthmore, we will use the function gcd, which calculates the greatest common divisor of two positive numbers:

def gcd(a, b):
""" returns the greatest common divisor of the
numbers a and b """
while b != 0:
a, b = b, a%b
return a


## Numbers to Notes

We map the numbers 0 to 10 to the notes of the E major scale:

%%writefile digits_to_notes.ly
<<
\relative c' {
\key g \major
\time 6/8
dis2 e2 fis2 gis2 a2 b2 cis2 dis2 e2 fis2
}
"0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "10"
}
>>


### OUTPUT:

Overwriting digits_to_notes.ly

!lilypond  digits_to_notes.ly


### OUTPUT:

GNU LilyPond 2.20.0
Processing digits_to_notes.ly'
Parsing...

\version "2.20.0"

for future compatibility
Interpreting music...
Preprocessing graphical objects...
Finding the ideal number of pages...
Fitting music on 1 page...
Drawing systems...
Layout output to /tmp/lilypond-KrL4cS'...
Converting to digits_to_notes.pdf'...
Deleting /tmp/lilypond-KrL4cS'...
Success: compilation successfully completed


We need a mapping of digits to notes. We will use an e major scale and give each note the following numbers: In Python we do this mapping by using a list and the indices correspond to the numbers abo

digits2notes = ["dis", "e", "fis",
"gis", "a", "b",
"cis'", "dis'", "e'",
"fis'"]
#digits2notes = ["b", "c", "d", "e", "f", "g", "a'", "b'", "c'", "d'"]

notes = str(fib(24)) + str(fib(12))
notes = str(fib(24))
notes


### OUTPUT:

'46368'
def notesgenerator(func, *numbers,k=1):
"""
notesgenerator takes a function 'func', which will be applied to
the tuple of 'numbers'. The results are transformed into strings
and concatenated into a string 'notes'
The length of the string 'notes' is the first value which will be yielded
by a generator object.
After this it will cycle forever over the notes and will each
time return the next k digits. The value of k can be changed be
calling the iterator with send and a new k-value.

Example:
notesgenerator(fib, 1, 1, 2, 3, 5, 8, k=2)
The results of applying fib to the numbers are
1, 1, 1, 2, 5, 21
The string concatenations gives us
1112521
When called the first time, it will yield 7, i.e. the length of
the string notes.
After this it will yield 11, 12, 52, 11, 11, 25, 21, 11, 12, ...
"""
func_values_as_strings = [str(func(x)) for x in numbers]
notes = "".join( func_values_as_strings)
pos = 0
n = len(notes)
rval = yield n
while True:
new_pos = (pos + k) % n
if pos + k < n:
rval = yield notes[pos:new_pos]
else:
rval = yield notes[pos:] + notes[:new_pos]
if rval:
k = rval
pos = new_pos


from itertools import cycle
note = notesgenerator(fib, 5, 8, 13, k=1)

print("length of the created 'notes' string", next(note))
for i in range(5):
print(next(note), end=", ")
print("We set k to 2:")
print(note.send(2))
for i in range(5):
print(next(note), end=", ")


### OUTPUT:

length of the created 'notes' string 6
5, 2, 1, 2, 3, We set k to 2:
35
21, 23, 35, 21, 23, 
def create_variable_score(notes_gen,
upper,
lower,
beat_numbers,
number_of_notes=80):
""" create_variable_score creates the variable part of our score,
i.e. without the header. It consists of a melody with a chords accompaniment.
"""

counter = 0
old_res = ""
for beat in cycle(beat_numbers):
res = notes_gen.send(beat)
#print(res)
seq = ""
if beat == 1:
upper += digits2notes[int(res)] + "4 "
elif beat == 2:
for i in range(beat):
upper += digits2notes[int(res[i])] + "8 "
elif beat == 3:
upper += " \\times 2/3 { "
note = digits2notes[int(res)]
upper += note + "8 "
for i in range(1, beat):
note = digits2notes[int(res[i])]
upper += note + " "
upper += "}"
#elif beat == 4:
#    for i in range(beat):
#        upper += digits2notes[int(res[i])] + "16 "
elif beat == 5:
upper += " \\times 4/5 { "
note = digits2notes[int(res)]
upper += note + "16 "
for i in range(1, beat):
note = digits2notes[int(res[i])]
upper += note + " "
upper += "}"
elif beat == 8:
upper += " \\times 8/8 { "
note = digits2notes[int(res)]
upper += note + "32 "
for i in range(1, beat):
note = digits2notes[int(res[i])]
upper += note + " "
upper += "}"
if counter > number_of_notes:
break

if old_res:
accord = set()
for i in range(len(old_res)):
note = digits2notes[int(old_res[i])]
accord = " ".join(list(accord))
lower += "<" +  accord + ">"

else:
lower += "r4 "

counter += 1

old_res = res

return upper + "}", lower +  "}"

def create_score_header(key="e \major", time="5/4"):

""" This creates the score header """

upper = r"""
\version "2.20.0"
upper = \fixed c' {
\clef treble
\key """ + key  +      r"""
\time """ + time + r"""
"""

lower = r"""
lower = \fixed c {
\clef bass
\key """ + key  +      r"""
\time """ + time + r"""

"""
return upper, lower

def create_score(upper,
lower,
number_seq):
""" create score combines everything to the final score """

title = ",".join([str(x) for x in number_seq])
score = upper + "\n" + lower + "\n" + r"""
title = " """

score += title
score += r""" Fibonacci"
composer = "Bernd Klein"
}

\score {
\new PianoStaff \with { instrumentName = "Piano" }
<<
\new Staff = "upper" \upper
\new Staff = "lower" \lower
>>
\layout { }
\midi {\tempo 4 = 45 }
}
"""
return score

from itertools import cycle
number_seq = (2021,)
x = notesgenerator(fib, *number_seq)

print(next(x))

t = (1, 1, 2, 3, 5, 5, 3, 2, 1, 1, 2, 3, 5, 8, 8, 5, 3, 2, 1, 1)
# = (1, 1, 2, 3, 5, 8)
ng = notesgenerator(fib, *number_seq)
number_of_notes = next(ng)
print(f"n of notes: {number_of_notes}")
upper, lower = create_variable_score(ng,
upper,
lower,
t,
number_of_notes)

score = create_score(upper, lower, number_seq)

open("score.ly", "w").write(score)


### OUTPUT:

423
n of notes: 423
13555

With the previous code we created the file score.ly which is the lilypond reüresentation of our score. To create a PDF version and a midi file, we have to call lilypond on a command line with

lilypond score.ly

The following call with !lilypond score.ly is the equivalent in a jupter notebook cell:

!lilypond  score.ly


### OUTPUT:

GNU LilyPond 2.20.0
Processing score.ly'
Parsing...
Interpreting music...
Preprocessing graphical objects...
Interpreting music...
MIDI output to score.midi'...
Finding the ideal number of pages...
Fitting music on 6 or 7 pages...
Drawing systems...
Layout output to /tmp/lilypond-TcDnC5'...
Converting to score.pdf'...
Deleting /tmp/lilypond-TcDnC5'...
Success: compilation successfully completed


In case you cannot create create it yourself, you can downoad it here:

We created the mp3 file with the following Linux commands:

timidity score.midi -Ow -o - | ffmpeg -i - -acodec libmp3lame -ab 64k score.mp3

The program timidity can also be used to listen to the midi file by calling it on the command line:

timidity score.midi

## Polyphonic Score from Fibonacci Numbers

While the first score consists of a melody underlaid with chords, we now want to create a score that has a melody both in the left and right hand.

def create_polyphone_score(notes_gen,
upper,
lower,
beat_numbers,
number_of_notes=80):
""" This function is similar to create_variable score
but it creates melodies in the left and right hand."""

counter = 0
old_res = ""
group = ""
print(beat_numbers)
for beat in cycle(beat_numbers):
res = notes_gen.send(beat)
#print(beat, res)
seq = ""
old_group = group
if beat == 1:
group = digits2notes[int(res)] + "4 "
upper += group
elif beat == 2:
group = ""
for i in range(beat):
group += digits2notes[int(res[i])] + "8 "
upper += group
elif beat == 3:
group = ""
group += "\\times 2/3 { "
note = digits2notes[int(res)]
group += note + "8 "
for i in range(1, beat):
note = digits2notes[int(res[i])]
group += note + " "
group += "}"
upper += group
#elif beat == 4:
#    for i in range(beat):
#        upper += digits2notes[int(res[i])] + "16 "
elif beat == 5:
group = ""
group += "\\times 4/5 { "
note = digits2notes[int(res)]
group += note + "16 "
for i in range(1, beat):
note = digits2notes[int(res[i])]
group += note + " "
group += "}"
upper += group
elif beat == 8:
group = ""
group += "{ "
note = digits2notes[int(res)]
group += note + "32 "
for i in range(1, beat):
note = digits2notes[int(res[i])]
group += note + " "
group += "}"
upper += group
elif beat == 13:
group = ""
group += "\\times 4/13 { "
note = digits2notes[int(res)]
group += note + "16 "
for i in range(1, beat):
note = digits2notes[int(res[i])]
group += note + " "
group += "}"
upper += group

if old_group:
lower += old_group
else:
lower += "r4 "

counter += beat
if counter > number_of_notes:
break

old_res = res
return upper +  "}", lower +  "}"

from itertools import cycle

t = (1, 1, 2, 3, 5, 5, 3, 2, 1, 1, 2, 3, 5, 8, 8, 5, 3, 2, 1, 1)
t = (1, 1, 2, 3, 5, 8, 13, 5, 1, 1)
ng = notesgenerator(fib, *number_seq)
number_of_notes = next(ng)
print(f"n of notes: {number_of_notes}")
upper, lower = create_polyphone_score(ng,
upper,
lower,
t,
number_of_notes*3)

score = create_score(upper, lower, number_seq)

open("score2.ly", "w").write(score)


### OUTPUT:

n of notes: 423
(1, 1, 2, 3, 5, 8, 13, 5, 1, 1)
14497
!lilypond  score2.ly


### OUTPUT:

GNU LilyPond 2.20.0
Processing score2.ly'
Parsing...
Interpreting music...
Preprocessing graphical objects...
Interpreting music...
MIDI output to score2.midi'...
Finding the ideal number of pages...
Fitting music on 4 or 5 pages...
Drawing systems...
Layout output to /tmp/lilypond-jakQyo'...
Converting to score2.pdf'...
Deleting /tmp/lilypond-jakQyo'...
Success: compilation successfully completed


## Pentatonic Score from Fibonacci Numbers

In the following example, we will use a Pentatonic scale:

digits2notes = ["a,", "c", "d",
"e", "g", "a",
"c'", "d'", "e'",
"g'"]

from itertools import cycle

upper, lower = create_score_header(key="a \minor", time="4/4")

t = (1, 1, 2, 3, 5, 5, 3, 2, 1, 1, 2, 3, 5, 8, 8, 5, 3, 2, 1, 1)
t = (1, 1, 2, 3, 5, 8, 13, 5, 1, 1)
ng = notesgenerator(fib, *number_seq)
number_of_notes = next(ng)

print(f"n of notes: {number_of_notes}")
upper, lower = create_polyphone_score(ng,
upper,
lower,
t,
number_of_notes*3)

score = create_score(upper, lower, number_seq)

open("score3.ly", "w").write(score)


### OUTPUT:

n of notes: 423
(1, 1, 2, 3, 5, 8, 13, 5, 1, 1)
11591
!lilypond  score3.ly


### OUTPUT:

GNU LilyPond 2.20.0
Processing score3.ly'
Parsing...
Interpreting music...
Preprocessing graphical objects...
Interpreting music...
MIDI output to score3.midi'...
Finding the ideal number of pages...
Fitting music on 7 or 8 pages...
Drawing systems...
Layout output to /tmp/lilypond-Lj1dnx'...
Converting to score3.pdf'...
Deleting /tmp/lilypond-Lj1dnx'...
Success: compilation successfully completed
`