musa-dsl 0.14.32 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/README.md +5 -1
  4. data/lib/musa-dsl.rb +54 -11
  5. data/lib/musa-dsl/core-ext.rb +7 -13
  6. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +15 -23
  7. data/lib/musa-dsl/core-ext/arrayfy.rb +30 -12
  8. data/lib/musa-dsl/core-ext/attribute-builder.rb +194 -0
  9. data/lib/musa-dsl/core-ext/deep-copy.rb +185 -0
  10. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +44 -40
  11. data/lib/musa-dsl/core-ext/inspect-nice.rb +40 -22
  12. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +108 -0
  13. data/lib/musa-dsl/core-ext/with.rb +26 -0
  14. data/lib/musa-dsl/datasets.rb +8 -3
  15. data/lib/musa-dsl/datasets/dataset.rb +3 -0
  16. data/lib/musa-dsl/datasets/delta-d.rb +12 -0
  17. data/lib/musa-dsl/datasets/e.rb +61 -0
  18. data/lib/musa-dsl/datasets/gdv.rb +51 -411
  19. data/lib/musa-dsl/datasets/gdvd.rb +179 -0
  20. data/lib/musa-dsl/datasets/helper.rb +41 -0
  21. data/lib/musa-dsl/datasets/p.rb +68 -0
  22. data/lib/musa-dsl/datasets/packed-v.rb +19 -0
  23. data/lib/musa-dsl/datasets/pdv.rb +22 -15
  24. data/lib/musa-dsl/datasets/ps.rb +113 -0
  25. data/lib/musa-dsl/datasets/score.rb +210 -0
  26. data/lib/musa-dsl/datasets/score/queriable.rb +48 -0
  27. data/lib/musa-dsl/datasets/score/render.rb +31 -0
  28. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +160 -0
  29. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +51 -0
  30. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +153 -0
  31. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +158 -0
  32. data/lib/musa-dsl/datasets/v.rb +23 -0
  33. data/lib/musa-dsl/generative.rb +5 -5
  34. data/lib/musa-dsl/generative/backboner.rb +274 -0
  35. data/lib/musa-dsl/generative/darwin.rb +102 -96
  36. data/lib/musa-dsl/generative/generative-grammar.rb +182 -187
  37. data/lib/musa-dsl/generative/markov.rb +56 -53
  38. data/lib/musa-dsl/generative/variatio.rb +234 -222
  39. data/lib/musa-dsl/logger.rb +1 -0
  40. data/lib/musa-dsl/logger/logger.rb +31 -0
  41. data/lib/musa-dsl/matrix.rb +1 -0
  42. data/lib/musa-dsl/matrix/matrix.rb +210 -0
  43. data/lib/musa-dsl/midi.rb +2 -2
  44. data/lib/musa-dsl/midi/midi-recorder.rb +54 -52
  45. data/lib/musa-dsl/midi/midi-voices.rb +183 -182
  46. data/lib/musa-dsl/music.rb +5 -5
  47. data/lib/musa-dsl/music/chord-definition.rb +54 -50
  48. data/lib/musa-dsl/music/chord-definitions.rb +13 -9
  49. data/lib/musa-dsl/music/chords.rb +236 -238
  50. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +187 -183
  51. data/lib/musa-dsl/music/scales.rb +331 -332
  52. data/lib/musa-dsl/musicxml.rb +1 -0
  53. data/lib/musa-dsl/musicxml/builder/attributes.rb +155 -0
  54. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +45 -0
  55. data/lib/musa-dsl/musicxml/builder/direction.rb +322 -0
  56. data/lib/musa-dsl/musicxml/builder/helper.rb +90 -0
  57. data/lib/musa-dsl/musicxml/builder/measure.rb +137 -0
  58. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +152 -0
  59. data/lib/musa-dsl/musicxml/builder/note.rb +577 -0
  60. data/lib/musa-dsl/musicxml/builder/part-group.rb +44 -0
  61. data/lib/musa-dsl/musicxml/builder/part.rb +67 -0
  62. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +126 -0
  63. data/lib/musa-dsl/musicxml/builder/rest.rb +117 -0
  64. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +120 -0
  65. data/lib/musa-dsl/musicxml/builder/typed-text.rb +43 -0
  66. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +112 -0
  67. data/lib/musa-dsl/neumalang.rb +1 -1
  68. data/lib/musa-dsl/neumalang/datatypes.citrus +79 -0
  69. data/lib/musa-dsl/neumalang/neuma.citrus +165 -0
  70. data/lib/musa-dsl/neumalang/neumalang.citrus +32 -242
  71. data/lib/musa-dsl/neumalang/neumalang.rb +373 -142
  72. data/lib/musa-dsl/neumalang/process.citrus +21 -0
  73. data/lib/musa-dsl/neumalang/terminals.citrus +67 -0
  74. data/lib/musa-dsl/neumalang/vectors.citrus +23 -0
  75. data/lib/musa-dsl/neumas.rb +5 -0
  76. data/lib/musa-dsl/neumas/array-to-neumas.rb +34 -0
  77. data/lib/musa-dsl/neumas/neuma-decoder.rb +63 -0
  78. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +57 -0
  79. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +15 -0
  80. data/lib/musa-dsl/neumas/neumas.rb +37 -0
  81. data/lib/musa-dsl/neumas/string-to-neumas.rb +33 -0
  82. data/lib/musa-dsl/repl.rb +1 -1
  83. data/lib/musa-dsl/repl/repl.rb +103 -110
  84. data/lib/musa-dsl/sequencer.rb +1 -1
  85. data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +163 -136
  86. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +301 -286
  87. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +548 -321
  88. data/lib/musa-dsl/sequencer/base-sequencer-public.rb +198 -176
  89. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +77 -0
  90. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +75 -0
  91. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +105 -85
  92. data/lib/musa-dsl/sequencer/timeslots.rb +34 -0
  93. data/lib/musa-dsl/series.rb +1 -1
  94. data/lib/musa-dsl/{core-ext → series}/array-to-serie.rb +1 -1
  95. data/lib/musa-dsl/series/base-series.rb +171 -168
  96. data/lib/musa-dsl/series/hash-serie-splitter.rb +134 -132
  97. data/lib/musa-dsl/series/holder-serie.rb +1 -1
  98. data/lib/musa-dsl/series/main-serie-constructors.rb +6 -1
  99. data/lib/musa-dsl/series/main-serie-operations.rb +807 -797
  100. data/lib/musa-dsl/series/proxy-serie.rb +6 -6
  101. data/lib/musa-dsl/series/queue-serie.rb +5 -5
  102. data/lib/musa-dsl/series/series.rb +2 -0
  103. data/lib/musa-dsl/transcription.rb +4 -0
  104. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +227 -0
  105. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +36 -0
  106. data/lib/musa-dsl/transcription/from-gdv.rb +17 -0
  107. data/lib/musa-dsl/transcription/transcription.rb +55 -0
  108. data/lib/musa-dsl/transport.rb +6 -6
  109. data/lib/musa-dsl/transport/clock.rb +26 -26
  110. data/lib/musa-dsl/transport/dummy-clock.rb +32 -30
  111. data/lib/musa-dsl/transport/external-tick-clock.rb +21 -20
  112. data/lib/musa-dsl/transport/input-midi-clock.rb +82 -80
  113. data/lib/musa-dsl/transport/timer-clock.rb +72 -71
  114. data/lib/musa-dsl/transport/timer.rb +28 -26
  115. data/lib/musa-dsl/transport/transport.rb +100 -95
  116. data/musa-dsl.gemspec +3 -3
  117. metadata +73 -24
  118. data/lib/musa-dsl/core-ext/array-apply-get.rb +0 -18
  119. data/lib/musa-dsl/core-ext/array-to-neumas.rb +0 -28
  120. data/lib/musa-dsl/core-ext/as-context-run.rb +0 -44
  121. data/lib/musa-dsl/core-ext/duplicate.rb +0 -134
  122. data/lib/musa-dsl/core-ext/key-parameters-procedure-binder.rb +0 -85
  123. data/lib/musa-dsl/core-ext/proc-nice.rb +0 -13
  124. data/lib/musa-dsl/core-ext/send-nice.rb +0 -21
  125. data/lib/musa-dsl/core-ext/string-to-neumas.rb +0 -27
  126. data/lib/musa-dsl/datasets/gdv-decorators.rb +0 -221
  127. data/lib/musa-dsl/generative/rules.rb +0 -282
  128. data/lib/musa-dsl/neuma.rb +0 -1
  129. data/lib/musa-dsl/neuma/neuma.rb +0 -181
@@ -0,0 +1,90 @@
1
+ module Musa
2
+ module MusicXML
3
+ module Builder
4
+ module Internal
5
+ module Helper
6
+ module NotImplemented
7
+ def initialize(**_args); end
8
+
9
+ def to_xml(io = nil, indent: nil)
10
+ raise NotImplementedError, "#{self.class} not yet implemented. Ask Javier do his work!"
11
+ end
12
+ end
13
+
14
+ module ToXML
15
+ def to_xml(io = nil, indent: nil)
16
+ io ||= StringIO.new
17
+ indent ||= 0
18
+
19
+ tabs = "\t" * indent
20
+
21
+ _to_xml(io, indent: indent, tabs: tabs)
22
+
23
+ io
24
+ end
25
+
26
+ private
27
+
28
+ def _to_xml(io, indent:, tabs:); end
29
+ end
30
+
31
+ module HeaderToXML
32
+ def header_to_xml(io = nil, indent: nil)
33
+ io ||= StringIO.new
34
+ indent ||= 0
35
+
36
+ tabs = "\t" * indent
37
+
38
+ _header_to_xml(io, indent: indent, tabs: tabs)
39
+
40
+ io
41
+ end
42
+
43
+ private
44
+
45
+ def _header_to_xml(io, indent:, tabs:); end
46
+ end
47
+
48
+ private
49
+
50
+ def make_instance_if_needed(klass, hash_or_class_instance)
51
+ case hash_or_class_instance
52
+ when klass
53
+ hash_or_class_instance
54
+ when Hash
55
+ klass.new **hash_or_class_instance
56
+ when nil
57
+ nil
58
+ else
59
+ raise ArgumentError, "#{hash_or_class_instance} is not a Hash, nor a #{klass.name} nor nil"
60
+ end
61
+ end
62
+
63
+ def decode_bool_or_string_attribute(value, attribute, true_value = nil, false_value = nil)
64
+ if value.is_a?(String) || value.is_a?(Numeric)
65
+ " #{attribute}=\"#{value}\""
66
+ elsif value.is_a?(TrueClass) && true_value
67
+ " #{attribute}=\"#{true_value}\""
68
+ elsif value.is_a?(FalseClass) && false_value
69
+ " #{attribute}=\"#{false_value}\""
70
+ else
71
+ ''
72
+ end
73
+ end
74
+
75
+ def decode_bool_or_string_value(value, true_value = nil, false_value = nil)
76
+ if value.is_a?(String) || value.is_a?(Numeric)
77
+ value
78
+ elsif value.is_a?(TrueClass) && true_value
79
+ true_value
80
+ elsif value.is_a?(FalseClass) && false_value
81
+ false_value
82
+ else
83
+ ''
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,137 @@
1
+ require_relative '../../core-ext/with'
2
+
3
+ require_relative 'attributes'
4
+ require_relative 'pitched-note'
5
+ require_relative 'rest'
6
+ require_relative 'unpitched-note'
7
+ require_relative 'backup-forward'
8
+ require_relative 'direction'
9
+
10
+ require_relative 'helper'
11
+
12
+ module Musa
13
+ module MusicXML
14
+ module Builder
15
+ module Internal
16
+ class Measure
17
+ extend Musa::Extension::AttributeBuilder
18
+ include Musa::Extension::With
19
+
20
+ include Helper::ToXML
21
+
22
+ def initialize(number, divisions: nil,
23
+ key_cancel: nil, key_fifths: nil, key_mode: nil,
24
+ time_senza_misura: nil, time_beats: nil, time_beat_type: nil,
25
+ clef_sign: nil, clef_line: nil, clef_octave_change: nil,
26
+ &block)
27
+
28
+ @number = number
29
+ @elements = []
30
+
31
+ @attributes = []
32
+
33
+ if divisions ||
34
+ key_cancel || key_fifths || key_mode ||
35
+ time_senza_misura || time_beats || time_beat_type ||
36
+ clef_sign || clef_line || clef_octave_change
37
+
38
+ add_attributes divisions: divisions,
39
+ key_cancel: key_cancel, key_fifths: key_fifths, key_mode: key_mode,
40
+ time_senza_misura: time_senza_misura, time_beats: time_beats, time_beat_type: time_beat_type,
41
+ clef_sign: clef_sign, clef_line: clef_line, clef_octave_change: clef_octave_change
42
+ end
43
+
44
+ with &block if block_given?
45
+ end
46
+
47
+ attr_accessor :number
48
+ attr_reader :elements
49
+
50
+ attr_complex_adder_to_custom :attributes, plural: :attributes, variable: :@attributes do
51
+ | divisions: nil,
52
+ key_cancel: nil, key_fifths: nil, key_mode: nil,
53
+ time_senza_misura: nil, time_beats: nil, time_beat_type: nil,
54
+ clef_sign: nil, clef_line: nil, clef_octave_change: nil,
55
+ &block |
56
+
57
+ Attributes.new(divisions: divisions,
58
+ key_cancel: key_cancel, key_fifths: key_fifths, key_mode: key_mode,
59
+ time_senza_misura: time_senza_misura, time_beats: time_beats, time_beat_type: time_beat_type,
60
+ clef_sign: clef_sign, clef_line: clef_line, clef_octave_change: clef_octave_change, &block) \
61
+ .tap do |attributes|
62
+
63
+ @attributes << attributes
64
+ @elements << attributes
65
+ end
66
+ end
67
+
68
+ attr_complex_adder_to_custom :pitch do | *parameters, **key_parameters |
69
+ PitchedNote.new(*parameters, **key_parameters).tap { |note| @elements << note }
70
+ end
71
+
72
+ attr_complex_adder_to_custom :rest do | *parameters, **key_parameters |
73
+ Rest.new(*parameters, **key_parameters).tap { |rest| @elements << rest }
74
+ end
75
+
76
+ attr_complex_adder_to_custom :unpitched do | *parameters, **key_parameters |
77
+ UnpitchedNote.new(*parameters, **key_parameters).tap { |unpitched| @elements << unpitched }
78
+ end
79
+
80
+ attr_complex_adder_to_custom :backup do |duration|
81
+ Backup.new(duration).tap { |backup| @elements << backup }
82
+ end
83
+
84
+ attr_complex_adder_to_custom :forward do |duration, voice: nil, staff: nil|
85
+ Forward.new(duration, voice: voice, staff: staff).tap { |forward| @elements << forward }
86
+ end
87
+
88
+ attr_complex_adder_to_custom :direction do |*parameters, **key_parameters, &block|
89
+ Direction.new(*parameters, **key_parameters, &block).tap { |direction| @elements << direction }
90
+ end
91
+
92
+ attr_complex_adder_to_custom(:metronome) {
93
+ |*p, placement: nil, offset: nil, **kp, &b|
94
+ direction(placement: placement, offset: offset) { metronome *p, **kp, &b } }
95
+
96
+ attr_complex_adder_to_custom(:wedge) {
97
+ |*p, placement: nil, offset: nil, **kp, &b|
98
+ direction(placement: placement, offset: offset) { wedge *p, **kp, &b } }
99
+
100
+ attr_complex_adder_to_custom(:dynamics) {
101
+ |*p, placement: nil, offset: nil, **kp, &b|
102
+ direction(placement: placement, offset: offset) { dynamics *p, **kp, &b } }
103
+
104
+ attr_complex_adder_to_custom(:pedal) {
105
+ |*p, placement: nil, offset: nil, **kp, &b|
106
+ direction(placement: placement, offset: offset) { pedal *p, **kp, &b } }
107
+
108
+ attr_complex_adder_to_custom(:bracket) {
109
+ |*p, placement: nil, offset: nil, **kp, &b|
110
+ direction(placement: placement, offset: offset) { bracket *p, **kp, &b } }
111
+
112
+ attr_complex_adder_to_custom(:dashes) {
113
+ |*p, placement: nil, offset: nil, **kp, &b|
114
+ direction(placement: placement, offset: offset) { dashes *p, **kp, &b } }
115
+
116
+ attr_complex_adder_to_custom(:words) {
117
+ |*p, placement: nil, offset: nil, **kp, &b|
118
+ direction(placement: placement, offset: offset) { words *p, **kp, &b } }
119
+
120
+ attr_complex_adder_to_custom(:octave_shift) {
121
+ |*p, placement: nil, offset: nil, **kp, &b|
122
+ direction(placement: placement, offset: offset) { octave_shift *p, **kp, &b } }
123
+
124
+ def _to_xml(io, indent:, tabs:)
125
+ io.puts "#{tabs}<measure number=\"#{@number.to_i}\">"
126
+
127
+ @elements.each do |element|
128
+ element.to_xml(io, indent: indent + 1)
129
+ end
130
+
131
+ io.puts "#{tabs}</measure>"
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,152 @@
1
+ require_relative 'helper'
2
+
3
+ module Musa
4
+ module MusicXML
5
+ module Builder
6
+ module Internal
7
+ class TimeModification
8
+ include Helper
9
+ include ToXML
10
+
11
+ def initialize(actual_notes:, # number
12
+ normal_notes:, # number
13
+ normal_type: nil, # quarter / ...
14
+ normal_dots: nil) # number
15
+
16
+ @actual_notes = actual_notes
17
+ @normal_notes = normal_notes
18
+ @normal_type = normal_type
19
+ @normal_dots = normal_dots
20
+ end
21
+
22
+ attr_accessor :actual_notes, :normal_notes, :normal_type, :normal_dots
23
+
24
+ def _to_xml(io, indent:, tabs:)
25
+ io.puts "#{tabs}<time-modification>"
26
+
27
+ io.puts "#{tabs}\t<actual-notes>#{@actual_notes.to_i}</actual-notes>"
28
+ io.puts "#{tabs}\t<normal-notes>#{@normal_notes.to_i}</normal-notes>"
29
+ io.puts "#{tabs}\t<normal-type>#{@normal_type}</normal-type>" if @normal_type
30
+ @normal_dots&.times do
31
+ io.puts "#{tabs}\t<normal-dot />"
32
+ end
33
+
34
+ io.puts "#{tabs}</time-modification>"
35
+ end
36
+ end
37
+
38
+ class Tuplet
39
+ include Helper
40
+ include ToXML
41
+
42
+ def initialize(type:, # start / stop
43
+ number: nil, # number
44
+ bracket: nil, # true
45
+ show_number: nil, # actual / both / none
46
+ show_type: nil, # actual / both / none
47
+ actual_number: nil, # number
48
+ actual_type: nil, # quarter / eigth / ...
49
+ actual_dots: nil, # number,
50
+ normal_number: nil, # number
51
+ normal_type: nil, # quarter / eigth / ...
52
+ normal_dots: nil) # number
53
+
54
+ @type = type
55
+ @number = number
56
+ @bracket = bracket
57
+ @show_number = show_number
58
+ @show_type = show_type
59
+ @actual_number = actual_number
60
+ @actual_type = actual_type
61
+ @actual_dots = actual_dots
62
+ @normal_number = normal_number
63
+ @normal_type = normal_type
64
+ @normal_dots = normal_dots
65
+ end
66
+
67
+ attr_accessor :type, :number, :bracket, :show_number, :show_type
68
+ attr_accessor :actual_number, :actual_type, :actual_dots
69
+ attr_accessor :normal_number, :normal_type, :normal_dots
70
+
71
+ def _to_xml(io, indent:, tabs:)
72
+ io.puts "#{tabs}<tuplet type=\"#{@type}\"" \
73
+ "#{decode_bool_or_string_attribute(@number&.to_i, 'number')}" \
74
+ "#{decode_bool_or_string_attribute(@bracket, 'bracket', 'yes', 'no')}" \
75
+ "#{decode_bool_or_string_attribute(@show_number, 'show-number')}" \
76
+ "#{decode_bool_or_string_attribute(@show_type, 'show-type')}" \
77
+ ">"
78
+
79
+ if @actual_number || @actual_type || @actual_dots
80
+ io.puts "#{tabs}\t<tuplet-actual>"
81
+
82
+ io.puts "#{tabs}\t\t<tuplet-number>#{@actual_number.to_i}</tuplet-number>" if @actual_number
83
+ io.puts "#{tabs}\t\t<tuplet-type>#{@actual_type}</tuplet-type>" if @actual_type
84
+
85
+ @actual_dots&.times do
86
+ io.puts "#{tabs}\t\t<tuplet-dot />"
87
+ end
88
+
89
+ io.puts "#{tabs}\t</tuplet-actual>"
90
+ end
91
+
92
+ if @normal_number || @normal_type || @normal_dots
93
+ io.puts "#{tabs}\t<tuplet-normal>"
94
+
95
+ io.puts "#{tabs}\t\t<tuplet-number>#{@normal_number.to_i}</tuplet-number>" if @normal_number
96
+ io.puts "#{tabs}\t\t<tuplet-type>#{@normal_type}</tuplet-type>" if @normal_type
97
+
98
+ @normal_dots&.times do
99
+ io.puts "#{tabs}\t\t<tuplet-dot />"
100
+ end
101
+
102
+ io.puts "#{tabs}\t</tuplet-normal>"
103
+ end
104
+
105
+ io.puts "#{tabs}</tuplet>"
106
+ end
107
+ end
108
+
109
+ class Harmonic
110
+ include Helper::ToXML
111
+
112
+ def initialize(kind: nil, # natural / artificial
113
+ pitch: nil) # base-pitch / sounding-pitch / touching-pitch
114
+
115
+ @kind = kind
116
+ @pitch = pitch
117
+ end
118
+
119
+ attr_accessor :kind, :pitch
120
+
121
+ def _to_xml(io, indent:, tabs:)
122
+ io.puts "#{tabs}<harmonic>"
123
+ io.puts "#{tabs}\t<#{@kind} />" if @kind
124
+ io.puts "#{tabs}\t<#{@pitch} />" if @pitch
125
+ io.puts "#{tabs}</harmonic>"
126
+ end
127
+ end
128
+
129
+ class Notehead
130
+ include Helper::NotImplemented
131
+ end
132
+
133
+ class Arrow
134
+ include Helper::NotImplemented
135
+ end
136
+
137
+ class Bend
138
+ include Helper::NotImplemented
139
+ end
140
+
141
+ class Fingering
142
+ include Helper::NotImplemented
143
+ end
144
+
145
+ class Hole
146
+ include Helper::NotImplemented
147
+ end
148
+
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,577 @@
1
+ require_relative '../../core-ext/with'
2
+
3
+ require_relative 'note-complexities'
4
+ require_relative 'helper'
5
+
6
+ module Musa
7
+ module MusicXML
8
+ module Builder
9
+ module Internal
10
+ class Notation
11
+ include Helper::ToXML
12
+
13
+ def self.create(tag, value_or_attributes, true_value = nil, false_value = nil)
14
+ case value_or_attributes
15
+ when Hash
16
+ value_or_attributes = value_or_attributes.clone
17
+
18
+ type = value_or_attributes.delete :type
19
+ content = value_or_attributes.delete :content
20
+
21
+ Notation.new(tag, type, content, true_value, false_value, **value_or_attributes)
22
+
23
+ when NilClass
24
+ nil
25
+
26
+ else
27
+ Notation.new(tag, value_or_attributes)
28
+ end
29
+ end
30
+
31
+ def initialize(tag, type, content = nil, true_value = nil, false_value = nil, **attributes)
32
+ @tag = tag
33
+
34
+ if type == true && true_value
35
+ @type = true_value
36
+ elsif type == false && false_value
37
+ @type = false_value
38
+ else
39
+ @type = type
40
+ end
41
+
42
+ @content = content
43
+ @attributes = attributes
44
+ end
45
+
46
+ def _to_xml(io, indent:, tabs:)
47
+ io.print "#{tabs}\t<#{ @tag } "
48
+
49
+ io.print "type=\"#{@type}\""
50
+
51
+ @attributes.each_pair do |name, value|
52
+ io.print " #{name}=\"#{value}\""
53
+ end
54
+
55
+ if @content
56
+ io.puts ">#{@content}</#{ @tag }>"
57
+ else
58
+ io.puts "/>"
59
+ end
60
+ end
61
+ end
62
+
63
+ private_constant :Notation
64
+
65
+ class Note
66
+ extend Musa::Extension::AttributeBuilder
67
+ include Musa::Extension::With
68
+
69
+ include Helper
70
+ include ToXML
71
+
72
+ def initialize(*_rest,
73
+ pizzicato: nil, # true
74
+ # main content
75
+ grace: nil, # true
76
+ cue: nil, # true
77
+ chord: nil, # true
78
+ duration: nil, # number positive divisions
79
+ tie_start: nil, tie_stop: nil, # true
80
+ voice: nil, # number
81
+ type: nil, # whole / half / quarter / ...
82
+ dots: nil, # number
83
+ accidental: nil, # sharp / flat / ...
84
+ time_modification: nil, # TimeModification class instance
85
+ stem: nil, # up / down / double
86
+ notehead: nil, # Notehead class instance
87
+ staff: nil, # number
88
+
89
+ # notations
90
+ accidental_mark: nil, # sharp / natural / flat / ...
91
+ arpeggiate: nil, # true / up / down
92
+
93
+ tied: nil, # start / stop / continue
94
+ tuplet: nil, # Tuplet class instance
95
+
96
+ dynamics: nil, # pppp...ffff (single or array of)
97
+ fermata: nil, # true / upright / inverted
98
+ glissando: nil, # start / stop
99
+ non_arpeggiate: nil, # top / bottom
100
+
101
+ slide: nil, # start / stop
102
+ slur: nil, # start / stop / continue | { type: start/stop/continue, [**other_attributes: other_values] }
103
+
104
+ ## articulations
105
+ accent: nil, # true
106
+ breath_mark: nil, # true / comma / tick
107
+ caesura: nil, # true
108
+ detached_legato: nil, # true
109
+ doit: nil, # true
110
+ falloff: nil, # true
111
+ other_articulation: nil, # text
112
+ plop: nil, # true
113
+ scoop: nil, # true
114
+ spiccato: nil, # true
115
+ staccatissimo: nil, # true
116
+ staccato: nil, # true
117
+ stress: nil, # true
118
+ strong_accent: nil, # true / up / down
119
+ tenuto: nil, # true
120
+ unstress: nil, # true
121
+
122
+ ## ornaments
123
+ delayed_inverted_turn: nil, # true
124
+ delayed_turn: nil, # true
125
+ inverted_mordent: nil, # true
126
+ inverted_turn: nil, # true
127
+ mordent: nil, # true
128
+ schleifer: nil, # true
129
+ shake: nil, # true
130
+ tremolo: nil, # start / stop / single,
131
+ trill_mark: nil, # true
132
+ turn: nil, # true
133
+ vertical_turn: nil, # true
134
+ wavy_line: nil, # true
135
+ other_ornament: nil, # true
136
+ ornament_accidental_mark: nil, # sharp / natural / flat / ...
137
+
138
+ ## technical
139
+ arrow: nil, # Arrow class instance
140
+ bend: nil, # Bend class instance
141
+ double_tongue: nil, # true
142
+ down_bow: nil, # true
143
+ fingering: nil, # Fingering class instance
144
+ fingernails: nil, # true
145
+ fret: nil, # number
146
+ hammer_on: nil, # start / stop
147
+ handbell: nil, # damp / echo / ...
148
+ harmonic: nil, # Harmonic class instance
149
+ heel: nil, # true
150
+ hole: nil, # Hole class instance
151
+ open_string: nil, # true
152
+ other_technical: nil, # text
153
+ pluck: nil, # text
154
+ pull_off: nil, # start / stop
155
+ snap_pizzicato: nil, # true
156
+ stopped: nil, # true
157
+ string: nil, # number (string number)
158
+ tap: nil, # text
159
+ thumb_position: nil, # true
160
+ toe: nil, # true
161
+ triple_tongue: nil, # true
162
+ up_bow: nil, # true
163
+ **_keyrest,
164
+ &block)
165
+
166
+ @tuplets = []
167
+
168
+ @pizzicato = pizzicato
169
+
170
+ @grace = grace
171
+ @cue = cue
172
+ @chord = chord
173
+ @duration = duration
174
+ @tie_start = tie_start
175
+ @tie_stop = tie_stop
176
+ @voice = voice
177
+ @type = type
178
+ @dots = dots
179
+ @accidental = accidental
180
+ @time_modification = make_instance_if_needed(TimeModification, time_modification)
181
+ @stem = stem
182
+ @notehead = make_instance_if_needed(Notehead, notehead)
183
+ @staff = staff
184
+
185
+ # notations
186
+ @accidental_mark = accidental_mark
187
+ @arpeggiate = arpeggiate
188
+ @tied = tied
189
+ @tuplets << make_instance_if_needed(Tuplet, tuplet) if tuplet
190
+ @dynamics = dynamics
191
+ @fermata = fermata
192
+ @glissando = glissando
193
+ @non_arpeggiate = non_arpeggiate
194
+ @slide = slide
195
+ @slur = Notation.create('slur', slur)
196
+
197
+ ## articulations
198
+ @accent = accent
199
+ @breath_mark = breath_mark
200
+ @caesura = caesura
201
+ @detached_legato = detached_legato
202
+ @doit = doit
203
+ @falloff = falloff
204
+ @other_articulation = other_articulation
205
+ @plop = plop
206
+ @scoop = scoop
207
+ @spiccato = spiccato
208
+ @staccatissimo = staccatissimo
209
+ @staccato = staccato
210
+ @stress = stress
211
+ @strong_accent = strong_accent
212
+ @tenuto = tenuto
213
+ @unstress = unstress
214
+
215
+ ## ornaments
216
+ @delayed_inverted_turn = delayed_inverted_turn
217
+ @delayed_turn = delayed_turn
218
+ @inverted_mordent = inverted_mordent
219
+ @inverted_turn = inverted_turn
220
+ @mordent = mordent
221
+ @schleifer = schleifer
222
+ @shake = shake
223
+ @tremolo = tremolo
224
+ @trill_mark = trill_mark
225
+ @turn = turn
226
+ @vertical_turn = vertical_turn
227
+ @wavy_line = wavy_line
228
+ @other_ornament = other_ornament
229
+ @ornament_accidental_mark = ornament_accidental_mark
230
+
231
+ ## technical
232
+ @arrow = make_instance_if_needed(Arrow, arrow)
233
+ @bend = make_instance_if_needed(Bend, bend)
234
+ @double_tongue = double_tongue
235
+ @down_bow = down_bow
236
+ @fingering = make_instance_if_needed(Fingering, fingering)
237
+ @fingernails = fingernails
238
+ @fret = fret
239
+ @hammer_on = hammer_on
240
+ @handbell = handbell
241
+ @harmonic = make_instance_if_needed(Harmonic, harmonic)
242
+ @heel = heel
243
+ @hole = make_instance_if_needed(Hole, hole)
244
+ @open_string = open_string
245
+ @other_technical = other_technical
246
+ @pluck = pluck
247
+ @pull_off = pull_off
248
+ @snap_pizzicato = snap_pizzicato
249
+ @stopped = stopped
250
+ @string = string
251
+ @tap_ = tap
252
+ @thumb_position = thumb_position
253
+ @toe = toe
254
+ @triple_tongue = triple_tongue
255
+ @up_bow = up_bow
256
+
257
+ with &block if block_given?
258
+ end
259
+
260
+ attr_simple_builder :pizzicato
261
+ attr_simple_builder :grace
262
+ attr_simple_builder :cue
263
+ attr_simple_builder :chord
264
+
265
+ attr_simple_builder :duration
266
+ attr_simple_builder :tie_start
267
+ attr_simple_builder :tie_stop
268
+ attr_simple_builder :type
269
+ attr_simple_builder :dots
270
+ attr_simple_builder :accidental
271
+ attr_simple_builder :stem
272
+ attr_simple_builder :notehead
273
+ attr_simple_builder :voice
274
+ attr_simple_builder :staff
275
+
276
+ attr_complex_builder :time_modification, TimeModification
277
+
278
+ # notations
279
+ attr_simple_builder :accidental_mark
280
+ attr_simple_builder :arpeggiate
281
+ attr_simple_builder :tied
282
+ attr_simple_builder :dynamics
283
+ attr_simple_builder :fermata
284
+ attr_simple_builder :glissando
285
+ attr_simple_builder :non_arpeggiate
286
+ attr_simple_builder :slide
287
+
288
+ attr_complex_adder_to_custom :tuplet do | *parameters, **key_parameters |
289
+ Tuplet.new(*parameters, **key_parameters).tap { |tuplet| @tuplets << tuplet }
290
+ end
291
+
292
+ attr_complex_builder :slur, Notation, first_parameter: 'slur'
293
+
294
+ ## articulations
295
+ attr_simple_builder :accent
296
+ attr_simple_builder :breath_mark
297
+ attr_simple_builder :caesura
298
+ attr_simple_builder :detached_legato
299
+ attr_simple_builder :doit
300
+ attr_simple_builder :falloff
301
+ attr_simple_builder :other_articulation
302
+ attr_simple_builder :plop
303
+ attr_simple_builder :scoop
304
+ attr_simple_builder :spiccato
305
+ attr_simple_builder :staccatissimo
306
+ attr_simple_builder :staccato
307
+ attr_simple_builder :stress
308
+ attr_simple_builder :strong_accent
309
+ attr_simple_builder :tenuto
310
+ attr_simple_builder :unstress
311
+
312
+ ## ornaments
313
+ attr_simple_builder :delayed_inverted_turn
314
+ attr_simple_builder :delayed_turn
315
+ attr_simple_builder :inverted_mordent
316
+ attr_simple_builder :inverted_turn
317
+ attr_simple_builder :mordent
318
+ attr_simple_builder :schleifer
319
+ attr_simple_builder :shake
320
+ attr_simple_builder :tremolo
321
+ attr_simple_builder :trill_mark
322
+ attr_simple_builder :turn
323
+ attr_simple_builder :vertical_turn
324
+ attr_simple_builder :wavy_line
325
+ attr_simple_builder :other_ornament
326
+ attr_simple_builder :ornament_accidental_mark
327
+
328
+ ## technical
329
+ attr_simple_builder :arrow
330
+ attr_simple_builder :bend
331
+ attr_simple_builder :double_tongue
332
+ attr_simple_builder :down_bow
333
+ attr_simple_builder :fingering
334
+ attr_simple_builder :fingernails
335
+ attr_simple_builder :fret
336
+ attr_simple_builder :hammer_on
337
+ attr_simple_builder :handbell
338
+ attr_simple_builder :harmonic
339
+ attr_simple_builder :heel
340
+ attr_simple_builder :hole
341
+ attr_simple_builder :open_string
342
+ attr_simple_builder :other_technical
343
+ attr_simple_builder :pluck
344
+ attr_simple_builder :pull_off
345
+ attr_simple_builder :snap_pizzicato
346
+ attr_simple_builder :stopped
347
+ attr_simple_builder :string
348
+ attr_simple_builder :tap_
349
+ attr_simple_builder :thumb_position
350
+ attr_simple_builder :toe
351
+ attr_simple_builder :triple_tongue
352
+ attr_simple_builder :up_bow
353
+
354
+ def _to_xml(io, indent:, tabs:)
355
+ io.puts "#{tabs}<note#{" pizzicato=\"yes\"" if @pizzicato}>"
356
+
357
+ io.puts "#{tabs}\t<grace />" if @grace
358
+ io.puts "#{tabs}\t<cue />" if @cue
359
+ io.puts "#{tabs}\t<chord />" if @chord
360
+
361
+ specific_to_xml(io, indent: indent + 1)
362
+
363
+ io.puts "#{tabs}\t<duration>#{@duration}</duration>"
364
+
365
+ io.puts "#{tabs}\t<tie type=\"stop\"/>" if @tie_stop
366
+ io.puts "#{tabs}\t<tie type=\"start\"/>" if @tie_start
367
+
368
+ io.puts "#{tabs}\t<voice>#{@voice}</voice>" if @voice
369
+
370
+ io.puts "#{tabs}\t<type>#{@type}</type>"
371
+
372
+ dots&.times do
373
+ io.puts "#{tabs}\t<dot />"
374
+ end
375
+
376
+ io.puts "#{tabs}\t<accidental>#{@accidental}</accidental>" if @accidental
377
+
378
+ @time_modification&.to_xml(io, indent: indent + 1)
379
+
380
+ io.puts "#{tabs}\t<stem>#{@stem}</stem>" if @stem
381
+
382
+ @notehead&.to_xml(io, indent: indent + 1)
383
+
384
+ io.puts "#{tabs}\t<staff>#{@staff.to_i}</staff>" if @staff
385
+
386
+ if _notations
387
+ io.puts "#{tabs}\t<notations>"
388
+ io.puts "#{tabs}\t\t<accidental-mark>#{@accidental_mark}</accidental-mark>" if @accidental_mark
389
+ io.puts "#{tabs}\t\t<arpeggiate#{ decode_bool_or_string_attribute(@arpeggiate, 'direction') }/>" if @arpeggiate
390
+ io.puts "#{tabs}\t\t<tied type=\"#{@tied}\"/>" if @tied
391
+ @tuplets.each do |tuplet|
392
+ tuplet.to_xml(io, indent: indent + 3)
393
+ end
394
+
395
+ if @dynamics
396
+ io.puts "#{tabs}\t\t<dynamics>"
397
+ @dynamics.arrayfy.each do |dynamics|
398
+ io.puts "#{tabs}\t\t\t<#{dynamics} />"
399
+ end
400
+ io.puts "#{tabs}\t\t</dynamics>"
401
+ end
402
+
403
+ io.puts "#{tabs}\t\t<fermata#{ decode_bool_or_string_attribute(@fermata, 'type') }/>" if @fermata
404
+ io.puts "#{tabs}\t\t<glissando type=\"#{@glissando}\"/>" if @glissando
405
+ io.puts "#{tabs}\t\t<non-arpeggiate type=\"#{@non_arpeggiate}\"/>" if @non_arpeggiate
406
+ io.puts "#{tabs}\t\t<slide type=\"#{@slide}\"/>" if @slide
407
+ @slur&.to_xml(io, indent: indent + 1)
408
+
409
+ if _articulations
410
+ io.puts "#{tabs}\t\t<articulations>"
411
+
412
+ io.puts "#{tabs}\t\t\t<accent />" if @accent
413
+ io.puts "#{tabs}\t\t\t<breath-mark>#{decode_bool_or_string_value(@breath_mark)}</breath-mark>" if @breath_mark
414
+ io.puts "#{tabs}\t\t\t<caesura />" if @caesura
415
+ io.puts "#{tabs}\t\t\t<detached-legato />" if @detached_legato
416
+ io.puts "#{tabs}\t\t\t<doit />" if @doit
417
+ io.puts "#{tabs}\t\t\t<falloff />" if @falloff
418
+ io.puts "#{tabs}\t\t\t<other-articulation>#{decode_bool_or_string_value(@other_articulation)}</other-articulation>" if @other_articulation
419
+ io.puts "#{tabs}\t\t\t<plop />" if @plop
420
+ io.puts "#{tabs}\t\t\t<scoop />" if @scoop
421
+ io.puts "#{tabs}\t\t\t<spiccato />" if @spiccato
422
+ io.puts "#{tabs}\t\t\t<staccatissimo />" if @staccatissimo
423
+ io.puts "#{tabs}\t\t\t<staccato />" if @staccato
424
+ io.puts "#{tabs}\t\t\t<stress />" if @stress
425
+ io.puts "#{tabs}\t\t\t<strong-accent#{ decode_bool_or_string_attribute(@strong_accent, 'type') }/>" if @strong_accent
426
+ io.puts "#{tabs}\t\t\t<tenuto />" if @tenuto
427
+ io.puts "#{tabs}\t\t\t<unstress />" if @unstress
428
+
429
+ io.puts "#{tabs}\t\t</articulations>"
430
+ end
431
+
432
+ if _ornaments
433
+ io.puts "#{tabs}\t\t<ornaments>"
434
+
435
+ io.puts "#{tabs}\t\t\t<delayed-inverted-turn />" if @delayed_inverted_turn
436
+ io.puts "#{tabs}\t\t\t<delayed-turn />" if @delayed_turn
437
+ io.puts "#{tabs}\t\t\t<inverted-mordent />" if @inverted_mordent
438
+ io.puts "#{tabs}\t\t\t<inverted-turn />" if @inverted_turn
439
+ io.puts "#{tabs}\t\t\t<mordent />" if @mordent
440
+ io.puts "#{tabs}\t\t\t<other-ornament>#{decode_bool_or_string_value(@other_ornament)}</other-ornament>" if @other_ornament
441
+ io.puts "#{tabs}\t\t\t<schleifer />" if @schleifer
442
+ io.puts "#{tabs}\t\t\t<shake />" if @shake
443
+ io.puts "#{tabs}\t\t\t<tremolo#{ decode_bool_or_string_attribute(@tremolo, 'type') }/>" if @tremolo
444
+ io.puts "#{tabs}\t\t\t<trill-mark />" if @trill_mark
445
+ io.puts "#{tabs}\t\t\t<turn />" if @turn
446
+ io.puts "#{tabs}\t\t\t<wavy-line#{ decode_bool_or_string_attribute(@wavy_line, 'type') }/>" if @wavy_line
447
+ io.puts "#{tabs}\t\t\t<accidental-mark>#{@ornament_accidental_mark}</accidental-mark>" if @ornament_accidental_mark
448
+
449
+ io.puts "#{tabs}\t\t</ornaments>"
450
+ end
451
+
452
+ if _technical
453
+ io.puts "#{tabs}\t\t<technical>"
454
+
455
+ @arrow&.to_xml(io, indent: indent + 3)
456
+ @bend&.to_xml(io, indent: indent + 3)
457
+ io.puts "#{tabs}\t\t\t<double-tongue />" if @double_tongue
458
+ io.puts "#{tabs}\t\t\t<down-bow />" if @down_bow
459
+ @fingering&.to_xml(io, indent: indent + 3)
460
+ io.puts "#{tabs}\t\t\t<fingernails />" if @fingernails
461
+ io.puts "#{tabs}\t\t\t<fret>#{@fret}</fret>" if @fret
462
+ io.puts "#{tabs}\t\t\t<hammer-on>#{@hammer_on}</hammer-on>" if @hammer_on
463
+ io.puts "#{tabs}\t\t\t<handbell>#{@handbell}</handbell>" if @handbell
464
+ @harmonic&.to_xml(io, indent: indent + 3)
465
+ io.puts "#{tabs}\t\t\t<heel />" if @heel
466
+ @hole&.to_xml(io, indent: indent + 3)
467
+ io.puts "#{tabs}\t\t\t<open-string />" if @open_string
468
+ io.puts "#{tabs}\t\t\t<other-technical>#{@other_technical}</other-technical>" if @other_technical
469
+ io.puts "#{tabs}\t\t\t<pluck>#{@pluck}</pluck>" if @pluck
470
+ io.puts "#{tabs}\t\t\t<pull-off>#{@pull_off}</pull-off>" if @pull_off
471
+ io.puts "#{tabs}\t\t\t<snap-pizzicato />" if @snap_pizzicato
472
+ io.puts "#{tabs}\t\t\t<stopped />" if @stopped
473
+ io.puts "#{tabs}\t\t\t<string>#{@string}</string>" if @string
474
+ io.puts "#{tabs}\t\t\t<tap>#{@tap_}</tap>" if @tap_
475
+ io.puts "#{tabs}\t\t\t<thumb-position />" if @thumb_position
476
+ io.puts "#{tabs}\t\t\t<toe />" if @toe
477
+ io.puts "#{tabs}\t\t\t<triple-tongue />" if @triple_tongue
478
+ io.puts "#{tabs}\t\t\t<up-bow />" if @up_bow
479
+
480
+ io.puts "#{tabs}\t\t</technical>"
481
+ end
482
+
483
+ io.puts "#{tabs}\t</notations>"
484
+ end
485
+
486
+ io.puts "#{tabs}</note>"
487
+ end
488
+
489
+ private
490
+
491
+ def specific_to_xml(io, indent:); end
492
+
493
+ def _notations
494
+ @accidental_mark ||
495
+ @arpeggiate ||
496
+ @tied ||
497
+ !@tuplets.empty? ||
498
+ @dynamics ||
499
+ @fermata ||
500
+ @glissando ||
501
+ @non_arpeggiate ||
502
+ @slide ||
503
+ @slur ||
504
+ _articulations ||
505
+ _ornaments ||
506
+ _technical
507
+ end
508
+
509
+ def _articulations
510
+ @accent ||
511
+ @breath_mark ||
512
+ @caesura ||
513
+ @detached_legato ||
514
+ @doit ||
515
+ @falloff ||
516
+ @other_articulation ||
517
+ @plop ||
518
+ @scoop ||
519
+ @spiccato ||
520
+ @staccatissimo ||
521
+ @staccato ||
522
+ @stress ||
523
+ @strong_accent ||
524
+ @tenuto ||
525
+ @unstress
526
+ end
527
+
528
+ def _ornaments
529
+ @delayed_inverted_turn ||
530
+ @delayed_turn ||
531
+ @inverted_mordent ||
532
+ @inverted_turn ||
533
+ @mordent ||
534
+ @schleifer ||
535
+ @shake ||
536
+ @tremolo ||
537
+ @trill_mark ||
538
+ @turn ||
539
+ @vertical_turn ||
540
+ @wavy_line ||
541
+ @other_ornament ||
542
+ @ornament_accidental_mark
543
+ end
544
+
545
+ def _technical
546
+ @arrow ||
547
+ @bend ||
548
+ @double_tongue ||
549
+ @down_bow ||
550
+ @fingering ||
551
+ @fingernails ||
552
+ @fret ||
553
+ @hammer_on ||
554
+ @handbell ||
555
+ @harmonic ||
556
+ @heel ||
557
+ @hole ||
558
+ @open_string ||
559
+ @other_technical ||
560
+ @pluck ||
561
+ @pull_off ||
562
+ @snap_pizzicato ||
563
+ @stopped ||
564
+ @string ||
565
+ @tap_ ||
566
+ @thumb_position ||
567
+ @toe ||
568
+ @triple_tongue ||
569
+ @up_bow
570
+ end
571
+ end
572
+
573
+ private_constant :Note
574
+ end
575
+ end
576
+ end
577
+ end