Module: Musa::Datasets::PS

Includes:
AbsD, Helper
Defined in:
lib/musa-dsl/datasets/ps.rb

Overview

Parameter segments for continuous changes between multidimensional points.

PS (Parameter Segment) represents a continuous change from one point to another over a duration. Extends AbsD for duration support.

Purpose

PS is used to represent:

  • Glissandi: Continuous pitch slides (portamento)
  • Parameter sweeps: Gradual changes in any sonic parameter
  • Interpolations: Smooth transitions between multidimensional states

Unlike discrete events that jump from one value to another, PS represents the continuous path between values.

Natural Keys

  • :from: Starting value (number, array, or hash)
  • :to: Ending value (must match :from type and structure)
  • :right_open: Whether endpoint is included (true = open interval)
  • :duration: Duration of the change (from AbsD)
  • :note_duration, :forward_duration: Additional duration keys (from AbsD)

Value Types

Single Values

{ from: 60, to: 72, duration: 1.0 }
# Single value glissando

Arrays (parallel interpolation)

{ from: [60, 64], to: [72, 76], duration: 1.0 }
# Both values interpolate in parallel
# Arrays must be same size

Hashes (named parameters)

{ from: { pitch: 60, velocity: 64 },
  to: { pitch: 72, velocity: 80 },
  duration: 1.0 }
# Multiple parameters interpolate together
# Hashes must have same keys

Right Open Intervals

The :right_open flag determines if the ending value is reached:

  • false (closed): Interpolation reaches :to value
  • true (open): Interpolation stops just before :to value

This is important for consecutive segments where you don't want discontinuities at the boundaries.

Examples:

Basic parameter segment (pitch glissando)

ps = { from: 60, to: 72, duration: 2.0 }.extend(Musa::Datasets::PS)
# Continuous slide from C4 to C5 over 2 beats

Parallel interpolation (multidimensional)

ps = {
  from: [60, 64],  # C4 and E4
  to: [72, 76],    # C5 and E5
  duration: 1.0
}.extend(PS)
# Both parameters move in parallel

Multiple parameters (sonic gesture)

ps = {
  from: { pitch: 60, velocity: 64, pan: -1.0 },
  to: { pitch: 72, velocity: 80, pan: 1.0 },
  duration: 2.0
}.extend(PS)
# Pitch, velocity, and pan all change smoothly

Right open interval

ps1 = { from: 60, to: 64, duration: 1.0, right_open: true }.extend(PS)
ps2 = { from: 64, to: 67, duration: 1.0, right_open: false }.extend(PS)
# ps1 stops just before 64, ps2 starts at 64 - no discontinuity

Created from P point series

p = [60, 4, 64, 8, 67].extend(P)
serie = p.to_ps_serie
ps1 = serie.next_value
# => { from: 60, to: 64, duration: 1.0, right_open: true }

See Also:

Constant Summary collapse

NaturalKeys =

Natural keys including segment endpoints.

Returns:

(NaturalKeys + [:from, :to, :right_open]).freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#base_durationRational

Base duration for time calculations.

Returns:



107
108
109
# File 'lib/musa-dsl/datasets/ps.rb', line 107

def base_duration
  @base_duration
end

Instance Method Details

#durationNumeric Originally defined in module AbsD

Returns event duration.

Examples:

event.duration  # => 1.0

Returns:

  • (Numeric)

    duration

#forward_durationNumeric Originally defined in module AbsD

Returns forward duration (time until next event).

Defaults to :duration if :forward_duration not specified.

Examples:

event.forward_duration  # => 1.0

Returns:

  • (Numeric)

    forward duration

#note_durationNumeric Originally defined in module AbsD

Returns actual note duration.

Defaults to :duration if :note_duration not specified.

Examples:

event.note_duration  # => 0.5 (staccato)

Returns:

  • (Numeric)

    note duration

#to_absIAbsI

TODO:

Not yet implemented

Converts to absolute indexed format.

Returns:

  • (AbsI)

    indexed dataset

Raises:

  • (NotImplementedError)


137
138
139
# File 'lib/musa-dsl/datasets/ps.rb', line 137

def to_absI
  raise NotImplementedError, 'PS to_absI conversion is not yet implemented'
end

#to_gdvGDV

TODO:

Not yet implemented

Converts to GDV (Grade/Duration/Velocity).

Returns:

  • (GDV)

    GDV dataset

Raises:

  • (NotImplementedError)


129
130
131
# File 'lib/musa-dsl/datasets/ps.rb', line 129

def to_gdv
  raise NotImplementedError, 'PS to_gdv conversion is not yet implemented'
end

#to_neumaString

TODO:

Not yet implemented

Converts to Neuma notation string.

Returns:

Raises:

  • (NotImplementedError)


113
114
115
# File 'lib/musa-dsl/datasets/ps.rb', line 113

def to_neuma
  raise NotImplementedError, 'PS to_neuma conversion is not yet implemented'
end

#to_pdvPDV

TODO:

Not yet implemented

Converts to PDV (Pitch/Duration/Velocity).

Returns:

  • (PDV)

    PDV dataset

Raises:

  • (NotImplementedError)


121
122
123
# File 'lib/musa-dsl/datasets/ps.rb', line 121

def to_pdv
  raise NotImplementedError, 'PS to_pdv conversion is not yet implemented'
end

#valid?Boolean

Validates PS structure.

Checks that:

  • :from and :to have compatible types
  • Arrays have same size
  • Hashes have same keys
  • Duration is positive numeric

Examples:

Valid array segment

ps = { from: [60, 64], to: [72, 76], duration: 1.0 }.extend(PS)
ps.valid?  # => true

Invalid - mismatched array sizes

ps = { from: [60, 64], to: [72], duration: 1.0 }.extend(PS)
ps.valid?  # => false

Invalid - mismatched hash keys

ps = { from: { a: 1 }, to: { b: 2 }, duration: 1.0 }.extend(PS)
ps.valid?  # => false

Returns:

  • (Boolean)

    true if valid



162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/musa-dsl/datasets/ps.rb', line 162

def valid?
  case self[:from]
  when Array
    self[:to].is_a?(Array) &&
        self[:from].size == self[:to].size
  when Hash
    self[:to].is_a?(Hash) &&
        self[:from].keys == self[:to].keys
  else
    false
  end && self[:duration].is_a?(Numeric) && self[:duration] > 0
end

#validate!void Originally defined in module E

This method returns an undefined value.

Validates event, raising if invalid.

Examples:

event.validate!  # Raises if invalid

Raises:

  • (RuntimeError)

    if event is not valid