Permalink
Browse files

First pass of "Lines" (piecewise line) Pattern

[delivers #15197175]
  • Loading branch information...
1 parent f9d3cca commit 1dbe93f5e9117a67214a9d692ec551a6b2459227 @adamjmurray committed Jun 30, 2011
Showing with 154 additions and 0 deletions.
  1. +1 −0 lib/mtk.rb
  2. +60 −0 lib/mtk/pattern/lines.rb
  3. +93 −0 spec/mtk/pattern/lines_spec.rb
View
@@ -37,6 +37,7 @@ module MIDI
require 'mtk/pattern/sequence'
require 'mtk/pattern/cycle'
require 'mtk/pattern/choice'
+require 'mtk/pattern/lines'
require 'mtk/pattern/palindrome'
require 'mtk/sequencer/event_builder'
View
@@ -0,0 +1,60 @@
+module MTK
+ module Pattern
+
+ # A piecewise linear function (see {http://en.wikipedia.org/wiki/File:PiecewiseLinear.png}) defined in terms
+ # of [value, steps_to_reach_value] pairs.
+ #
+ # The "steps_to_reach_value" for the first element is ignored and may be omitted, since it takes 0 steps to start.
+ class Lines < AbstractPattern
+
+ # Reset the sequence to the beginning
+ def rewind
+ @index = -1
+ @steps = -1
+ @step_count = -1
+ @prev = nil
+ @next = nil
+ super
+ end
+
+ ###################
+ protected
+
+ # (see AbstractPattern#advance!)
+ def advance!
+ super
+
+ while @step_count >= @steps
+ @step_count = 0
+
+ @index += 1
+ raise StopIteration if @index >= @elements.length
+
+ @prev = @next
+ next_elem = @elements[@index]
+ if next_elem.is_a? Array
+ @next = next_elem.first
+ @steps = next_elem.last.to_f
+ else
+ @next = next_elem
+ @steps = 1.0
+ end
+ end
+
+ @step_count += 1
+ end
+
+ # (see AbstractPattern#current)
+ def current
+ if @prev and @next
+ # linear interpolation
+ @prev + (@next - @prev)*@step_count/@steps
+ else
+ @next
+ end
+ end
+
+ end
+
+ end
+end
@@ -0,0 +1,93 @@
+require 'spec_helper'
+
+describe MTK::Pattern::Lines do
+
+ LINES = MTK::Pattern::Lines
+
+ let(:elements) { [0, [10,5], [5,10]] }
+ let(:lines) { LINES.new elements }
+
+ describe "#next" do
+ it "interpolates between values, treating each element as [value, steps_to_value] pairs" do
+ nexts = []
+ loop do
+ nexts << lines.next
+ end
+ nexts.should == [0, 2,4,6,8,10, 9.5,9,8.5,8,7.5,7,6.5,6,5.5,5]
+ end
+ end
+
+ describe "#rewind" do
+ it "starts the pattern from the beginning" do
+ 10.times { lines.next }
+ lines.rewind
+ nexts = []
+ loop do
+ nexts << lines.next
+ end
+ nexts.should == [0, 2,4,6,8,10, 9.5,9,8.5,8,7.5,7,6.5,6,5.5,5]
+ end
+ end
+
+end
+
+
+describe MTK::Pattern do
+
+ describe "#Lines" do
+ it "creates a Lines" do
+ MTK::Pattern.Lines(1,2,3).should be_a MTK::Pattern::Lines
+ end
+
+ it "sets #elements from the varargs" do
+ MTK::Pattern.Lines(1,2,3).elements.should == [1,2,3]
+ end
+
+ it "does not set a type" do
+ MTK::Pattern.Lines(1,2,3).type.should be_nil
+ end
+ end
+
+ describe "#PitchLines" do
+ it "creates a Lines" do
+ MTK::Pattern.PitchLines(1,2,3).should be_a MTK::Pattern::Lines
+ end
+
+ it "sets #elements from the varargs" do
+ MTK::Pattern.PitchLines(1,2,3).elements.should == [1,2,3]
+ end
+
+ it "sets #type to :pitch" do
+ MTK::Pattern.PitchLines([]).type.should == :pitch
+ end
+ end
+
+ describe "#IntensityLines" do
+ it "creates a Lines" do
+ MTK::Pattern.IntensityLines(1,2,3).should be_a MTK::Pattern::Lines
+ end
+
+ it "sets #elements from the varargs" do
+ MTK::Pattern.IntensityLines(1,2,3).elements.should == [1,2,3]
+ end
+
+ it "sets #type to :pitch" do
+ MTK::Pattern.IntensityLines([]).type.should == :intensity
+ end
+ end
+
+ describe "#DurationLines" do
+ it "creates a Lines" do
+ MTK::Pattern.DurationLines(1,2,3).should be_a MTK::Pattern::Lines
+ end
+
+ it "sets #elements from the varargs" do
+ MTK::Pattern.DurationLines(1,2,3).elements.should == [1,2,3]
+ end
+
+ it "sets #type to :pitch" do
+ MTK::Pattern.DurationLines([]).type.should == :duration
+ end
+ end
+
+end

0 comments on commit 1dbe93f

Please sign in to comment.