class Music::Performance::ValueComputer

Given a start value, and value changes, compute the value at any offset.

Attributes

piecewise_function[R]

Public Class Methods

domain_max() click to toggle source

finds the maximum domain value

# File lib/music-performance/util/value_computer.rb, line 56
def self.domain_max
  Float::INFINITY
end
domain_min() click to toggle source

finds the minimum domain value

# File lib/music-performance/util/value_computer.rb, line 51
def self.domain_min
  -Float::INFINITY
end
new(start_value, value_changes = {}) click to toggle source
# File lib/music-performance/util/value_computer.rb, line 8
def initialize start_value, value_changes = {}
  @piecewise_function = PiecewiseFunction.new
  set_default_value start_value
  
  if value_changes.any?
    value_changes.sort.each do |offset,change|
      
      case change
      when Music::Transcription::Change::Immediate
        add_immediate_change change, offset
      when Music::Transcription::Change::Gradual
        add_linear_change change, offset
      #  add_sigmoid_change change, offset
      end
      
    end
  end
end

Public Instance Methods

domain_max() click to toggle source

finds the maximum domain value

# File lib/music-performance/util/value_computer.rb, line 46
def domain_max
  Float::INFINITY
end
domain_min() click to toggle source

finds the minimum domain value

# File lib/music-performance/util/value_computer.rb, line 41
def domain_min
  -Float::INFINITY
end
sample(xmin, xmax, srate) click to toggle source
# File lib/music-performance/util/value_computer.rb, line 33
def sample xmin, xmax, srate
  sample_period = Rational(1,srate)
  ((xmin.to_r)..(xmax.to_r)).step(sample_period).map do |x|
    value_at(x)
  end
end
value_at(offset) click to toggle source

Compute the value at the given offset. @param [Numeric] offset The given offset to compute value at.

# File lib/music-performance/util/value_computer.rb, line 29
def value_at offset
  @piecewise_function.eval offset
end

Private Instance Methods

add_immediate_change(value_change, offset) click to toggle source

Add a function piece to the piecewise function, which will to compute value for a matching note offset. Transition duration will be ignored since the change is immediate.

@param [ValueChange] value_change An event with information about the new value. @param [Numeric] offset

# File lib/music-performance/util/value_computer.rb, line 73
def add_immediate_change value_change, offset
  func = nil
  value = value_change.value
  domain = offset..domain_max
  func = lambda {|x| value }
  
  @piecewise_function.add_piece domain, func
end
add_linear_change(value_change, offset) click to toggle source

Add a function piece to the piecewise function, which will to compute value for a matching note offset. If the dynamic event duration is non-zero, a linear transition function is created.

@param [ValueChange] value_change An event with information about the new value. @param [Numeric] offset

# File lib/music-performance/util/value_computer.rb, line 88
def add_linear_change value_change, offset
  
  func = nil
  value = value_change.value
  duration = value_change.duration
  domain = offset..domain_max
  
  if duration == 0
    add_immediate_change(value_change, offset)
  else
    b = @piecewise_function.eval domain.first
    m = (value.to_f - b.to_f) / duration.to_f
    
    func = lambda do |x|
      raise RangeError, "#{x} is not in the domain" if !domain.include?(x)
      
      if x < (domain.first + duration)
        (m * (x - domain.first)) + b
      else
        value
      end
    end
    @piecewise_function.add_piece domain, func
  end
end
add_sigmoid_change(value_change, offset) click to toggle source

Add a function piece to the piecewise function, which will to compute value for a matching note offset. If the dynamic event duration is non-zero, a linear transition function is created.

@param [ValueChange] value_change An event with information about the new value. @param [Numeric] offset

# File lib/music-performance/util/value_computer.rb, line 120
def add_sigmoid_change value_change, offset
  
  func = nil
  start_value = @piecewise_function.eval offset
  end_value = value_change.value
  value_diff = end_value - start_value
  duration = value_change.duration
  domain = offset.to_f..domain_max
  abruptness = 0.7 # value_change.transition.abruptness.to_f

  if duration == 0
    add_immediate_change(value_change,offset)
  else
    raise ArgumentError, "abruptness is not between 0 and 1" unless abruptness.between?(0,1)
    
    min_magn = 2
    max_magn = 6
    tanh_domain_magn = abruptness * (max_magn - min_magn) + min_magn
    tanh_domain = -tanh_domain_magn..tanh_domain_magn

    tanh_range = Math::tanh(tanh_domain.first)..Math::tanh(tanh_domain.last)
    tanh_span = tanh_range.last - tanh_range.first

    func = lambda do |x|
      raise RangeError, "#{x} is not in the domain" if !domain.include?(x)
        if x < (domain.first + duration)
          start_domain = domain.first...(domain.first + duration)
          x2 = transform_domains(start_domain, tanh_domain, x)
          y = Math::tanh x2
          z = (y / tanh_span) + 0.5 # ranges from 0 to 1
          start_value + (z * value_diff)
        else
          end_value
        end
    end
    @piecewise_function.add_piece domain, func
  end
end
logistic(x) click to toggle source

0 to 1

# File lib/music-performance/util/value_computer.rb, line 166
def logistic x
  1.0 / (1 + Math::exp(-x))
end
set_default_value(value) click to toggle source
# File lib/music-performance/util/value_computer.rb, line 62
def set_default_value value
  func = lambda {|x| value }
  @piecewise_function.add_piece( domain_min..domain_max, func )
end
transform_domains(start_domain, end_domain, x) click to toggle source

x should be in the start domain

# File lib/music-performance/util/value_computer.rb, line 160
def transform_domains start_domain, end_domain, x
  perc = (x - start_domain.first) / (start_domain.last - start_domain.first).to_f
  x2 = perc * (end_domain.last - end_domain.first) + end_domain.first
end