Module: Musa::Datasets::GDVd

Includes:
DeltaD, DeltaI, Helper
Defined in:
lib/musa-dsl/datasets/gdvd.rb

Overview

Score events written as movement from whatever came before.

GDVd (Grade/Duration/Velocity delta) states each event as a change from a previous one: an interval rather than a grade, a factor rather than a duration. Extends DeltaD for flexible duration encoding and DeltaI for indexed deltas.

Purpose

NOT compression. The point is that a passage written this way says nothing about where it starts, so it can start anywhere: the same GDVd sequence applied to a different GDV is the same music transposed, in another register, at another dynamic, at another speed -- and applied to a different scale it is the same shape read through other intervals.

That is what #to_gdv's previous: is: the base the movement departs from. A motif is its profile, and the profile is the dataset.

  • Relative motion: intervals, dynamic changes, duration factors
  • Re-rootable: the same material from any starting point
  • The notation is written this way too: (+1 -o1 *2) is a GDVd

Encoding Types

Each parameter can be encoded as absolute or delta:

Pitch Encoding

Absolute:

  • abs_grade: Set grade to specific value
  • abs_sharps: Set chromatic alteration
  • abs_octave: Set octave to specific value

Delta:

  • delta_grade: Change grade by semitones
  • delta_sharps: Change chromatic alteration
  • delta_interval: Change by scale interval (with delta_interval_sign)
  • delta_octave: Change octave

Duration Encoding (from DeltaD)

  • abs_duration: Set duration
  • delta_duration: Add to duration
  • factor_duration: Multiply duration

Velocity Encoding

  • abs_velocity: Set velocity
  • delta_velocity: Add to velocity

Natural Keys

  • :abs_grade, :abs_sharps, :abs_octave: Absolute pitch
  • :delta_grade, :delta_sharps, :delta_interval, :delta_interval_sign, :delta_octave: Delta pitch
  • :abs_velocity, :delta_velocity: Velocity encoding
  • :abs_duration, :delta_duration, :factor_duration: Duration encoding
  • :modifiers: Hash of additional modifiers

Reconstruction

Delta events require a previous event for reconstruction:

gdvd = { delta_grade: 2, delta_velocity: 1 }.extend(GDVd)
previous = { grade: 0, octave: 0, duration: 1.0, velocity: 0 }.extend(GDV)
gdv = gdvd.to_gdv(scale, previous: previous)
# => { grade: 2, octave: 0, duration: 1.0, velocity: 1 }

Neuma Delta Notation

Delta events use special notation in Neuma format:

  • Delta grade: "+2" or "-2" (semitone change)
  • Delta sharps: "+#" or "-_" (chromatic change)
  • Delta octave: "+o1" or "-o1" (octave change)
  • Delta duration: "+0.5" or "-0.5" (duration change)
  • Factor duration: "*2" or "*0.5" (duration multiply)
  • Delta velocity: "+f" or "-f" (dynamics change)

Examples:

First event (absolute encoding)

gdvd = { abs_grade: 0, abs_duration: 1r, abs_velocity: 0 }.extend(GDVd)
gdvd.to_neuma  # => "(0 4 mp)"
# One bar, written as 4 with the default base_duration of 1/4r.

Delta encoding (unchanged duration)

gdvd = { delta_grade: 2, delta_velocity: 1 }.extend(GDVd)
gdvd.base_duration = 1/4r
gdvd.to_neuma  # => "(+2 +f)"
# Grade +2 semitones, velocity +1 (one f louder)

Chromatic change

gdvd = { delta_sharps: 1 }.extend(GDVd)
gdvd.to_neuma  # => "(+#)"
# Add one sharp

Duration multiplication

gdvd = { factor_duration: 2 }.extend(GDVd)
gdvd.base_duration = 1/4r
gdvd.to_neuma  # => "(. *2)"
# Double duration

Reconstruction from delta

previous = { grade: 0, octave: 0, duration: 1.0, velocity: 0 }.extend(GDV)
gdvd = { delta_grade: 2, delta_velocity: 1 }.extend(GDVd)
scale = Musa::Scales::Scales.et12[440.0].major[60]
gdv = gdvd.to_gdv(scale, previous: previous)
# => { grade: 2, octave: 0, duration: 1.0, velocity: 1 }

Octave change

gdvd = { delta_grade: -2, delta_octave: 1 }.extend(GDVd)
gdvd.to_neuma  # => "(-2 +o1)"
# Down 2 semitones, up one octave

See Also:

Constant Summary collapse

NaturalKeys =

Natural keys for delta encoding.

Returns:

(NaturalKeys +
                    [:abs_grade, :abs_sharps, :abs_octave,
:delta_grade, :delta_sharps, :delta_interval_sign, :delta_interval, :delta_octave,
:abs_velocity, :delta_velocity,
:modifiers]).freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#base_durationRational

Base duration for time calculations.

Returns:



144
145
146
# File 'lib/musa-dsl/datasets/gdvd.rb', line 144

def base_duration
  @base_duration
end

Instance Method Details

#to_gdv(scale, previous: nil) ⇒ GDV

Reconstructs absolute GDV from delta encoding.

Applies delta changes to previous event to create new absolute event. Handles all encoding types (abs_, delta_, factor_) appropriately.

Examples:

Basic delta reconstruction

previous = { grade: 0, octave: 0, duration: 1.0, velocity: 0 }.extend(GDV)
gdvd = { delta_grade: 2, delta_velocity: 1 }.extend(GDVd)
gdv = gdvd.to_gdv(scale, previous: previous)
# => { grade: 2, octave: 0, duration: 1.0, velocity: 1 }

Absolute override

previous = { grade: 0, duration: 1.0 }.extend(GDV)
gdvd = { abs_grade: 5, abs_duration: 2.0 }.extend(GDVd)
gdv = gdvd.to_gdv(scale, previous: previous)
# => { grade: 5, duration: 2.0 }

Duration factor

previous = { grade: 0, duration: 1.0 }.extend(GDV)
gdvd = { factor_duration: 2 }.extend(GDVd)
gdv = gdvd.to_gdv(scale, previous: previous)
# => { grade: 0, duration: 2.0 }

Parameters:

  • scale (Musa::Scales::Scale)

    reference scale for pitch calculations

  • previous (GDV) (defaults to: nil)

    previous absolute event (required for reconstruction)

Returns:

  • (GDV)

    reconstructed absolute event



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/musa-dsl/datasets/gdvd.rb', line 201

def to_gdv(scale, previous: nil)
  # No previous event is the beginning, and the beginning is where reading
  # starts. What `to_gdvd` writes there is fully absolute -- abs_grade,
  # abs_duration, abs_velocity -- so it needs nothing to depart from; the
  # keyword used to be required anyway and the clone died on the nil, so the
  # library could write an event it could not read back (issue #88).
  #
  # A genuinely differential event still fails without one, which is right:
  # with nothing before it there is no movement to describe.
  r = (previous || {}).clone.delete_if { |k, _| !GDV::NaturalKeys.include?(k) }.extend GDV

  r.base_duration = @base_duration

  if include?(:abs_grade)
    if self[:abs_grade] == :silence
      r[:silence] = true
    else
      r.delete :silence
      r.delete :sharps

      r[:grade] = scale[self[:abs_grade]].wide_grade
      r[:sharps] = self[:abs_sharps] if include?(:abs_sharps)
    end

  elsif include?(:delta_grade)
    r.delete :silence

    r[:grade], r[:sharps] =
        normalize_to_scale(scale,
                           scale[r[:grade]].wide_grade + self[:delta_grade],
                           (r[:sharps] || 0) + (self[:delta_sharps] || 0))

    r.delete :sharps if r[:sharps].zero?

  elsif include?(:delta_interval)
    r.delete :silence

    sign = self[:delta_interval_sign] || 1

    r[:grade], r[:sharps] =
        normalize_to_scale scale,
                           scale[r[:grade]].wide_grade,
                           sign * scale.kind.tuning.scale_system.intervals[self[:delta_interval]]

    r.delete :sharps if r[:sharps].zero?

  elsif include?(:delta_sharps)
    r.delete :silence

    r[:grade], r[:sharps] =
        normalize_to_scale scale,
                           scale[r[:grade]].wide_grade,
                           (r[:sharps] || 0) + self[:delta_sharps]

    r.delete :sharps if r[:sharps].zero?
  end

  if include?(:abs_octave)
    r[:octave] = self[:abs_octave]
  elsif include?(:delta_octave)
    r[:octave] += self[:delta_octave]
  end

  if include?(:abs_duration)
    r[:duration] = self[:abs_duration]
  elsif include?(:delta_duration)
    r[:duration] += self[:delta_duration]
  elsif include?(:factor_duration)
    r[:duration] *= self[:factor_duration]
  end

  if include?(:abs_velocity)
    r[:velocity] = self[:abs_velocity]
  elsif include?(:delta_velocity)
    r[:velocity] += self[:delta_velocity]
  end

  if include?(:modifiers)
    self[:modifiers].each_pair do |k, v|
      r[k] = v
    end
  end

  (keys - NaturalKeys).each { |k| r[k] = self[k] }

  r
end

#to_neumaString

Converts to Neuma delta notation string.

Neuma delta format uses special notation for changes:

([grade_delta] [octave_delta] [duration_delta] [velocity_delta] [modifiers...])
  • Grade delta: "+2" or "-2" (semitone change)
  • Sharp delta: "+#" or "-_" (chromatic change)
  • Octave delta: "+o1" or "-o1" (octave change)
  • Duration delta: "+0.5", "-0.5", or "*2" (duration change)
  • Velocity delta: "+f" or "-f" (dynamics change by f's)

Examples:

Delta grade

gdvd = { delta_grade: 2 }.extend(GDVd)
gdvd.base_duration = 1/4r
gdvd.to_neuma  # => "(+2)"

Multiple deltas

gdvd = { delta_grade: -2, delta_velocity: 1 }.extend(GDVd)
gdvd.base_duration = 1/4r
gdvd.to_neuma  # => "(-2 +f)"

Duration factor

gdvd = { factor_duration: 2 }.extend(GDVd)
gdvd.base_duration = 1/4r
gdvd.to_neuma  # => "(. *2)"

Chromatic change

gdvd = { delta_sharps: 1 }.extend(GDVd)
gdvd.to_neuma  # => "(+#)"

Absolute values

gdvd = { abs_grade: 0, abs_duration: 1r }.extend(GDVd)
gdvd.to_neuma  # => "(0 4)"

Returns:

  • (String)

    Neuma delta notation



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/musa-dsl/datasets/gdvd.rb', line 348

def to_neuma
  @base_duration ||= Rational(1,4)

  attributes = []

  c = 0

  if include?(:abs_grade)
    attributes[c] = self[:abs_grade].to_s

  elsif include?(:delta_grade)
    attributes[c] = positive_sign_of(self[:delta_grade]) + self[:delta_grade].to_s unless self[:delta_grade].zero?

  elsif include?(:delta_interval)

    attributes[c] = self[:delta_interval_sign] if include?(:delta_interval_sign)
    attributes[c] ||= ''
    attributes[c] += self[:delta_interval].to_s
  end

  if include?(:delta_sharps) && !self[:delta_sharps].zero?
    char = self[:delta_sharps] > 0 ? '#' : '_'
    sign = attributes[c].nil? ? positive_sign_of(self[:delta_sharps]) : ''

    attributes[c] ||= ''
    attributes[c] += sign + char * self[:delta_sharps].abs
  end

  attributes[c] = '.' if attributes[c].nil? || attributes[c].empty?

  if include?(:abs_octave)
    attributes[c += 1] = 'o' + self[:abs_octave].to_s
  elsif include?(:delta_octave)
    attributes[c += 1] = sign_of(self[:delta_octave]) + 'o' + self[:delta_octave].abs.to_s if self[:delta_octave] != 0
  end

  if include?(:abs_duration)
    attributes[c += 1] = (self[:abs_duration] / @base_duration).to_s
  elsif include?(:delta_duration)
    attributes[c += 1] = positive_sign_of(self[:delta_duration]) + (self[:delta_duration] / @base_duration).to_s
  elsif include?(:factor_duration)
    attributes[c += 1] = '*' + self[:factor_duration].to_s
  end

  if include?(:abs_velocity)
    attributes[c += 1] = velocity_of(self[:abs_velocity])
  elsif include?(:delta_velocity)
    attributes[c += 1] = sign_of(self[:delta_velocity]) + 'f' * self[:delta_velocity].abs
  end

  (keys - NaturalKeys).each do |k|
    attributes[c += 1] = modificator_string(k, self[k])
  end

  '(' + attributes.join(' ') + ')'
end

#valid?Boolean Originally defined in module E

Checks if event is valid.

Base implementation always returns true. Subclasses should override to implement specific validation logic.

Examples:

event.valid?  # => true

Returns:

  • (Boolean)

    true if valid

#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