musa-dsl 0.30.2 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -1
- data/.version +6 -0
- data/.yardopts +7 -0
- data/Gemfile +0 -1
- data/README.md +227 -6
- data/docs/README.md +83 -0
- data/docs/api-reference.md +86 -0
- data/docs/getting-started/quick-start.md +93 -0
- data/docs/getting-started/tutorial.md +58 -0
- data/docs/subsystems/core-extensions.md +316 -0
- data/docs/subsystems/datasets.md +465 -0
- data/docs/subsystems/generative.md +290 -0
- data/docs/subsystems/matrix.md +63 -0
- data/docs/subsystems/midi.md +123 -0
- data/docs/subsystems/music.md +544 -0
- data/docs/subsystems/musicxml-builder.md +264 -0
- data/docs/subsystems/neumas.md +71 -0
- data/docs/subsystems/repl.md +135 -0
- data/docs/subsystems/sequencer.md +98 -0
- data/docs/subsystems/series.md +302 -0
- data/docs/subsystems/transcription.md +152 -0
- data/docs/subsystems/transport.md +177 -0
- data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
- data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
- data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
- data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
- data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
- data/lib/musa-dsl/core-ext/extension.rb +53 -0
- data/lib/musa-dsl/core-ext/hashify.rb +162 -1
- data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
- data/lib/musa-dsl/core-ext/with.rb +114 -0
- data/lib/musa-dsl/datasets/dataset.rb +109 -0
- data/lib/musa-dsl/datasets/delta-d.rb +78 -0
- data/lib/musa-dsl/datasets/e.rb +186 -2
- data/lib/musa-dsl/datasets/gdv.rb +279 -2
- data/lib/musa-dsl/datasets/gdvd.rb +201 -0
- data/lib/musa-dsl/datasets/helper.rb +75 -0
- data/lib/musa-dsl/datasets/p.rb +177 -2
- data/lib/musa-dsl/datasets/packed-v.rb +91 -0
- data/lib/musa-dsl/datasets/pdv.rb +136 -1
- data/lib/musa-dsl/datasets/ps.rb +134 -4
- data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
- data/lib/musa-dsl/datasets/score/render.rb +105 -1
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
- data/lib/musa-dsl/datasets/score.rb +279 -0
- data/lib/musa-dsl/datasets/v.rb +88 -0
- data/lib/musa-dsl/generative/darwin.rb +215 -1
- data/lib/musa-dsl/generative/generative-grammar.rb +387 -0
- data/lib/musa-dsl/generative/markov.rb +135 -3
- data/lib/musa-dsl/generative/rules.rb +312 -4
- data/lib/musa-dsl/generative/variatio.rb +286 -2
- data/lib/musa-dsl/logger/logger.rb +267 -2
- data/lib/musa-dsl/matrix/matrix.rb +256 -10
- data/lib/musa-dsl/midi/midi-recorder.rb +113 -2
- data/lib/musa-dsl/midi/midi-voices.rb +275 -4
- data/lib/musa-dsl/music/chord-definition.rb +233 -1
- data/lib/musa-dsl/music/chord-definitions.rb +33 -6
- data/lib/musa-dsl/music/chords.rb +353 -2
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +70 -206
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_dominant_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_major_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_minor_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/blues/blues_major_scale_kind.rb +100 -0
- data/lib/musa-dsl/music/scale_kinds/blues/blues_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_kinds/chromatic_scale_kind.rb +79 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/double_harmonic_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/hungarian_minor_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_major_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_minor_scale_kind.rb +101 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/phrygian_dominant_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/harmonic_major/harmonic_major_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/major_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/altered_scale_kind.rb +106 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/dorian_b2_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/locrian_sharp2_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_augmented_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_dominant_scale_kind.rb +106 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/melodic_minor_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/mixolydian_b6_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/minor_harmonic_scale_kind.rb +125 -0
- data/lib/musa-dsl/music/scale_kinds/minor_natural_scale_kind.rb +123 -0
- data/lib/musa-dsl/music/scale_kinds/modes/dorian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/locrian_scale_kind.rb +114 -0
- data/lib/musa-dsl/music/scale_kinds/modes/lydian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/mixolydian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/phrygian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_major_scale_kind.rb +93 -0
- data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_minor_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_hw_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_wh_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/whole_tone_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_systems/equally_tempered_12_tone_scale_system.rb +80 -0
- data/lib/musa-dsl/music/scale_systems/twelve_semitones_scale_system.rb +60 -0
- data/lib/musa-dsl/music/scales.rb +1384 -40
- data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
- data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
- data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
- data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
- data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
- data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
- data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
- data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
- data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
- data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
- data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
- data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
- data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
- data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
- data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
- data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
- data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
- data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
- data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
- data/lib/musa-dsl/neumas/neumas.rb +67 -0
- data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
- data/lib/musa-dsl/repl/repl.rb +550 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
- data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
- data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
- data/lib/musa-dsl/series/array-to-serie.rb +37 -1
- data/lib/musa-dsl/series/base-series.rb +843 -5
- data/lib/musa-dsl/series/buffer-serie.rb +54 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +64 -0
- data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
- data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
- data/lib/musa-dsl/series/proxy-serie.rb +67 -0
- data/lib/musa-dsl/series/quantizer-serie.rb +57 -7
- data/lib/musa-dsl/series/queue-serie.rb +78 -0
- data/lib/musa-dsl/series/series-composer.rb +701 -0
- data/lib/musa-dsl/series/timed-serie.rb +473 -28
- data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
- data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
- data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
- data/lib/musa-dsl/transcription/transcription.rb +265 -0
- data/lib/musa-dsl/transport/clock.rb +125 -0
- data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
- data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
- data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
- data/lib/musa-dsl/transport/timer-clock.rb +183 -1
- data/lib/musa-dsl/transport/timer.rb +83 -0
- data/lib/musa-dsl/transport/transport.rb +318 -0
- data/lib/musa-dsl/version.rb +2 -1
- data/lib/musa-dsl.rb +132 -25
- data/musa-dsl.gemspec +25 -18
- metadata +158 -16
|
@@ -1,3 +1,112 @@
|
|
|
1
|
+
# Musical dataset framework for MusaDSL.
|
|
2
|
+
#
|
|
3
|
+
# The Datasets module provides a comprehensive framework for representing and transforming
|
|
4
|
+
# musical events and data structures. It supports multiple representations (MIDI-style,
|
|
5
|
+
# score-style, serialized formats) and conversions between them.
|
|
6
|
+
#
|
|
7
|
+
# ## Architecture
|
|
8
|
+
#
|
|
9
|
+
# The framework consists of several layers:
|
|
10
|
+
#
|
|
11
|
+
# ### 1. Event Types ({E})
|
|
12
|
+
#
|
|
13
|
+
# Hierarchy of event types defining absolute vs. delta encoding:
|
|
14
|
+
#
|
|
15
|
+
# - **{E}**: Base event module
|
|
16
|
+
# - **{Abs}**: Absolute values (actual pitch, duration, etc.)
|
|
17
|
+
# - **{Delta}**: Delta values (incremental changes)
|
|
18
|
+
# - **{AbsI}**: Absolute indexed (array-based)
|
|
19
|
+
# - **{AbsTimed}**: Absolute with time component
|
|
20
|
+
# - **{AbsD}**: Absolute with duration
|
|
21
|
+
# - **{DeltaD}**: Delta duration (absolute/delta/factor)
|
|
22
|
+
#
|
|
23
|
+
# ### 2. Data Structures
|
|
24
|
+
#
|
|
25
|
+
# Basic container types:
|
|
26
|
+
#
|
|
27
|
+
# - **{V}**: Value array - simple ordered values
|
|
28
|
+
# - **{PackedV}**: Packed value hash - named key-value pairs
|
|
29
|
+
# - **{P}**: Pitch series - alternating values and durations
|
|
30
|
+
#
|
|
31
|
+
# ### 3. Musical Datasets
|
|
32
|
+
#
|
|
33
|
+
# Domain-specific musical representations:
|
|
34
|
+
#
|
|
35
|
+
# - **{PS}**: Pitch series (from/to/duration for glissandi)
|
|
36
|
+
# - **{PDV}**: Pitch/Duration/Velocity (MIDI-style representation)
|
|
37
|
+
# - **{GDV}**: Grade/Duration/Velocity (score-style with scale degrees)
|
|
38
|
+
# - **{GDVd}**: Grade/Duration/Velocity delta (incremental encoding)
|
|
39
|
+
#
|
|
40
|
+
# ### 4. Score Container
|
|
41
|
+
#
|
|
42
|
+
# - **{Score}**: Time-indexed container for musical events
|
|
43
|
+
#
|
|
44
|
+
# ## Basic Usage
|
|
45
|
+
#
|
|
46
|
+
# # Create a packed value (hash)
|
|
47
|
+
# pv = { a: 1, b: 2, c: 3 }.extend(Musa::Datasets::PackedV)
|
|
48
|
+
#
|
|
49
|
+
# # Convert to array
|
|
50
|
+
# v = pv.to_V([:c, :b, :a]) # => [3, 2, 1]
|
|
51
|
+
#
|
|
52
|
+
# # Create pitch series
|
|
53
|
+
# p = [60, 4, 64, 8, 67].extend(Musa::Datasets::P)
|
|
54
|
+
# # [pitch, duration, pitch, duration, pitch]
|
|
55
|
+
#
|
|
56
|
+
# # Convert to timed series
|
|
57
|
+
# timed = p.to_timed_serie
|
|
58
|
+
#
|
|
59
|
+
# ## Conversion Patterns
|
|
60
|
+
#
|
|
61
|
+
# The framework supports rich conversions:
|
|
62
|
+
#
|
|
63
|
+
# - PackedV ↔ V (hash to array and vice versa)
|
|
64
|
+
# - P → PS (pitch series to glissando segments)
|
|
65
|
+
# - PDV ↔ GDV (MIDI to score notation)
|
|
66
|
+
# - GDV ↔ GDVd (absolute to delta encoding)
|
|
67
|
+
# - GDV → Neuma (score notation string format)
|
|
68
|
+
#
|
|
69
|
+
# @example MIDI-style pitch/duration/velocity
|
|
70
|
+
# pdv = { pitch: 60, duration: 1.0, velocity: 64 }.extend(Musa::Datasets::PDV)
|
|
71
|
+
# pdv.base_duration = 1/4r
|
|
72
|
+
#
|
|
73
|
+
# # Convert to score notation using scale
|
|
74
|
+
# scale = Musa::Scales::Scales.et12[440.0].major[60]
|
|
75
|
+
# gdv = pdv.to_gdv(scale) # Uses scale degrees
|
|
76
|
+
#
|
|
77
|
+
# @example Score-style grade/duration/velocity
|
|
78
|
+
# gdv = { grade: 0, duration: 1.0, velocity: 0 }.extend(Musa::Datasets::GDV)
|
|
79
|
+
# gdv.base_duration = 1/4r
|
|
80
|
+
#
|
|
81
|
+
# # Convert to MIDI using scale
|
|
82
|
+
# scale = Musa::Scales::Scales.et12[440.0].major[60]
|
|
83
|
+
# pdv = gdv.to_pdv(scale) # Converts to MIDI pitches
|
|
84
|
+
#
|
|
85
|
+
# @example Delta encoding for compression
|
|
86
|
+
# scale = Musa::Scales::Scales.et12[440.0].major[60]
|
|
87
|
+
# gdv1 = { grade: 0, duration: 1.0, velocity: 0 }.extend(Musa::Datasets::GDV)
|
|
88
|
+
# gdv2 = { grade: 2, duration: 1.0, velocity: 1 }.extend(Musa::Datasets::GDV)
|
|
89
|
+
#
|
|
90
|
+
# gdvd = gdv2.to_gdvd(scale, previous: gdv1)
|
|
91
|
+
# # => { delta_grade: 2, delta_velocity: 1 }
|
|
92
|
+
# # Duration unchanged, so omitted
|
|
93
|
+
#
|
|
94
|
+
# @example Score container
|
|
95
|
+
# score = Musa::Datasets::Score.new
|
|
96
|
+
# score.at(0, add: { grade: 0, duration: 1.0 }.extend(Musa::Datasets::GDV))
|
|
97
|
+
# score.at(1, add: { grade: 2, duration: 1.0 }.extend(Musa::Datasets::GDV))
|
|
98
|
+
#
|
|
99
|
+
# @see E Base event type
|
|
100
|
+
# @see PDV MIDI-style representation
|
|
101
|
+
# @see GDV Score-style representation
|
|
102
|
+
# @see Score Event container
|
|
1
103
|
module Musa::Datasets
|
|
104
|
+
# Base marker module for dataset types.
|
|
105
|
+
#
|
|
106
|
+
# Dataset is a simple marker module included by various dataset types
|
|
107
|
+
# to indicate they are part of the dataset framework.
|
|
108
|
+
#
|
|
109
|
+
# @see E Event base module
|
|
110
|
+
# @see P Pitch series dataset
|
|
2
111
|
module Dataset; end
|
|
3
112
|
end
|
|
@@ -1,9 +1,87 @@
|
|
|
1
1
|
require_relative 'e'
|
|
2
2
|
|
|
3
3
|
module Musa::Datasets
|
|
4
|
+
# Delta events with flexible duration encoding.
|
|
5
|
+
#
|
|
6
|
+
# DeltaD (Delta Duration) extends {Delta} events with three different ways to
|
|
7
|
+
# specify duration changes in delta-encoded sequences. This provides flexibility
|
|
8
|
+
# for efficient encoding of musical sequences.
|
|
9
|
+
#
|
|
10
|
+
# ## Duration Encoding Modes
|
|
11
|
+
#
|
|
12
|
+
# **:abs_duration** - Absolute duration override
|
|
13
|
+
#
|
|
14
|
+
# Sets duration to an absolute value, regardless of previous duration.
|
|
15
|
+
# Use when duration changes to a completely different value.
|
|
16
|
+
#
|
|
17
|
+
# { abs_duration: 2.0 }
|
|
18
|
+
# # Sets duration to 2.0, ignoring previous
|
|
19
|
+
#
|
|
20
|
+
# **:delta_duration** - Incremental duration change
|
|
21
|
+
#
|
|
22
|
+
# Adds or subtracts from the previous duration value.
|
|
23
|
+
# Use for small adjustments to duration.
|
|
24
|
+
#
|
|
25
|
+
# { delta_duration: 0.5 } # Add 0.5 to previous
|
|
26
|
+
# { delta_duration: -0.25 } # Subtract 0.25 from previous
|
|
27
|
+
#
|
|
28
|
+
# **:factor_duration** - Multiplicative duration factor
|
|
29
|
+
#
|
|
30
|
+
# Multiplies previous duration by a factor.
|
|
31
|
+
# Use for proportional changes (doubling, halving, etc.).
|
|
32
|
+
#
|
|
33
|
+
# { factor_duration: 2 } # Double previous duration
|
|
34
|
+
# { factor_duration: 0.5 } # Half previous duration
|
|
35
|
+
#
|
|
36
|
+
# ## Natural Keys
|
|
37
|
+
#
|
|
38
|
+
# - **:abs_duration**: Absolute duration value
|
|
39
|
+
# - **:delta_duration**: Duration increment/decrement
|
|
40
|
+
# - **:factor_duration**: Duration multiplication factor
|
|
41
|
+
#
|
|
42
|
+
# Only one duration key should be present at a time.
|
|
43
|
+
#
|
|
44
|
+
# ## Usage in Delta Encoding
|
|
45
|
+
#
|
|
46
|
+
# Used by {GDVd} for efficient delta encoding of musical sequences:
|
|
47
|
+
#
|
|
48
|
+
# @example Different duration encoding modes
|
|
49
|
+
# previous = { duration: 1.0 }
|
|
50
|
+
#
|
|
51
|
+
# # Absolute: set to specific value
|
|
52
|
+
# delta1 = { abs_duration: 2.0 }.extend(DeltaD)
|
|
53
|
+
# # Result: duration becomes 2.0
|
|
54
|
+
#
|
|
55
|
+
# # Delta: add to previous
|
|
56
|
+
# delta2 = { delta_duration: 0.5 }.extend(DeltaD)
|
|
57
|
+
# # Result: duration becomes 1.5 (was 1.0)
|
|
58
|
+
#
|
|
59
|
+
# # Factor: multiply previous
|
|
60
|
+
# delta3 = { factor_duration: 2 }.extend(DeltaD)
|
|
61
|
+
# # Result: duration becomes 2.0 (was 1.0)
|
|
62
|
+
#
|
|
63
|
+
# @example Neuma notation representation
|
|
64
|
+
# # Absolute duration
|
|
65
|
+
# { abs_duration: 1.5 } => "1.5"
|
|
66
|
+
#
|
|
67
|
+
# # Delta duration
|
|
68
|
+
# { delta_duration: 0.5 } => "+0.5"
|
|
69
|
+
# { delta_duration: -0.5 } => "-0.5"
|
|
70
|
+
#
|
|
71
|
+
# # Factor duration
|
|
72
|
+
# { factor_duration: 2 } => "*2"
|
|
73
|
+
#
|
|
74
|
+
# @see Delta Parent delta module
|
|
75
|
+
# @see GDVd Grade/Duration/Velocity delta encoding
|
|
76
|
+
# @see AbsD Absolute duration events
|
|
4
77
|
module DeltaD
|
|
5
78
|
include Delta
|
|
6
79
|
|
|
80
|
+
# Natural keys for delta duration encoding.
|
|
81
|
+
#
|
|
82
|
+
# Only one of these keys should be present in a delta event.
|
|
83
|
+
#
|
|
84
|
+
# @return [Array<Symbol>] natural duration keys
|
|
7
85
|
NaturalKeys = [:abs_duration, # absolute duration
|
|
8
86
|
:delta_duration, # incremental duration
|
|
9
87
|
:factor_duration # multiplicative factor duration
|
data/lib/musa-dsl/datasets/e.rb
CHANGED
|
@@ -1,70 +1,254 @@
|
|
|
1
1
|
require_relative 'dataset'
|
|
2
2
|
|
|
3
3
|
module Musa::Datasets
|
|
4
|
+
# Base module for musical events.
|
|
5
|
+
#
|
|
6
|
+
# E (Event) is the base module for all dataset types representing musical events.
|
|
7
|
+
# It provides validation interface and defines the concept of "natural keys" -
|
|
8
|
+
# keys that are inherent to the dataset type.
|
|
9
|
+
#
|
|
10
|
+
# ## Natural Keys
|
|
11
|
+
#
|
|
12
|
+
# Each dataset type defines which keys are "natural" to it (i.e., semantically
|
|
13
|
+
# meaningful for that type). Keys not in NaturalKeys are considered modifiers
|
|
14
|
+
# or extensions.
|
|
15
|
+
#
|
|
16
|
+
# ## Validation
|
|
17
|
+
#
|
|
18
|
+
# Events can be validated to ensure they contain required keys and valid values.
|
|
19
|
+
# Subclasses should override {#valid?} to implement type-specific validation.
|
|
20
|
+
#
|
|
21
|
+
# @example Basic validation
|
|
22
|
+
# event = { pitch: 60, duration: 1.0 }.extend(Musa::Datasets::E)
|
|
23
|
+
# event.valid? # => true
|
|
24
|
+
# event.validate! # Returns if valid, raises if not
|
|
25
|
+
#
|
|
26
|
+
# @see Abs Absolute value events
|
|
27
|
+
# @see Delta Delta (incremental) events
|
|
4
28
|
module E
|
|
5
29
|
include Dataset
|
|
6
30
|
|
|
31
|
+
# Natural keys for base events (empty).
|
|
32
|
+
# @return [Array<Symbol>]
|
|
7
33
|
NaturalKeys = [].freeze
|
|
8
34
|
|
|
9
|
-
#
|
|
10
|
-
# TODO should valid? and validate! be on Dataset instead of E? P dataset inherits from Dataset but probably it could be validated
|
|
35
|
+
# Checks if event is valid.
|
|
11
36
|
#
|
|
37
|
+
# Base implementation always returns true. Subclasses should override
|
|
38
|
+
# to implement specific validation logic.
|
|
39
|
+
#
|
|
40
|
+
# @return [Boolean] true if valid
|
|
41
|
+
#
|
|
42
|
+
# @example
|
|
43
|
+
# event.valid? # => true
|
|
12
44
|
def valid?
|
|
13
45
|
true
|
|
14
46
|
end
|
|
15
47
|
|
|
48
|
+
# Validates event, raising if invalid.
|
|
49
|
+
#
|
|
50
|
+
# @raise [RuntimeError] if event is not valid
|
|
51
|
+
# @return [void]
|
|
52
|
+
#
|
|
53
|
+
# @example
|
|
54
|
+
# event.validate! # Raises if invalid
|
|
16
55
|
def validate!
|
|
17
56
|
raise RuntimeError, "Invalid dataset #{self}" unless valid?
|
|
18
57
|
end
|
|
19
58
|
end
|
|
20
59
|
|
|
60
|
+
# Events with absolute values.
|
|
61
|
+
#
|
|
62
|
+
# Abs (Absolute) represents events where all values are absolute (not relative).
|
|
63
|
+
# Examples: actual MIDI pitch 60, duration 1.0 seconds, velocity 64.
|
|
64
|
+
#
|
|
65
|
+
# Contrast with {Delta} where values are incremental.
|
|
66
|
+
#
|
|
67
|
+
# @see Delta Incremental events
|
|
68
|
+
# @see AbsI Absolute indexed (arrays)
|
|
69
|
+
# @see AbsTimed Absolute with time
|
|
70
|
+
# @see AbsD Absolute with duration
|
|
21
71
|
module Abs
|
|
22
72
|
include E
|
|
23
73
|
end
|
|
24
74
|
|
|
75
|
+
# Events with delta (incremental) values.
|
|
76
|
+
#
|
|
77
|
+
# Delta represents events where values are incremental changes from a previous
|
|
78
|
+
# state. Examples: pitch +2 semitones, duration +0.5 beats, velocity -10.
|
|
79
|
+
#
|
|
80
|
+
# Delta encoding is efficient for sequences where consecutive events have
|
|
81
|
+
# similar values.
|
|
82
|
+
#
|
|
83
|
+
# @example Delta vs Absolute
|
|
84
|
+
# # Absolute encoding (3 events)
|
|
85
|
+
# { pitch: 60, duration: 1.0 }
|
|
86
|
+
# { pitch: 62, duration: 1.0 }
|
|
87
|
+
# { pitch: 64, duration: 1.0 }
|
|
88
|
+
#
|
|
89
|
+
# # Delta encoding (same 3 events)
|
|
90
|
+
# { abs_pitch: 60, abs_duration: 1.0 } # First event absolute
|
|
91
|
+
# { delta_pitch: +2 } # Duration unchanged
|
|
92
|
+
# { delta_pitch: +2 } # Duration unchanged
|
|
93
|
+
#
|
|
94
|
+
# @see Abs Absolute events
|
|
95
|
+
# @see DeltaD Delta with duration
|
|
25
96
|
module Delta
|
|
26
97
|
include E
|
|
27
98
|
end
|
|
28
99
|
|
|
100
|
+
# Absolute indexed events (array-based).
|
|
101
|
+
#
|
|
102
|
+
# AbsI represents absolute events stored in indexed structures (arrays).
|
|
103
|
+
# Used by {V} and {PackedV} modules.
|
|
104
|
+
#
|
|
105
|
+
# @see Abs Parent absolute module
|
|
106
|
+
# @see V Value arrays
|
|
107
|
+
# @see PackedV Packed value hashes
|
|
29
108
|
module AbsI
|
|
30
109
|
include Abs
|
|
31
110
|
end
|
|
32
111
|
|
|
112
|
+
# Absolute events with time component.
|
|
113
|
+
#
|
|
114
|
+
# AbsTimed represents absolute events that occur at a specific time point.
|
|
115
|
+
# The `:time` key indicates when the event occurs.
|
|
116
|
+
#
|
|
117
|
+
# ## Natural Keys
|
|
118
|
+
#
|
|
119
|
+
# - **:time**: Absolute time position
|
|
120
|
+
#
|
|
121
|
+
# @example Timed event
|
|
122
|
+
# { time: 0.0, value: { pitch: 60 } }.extend(AbsTimed)
|
|
123
|
+
# { time: 1.0, value: { pitch: 64 } }.extend(AbsTimed)
|
|
124
|
+
#
|
|
125
|
+
# @see Abs Parent absolute module
|
|
126
|
+
# @see P Pitch series (produces AbsTimed)
|
|
33
127
|
module AbsTimed
|
|
34
128
|
include Abs
|
|
35
129
|
|
|
130
|
+
# Natural keys including time.
|
|
131
|
+
# @return [Array<Symbol>]
|
|
36
132
|
NaturalKeys = (NaturalKeys + [:time]).freeze
|
|
37
133
|
end
|
|
38
134
|
|
|
135
|
+
# Delta indexed events (array-based deltas).
|
|
136
|
+
#
|
|
137
|
+
# DeltaI represents delta events stored in indexed structures.
|
|
138
|
+
#
|
|
139
|
+
# @see Delta Parent delta module
|
|
39
140
|
module DeltaI
|
|
40
141
|
include Delta
|
|
41
142
|
end
|
|
42
143
|
|
|
144
|
+
# Absolute events with duration.
|
|
145
|
+
#
|
|
146
|
+
# AbsD represents absolute events that have duration - they occupy a time span
|
|
147
|
+
# rather than occurring at a single instant.
|
|
148
|
+
#
|
|
149
|
+
# ## Natural Keys
|
|
150
|
+
#
|
|
151
|
+
# - **:duration**: Total duration of the event process
|
|
152
|
+
# - **:note_duration**: Actual note duration (may differ for staccato, etc.)
|
|
153
|
+
# - **:forward_duration**: Time until next event (may be 0 for simultaneous events)
|
|
154
|
+
#
|
|
155
|
+
# ## Duration Types
|
|
156
|
+
#
|
|
157
|
+
# **duration**: How long the event process lasts (note playing, dynamics change, etc.)
|
|
158
|
+
#
|
|
159
|
+
# **note_duration**: Actual note length. For staccato, this is shorter than duration.
|
|
160
|
+
# Defaults to duration if not specified.
|
|
161
|
+
#
|
|
162
|
+
# **forward_duration**: Time to wait before next event. Can be:
|
|
163
|
+
#
|
|
164
|
+
# - Same as duration (default): next event starts when this one ends
|
|
165
|
+
# - Less than duration: events overlap
|
|
166
|
+
# - Zero: next event starts simultaneously
|
|
167
|
+
# - More than duration: gap/rest before next event
|
|
168
|
+
#
|
|
169
|
+
# @example Basic duration
|
|
170
|
+
# { pitch: 60, duration: 1.0 }.extend(AbsD)
|
|
171
|
+
# event.duration # => 1.0
|
|
172
|
+
# event.note_duration # => 1.0 (defaults to duration)
|
|
173
|
+
# event.forward_duration # => 1.0 (defaults to duration)
|
|
174
|
+
#
|
|
175
|
+
# @example Staccato note
|
|
176
|
+
# { pitch: 60, duration: 1.0, note_duration: 0.5 }.extend(AbsD)
|
|
177
|
+
# # Note sounds for 0.5, but next event waits 1.0
|
|
178
|
+
#
|
|
179
|
+
# @example Simultaneous events
|
|
180
|
+
# { pitch: 60, duration: 1.0, forward_duration: 0 }.extend(AbsD)
|
|
181
|
+
# # Next event starts immediately (chord)
|
|
182
|
+
#
|
|
183
|
+
# @see Abs Parent absolute module
|
|
184
|
+
# @see PS Pitch series with duration
|
|
185
|
+
# @see PDV Pitch/Duration/Velocity
|
|
186
|
+
# @see GDV Grade/Duration/Velocity
|
|
43
187
|
module AbsD
|
|
44
188
|
include Abs
|
|
45
189
|
|
|
190
|
+
# Natural keys including duration variants.
|
|
191
|
+
# @return [Array<Symbol>]
|
|
46
192
|
NaturalKeys = (NaturalKeys +
|
|
47
193
|
[:duration, # duration of the process (note reproduction, dynamics evolution, etc)
|
|
48
194
|
:note_duration, # duration of the note (a staccato note is effectively shorter than elapsed duration until next note)
|
|
49
195
|
:forward_duration # duration to wait until next event (if 0 means the next event should be executed at the same time than this one)
|
|
50
196
|
]).freeze
|
|
51
197
|
|
|
198
|
+
# Returns forward duration (time until next event).
|
|
199
|
+
#
|
|
200
|
+
# Defaults to `:duration` if `:forward_duration` not specified.
|
|
201
|
+
#
|
|
202
|
+
# @return [Numeric] forward duration
|
|
203
|
+
#
|
|
204
|
+
# @example
|
|
205
|
+
# event.forward_duration # => 1.0
|
|
52
206
|
def forward_duration
|
|
53
207
|
self[:forward_duration] || self[:duration]
|
|
54
208
|
end
|
|
55
209
|
|
|
210
|
+
# Returns actual note duration.
|
|
211
|
+
#
|
|
212
|
+
# Defaults to `:duration` if `:note_duration` not specified.
|
|
213
|
+
#
|
|
214
|
+
# @return [Numeric] note duration
|
|
215
|
+
#
|
|
216
|
+
# @example
|
|
217
|
+
# event.note_duration # => 0.5 (staccato)
|
|
56
218
|
def note_duration
|
|
57
219
|
self[:note_duration] || self[:duration]
|
|
58
220
|
end
|
|
59
221
|
|
|
222
|
+
# Returns event duration.
|
|
223
|
+
#
|
|
224
|
+
# @return [Numeric] duration
|
|
225
|
+
#
|
|
226
|
+
# @example
|
|
227
|
+
# event.duration # => 1.0
|
|
60
228
|
def duration
|
|
61
229
|
self[:duration]
|
|
62
230
|
end
|
|
63
231
|
|
|
232
|
+
# Checks if thing can be converted to AbsD.
|
|
233
|
+
#
|
|
234
|
+
# @param thing [Object] object to check
|
|
235
|
+
# @return [Boolean] true if compatible
|
|
236
|
+
#
|
|
237
|
+
# @example
|
|
238
|
+
# AbsD.is_compatible?({ duration: 1.0 }) # => true
|
|
239
|
+
# AbsD.is_compatible?({ pitch: 60 }) # => false
|
|
64
240
|
def self.is_compatible?(thing)
|
|
65
241
|
thing.is_a?(AbsD) || thing.is_a?(Hash) && thing.has_key?(:duration)
|
|
66
242
|
end
|
|
67
243
|
|
|
244
|
+
# Converts thing to AbsD if possible.
|
|
245
|
+
#
|
|
246
|
+
# @param thing [Object] object to convert
|
|
247
|
+
# @return [AbsD] AbsD dataset
|
|
248
|
+
# @raise [ArgumentError] if thing cannot be converted
|
|
249
|
+
#
|
|
250
|
+
# @example
|
|
251
|
+
# AbsD.to_AbsD({ duration: 1.0 }) # => AbsD dataset
|
|
68
252
|
def self.to_AbsD(thing)
|
|
69
253
|
if thing.is_a?(AbsD)
|
|
70
254
|
thing
|