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.writeNow 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 …