5 ms·
The code he wrote for showing how to generate is pretty ugly and left me confused. I think this shows much better the relation between the 12 notes and a scale:
by tinco 14y ago
The code he wrote for showing how to generate is pretty ugly and left me confused. I think this shows much better the relation between the 12 notes and a scale:
NOTES = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b']
# 2 is a whole step, 1 is a half step
MAJOR_STEPS = [2,2,1,2,2,2,1]
# returns the scale in the given key with the given steps
def scale(key, steps)
note = NOTES.index(key)
step = 0
notes = []
while step < steps.length
notes << NOTES[note % NOTES.length]
note += steps[step]
step += 1
end
notes
end
scale('d', MAJOR_STEPS) #=> ["d", "e", "f#", "g", "a", "b", "c#"]
I think this shows how a scale is just a list of steps applied to the notes from a certain key :)
- dizzystar 14y agoIs that Ruby? :o I agree. That is 1 million times better. Sometimes I wonder why I don't think of these things. I'll probably fix the code and use something like that... except in Python, obv. Thank you;
- jacquesm 14y agoThanks a ton for the great explanation, I dropped you a line about some typos and other off-topic stuff. Of all the bits and pieces I've looked at over the years explaining the circle of fifths this is the one that stands out for me as the easiest to remember. Being able to re-derive from basic principles is so much better than memorization. Thank you again!
- tinco 14y agoHey, yeah Ruby is a bit easier for me :) Your code is correct so it's not terrible. As to why you didn't think of it.. maybe it's because you practice less at making code look as pretty as possible? It is something I practice a lot, maybe because it's in Ruby's culture, or just because I enjoy making code look pretty. When I look at your two for loops that twice make a range, and have a bunch of magic numbers, all kinds of alarm bells go off. It should be one loop if possible, if there are magic numbers they should be in a constant at the top of the file. Actually, the way you construct the seedList is interesting. A Haskell programmer might have done the same thing, because keeping indexes is ugly in Haskell, and doing operations on infinite lists (which you emulate by appending a copy) are pretty. Perhaps you are just a functional programmer, using a non-functional language :D
- dizzystar 14y agoYeah, I am more of a functional programmer and yeah, I sometimes let some ugliness slip in especially in looping in Python. At least there is none of this crap in production somewhere. :) n00b. It would look better if it was in Scheme or Clojure, but that stuff's too scary for most readers. At least that's the story I'll maintain here.
- tinco 14y agoI would love to see a Scheme or Clojure version of the code. I posted an attempt at it in Haskell, the only functional programming language I know, but it isn't particularily beautiful.
- yayitswei 14y agoChris Ford gave an elegant explanation of scales at a Clojure talk in London: http://skillsmatter.com/podcast/home/functional-composition http://skillsmatter.com/podcast/home/functional-composition. He starts with a basic sine wave and builds up abstractions until he has a Bach canon.
- agumonkey 14y agoOnly at the first third and already delighted. He's moving between music, physics, programming in such a simple and pragmatic way, thanks for the bottom-up dynamic evaluation demonstration. Many many thanks. links aside, he uses (and is a project contributor) http://overtone.github.com/ http://overtone.github.com/ ps: they also provide a nice emacs setup https://github.com/overtone/emacs-live https://github.com/overtone/emacs-live
- tinco 14y agoThis is an excellent talk, thank you very much for linking it. Clojure is such a pretty language!
- Tycho 14y agoi'd like to see it written without a loop, using map or filter...
- kroger 14y agoYou can se a solution in Python here: http://news.ycombinator.com/item?id=4828828 http://news.ycombinator.com/item?id=4828828
- tinco 14y agoAlright, time to fire up ghci then.. it doesn't work with map or filter, but you get the idea: notes = concat $ repeat ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"] majorSteps = [1,1,0,1,1,1,0] scale key steps = key : skip notesFromKey majorSteps where (_:notesFromKey) = dropWhile ((/=) key) notes skip list skips = fst $ foldl next ([], list) skips where next (m, xs) s = (m ++ [xs !! s], drop (s + 1) xs) edit: cleaned it up a lot. This is Haskell by the way. The loop is replaced by a 'fold' in the skip function that reduces the list by skipping through it taking the skip distances from the skips argument.
- mifrai 14y agoSimilar concept but a little simpler and avoids manually written recursion. notes = cycle ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"] majorSteps = [2, 2, 1, 2, 2, 2] :: [Int] scale note = map (dropWhile (/= note) notes !!) . scanl (+) 0 I think it's less clear, but if you want to avoid the rescan via (!!), then scale note = map head . scanl (flip drop) (dropWhile (/= note) notes) Both result in λ> scale "d" majorSteps ["d","e","f#","g","a","b","c#"]
- kroger 14y agoThe following solution defines a function to transpose each scale taking advantage of the mathematical properties of notes: def notes_name(numbers): notes = "C C# D D# E F F# G G# A A# B".split() return [notes[(number % 12)] for number in numbers] def transposition(notes, start): index = start - notes[0] return [(n + index) % 12 for n in notes] def gen_scales(): c_scale = [0, 2, 4, 5, 7, 9, 11] return [notes_name(transposition(c_scale, note)) for note in range(0, 12)] for scale in gen_scales(): print " ".join(scale) EDIT: make the code prettier