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.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -1
  3. data/.version +6 -0
  4. data/.yardopts +7 -0
  5. data/Gemfile +0 -1
  6. data/README.md +227 -6
  7. data/docs/README.md +83 -0
  8. data/docs/api-reference.md +86 -0
  9. data/docs/getting-started/quick-start.md +93 -0
  10. data/docs/getting-started/tutorial.md +58 -0
  11. data/docs/subsystems/core-extensions.md +316 -0
  12. data/docs/subsystems/datasets.md +465 -0
  13. data/docs/subsystems/generative.md +290 -0
  14. data/docs/subsystems/matrix.md +63 -0
  15. data/docs/subsystems/midi.md +123 -0
  16. data/docs/subsystems/music.md +544 -0
  17. data/docs/subsystems/musicxml-builder.md +264 -0
  18. data/docs/subsystems/neumas.md +71 -0
  19. data/docs/subsystems/repl.md +135 -0
  20. data/docs/subsystems/sequencer.md +98 -0
  21. data/docs/subsystems/series.md +302 -0
  22. data/docs/subsystems/transcription.md +152 -0
  23. data/docs/subsystems/transport.md +177 -0
  24. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
  25. data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
  26. data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
  27. data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
  28. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
  29. data/lib/musa-dsl/core-ext/extension.rb +53 -0
  30. data/lib/musa-dsl/core-ext/hashify.rb +162 -1
  31. data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
  32. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
  33. data/lib/musa-dsl/core-ext/with.rb +114 -0
  34. data/lib/musa-dsl/datasets/dataset.rb +109 -0
  35. data/lib/musa-dsl/datasets/delta-d.rb +78 -0
  36. data/lib/musa-dsl/datasets/e.rb +186 -2
  37. data/lib/musa-dsl/datasets/gdv.rb +279 -2
  38. data/lib/musa-dsl/datasets/gdvd.rb +201 -0
  39. data/lib/musa-dsl/datasets/helper.rb +75 -0
  40. data/lib/musa-dsl/datasets/p.rb +177 -2
  41. data/lib/musa-dsl/datasets/packed-v.rb +91 -0
  42. data/lib/musa-dsl/datasets/pdv.rb +136 -1
  43. data/lib/musa-dsl/datasets/ps.rb +134 -4
  44. data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
  45. data/lib/musa-dsl/datasets/score/render.rb +105 -1
  46. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
  47. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
  48. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
  49. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
  50. data/lib/musa-dsl/datasets/score.rb +279 -0
  51. data/lib/musa-dsl/datasets/v.rb +88 -0
  52. data/lib/musa-dsl/generative/darwin.rb +215 -1
  53. data/lib/musa-dsl/generative/generative-grammar.rb +387 -0
  54. data/lib/musa-dsl/generative/markov.rb +135 -3
  55. data/lib/musa-dsl/generative/rules.rb +312 -4
  56. data/lib/musa-dsl/generative/variatio.rb +286 -2
  57. data/lib/musa-dsl/logger/logger.rb +267 -2
  58. data/lib/musa-dsl/matrix/matrix.rb +256 -10
  59. data/lib/musa-dsl/midi/midi-recorder.rb +113 -2
  60. data/lib/musa-dsl/midi/midi-voices.rb +275 -4
  61. data/lib/musa-dsl/music/chord-definition.rb +233 -1
  62. data/lib/musa-dsl/music/chord-definitions.rb +33 -6
  63. data/lib/musa-dsl/music/chords.rb +353 -2
  64. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +70 -206
  65. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_dominant_scale_kind.rb +110 -0
  66. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_major_scale_kind.rb +110 -0
  67. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_minor_scale_kind.rb +110 -0
  68. data/lib/musa-dsl/music/scale_kinds/blues/blues_major_scale_kind.rb +100 -0
  69. data/lib/musa-dsl/music/scale_kinds/blues/blues_scale_kind.rb +99 -0
  70. data/lib/musa-dsl/music/scale_kinds/chromatic_scale_kind.rb +79 -0
  71. data/lib/musa-dsl/music/scale_kinds/ethnic/double_harmonic_scale_kind.rb +102 -0
  72. data/lib/musa-dsl/music/scale_kinds/ethnic/hungarian_minor_scale_kind.rb +102 -0
  73. data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_major_scale_kind.rb +102 -0
  74. data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_minor_scale_kind.rb +101 -0
  75. data/lib/musa-dsl/music/scale_kinds/ethnic/phrygian_dominant_scale_kind.rb +103 -0
  76. data/lib/musa-dsl/music/scale_kinds/harmonic_major/harmonic_major_scale_kind.rb +104 -0
  77. data/lib/musa-dsl/music/scale_kinds/major_scale_kind.rb +110 -0
  78. data/lib/musa-dsl/music/scale_kinds/melodic_minor/altered_scale_kind.rb +106 -0
  79. data/lib/musa-dsl/music/scale_kinds/melodic_minor/dorian_b2_scale_kind.rb +104 -0
  80. data/lib/musa-dsl/music/scale_kinds/melodic_minor/locrian_sharp2_scale_kind.rb +103 -0
  81. data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_augmented_scale_kind.rb +103 -0
  82. data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_dominant_scale_kind.rb +106 -0
  83. data/lib/musa-dsl/music/scale_kinds/melodic_minor/melodic_minor_scale_kind.rb +104 -0
  84. data/lib/musa-dsl/music/scale_kinds/melodic_minor/mixolydian_b6_scale_kind.rb +103 -0
  85. data/lib/musa-dsl/music/scale_kinds/minor_harmonic_scale_kind.rb +125 -0
  86. data/lib/musa-dsl/music/scale_kinds/minor_natural_scale_kind.rb +123 -0
  87. data/lib/musa-dsl/music/scale_kinds/modes/dorian_scale_kind.rb +111 -0
  88. data/lib/musa-dsl/music/scale_kinds/modes/locrian_scale_kind.rb +114 -0
  89. data/lib/musa-dsl/music/scale_kinds/modes/lydian_scale_kind.rb +111 -0
  90. data/lib/musa-dsl/music/scale_kinds/modes/mixolydian_scale_kind.rb +111 -0
  91. data/lib/musa-dsl/music/scale_kinds/modes/phrygian_scale_kind.rb +111 -0
  92. data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_major_scale_kind.rb +93 -0
  93. data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_minor_scale_kind.rb +99 -0
  94. data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_hw_scale_kind.rb +110 -0
  95. data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_wh_scale_kind.rb +110 -0
  96. data/lib/musa-dsl/music/scale_kinds/symmetric/whole_tone_scale_kind.rb +99 -0
  97. data/lib/musa-dsl/music/scale_systems/equally_tempered_12_tone_scale_system.rb +80 -0
  98. data/lib/musa-dsl/music/scale_systems/twelve_semitones_scale_system.rb +60 -0
  99. data/lib/musa-dsl/music/scales.rb +1384 -40
  100. data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
  101. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
  102. data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
  103. data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
  104. data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
  105. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
  106. data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
  107. data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
  108. data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
  109. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
  110. data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
  111. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
  112. data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
  113. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
  114. data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
  115. data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
  116. data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
  117. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
  118. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
  119. data/lib/musa-dsl/neumas/neumas.rb +67 -0
  120. data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
  121. data/lib/musa-dsl/repl/repl.rb +550 -0
  122. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
  123. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
  124. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
  125. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
  126. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
  127. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
  128. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
  129. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
  130. data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
  131. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
  132. data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
  133. data/lib/musa-dsl/series/array-to-serie.rb +37 -1
  134. data/lib/musa-dsl/series/base-series.rb +843 -5
  135. data/lib/musa-dsl/series/buffer-serie.rb +54 -0
  136. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +64 -0
  137. data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
  138. data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
  139. data/lib/musa-dsl/series/proxy-serie.rb +67 -0
  140. data/lib/musa-dsl/series/quantizer-serie.rb +57 -7
  141. data/lib/musa-dsl/series/queue-serie.rb +78 -0
  142. data/lib/musa-dsl/series/series-composer.rb +701 -0
  143. data/lib/musa-dsl/series/timed-serie.rb +473 -28
  144. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
  145. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
  146. data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
  147. data/lib/musa-dsl/transcription/transcription.rb +265 -0
  148. data/lib/musa-dsl/transport/clock.rb +125 -0
  149. data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
  150. data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
  151. data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
  152. data/lib/musa-dsl/transport/timer-clock.rb +183 -1
  153. data/lib/musa-dsl/transport/timer.rb +83 -0
  154. data/lib/musa-dsl/transport/transport.rb +318 -0
  155. data/lib/musa-dsl/version.rb +2 -1
  156. data/lib/musa-dsl.rb +132 -25
  157. data/musa-dsl.gemspec +25 -18
  158. 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
@@ -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
- # TODO implement valid? in all 'subclasses'. This implies recollecting from other places where validations are done and refactoring
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