Class: Musa::Neumas::Decoders::NeumaDecoder

Inherits:
Decoder show all
Defined in:
lib/musa-dsl/neumas/neuma-gdv-decoder.rb

Overview

GDV neuma decoder for converting neumas to Grade-Duration-Velocity events.

Converts neuma notation (GDVD - differential format) to GDV (absolute format) using scale information. This decoder is the primary way to transform text-based neuma notation into playable musical events.

GDVD vs GDV

  • GDVD (differential): Relative changes +2 _2 (up 2 steps, double duration)
  • GDV (absolute): Absolute values {grade: 2, duration: 1/2r} ready for playback

Conversion Process

Neuma String 

Scale Integration

The decoder uses a scale to interpret grade values:

scale = Musa::Scales::Scales.et12[440.0].major[60]
decoder = NeumaDecoder.new(scale)

# Grade 2 in C major = E (C=0, D=1, E=2)

Appogiatura Handling

Grace notes (appogiatura) are processed recursively:

"(+1_/4)+2_"  # Grace note +1 with duration 1/4, main note +2

Both the grace note and main note are converted from GDVD to GDV.

Examples:

Using with transcriptor

scale = Musa::Scales::Scales.et12[440.0].major[60]

# Create mock transcriptor
transcriptor = Object.new
def transcriptor.transcript(gdv); [gdv]; end

decoder = Musa::Neumas::Decoders::NeumaDecoder.new(
  scale,
  base_duration: 1/4r,
  transcriptor: transcriptor
)

# Transcriptor will process decoded events
decoder.transcriptor  # => transcriptor object

See Also:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(scale, base_duration: nil, transcriptor: nil, base: nil) ⇒ NeumaDecoder

Creates GDV neuma decoder.

Examples:

Create decoder with scale

scale = Object.new
decoder = Musa::Neumas::Decoders::NeumaDecoder.new(
  scale,
  base_duration: 1/4r
)

# Check initial state
decoder.base[:grade]      # => 0
decoder.base[:duration]   # => 1/4r

Custom initial state

scale = Object.new
decoder = Musa::Neumas::Decoders::NeumaDecoder.new(
  scale,
  base: { grade: 2, octave: 1, duration: 1/8r, velocity: 0.8 }
)

# Verify custom state
decoder.base[:grade]   # => 2
decoder.base[:octave]  # => 1


95
96
97
98
99
100
101
102
103
104
105
# File 'lib/musa-dsl/neumas/neuma-gdv-decoder.rb', line 95

def initialize(scale, base_duration: nil, transcriptor: nil, base: nil)
  @base_duration = base_duration
  @base_duration ||= base[:duration] if base
  @base_duration ||= Rational(1, 4)

  base ||= { grade: 0, octave: 0, duration: @base_duration, velocity: 1 }

  @scale = scale

  super base, transcriptor: transcriptor
end

Instance Attribute Details

#base_durationRational

Base duration unit for duration calculations.



119
120
121
# File 'lib/musa-dsl/neumas/neuma-gdv-decoder.rb', line 119

def base_duration
  @base_duration
end

#scaleScale

Scale for interpreting grade values.



112
113
114
# File 'lib/musa-dsl/neumas/neuma-gdv-decoder.rb', line 112

def scale
  @scale
end

Instance Method Details

#apply(gdvd, on:) ⇒ Hash

Applies GDVD to previous state, producing absolute GDV.

Converts differential GDVD to absolute GDV using scale. Processes appogiatura modifiers recursively.

Examples:

Convert differential to absolute

# Previous: { grade: 0, duration: 1/4r }
# GDVD: { grade_diff: +2, duration_factor: 2 }
# Result: { grade: 2, duration: 1/2r, ... }


176
177
178
179
180
181
182
183
# File 'lib/musa-dsl/neumas/neuma-gdv-decoder.rb', line 176

def apply(gdvd, on:)
  gdv = gdvd.to_gdv @scale, previous: on

  appogiatura_action = gdvd.dig(:modifiers, :appogiatura)
  gdv[:appogiatura] = appogiatura_action.to_gdv @scale, previous: on if appogiatura_action

  gdv
end

#inspectString Also known as: to_s

Returns debug representation.



190
191
192
# File 'lib/musa-dsl/neumas/neuma-gdv-decoder.rb', line 190

def inspect
  "GDV NeumaDecoder: @last = #{@last}"
end

#process(gdvd) ⇒ Hash

Processes GDVD attributes before conversion.

Sets base_duration on GDVD object for duration calculations. Handles appogiatura (grace note) modifiers recursively.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/musa-dsl/neumas/neuma-gdv-decoder.rb', line 131

def process(gdvd)
  gdvd = gdvd.clone

  gdvd.base_duration = @base_duration

  appogiatura_gdvd = gdvd[:modifiers]&.delete :appogiatura

  if appogiatura_gdvd
    appogiatura_gdvd = appogiatura_gdvd.clone
    appogiatura_gdvd.base_duration = @base_duration

    gdvd[:modifiers][:appogiatura] = appogiatura_gdvd
  end

  gdvd
end

#subcontextNeumaDecoder

Creates independent subcontext decoder.

Returns new decoder with current @last state as base, enabling independent processing of nested structures (grace notes, etc.).



156
157
158
# File 'lib/musa-dsl/neumas/neuma-gdv-decoder.rb', line 156

def subcontext
  NeumaDecoder.new @scale, base_duration: @base_duration, transcriptor: @transcriptor, base: @last
end