From c5f9ba1c54f98fc5e629950ee354739c941440d4 Mon Sep 17 00:00:00 2001 From: Jeremy Voorhis Date: Sat, 1 Nov 2008 17:24:39 -0700 Subject: [PATCH] Simplified env mechanics. --- lib/music/env.rb | 4 +-- lib/music/interpreter.rb | 67 ++++++++++++++++++++++++++-------------- lib/music/score.rb | 2 +- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/lib/music/env.rb b/lib/music/env.rb index 0e6ea22..0875179 100644 --- a/lib/music/env.rb +++ b/lib/music/env.rb @@ -4,8 +4,8 @@ def initialize(&fn) @fn = fn end - def apply(name, val, context) - @fn.call(val, context.phase) + def apply(val, phase) + @fn.call(val, phase) end end end diff --git a/lib/music/interpreter.rb b/lib/music/interpreter.rb index 00c0e48..8040c81 100644 --- a/lib/music/interpreter.rb +++ b/lib/music/interpreter.rb @@ -6,7 +6,8 @@ def self.eval(music) new.eval(music) end - def eval(music, context = Context.default(music.duration)) + def eval(music, context = nil) + context ||= Context.init( Scope.new(0, music.duration, {}) ) music.eval(self, context) end @@ -18,43 +19,63 @@ def eval_section(music, context) end class Context - attr_reader :time, :attributes + attr_reader :time - def self.default(duration) - new(0, { :section_start => 0, :section_duration => duration}) + def self.init(scope) + new(0, [scope]) end - def initialize(time, attrs) - @time, @attributes = time, attrs + def initialize(time, scopes) + @time, @scopes = time, scopes end - def [](name) attributes[name] end + def keys + @keys ||= @scopes.inject([]) { |ns, s| ns | s.attributes.keys } + end + + def attributes + @attributes ||= begin + @scopes.inject({}) do |as, scope| + keys.inject(as) do |as_, key| + a1 = scope.attributes[key] + a2 = as_[key] + as_.merge key => case a1 + when Env: a1.apply(a2, phase) + else a1 || a2 + end + end + end + end + end + + def [](key) attributes[key] end def phase - (@time - @attributes[:section_start]) / @attributes[:section_duration].to_f + (@time - top.offset) / top.duration.to_f end def advance(dur) - self.class.new(time + dur, attributes) + self.class.new(time + dur, @scopes) end - def push(a0) - a1 = attributes.merge(a0) - self.class.new(time, a1) + def push(scope) + self.class.new(time, [scope, *@scopes]) end - def accept(a0) - names = a0.keys | self.attributes.keys - push(names.inject({}) { |a1, name| - a1.merge name => case new = self.attributes[name] - # If a0 yields a Gen, apply it - when Env: new.apply(name, a0[name], self) - # otherwise, inherit the attribute value from a0 if - # we haven't defined it - else self.attributes[name] || a0[name] - end - }) + def accept(attributes) + push(Scope.new(top.offset, top.duration, attributes)) end + + private + def top; @scopes.first end + end + end + + class Scope + attr_reader :offset, :duration, :attributes + + def initialize(offset, duration, attributes) + @offset, @duration, @attributes = offset, duration, attributes end end end diff --git a/lib/music/score.rb b/lib/music/score.rb index a3d023f..c1d6c86 100644 --- a/lib/music/score.rb +++ b/lib/music/score.rb @@ -207,7 +207,7 @@ def reverse end def eval(interpreter, c0) - c1 = c0.push(attributes.merge(:section_start => c0.time, :section_duration => duration)) + c1 = c0.push(Scope.new(c0.time, duration, attributes)) m = score.eval(interpreter, c1) interpreter.eval_section(m, c0) end