lydown 0.7.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|