Created
December 19, 2009 08:20
-
-
Save mattly/260006 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class MelodyBase | |
| include Alexandria | |
| design :hooks do | |
| hash :predictive | |
| # this indicates there are map.js and reduce.js functions in | |
| # designs/hooks/views/predictive that are designed to be | |
| # called from a group reduce, the results of which look like a | |
| # ruby hash full of keys/values. | |
| # predictive is a map/reduce view that looks at a "hook" document, | |
| # or a 1/2/4/8 bar melody and iterates across it | |
| # using a markov chain type of function. it uses collation to narrow specificity. | |
| # for details on the theory, read up on jchris's markov chains with mapreduce: | |
| # http://jchrisa.net/drl/_design/sofa/_show/post/markov_chains_using_couchdb_s_g | |
| # for a melody that looks like: C E G F E <rest> D A it would emit: | |
| # | key | value | |
| # | ["C", "E", "G", "F", "E"] | 1 | |
| # | ["D", "A"] | 1 | |
| # | ["E", null, "D", "A"] | 1 | |
| # | ["E", "G", "F", "E", null] | 1 | |
| # | ["F", "E", null, "D", "A"] | 1 | |
| # | ["G", "F", "E", null, "D"] | 1 | |
| # you'll notice that for any given note, it will emit the next four notes | |
| # alternately, we could emit intervals, but that makes explaining how | |
| # the library works a lot trickier | |
| # and the reduce is simply a sum | |
| end | |
| end | |
| # so let's insert a few melodies at the "nes" database in the local couch, | |
| # remembering how the indexes are constructed above: | |
| # C E G F E <rest> D A | |
| # C Db F Ab F E E B | |
| # C F D A G F A <rest> | |
| # | |
| # ok, so now we want to say, hey, I've got an 'F', what comes next? | |
| @db = MelodyBase.new('nes') | |
| f = @db.hooks.predictive(:range => ["F"]) | |
| # translates into :startkey => ["F", nil], :endkey => ["F", {}], :group => 2 | |
| # it infers the group number from the length of the range and the fact that | |
| # this is a 'hash' group reduce view. | |
| # returns: | |
| # { "A" => 1, "Ab" => 1, "D" => 1, "E" => 2 } | |
| # the query handler knows about the parent range and automatically removes | |
| # it from the keys presented from the results returned | |
| # so we infer that there's a 20% chance each of the next note being an A, | |
| # Ab or D, and a 40% chance of it being an E. | |
| # let's pretend for the sake of interestingness our markov chain chooses the E | |
| f.range.push("E").get | |
| # appends "E" to our range, so we're now querying for | |
| # :startkey => ["F", "E", nil], :endkey => ["F", "E", {}], :group => 3 | |
| # returns: | |
| # { nil => 1, "E" => 1 } | |
| # neat! | |
| # let's say we're operating in the context of a step sequencer that doesn't | |
| # like rests, and whose scale rejects the repeated "E" value: | |
| f.range.pop | |
| # do our random thing again, this time choosing "A". our range is now ["F", "A"] | |
| f.range.push("A").get | |
| # { nil => 1 } | |
| # d'oh!, we don't like rests, we're too house for that | |
| f.range.shift | |
| # the range is now ["A"] | |
| f.get | |
| # { "G" => 1, nil => 1 } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment