lydown 0.7.2 → 0.9.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/README.md +25 -14
- data/bin/lydown +1 -122
- data/lib/lydown.rb +3 -3
- data/lib/lydown/cli.rb +4 -0
- data/lib/lydown/cli/commands.rb +98 -0
- data/lib/lydown/cli/compiler.rb +11 -14
- data/lib/lydown/cli/diff.rb +3 -3
- data/lib/lydown/cli/output.rb +18 -0
- data/lib/lydown/cli/proofing.rb +8 -6
- data/lib/lydown/cli/support.rb +34 -0
- data/lib/lydown/cli/translation.rb +73 -0
- data/lib/lydown/core_ext.rb +1 -1
- data/lib/lydown/lilypond.rb +49 -11
- data/lib/lydown/parsing.rb +37 -6
- data/lib/lydown/parsing/nodes.rb +57 -56
- data/lib/lydown/rendering.rb +0 -9
- data/lib/lydown/rendering/base.rb +2 -2
- data/lib/lydown/rendering/command.rb +2 -2
- data/lib/lydown/rendering/comments.rb +1 -1
- data/lib/lydown/rendering/figures.rb +14 -14
- data/lib/lydown/rendering/lib.ly +22 -0
- data/lib/lydown/rendering/lyrics.rb +2 -2
- data/lib/lydown/rendering/movement.rb +2 -2
- data/lib/lydown/rendering/music.rb +46 -46
- data/lib/lydown/rendering/notes.rb +149 -70
- data/lib/lydown/rendering/settings.rb +21 -16
- data/lib/lydown/rendering/skipping.rb +1 -1
- data/lib/lydown/rendering/source_ref.rb +4 -4
- data/lib/lydown/rendering/staff.rb +8 -4
- data/lib/lydown/rendering/voices.rb +9 -9
- data/lib/lydown/templates.rb +9 -0
- data/lib/lydown/templates/lilypond_doc.erb +1 -1
- data/lib/lydown/templates/movement.erb +9 -1
- data/lib/lydown/templates/part.erb +12 -3
- data/lib/lydown/translation.rb +17 -0
- data/lib/lydown/translation/ripple.rb +30 -0
- data/lib/lydown/translation/ripple/nodes.rb +191 -0
- data/lib/lydown/translation/ripple/ripple.treetop +100 -0
- data/lib/lydown/version.rb +1 -1
- data/lib/lydown/work.rb +144 -198
- data/lib/lydown/work_context.rb +152 -0
- metadata +11 -2
data/lib/lydown/rendering.rb
CHANGED
@@ -38,15 +38,6 @@ module Lydown::Rendering
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
42
|
-
class Base
|
43
|
-
def initialize(event, work, stream, idx)
|
44
|
-
@event = event
|
45
|
-
@work = work
|
46
|
-
@stream = stream
|
47
|
-
@idx = idx
|
48
|
-
end
|
49
|
-
end
|
50
41
|
end
|
51
42
|
|
52
43
|
LY_LIB_DIR = File.join(File.dirname(__FILE__), 'rendering')
|
@@ -3,12 +3,12 @@ module Lydown::Rendering
|
|
3
3
|
include Notes
|
4
4
|
|
5
5
|
def translate
|
6
|
-
if @
|
6
|
+
if @context['process/duration_macro']
|
7
7
|
add_macro_event(@event[:raw] || cmd_to_lydown(@event))
|
8
8
|
else
|
9
9
|
once = @event[:once] ? '\once ' : ''
|
10
10
|
cmd = "#{once}\\#{@event[:key]} #{(@event[:arguments] || []).join(' ')} "
|
11
|
-
@
|
11
|
+
@context.emit(:music, cmd)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -1,37 +1,37 @@
|
|
1
1
|
module Lydown::Rendering
|
2
2
|
module Figures
|
3
3
|
def add_figures(figures, value)
|
4
|
-
if @
|
5
|
-
@
|
4
|
+
if @context['process/running_values']
|
5
|
+
@context['process/running_values'].each do |v|
|
6
6
|
silence = "s"
|
7
|
-
if v != @
|
7
|
+
if v != @context['process/last_figures_value']
|
8
8
|
silence << v
|
9
|
-
@
|
9
|
+
@context['process/last_figures_value'] = v
|
10
10
|
end
|
11
|
-
@
|
11
|
+
@context.emit(:figures, "#{silence} ")
|
12
12
|
end
|
13
|
-
@
|
13
|
+
@context['process/running_values'] = []
|
14
14
|
end
|
15
15
|
|
16
16
|
figures = lilypond_figures(figures)
|
17
|
-
if value != @
|
17
|
+
if value != @context['process/last_figures_value']
|
18
18
|
figures << value
|
19
|
-
@
|
19
|
+
@context['process/last_figures_value'] = value
|
20
20
|
end
|
21
21
|
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@
|
22
|
+
@context.emit(:figures, "#{figures} ")
|
23
|
+
@context.emit(:figures, EXTENDERS_ON) if @event[:figure_extenders_on]
|
24
|
+
@context.emit(:figures, EXTENDERS_OFF) if @event[:figure_extenders_off]
|
25
25
|
end
|
26
26
|
|
27
27
|
def add_stand_alone_figures(figures)
|
28
|
-
if @
|
28
|
+
if @context['process/running_values']
|
29
29
|
# for stand alone figures, we regard the stand alone figure as being
|
30
30
|
# aligned to the last note. Therefore we pop its value from
|
31
31
|
# running_values array.
|
32
|
-
@
|
32
|
+
@context['process/running_values'].pop
|
33
33
|
end
|
34
|
-
value = @
|
34
|
+
value = @context['process/figures_duration_value'] || @context['process/last_value']
|
35
35
|
add_figures(figures, value)
|
36
36
|
end
|
37
37
|
|
data/lib/lydown/rendering/lib.ly
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
\header {
|
2
|
+
tagline = \markup {
|
3
|
+
Engraved using \bold {
|
4
|
+
\with-url #"http://github.com/ciconia/lydown" {
|
5
|
+
Lydown
|
6
|
+
}
|
7
|
+
}
|
8
|
+
and \bold {
|
9
|
+
\with-url #"http://lilypond.org/" {
|
10
|
+
Lilypond
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
1
16
|
segno = {
|
2
17
|
\once \override Score.RehearsalMark #'font-size = #-2
|
3
18
|
\mark \markup { \musicglyph #"scripts.segno" }
|
@@ -48,6 +63,13 @@ ficta = {
|
|
48
63
|
\once \set suggestAccidentals = ##t
|
49
64
|
}
|
50
65
|
|
66
|
+
prallupbefore = {
|
67
|
+
\mark\markup {
|
68
|
+
\musicglyph #"scripts.prallup"
|
69
|
+
\hspace #1
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
51
73
|
%{
|
52
74
|
http://www.lilypond.org/doc/v2.18/Documentation/snippets/editorial-annotations#editorial-annotations-adding-links-to-objects
|
53
75
|
%}
|
@@ -4,9 +4,9 @@ module Lydown::Rendering
|
|
4
4
|
value = lilypond_lyrics(@event[:content])
|
5
5
|
|
6
6
|
lyrics_idx = @event[:stream_index] || 1
|
7
|
-
voice = @
|
7
|
+
voice = @context['process/voice_selector'] || 'voice1'
|
8
8
|
|
9
|
-
@
|
9
|
+
@context.emit("lyrics/#{voice}/#{lyrics_idx}", value, ' ')
|
10
10
|
end
|
11
11
|
|
12
12
|
def lilypond_lyrics(lyrics)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Lydown::Rendering
|
2
2
|
module Movement
|
3
|
-
def self.movement_title(
|
3
|
+
def self.movement_title(context, name)
|
4
4
|
return nil if name.nil? || name.empty?
|
5
5
|
|
6
6
|
if name =~ /^(?:([0-9]+)([a-z]*))\-(.+)$/
|
@@ -9,7 +9,7 @@ module Lydown::Rendering
|
|
9
9
|
title = name
|
10
10
|
end
|
11
11
|
|
12
|
-
if
|
12
|
+
if context["movements/#{name}/parts"].empty?
|
13
13
|
title += " - tacet"
|
14
14
|
end
|
15
15
|
|
@@ -9,80 +9,80 @@ module Lydown::Rendering
|
|
9
9
|
|
10
10
|
class Duration < Base
|
11
11
|
def translate
|
12
|
-
Notes.cleanup_duration_macro(@
|
12
|
+
Notes.cleanup_duration_macro(@context)
|
13
13
|
|
14
14
|
# close tuplet braces
|
15
|
-
if @
|
16
|
-
TupletDuration.emit_tuplet_end(@
|
17
|
-
@
|
15
|
+
if @context['process/tuplet_mode']
|
16
|
+
TupletDuration.emit_tuplet_end(@context)
|
17
|
+
@context['process/tuplet_mode'] = nil
|
18
18
|
end
|
19
19
|
|
20
|
-
if @
|
21
|
-
Grace.emit_grace_end(@
|
22
|
-
@
|
20
|
+
if @context['process/grace_mode']
|
21
|
+
Grace.emit_grace_end(@context)
|
22
|
+
@context['process/grace_mode'] = nil
|
23
23
|
end
|
24
24
|
|
25
25
|
value = @event[:value].sub(/^[0-9]+/) {|m| LILYPOND_DURATIONS[m] || m}
|
26
26
|
|
27
27
|
if next_event && next_event[:type] == :stand_alone_figures
|
28
|
-
@
|
28
|
+
@context['process/figures_duration_value'] = value
|
29
29
|
else
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
30
|
+
@context['process/duration_values'] = [value]
|
31
|
+
@context['process/tuplet_mode'] = nil
|
32
|
+
@context['process/duration_macro'] = nil unless @context['process/macro_group']
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
37
37
|
|
38
38
|
class TupletDuration < Base
|
39
|
-
def self.emit_tuplet_end(
|
40
|
-
|
39
|
+
def self.emit_tuplet_end(context)
|
40
|
+
context.emit(:music, '} ')
|
41
41
|
end
|
42
42
|
|
43
43
|
def translate
|
44
|
-
Notes.cleanup_duration_macro(@
|
44
|
+
Notes.cleanup_duration_macro(@context)
|
45
45
|
|
46
46
|
# close tuplet braces
|
47
|
-
if @
|
48
|
-
TupletDuration.emit_tuplet_end(@
|
49
|
-
@
|
47
|
+
if @context['process/tuplet_mode']
|
48
|
+
TupletDuration.emit_tuplet_end(@context)
|
49
|
+
@context['process/tuplet_mode'] = nil
|
50
50
|
end
|
51
51
|
|
52
52
|
if next_event && next_event[:type] == :stand_alone_figures
|
53
|
-
@
|
53
|
+
@context['process/figures_duration_value'] = "#{@event[:value]}*#{@event[:fraction]}"
|
54
54
|
else
|
55
55
|
value = LILYPOND_DURATIONS[@event[:value]] || @event[:value]
|
56
56
|
|
57
|
-
@
|
58
|
-
@
|
59
|
-
@
|
57
|
+
@context['process/duration_values'] = [value]
|
58
|
+
@context['process/last_value'] = nil
|
59
|
+
@context['process/tuplet_mode'] = true
|
60
60
|
|
61
61
|
group_value = value.to_i / @event[:group_length].to_i
|
62
|
-
@
|
62
|
+
@context.emit(:music, "\\tuplet #{@event[:fraction]} #{group_value} { ")
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
67
|
class Grace < Base
|
68
|
-
def self.emit_grace_end(
|
69
|
-
|
68
|
+
def self.emit_grace_end(context)
|
69
|
+
context.emit(:music, '} ')
|
70
70
|
end
|
71
71
|
|
72
72
|
def translate
|
73
73
|
# close tuplet braces
|
74
|
-
if @
|
75
|
-
Grace.emit_grace_end(@
|
76
|
-
@
|
74
|
+
if @context['process/grace_mode']
|
75
|
+
Grace.emit_grace_end(@context)
|
76
|
+
@context['process/grace_mode'] = nil
|
77
77
|
end
|
78
78
|
|
79
79
|
value = LILYPOND_DURATIONS[@event[:value]] || @event[:value]
|
80
80
|
|
81
|
-
@
|
82
|
-
@
|
83
|
-
@
|
81
|
+
@context['process/duration_values'] = [value]
|
82
|
+
@context['process/last_value'] = nil
|
83
|
+
@context['process/grace_mode'] = true
|
84
84
|
|
85
|
-
@
|
85
|
+
@context.emit(:music, "\\#{@event[:kind]} { ")
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -141,7 +141,7 @@ module Lydown::Rendering
|
|
141
141
|
|
142
142
|
class BeamOpen < Base
|
143
143
|
def translate
|
144
|
-
@
|
144
|
+
@context['process/open_beam'] = true
|
145
145
|
end
|
146
146
|
end
|
147
147
|
|
@@ -152,7 +152,7 @@ module Lydown::Rendering
|
|
152
152
|
|
153
153
|
class SlurOpen < Base
|
154
154
|
def translate
|
155
|
-
@
|
155
|
+
@context['process/open_slur'] = true
|
156
156
|
end
|
157
157
|
end
|
158
158
|
|
@@ -161,7 +161,7 @@ module Lydown::Rendering
|
|
161
161
|
|
162
162
|
class Tie < Base
|
163
163
|
def translate
|
164
|
-
@
|
164
|
+
@context.emit(:music, '~ ')
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
@@ -169,8 +169,8 @@ module Lydown::Rendering
|
|
169
169
|
include Notes
|
170
170
|
|
171
171
|
def translate
|
172
|
-
note_head = @
|
173
|
-
@
|
172
|
+
note_head = @context['process/last_note_head']
|
173
|
+
@context.emit(:music, '~ ')
|
174
174
|
add_note({head: note_head})
|
175
175
|
end
|
176
176
|
end
|
@@ -194,17 +194,17 @@ module Lydown::Rendering
|
|
194
194
|
translate_expressions
|
195
195
|
|
196
196
|
if @event[:multiplier]
|
197
|
-
value = full_bar_value(@
|
198
|
-
@
|
197
|
+
value = full_bar_value(@context[:time])
|
198
|
+
@context['process/duration_macro'] = nil unless @context['process/macro_group']
|
199
199
|
if value
|
200
200
|
@event[:rest_value] = "#{value}*#{@event[:multiplier]}"
|
201
201
|
@event[:head] = "#{@event[:head]}#{@event[:rest_value]}"
|
202
202
|
else
|
203
|
-
@event[:head] = "#{@event[:head]}#{@event[:multiplier]}*#{@
|
203
|
+
@event[:head] = "#{@event[:head]}#{@event[:multiplier]}*#{@context[:time]}"
|
204
204
|
end
|
205
205
|
# reset the last value so the next note will be rendered with its value
|
206
|
-
@
|
207
|
-
@
|
206
|
+
@context['process/last_value'] = nil
|
207
|
+
@context['process/duration_values'] = []
|
208
208
|
end
|
209
209
|
|
210
210
|
add_note(@event)
|
@@ -230,22 +230,22 @@ module Lydown::Rendering
|
|
230
230
|
|
231
231
|
class DurationMacro < Base
|
232
232
|
def translate
|
233
|
-
Notes.cleanup_duration_macro(@
|
233
|
+
Notes.cleanup_duration_macro(@context)
|
234
234
|
|
235
235
|
if @event[:macro] =~ /^[a-zA-Z_]/
|
236
|
-
macro = @
|
236
|
+
macro = @context['macros'][@event[:macro]]
|
237
237
|
if macro
|
238
238
|
if macro =~ /^\{(.+)\}$/
|
239
239
|
macro = $1
|
240
240
|
end
|
241
241
|
# replace the repeating note placeholder with another sign in order to
|
242
242
|
# avoid mixing up with repeating notes from outside the macro
|
243
|
-
@
|
243
|
+
@context['process/duration_macro'] = macro.gsub('@', '∞')
|
244
244
|
else
|
245
245
|
raise LydownError, "Unknown macro #{@event[:macro]}"
|
246
246
|
end
|
247
247
|
else
|
248
|
-
@
|
248
|
+
@context['process/duration_macro'] = @event[:macro]
|
249
249
|
end
|
250
250
|
end
|
251
251
|
end
|
@@ -254,7 +254,7 @@ module Lydown::Rendering
|
|
254
254
|
def translate
|
255
255
|
barline = @event[:barline]
|
256
256
|
barline = '' if barline == '?|'
|
257
|
-
@
|
257
|
+
@context.emit(:music, "\\bar \"#{barline}\" ")
|
258
258
|
end
|
259
259
|
end
|
260
260
|
end
|
@@ -59,85 +59,156 @@ module Lydown::Rendering
|
|
59
59
|
|
60
60
|
note + (value >= 0 ? 'is' * value : 'es' * -value)
|
61
61
|
end
|
62
|
+
|
63
|
+
def self.chromatic_to_diatonic(note, key_signature = 'c major')
|
64
|
+
note =~ /([a-g])([\+\-]*)/
|
65
|
+
diatonic_note = $1
|
66
|
+
chromatic_value = $2.count('+') - $2.count('-')
|
67
|
+
|
68
|
+
key_accidentals = accidentals_for_key_signature(key_signature)
|
69
|
+
diatonic_value = key_accidentals[diatonic_note] || 0
|
70
|
+
value = chromatic_value - diatonic_value
|
71
|
+
|
72
|
+
"#{diatonic_note}#{value >= 0 ? '+' * value : '-' * -value}"
|
73
|
+
end
|
62
74
|
|
63
75
|
# Takes into account the accidentals mode
|
64
|
-
def self.translate_note_name(
|
65
|
-
if
|
76
|
+
def self.translate_note_name(context, note)
|
77
|
+
if context[:accidentals] == 'manual'
|
66
78
|
key = 'c major'
|
67
79
|
else
|
68
|
-
key =
|
80
|
+
key = context[:key]
|
69
81
|
end
|
70
82
|
lilypond_note_name(note, key)
|
71
83
|
end
|
72
84
|
end
|
85
|
+
|
86
|
+
module Octaves
|
87
|
+
DIATONICS = %w{a b c d e f g}
|
88
|
+
|
89
|
+
# calculates the octave markers needed to put a first note in the right
|
90
|
+
# octave. In lydown, octaves are relative (i.e. lilypond's relative mode).
|
91
|
+
# But the first note gives the octave to start on, rather than a relative
|
92
|
+
# note to c (or any other reference note).
|
93
|
+
#
|
94
|
+
# In that manner, d' is d above middle c, g'' is g an octave and fifth
|
95
|
+
# above middle c, a is a a below middle c, and eß, is great e flat.
|
96
|
+
#
|
97
|
+
# The return value is a string with octave markers for relative mode,
|
98
|
+
# based on the refence note
|
99
|
+
def self.relative_octave(note, ref_note = 'c')
|
100
|
+
note_diatonic, ref_diatonic = note[0], ref_note[0]
|
101
|
+
raise LydownError, "Invalid note #{note}" unless DIATONICS.index(note_diatonic)
|
102
|
+
raise LydownError, "Invalid reference note #{ref_note}" unless DIATONICS.index(ref_diatonic)
|
103
|
+
|
104
|
+
# calculate diatonic interval
|
105
|
+
note_array = DIATONICS.rotate(DIATONICS.index(ref_diatonic))
|
106
|
+
interval = note_array.index(note_diatonic)
|
107
|
+
|
108
|
+
# calculate octave interval and
|
109
|
+
octave_value = note.count("'") - note.count(',')
|
110
|
+
ref_value = ref_note.count("'") - ref_note.count(',')
|
111
|
+
octave_interval = octave_value - ref_value
|
112
|
+
octave_interval += 1 if interval >= 4
|
113
|
+
|
114
|
+
# generate octave markers
|
115
|
+
octave_interval >= 0 ? "'" * octave_interval : "," * -octave_interval
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.absolute_octave(note, ref_note = 'c')
|
119
|
+
note_diatonic, ref_diatonic = note[0], ref_note[0]
|
120
|
+
raise LydownError, "Invalid note #{note}" unless DIATONICS.index(note_diatonic)
|
121
|
+
raise LydownError, "Invalid reference note #{ref_note}" unless DIATONICS.index(ref_diatonic)
|
122
|
+
|
123
|
+
# calculate diatonic interval
|
124
|
+
note_array = DIATONICS.rotate(DIATONICS.index(ref_diatonic))
|
125
|
+
interval = note_array.index(note_diatonic)
|
126
|
+
|
127
|
+
# calculate octave interval and
|
128
|
+
note_value = note.count("'") - note.count(',')
|
129
|
+
ref_value = ref_note.count("'") - ref_note.count(',')
|
130
|
+
octave_interval = ref_value + note_value
|
131
|
+
octave_interval -= 1 if interval >= 4
|
132
|
+
|
133
|
+
# generate octave markers
|
134
|
+
octave_interval >= 0 ? "'" * octave_interval : "," * -octave_interval
|
135
|
+
end
|
136
|
+
end
|
73
137
|
|
74
138
|
module Notes
|
75
139
|
include Lydown::Rendering::Figures
|
76
140
|
|
77
141
|
def add_note(event, options = {})
|
78
|
-
return add_macro_note(event) if @
|
142
|
+
return add_macro_note(event) if @context['process/duration_macro']
|
143
|
+
|
144
|
+
# calculate relative octave markers for first note
|
145
|
+
unless @context['process/first_note'] || event[:head] =~ /^[rsR]/
|
146
|
+
note = event[:head] + (event[:octave] || '')
|
147
|
+
event[:octave] = Lydown::Rendering::Octaves.relative_octave(note)
|
148
|
+
@context['process/first_note'] = note
|
149
|
+
end
|
79
150
|
|
80
|
-
if
|
151
|
+
if event[:head] == '@'
|
81
152
|
# replace repeating note head
|
82
|
-
|
153
|
+
event[:head] = @context['process/last_note_head']
|
83
154
|
else
|
84
|
-
@
|
155
|
+
@context['process/last_note_head'] = event[:head]
|
85
156
|
end
|
86
157
|
|
87
|
-
value = @
|
88
|
-
@
|
158
|
+
value = @context['process/duration_values'].first
|
159
|
+
@context['process/duration_values'].rotate!
|
89
160
|
|
90
161
|
add_figures(event[:figures], value) if event[:figures]
|
91
162
|
|
92
163
|
# push value into running values accumulator. This is used to synthesize
|
93
164
|
# the bass figures durations.
|
94
165
|
unless event[:figures]
|
95
|
-
@
|
166
|
+
@context['process/running_values'] ||= []
|
96
167
|
if event[:rest_value]
|
97
|
-
@
|
168
|
+
@context['process/running_values'] << event[:rest_value]
|
98
169
|
else
|
99
|
-
@
|
170
|
+
@context['process/running_values'] << value
|
100
171
|
end
|
101
172
|
end
|
102
173
|
|
103
174
|
# only add the value if different than the last used
|
104
|
-
if options[:no_value] || (value == @
|
175
|
+
if options[:no_value] || (value == @context['process/last_value'])
|
105
176
|
value = ''
|
106
177
|
else
|
107
|
-
@
|
178
|
+
@context['process/last_value'] = value
|
108
179
|
end
|
109
|
-
|
110
|
-
if event[:line] && @
|
111
|
-
@
|
112
|
-
# @
|
180
|
+
|
181
|
+
if event[:line] && @context['options/proof_mode']
|
182
|
+
@context.emit(event[:stream] || :music, note_event_url_link(event))
|
183
|
+
# @context.emit(event[:stream] || :music, "%{#{event[:line]}:#{event[:column]}%}")
|
113
184
|
end
|
114
185
|
|
115
186
|
code = lilypond_note(event, options.merge(value: value))
|
116
|
-
@
|
187
|
+
@context.emit(event[:stream] || :music, code)
|
117
188
|
end
|
118
189
|
|
119
190
|
def add_chord(event, options = {})
|
120
|
-
value = @
|
121
|
-
@
|
191
|
+
value = @context['process/duration_values'].first
|
192
|
+
@context['process/duration_values'].rotate!
|
122
193
|
|
123
194
|
add_figures(event[:figures], value) if event[:figures]
|
124
195
|
|
125
196
|
# push value into running values accumulator. This is used to synthesize
|
126
197
|
# the bass figures durations.
|
127
198
|
unless event[:figures]
|
128
|
-
@
|
199
|
+
@context['process/running_values'] ||= []
|
129
200
|
if event[:rest_value]
|
130
|
-
@
|
201
|
+
@context['process/running_values'] << event[:rest_value]
|
131
202
|
else
|
132
|
-
@
|
203
|
+
@context['process/running_values'] << value
|
133
204
|
end
|
134
205
|
end
|
135
206
|
|
136
207
|
# only add the value if different than the last used
|
137
|
-
if value == @
|
208
|
+
if value == @context['process/last_value']
|
138
209
|
value = ''
|
139
210
|
else
|
140
|
-
@
|
211
|
+
@context['process/last_value'] = value
|
141
212
|
end
|
142
213
|
|
143
214
|
notes = event[:notes].map do |note|
|
@@ -145,11 +216,11 @@ module Lydown::Rendering
|
|
145
216
|
end
|
146
217
|
|
147
218
|
options = options.merge(value: value)
|
148
|
-
@
|
219
|
+
@context.emit(event[:stream] || :music, lilypond_chord(event, notes, options))
|
149
220
|
end
|
150
221
|
|
151
222
|
def lilypond_note(event, options = {})
|
152
|
-
head = Accidentals.translate_note_name(@
|
223
|
+
head = Accidentals.translate_note_name(@context, event[:head])
|
153
224
|
if options[:head_only]
|
154
225
|
head
|
155
226
|
else
|
@@ -187,13 +258,13 @@ module Lydown::Rendering
|
|
187
258
|
|
188
259
|
def lilypond_phrasing(event)
|
189
260
|
phrasing = ''
|
190
|
-
if @
|
261
|
+
if @context['process/open_beam']
|
191
262
|
phrasing << '['
|
192
|
-
@
|
263
|
+
@context['process/open_beam'] = nil
|
193
264
|
end
|
194
|
-
if @
|
265
|
+
if @context['process/open_slur']
|
195
266
|
phrasing << '('
|
196
|
-
@
|
267
|
+
@context['process/open_slur'] = nil
|
197
268
|
end
|
198
269
|
phrasing << ']' if event[:beam_close]
|
199
270
|
phrasing << ')' if event[:slur_close]
|
@@ -202,13 +273,13 @@ module Lydown::Rendering
|
|
202
273
|
|
203
274
|
def lydown_phrasing_open(event)
|
204
275
|
phrasing = ''
|
205
|
-
if @
|
276
|
+
if @context['process/open_beam']
|
206
277
|
phrasing << '['
|
207
|
-
@
|
278
|
+
@context['process/open_beam'] = nil
|
208
279
|
end
|
209
|
-
if @
|
280
|
+
if @context['process/open_slur']
|
210
281
|
phrasing << '('
|
211
|
-
@
|
282
|
+
@context['process/open_slur'] = nil
|
212
283
|
end
|
213
284
|
phrasing
|
214
285
|
end
|
@@ -221,7 +292,7 @@ module Lydown::Rendering
|
|
221
292
|
end
|
222
293
|
|
223
294
|
def add_macro_note(event)
|
224
|
-
@
|
295
|
+
@context['process/macro_group'] ||= @context['process/duration_macro'].clone
|
225
296
|
underscore_count = 0
|
226
297
|
|
227
298
|
lydown_note = "{%d:%d}%s%s%s%s%s%s%s" % [
|
@@ -233,9 +304,9 @@ module Lydown::Rendering
|
|
233
304
|
event[:figures] ? "<#{event[:figures].join}>" : '',
|
234
305
|
event[:expressions] ? event[:expressions].join + ' ' : ''
|
235
306
|
]
|
236
|
-
|
307
|
+
|
237
308
|
# replace place holder and repeaters in macro group with actual note
|
238
|
-
@
|
309
|
+
@context['process/macro_group'].gsub!(/[_∞]/) do |match|
|
239
310
|
case match
|
240
311
|
when '_'
|
241
312
|
underscore_count += 1
|
@@ -245,52 +316,60 @@ module Lydown::Rendering
|
|
245
316
|
end
|
246
317
|
end
|
247
318
|
|
248
|
-
#
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
macro = @work['process/duration_macro']
|
257
|
-
@work['process/duration_macro'] = nil
|
258
|
-
@work['process/macro_group'] = nil
|
259
|
-
|
260
|
-
@work.process(code, no_reset: true)
|
319
|
+
# keep filename and source in order to ensure source references are kept
|
320
|
+
# correct
|
321
|
+
@context['process/macro_filename'] = event[:filename]
|
322
|
+
@context['process/macro_source'] = event[:source]
|
323
|
+
|
324
|
+
# increment group note count
|
325
|
+
@context['process/macro_group_note_count'] ||= 0
|
326
|
+
@context['process/macro_group_note_count'] += 1
|
261
327
|
|
262
|
-
|
263
|
-
|
328
|
+
# if group is complete, compile it just like regular code
|
329
|
+
unless @context['process/macro_group'].include?('_')
|
330
|
+
Notes.add_duration_macro_group(@context, @context['process/macro_group'])
|
264
331
|
end
|
265
332
|
end
|
266
333
|
|
267
334
|
# emits the current macro group up to the first placeholder character.
|
268
335
|
# this method is called
|
269
|
-
def self.cleanup_duration_macro(
|
270
|
-
return unless
|
336
|
+
def self.cleanup_duration_macro(context)
|
337
|
+
return unless context['process/macro_group_note_count'] &&
|
338
|
+
context['process/macro_group_note_count'] > 0
|
271
339
|
|
272
340
|
# truncate macro group up until first placeholder
|
273
|
-
group =
|
274
|
-
|
275
|
-
# stash macro, in order to compile macro group
|
276
|
-
macro = work['process/duration_macro']
|
277
|
-
work['process/duration_macro'] = nil
|
278
|
-
work['process/macro_group'] = nil
|
279
|
-
|
280
|
-
code = LydownParser.parse(group)
|
281
|
-
work.process(code, no_reset: true)
|
341
|
+
group = context['process/macro_group'].sub(/_.*$/, '')
|
282
342
|
|
343
|
+
# Refrain from adding
|
344
|
+
add_duration_macro_group(context, group)
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.add_duration_macro_group(context, group)
|
348
|
+
opts = (context[:options] || {}).merge({
|
349
|
+
filename: context['process/macro_filename'],
|
350
|
+
source: context['process/macro_source']
|
351
|
+
}).deep!
|
352
|
+
code = LydownParser.parse_macro_group(group, opts)
|
353
|
+
|
354
|
+
# stash macro
|
355
|
+
macro = context['process/duration_macro']
|
356
|
+
context['process/duration_macro'] = nil
|
357
|
+
context['process/macro_group'] = nil
|
358
|
+
context['process/macro_group_note_count'] = nil
|
359
|
+
|
360
|
+
context.translate(code, macro_group: true)
|
361
|
+
ensure
|
283
362
|
# restore macro
|
284
|
-
|
363
|
+
context['process/duration_macro'] = macro
|
285
364
|
end
|
286
365
|
|
287
366
|
def add_macro_event(code)
|
288
|
-
case @
|
367
|
+
case @context['process/macro_group']
|
289
368
|
when nil
|
290
|
-
@
|
291
|
-
@
|
369
|
+
@context['process/macro_group'] = @context['process/duration_macro'].clone
|
370
|
+
@context['process/macro_group'].insert(0, " #{code} ")
|
292
371
|
when /_/
|
293
|
-
@
|
372
|
+
@context['process/macro_group'].sub!(/([_∞])/, " #{code} \\0")
|
294
373
|
end
|
295
374
|
end
|
296
375
|
|