SimpleBlog
Index of all articlesHow it came to the scores (hummingbird mind thinking and one day of fun)
I’ve read Martin Gardener’s ’’The Colossal Book of Mathematics’’. In chapter 27 ’’Fractal Music’’ he discusses 1/f (pink noise) music generartion. So I was allways interested in algorithmic music and decided to try it with pink noise. Gardener describes Voss’s dice algorithm. It sounded easy, so a straight forward implementation in Ruby (which is the language of choice doing rapid prototyping) was even easier:
The Pink Noise Generator
#!/bin/ruby
# save this as "pinknoise.rb"
# helpers:
# Voss uses binary representations of numbers to decide,
# which dice has to be to diced. Here is the function that checks the represtentations:
def getpdif(a,b)
r=Array.new
a.size.times do |i|
r << i if a[i]!=b[i]
end
r
end
# classes:
# Dice Class, but every number of sides is possible:
class Dice
def initialize(sides=6)
@sides=sides
@shows=rand(@sides)
end
def die
@shows=rand(@sides)
end
def shows
@shows
end
end
# Pink Noise Class that computes first what dices
# (number of sides) will be needed, to generate pink noise in the range of r:
class PinkNoise
def initialize(r=12)
@sides=(Math.sqrt(r).to_i)+1
@size=(Math.sqrt(r).to_i)+1
@p0=2**@size
@start=(@p0-1)
@dices=Hash.new
@size.times do |i|
@dices[i+1]=Dice.new(@sides)
end
end
def to_s
[@size,@start,@dices]
end
def adddices
r=0
@dices.values.each do |y|
r+=y.shows
end
r
end
def next
n=@start+1
n=(n%(2**@size)+@p0)
r=getpdif(n.to_s(2),@start.to_s(2))
@start=n
r.each do |i|
@dices[i].die
end
adddices
end
end
# Example:
# pinknoisegenerator = PinkNoise.new(20)
# 10.times do
# nextevent = pinknoisegenerator.next
# end
Fine this works!
If you want to do this and all that follows I reconmend:
- Use Linux, cause every thing is available (Ruby, Lilypond etc.) and this is an example of thinking unix.
- Or if you have a MS Laptop like me, use Cygwin where also every thing, you’ll need is available in a unix like enviroment.
Generating Music and Score (first try)
So what is the easiest way to generate score and midi?
I added this piece of code to pinknoise.rb (remember this is really rapid prototyping):
# header and footer of a lilypond source file:
$H=<<EOH
\\version "2.10.33"
\\header { }
\\score {
EOH
$F=<<EOF
\\layout { }
\\midi { }
}
EOF
class Lily
def initialize(n,tones)
@n=n
@tones=tones
@m=@tones.size
@png=PinkNoise.new(@m)
end
def write
erg=$H+"{\n"
@n.times do |i|
erg << "\n" if (i+1)%16==0
erg << @tones[@png.next % @m] << "16 "
end
erg << "\n}" << $F
erg
end
end
c_dur_pent=["c'","e'","f'","g'","a'","c''","e''","f''","g''","a''"]
c_dur=["c'","d'","e'","f'","g'","a'","b'","c''","d''","e''","f''","g''","a''","b''"]
l=Lily.new(64,c_dur)
print l.write
Now you do:
- ruby pinknoise.rb >test.ly
- lilypond test.ly
and you get ’’test.mid’’ and ’’test.pdf’’, so you can look at the score and hear the midi file.
The Hummingbird Situation
Until now everything was fine and quite easy. So I began to think about musical form, rythmn, harmonies and so on. Quite complex things. And I really got in panic about all this work with the moods and scales. (Ok, this is not the hard thing in algorithmic music, but it’s still a lot of typing) I thought, there must be someone, who has done all this work and there should be a repository of scales in a common format. So I googeled. And yapadapadoo I found lucytune where Charles Lucy (By the way: Sorry Charles, that until now I have not read all the other material on the luzytune side, but I’m really a hummingbird mind, but I’m sure it’s worth reading) has done a great thing, that caught me at once, cause of his mathematical approach (every possible scale in enharmonic space from unison to twelfetone with base tone c) and on the other side cause he did such a lot of work in finding the namings of the scales.
I downloaded his Excelfile of all Scales base tone c and wow there are so many that make sense.
Visualisation
Hey Mr. Hummingbird, you wanted to make pink noise music? Ok, this could be done later. Let’s first see what we have got here. There are so many Ragas and I love classical indian music. So let’s visualize first the scales.
Again this could be done with lilypond. But first we need a parser for the xls file.
So let’s save it as csv first. (I used OpenOffice to do this) We now have a 2339.csv file.
Here the ruby programm that reads the csv and makes the lilypond sourcefiles for generating the scores:
#!/bin/ruby
# the schema of the csv:
$Pattern=["Name Of Scale", "ScaleCoding", "Pitch Set binary",
"Binary 12notes 1&0", "PitchSet Notation 12 edo", "Note Names from C",
"NotesInStepsOfFifiths", "L and s Interval Sequence", "Major Triads",
"Minor Triads", "Aug. Triads", "Dim. Triads", "Number Of Notes In Scale",
"Ascending Note Positions in Scale", "LengthOfChain", "Flatmost Note",
"Sharpmost Note", "Contiguous Notes", "PositionOfTonic"]
# should be called makedutch!
def makeenglish(s)
s=s.gsub("#","is")
s=s.gsub("b","es")
s=s.gsub("Ces","Ces'")
l=s.split(" ").map { |x| x+"'" }
s=l.join(" ")
s.downcase
end
# The parser class (not much parsing more mapping)
class AbstractScale
def initialize(s)
@a=s.chop.split(";")
@attr=Hash.new
@a.each_with_index do |e,i|
@attr[$Pattern[i]]=e
end
end
def show
p @attr
end
def nameandtones
[makeenglish(@attr["Note Names from C"]),@attr["Name Of Scale"]]
end
def tonesnames
[@attr["Name Of Scale"], makeenglish(@attr["Note Names from C"])]
end
def allattrwithname
erg=[]
$Pattern.each do |k|
erg << k
erg << @attr[k]
end
erg
end
def astxt
muster="|%s:|%s|\n"
ergh=""
erg=""
li=0
$Pattern.each do |k|
if li==0
ergh << "h3. %s\n\n" % @attr[k]
else
erg << muster % [k, @attr[k]]
end
li+=1
end
[ergh,erg]
end
def tnumber
@attr["Number Of Notes In Scale"].to_i
end
def nameofscale
@attr["Name Of Scale"]
end
def notenamesbasec
@attr["Note Names from C"]
end
end
# a little helper to make filenames that sort right:
def mn(n)
s=n.to_s
while s.size<4
s="0"+s
end
s
end
# Reading the scales into the list erg.
# 2339-gut because, I had to remove some unicodes to be able to process with lilypond.
erg=[]
open("2339-gut.csv") do |f|
f.readlines.each do |l|
l=l.delete('"')
erg << AbstractScale.new(l)
end
end
# removing the header of the table
erg.delete_at(0)
# test functions: It reads all 2338 scales.
def donan(erg)
erg.each do |as|
p as.nameandtones
end
end
#erg[0].show
#print erg[100].astxt[0]
#donan(erg)
# The lilypond sorcefile template:
$Score=<<EOSS
\\version "2.10.33"
\\markup { \"%s:\" }
\\score {
\\new GregorianTranscriptionStaff {
%s
}
}
EOSS
# This is the processing. You'll need a pngs dir
li=0
erg.each do |as|
open("pngs/scale%s.ly" % mn(li),"w") do |f|
f.write($Score % as.tonesnames)
end
li+=1
end
After running this we will have 2338 ’’.ly’’ files named scale0000.ly up to scale2337.ly in the pngs directory.
So now:- cd pngs
- lilypond -dbackend=eps -dno-gs-load-fonts -dinclude-eps-fonts—png *.ly (we want png files not pdf)
and you’ll get 2338 png files, one for every scale. So they will be a4 format, cause my version of lilypond can not produce smaller pngs, but I think version 2.12 can do it. But this version is not available on Cygwin yet.
But this is not a problem, cause we can convert the pngs with the convert tool of ImageMagick which is also available on Linux and Cygwin:
#!/bin/ruby
# save this as convert.rb
# to become sortable filenames:
def mn(n)
s=n.to_s
while s.size<4
s="0"+s
end
s
end
# I decided to make the small pngs 415x165:
li=0
Dir["pngs/scale*.png"].each do |path|
p path
np="pngs4/s%s.jpg" % mn (li)
`convert #{path} -crop 415x165+210+65 #{np}`
li+=1
end
In the pngs dir call ’’ruby convert.rb’’ and you will get s0000.png upto s2337.png which are the small versions of the score pics, which you can download here http://www.muzuu.org/new_life/pics/simpleblog/scales/scalespngs.zip (10 MB)
Just for fun: A movie with all the scales
This was made with Mencoder of the Mplayer suite.
Ah, I wanted to make pink noise music …
muzuu.org ist im Besitz von Kizuu, welche ein Produkt von Zuufuzu ist. Kontakt: