The flute voice that I produced for Sonic Pi featured here worked, but the tuning across the range of notes was not perfect and difficult to adjust accurately using the method I adopted. For subsequent voices (grand-piano, xylophone and glass harmonica) which have been featured in later pages, I adopted a different approach. This was to take a list of the frequencies of the notes on a piano and work out directly the ratio between two different notes, one of which had a sample and would be played at a rate = 1 and thus get the factor by which the rate had to be changed to produce the second note. I put the frequencies into a spreadsheet and used this to calculate directly the factors required, and this meant that I could dispense with the previous rateoffset function and put the calculated rates directly into the array elements for the sam array. so previously I had:

define :rateoffset do |s,c=0|#first parameter semitone shift, second correction in cents (1/1200 of an octave) case s when 0 val=1 when -1 val=(1-0.5/12) when 1 val=(1+1.0/12) end return val*(1+c.to_f/1200) #make sure cent value is a float to allow division end sam=[[:c3,:flute_c3,rateoffset(0)],[:cs3,:flute_c3,rateoffset(1)]] sam.concat [[:d3,:flute_ds3,rateoffset(-1)],[:ds3,:flute_ds3,rateoffset(0)],[:e3,:flute_ds3,rateoffset(1)]] sam.concat [[:f3,:flute_fs3,rateoffset(-1)],[:fs3,:flute_fs3,rateoffset(0)],[:g3,:flute_fs3,rateoffset(1,-15)]] sam.concat [[:gs3,:flute_a3,rateoffset(-1)],[:a3,:flute_a3,rateoffset(0)],[:as3,:flute_a3,rateoffset(1)]] sam.concat [[:b3,:flute_c4,rateoffset(-1,-16)],[:c4,:flute_c4,rateoffset(0)],[:cs4,:flute_c4,rateoffset(1,-16)]] sam.concat [[:d4,:flute_ds4,rateoffset(-1,-7)],[:ds4,:flute_ds4,rateoffset(0)],[:e4,:flute_ds4,rateoffset(1,-7)]] sam.concat [[:f4,:flute_fs4,rateoffset(-1,-4)],[:fs4,:flute_fs4,rateoffset(0)],[:g4,:flute_fs4,rateoffset(1,-4)]] sam.concat [[:gs4,:flute_a4,rateoffset(-1,-17)],[:a4,:flute_a4,rateoffset(0)],[:as4,:flute_a4,rateoffset(1,-17)]] sam.concat [[:b4,:flute_c5,rateoffset(-1,-4)],[:c5,:flute_c5,rateoffset(0)],[:cs5,:flute_c5,rateoffset(1,-4)]] sam.concat [[:d5,:flute_ds5,rateoffset(-1,-8)],[:ds5,:flute_ds5,rateoffset(0)],[:e5,:flute_ds5,rateoffset(1,-8)]] sam.concat [[:f5,:flute_fs5,rateoffset(-1,-16)],[:fs5,:flute_fs5,rateoffset(0)],[:g5,:flute_fs5,rateoffset(1,-16)]] sam.concat [[:gs5,:flute_a5,rateoffset(-1,-17)],[:a5,:flute_a5,rateoffset(0)],[:as5,:flute_a5,rateoffset(1,-17)]] sam.concat [[:b5,:flute_c6,rateoffset(-1,-18)],[:c6,:flute_c6,rateoffset(0)]]

and this was replaced by

sam = [[:c3,:flute_c3,1],[:cs3,:flute_c3,1.05945892227837]] sam.concat [[:d3,:flute_ds3,0.943874828847477],[:ds3,:flute_ds3,1],[:e3,:flute_ds3,1.05946786832344]] sam.concat [[:f3,:flute_fs3,0.943874765536738],[:fs3,:flute_fs3,1],[:g3,:flute_fs3,1.05946582917561]] sam.concat [[:gs3,:flute_a3,0.943872727272727],[:a3,:flute_a3,1],[:as3,:flute_a3,1.05946363636364]] sam.concat [[:b3,:flute_c4,0.943874079793293],[:c4,:flute_c4,1],[:cs4,:flute_c4,1.05946274452845]] sam.concat [[:d4,:flute_ds4,0.9438750092406],[:ds4,:flute_ds4,1],[:e4,:flute_ds4,1.05946446306492]] sam.concat [[:f4,:flute_fs4,0.943874765536738],[:fs4,:flute_fs4,1],[:g4,:flute_fs4,1.05946312642908]] sam.concat [[:gs4,:flute_a4,0.943875],[:a4,:flute_a4,1],[:as4,:flute_a4,1.05946363636364]] sam.concat [[:b4,:flute_c5,0.943873972529436],[:c5,:flute_c5,1],[:cs5,:flute_c5,1.05946285816941]] sam.concat [[:d5,:flute_ds5,0.9438750092406],[:ds5,:flute_ds5,1],[:e5,:flute_ds5,1.05946285600414]] sam.concat [[:f5,:flute_fs5,0.943873490011338],[:fs5,:flute_fs5,1],[:g5,:flute_fs5,1.05946304607231]] sam.concat [[:gs5,:flute_a5,0.943873863636364],[:a5,:flute_a5,1],[:as5,:flute_a5,1.05946363636364]] sam.concat [[:b5,:flute_c6,0.943876731963688],[:c6,:flute_c6,1]]

This change gives a flute voice with much better tuning across the whole range.

You can download a zip file containing the program plus flute samples here

You can listen to the piece by Telemann for two flutes which is produced by the program here

The reason that your original frequency adjustment for semitones up and down did not produce good results is that you used a linear adjustment of the frequency, based on the assumption that the 12 pitches that span an octave are at intervals that correspond to 1/12 of the frequency range in the octave. This is incorrect, because the progression of “equal” semitone pitches that span the octave are at *geometric*, not arithmetic intervals. Put another way, it is the *logarithm* (exponent) of the frequency that is divided in equal twelfths across the octave. (This is because of the way that the cochlea of our inner ear detects pitch). Each successive pitch is derived by multiplying the previous pitch by the twelfth root of 2. After doing this twelve times, the frequency is doubled — in other words, the frequency of the next higher octave is obtained.

Thanks for this explanation Jefferson. I had realised that I had used equal intervals, but had not explained the reason fully for the change. You put it very clearly. Of course, for those with the time and the inclination Sonic-Pi is great for exploring all different means of tuning, weird or otherwise.