lydown 0.4.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +51 -4
- data/lib/lydown/parsing/lydown.treetop +36 -14
- data/lib/lydown/parsing/nodes.rb +87 -5
- data/lib/lydown/parsing.rb +1 -1
- data/lib/lydown/rendering/command.rb +10 -0
- data/lib/lydown/rendering/lyrics.rb +5 -1
- data/lib/lydown/rendering/music.rb +61 -29
- data/lib/lydown/rendering/settings.rb +14 -6
- data/lib/lydown/rendering/voices.rb +42 -0
- data/lib/lydown/rendering.rb +2 -0
- data/lib/lydown/templates/movement.erb +10 -1
- data/lib/lydown/templates/multi_voice.erb +16 -0
- data/lib/lydown/templates/part.erb +35 -15
- data/lib/lydown/version.rb +1 -1
- data/lib/lydown/work.rb +20 -8
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 768154bba0df3c6550e92d7bab1846d78e767652
|
4
|
+
data.tar.gz: a964448d153898925ee16869131f9888a1298ca2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdc5ae5af05d007763399b2e33b4d7c7ab98050f2abbfd2ffc2f84a49fa8ed6e907762aed1ea577c58bbe98a5ff3d44ab21e11a9382fcd02a830719740fda29d
|
7
|
+
data.tar.gz: 286f620033bd8c01c53bd1dffacb6d715264d4a135674389d892b5602e8c4d23fc3f55f540514a75a1e46b499316cea09766aa9764252e2e9b02b64332e76ab3
|
data/README.md
CHANGED
@@ -87,6 +87,12 @@ Augmentation dots are entered like in lilypond:
|
|
87
87
|
|
88
88
|
8.c6d 8.e6f 2g => c8. d16 e8. f16 g2
|
89
89
|
8..g 3g 4c => g8.. g32 c4
|
90
|
+
|
91
|
+
Notes can be repeated using the <code>@</code> placeholder:
|
92
|
+
|
93
|
+
4c@@@ => c4 c c c
|
94
|
+
|
95
|
+
(The repeating note placeholder is useful when entering repeated notes with accidentals).
|
90
96
|
|
91
97
|
### Rests
|
92
98
|
|
@@ -239,6 +245,11 @@ In the case of key signatures, accidentals will follow the lydown syntax:
|
|
239
245
|
|
240
246
|
- key: b- major
|
241
247
|
- key: f+ minor
|
248
|
+
|
249
|
+
Key signatures can also be specified using shorthand notation (upper case for major, lower case for minor):
|
250
|
+
|
251
|
+
- key: B- // b flat major
|
252
|
+
- key: f+ // f sharp minor
|
242
253
|
|
243
254
|
The default key signature is C major, and the default time signature is 4/4.
|
244
255
|
|
@@ -248,7 +259,7 @@ Key or time signatures can be changed on the fly:
|
|
248
259
|
4c e g b
|
249
260
|
- time: 3/4
|
250
261
|
c e g 2.c
|
251
|
-
|
262
|
+
|
252
263
|
### Pickup bars
|
253
264
|
|
254
265
|
[Pickup bars](http://www.lilypond.org/doc/v2.18/Documentation/notation/displaying-rhythms#upbeats) (anacrusis, upbeat) are defined with the pickup setting:
|
@@ -259,6 +270,20 @@ Key or time signatures can be changed on the fly:
|
|
259
270
|
c8cdcb4aaa
|
260
271
|
d8dedc4bb
|
261
272
|
|
273
|
+
### Lilypond Commands and inline settings
|
274
|
+
|
275
|
+
Lilypond commands and settings can be entered inline as part of the note stream:
|
276
|
+
|
277
|
+
cd \key:E- e \stemDown f
|
278
|
+
|
279
|
+
A useful shorthand is for one-time (<code>\once</code>) overrides, with an exclamation mark between the backslash and the command:
|
280
|
+
|
281
|
+
\!override:"NoteHead.color = #red"
|
282
|
+
|
283
|
+
Multiple arguments can be given, separated by colons. Arguments need to be quoted only if they contain whitespace, or colons:
|
284
|
+
|
285
|
+
\!override:AccidentalSuggestion:"#'avoid-slur = #'outside"
|
286
|
+
|
262
287
|
### Inline lyrics
|
263
288
|
|
264
289
|
Lyrics for vocal parts can be entered on separate lines prefixed by a > symbol:
|
@@ -266,7 +291,17 @@ Lyrics for vocal parts can be entered on separate lines prefixed by a > symbol:
|
|
266
291
|
4c[8de]4fd(4c[8de]2f)
|
267
292
|
> Ly-down is the bomb__
|
268
293
|
|
269
|
-
|
294
|
+
Or between notes using quotes:
|
295
|
+
|
296
|
+
4c[8de]4fd(4c[8de]2f) >"Ly-down is the bomb__"
|
297
|
+
|
298
|
+
Text alignment follows the duration, beaming and slurring of the music, [just like in lilypond](http://www.lilypond.org/doc/v2.18/Documentation/notation/common-notation-for-vocal-music#automatic-syllable-durations). Sillables are expected to be separated by a dash. Melismas, i.e. a single sillable streched over multiple notes, is signified by one or more underscores.
|
299
|
+
|
300
|
+
Multiple stanzas for the same music can be specified by including the stanza number in parens:
|
301
|
+
|
302
|
+
4cege1c
|
303
|
+
> Ly-down is the bomb.
|
304
|
+
>(2) Li-ly-pond is too.
|
270
305
|
|
271
306
|
### Stream switching
|
272
307
|
|
@@ -280,6 +315,13 @@ Lyrics can be entered in a block, before or after musical notation, by switching
|
|
280
315
|
=music
|
281
316
|
8g'gffeed4
|
282
317
|
...
|
318
|
+
|
319
|
+
Multiple lyrics stanzas can be written by including the stanza number in parens:
|
320
|
+
|
321
|
+
=lyrics(1) // optional, same as =lyrics
|
322
|
+
...
|
323
|
+
=lyrics(2)
|
324
|
+
...
|
283
325
|
|
284
326
|
### Figured bass
|
285
327
|
|
@@ -308,8 +350,13 @@ For multi-movement works, prefix each movement with a -movement setting:
|
|
308
350
|
|
309
351
|
[Multiple voices](http://www.lilypond.org/doc/v2.18/Documentation/notation/multiple-voices#single_002dstaff-polyphony) on the same staff can be easily entered using the following notation:
|
310
352
|
|
311
|
-
1: 8egfdeg4f
|
312
|
-
|
353
|
+
1: 8egfdeg4f 2: 4cded u: ...
|
354
|
+
|
355
|
+
the <code>u:<\code> command is used to return to single voice (_unisono_) mode.
|
356
|
+
|
357
|
+
Lyrics can be added for individual voices by using inline lyrics:
|
358
|
+
|
359
|
+
1: ceg >"yeah yeah yeah" 2: gbd >"no no no" u: ...
|
313
360
|
|
314
361
|
## Piano scores
|
315
362
|
|
@@ -5,16 +5,13 @@ grammar Lydown
|
|
5
5
|
line ([\n] line)* <Root>
|
6
6
|
end
|
7
7
|
rule stream_switch
|
8
|
-
music_stream / lyrics_stream
|
8
|
+
music_stream / lyrics_stream
|
9
9
|
end
|
10
10
|
rule music_stream
|
11
11
|
'=music' white_space? [\n] music ([\n] !stream_breaker music)*
|
12
12
|
end
|
13
13
|
rule lyrics_stream
|
14
|
-
'=lyrics' white_space? [\n] lyrics_content ([\n] !stream_breaker lyrics_content)*
|
15
|
-
end
|
16
|
-
rule lyrics2_stream
|
17
|
-
'=lyrics2' white_space? [\n] lyrics2_content ([\n] !stream_breaker lyrics2_content)*
|
14
|
+
'=lyrics' stream_idx? white_space? [\n] lyrics_content ([\n] !stream_breaker lyrics_content)* <Lyrics>
|
18
15
|
end
|
19
16
|
rule stream_breaker
|
20
17
|
stream_switch / stream_breaking_setting
|
@@ -47,7 +44,8 @@ grammar Lydown
|
|
47
44
|
[ \t]+
|
48
45
|
end
|
49
46
|
rule event
|
50
|
-
(
|
47
|
+
(inline_command / inline_lyrics / voice_selector / barline / duration /
|
48
|
+
note / standalone_figures / rest / silence / phrasing / tie) white_space*
|
51
49
|
end
|
52
50
|
rule barline
|
53
51
|
('?|' / ':|][|:' / '[|:' / ':|]' / [\|\.\:]+) <Barline>
|
@@ -91,7 +89,7 @@ grammar Lydown
|
|
91
89
|
note_head octave* accidental_flag? figures? expression* <Note>
|
92
90
|
end
|
93
91
|
rule expression
|
94
|
-
(expression_shorthand / expression_longhand) <Note::Expression>
|
92
|
+
(expression_shorthand / expression_longhand / string) <Note::Expression>
|
95
93
|
end
|
96
94
|
|
97
95
|
rule expression_shorthand
|
@@ -100,6 +98,9 @@ grammar Lydown
|
|
100
98
|
rule expression_longhand
|
101
99
|
'\\' [^\s\n]+
|
102
100
|
end
|
101
|
+
rule string
|
102
|
+
'"' ('\"' / !'"' .)* '"'
|
103
|
+
end
|
103
104
|
rule figures # bass figures
|
104
105
|
'<' figures_component? (white_space? figures_component)* '>'
|
105
106
|
end
|
@@ -110,13 +111,16 @@ grammar Lydown
|
|
110
111
|
duration_value? figures <StandAloneFigures>
|
111
112
|
end
|
112
113
|
rule rest
|
113
|
-
[rR] multiplier* <Rest>
|
114
|
+
[rR] multiplier* rest_expression* <Rest>
|
115
|
+
end
|
116
|
+
rule rest_expression
|
117
|
+
(expression_longhand / string) <Note::Expression>
|
114
118
|
end
|
115
119
|
rule silence
|
116
120
|
[s] multiplier* <Silence>
|
117
121
|
end
|
118
122
|
rule note_head
|
119
|
-
[a-g] octave* accidental* <Note::Head>
|
123
|
+
[a-g@] octave* accidental* <Note::Head>
|
120
124
|
end
|
121
125
|
rule accidental
|
122
126
|
[\+\-]+
|
@@ -125,7 +129,7 @@ grammar Lydown
|
|
125
129
|
[\,']+ <Note::Octave>
|
126
130
|
end
|
127
131
|
rule accidental_flag
|
128
|
-
[
|
132
|
+
[\!\?\^] <Note::AccidentalFlag>
|
129
133
|
end
|
130
134
|
rule phrasing
|
131
135
|
beam_open / beam_close / slur_open / slur_close
|
@@ -152,12 +156,30 @@ grammar Lydown
|
|
152
156
|
'&' <ShortTie>
|
153
157
|
end
|
154
158
|
rule lyrics
|
155
|
-
'>' white_space* lyrics_content
|
159
|
+
'>' stream_idx? white_space* lyrics_content <Lyrics>
|
160
|
+
end
|
161
|
+
rule inline_lyrics
|
162
|
+
'>' stream_idx? lyrics_quoted_content <Lyrics>
|
163
|
+
end
|
164
|
+
rule stream_idx
|
165
|
+
'(' [\d] ')' <StreamIndex>
|
156
166
|
end
|
157
167
|
rule lyrics_content
|
158
|
-
(!"\n" !"//" .)* <Lyrics>
|
168
|
+
(!"\n" !"//" .)* <Lyrics::Content>
|
169
|
+
end
|
170
|
+
rule lyrics_quoted_content
|
171
|
+
string <Lyrics::QuotedContent>
|
172
|
+
end
|
173
|
+
rule inline_command
|
174
|
+
'\\' '!'? inline_command_key (':' inline_command_argument)* <Command>
|
175
|
+
end
|
176
|
+
rule inline_command_key
|
177
|
+
[a-zA-Z_0-9]+ <Command::Key>
|
178
|
+
end
|
179
|
+
rule inline_command_argument
|
180
|
+
(string / [^\s\t\n\:]+) <Command::Argument>
|
159
181
|
end
|
160
|
-
rule
|
161
|
-
|
182
|
+
rule voice_selector
|
183
|
+
[1234u] ':' <VoiceSelector>
|
162
184
|
end
|
163
185
|
end
|
data/lib/lydown/parsing/nodes.rb
CHANGED
@@ -171,12 +171,16 @@ module Lydown::Parsing
|
|
171
171
|
end
|
172
172
|
|
173
173
|
module Rest
|
174
|
+
include Root
|
175
|
+
|
174
176
|
def to_stream(stream)
|
175
177
|
rest = {type: :rest, raw: text_value, head: text_value[0]}
|
176
178
|
if text_value =~ /^R(\*([0-9]+))?$/
|
177
179
|
rest[:multiplier] = $2 || '1'
|
178
180
|
end
|
179
181
|
|
182
|
+
_to_stream(self, rest)
|
183
|
+
|
180
184
|
stream << rest
|
181
185
|
end
|
182
186
|
end
|
@@ -194,14 +198,47 @@ module Lydown::Parsing
|
|
194
198
|
end
|
195
199
|
|
196
200
|
module Lyrics
|
201
|
+
include Root
|
197
202
|
def to_stream(stream)
|
198
|
-
|
203
|
+
o = {type: :lyrics}
|
204
|
+
_to_stream(self, o)
|
205
|
+
stream << o
|
199
206
|
end
|
200
|
-
|
207
|
+
|
208
|
+
module Content
|
209
|
+
def to_stream(o)
|
210
|
+
if o[:content]
|
211
|
+
o[:content] << ' ' << text_value
|
212
|
+
else
|
213
|
+
o[:content] = text_value
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
module QuotedContent
|
219
|
+
def to_stream(o)
|
220
|
+
if text_value =~ /^"(.+)"$/
|
221
|
+
content = $1
|
222
|
+
else
|
223
|
+
raise LydownError, "Unexpected quoted lyrics content (#{text_value.inspect})"
|
224
|
+
end
|
201
225
|
|
202
|
-
|
203
|
-
|
204
|
-
|
226
|
+
if o[:content]
|
227
|
+
o[:content] << ' ' << content
|
228
|
+
else
|
229
|
+
o[:content] = content
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
module StreamIndex
|
236
|
+
def to_stream(o)
|
237
|
+
idx = (text_value =~ /\(([\d]+)\)/) && $1.to_i
|
238
|
+
if idx.nil?
|
239
|
+
raise LydownError, "Invalid stream index (#{text_value.inspect})"
|
240
|
+
end
|
241
|
+
o[:stream_index] = idx
|
205
242
|
end
|
206
243
|
end
|
207
244
|
|
@@ -210,4 +247,49 @@ module Lydown::Parsing
|
|
210
247
|
stream << {type: :barline, barline: text_value}
|
211
248
|
end
|
212
249
|
end
|
250
|
+
|
251
|
+
module Command
|
252
|
+
include Root
|
253
|
+
def to_stream(stream)
|
254
|
+
cmd = {type: :command}
|
255
|
+
cmd[:once] = true if text_value =~ /^\\\!/
|
256
|
+
_to_stream(self, cmd)
|
257
|
+
stream << cmd
|
258
|
+
end
|
259
|
+
|
260
|
+
SETTING_KEYS = %w{time key clef}
|
261
|
+
|
262
|
+
module Key
|
263
|
+
def to_stream(cmd)
|
264
|
+
cmd[:key] = text_value
|
265
|
+
if SETTING_KEYS.include?(text_value)
|
266
|
+
cmd[:type] = :setting
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
module Argument
|
272
|
+
def to_stream(cmd)
|
273
|
+
if text_value =~ /^"(.+)"$/
|
274
|
+
value = $1
|
275
|
+
else
|
276
|
+
value = text_value
|
277
|
+
end
|
278
|
+
|
279
|
+
if cmd[:type] == :setting
|
280
|
+
cmd[:value] = value
|
281
|
+
else
|
282
|
+
cmd[:arguments] ||= []
|
283
|
+
cmd[:arguments] << value
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
module VoiceSelector
|
290
|
+
def to_stream(stream)
|
291
|
+
voice = (text_value =~ /^([1234])/) && $1.to_i
|
292
|
+
stream << {type: :voice_select, voice: voice}
|
293
|
+
end
|
294
|
+
end
|
213
295
|
end
|
data/lib/lydown/parsing.rb
CHANGED
@@ -26,7 +26,7 @@ class LydownParser
|
|
26
26
|
else
|
27
27
|
msg << "#{parser.failure_reason}:\n"
|
28
28
|
end
|
29
|
-
msg << " #{source.lines[parser.failure_line - 1]} #{' ' * parser.failure_column}^"
|
29
|
+
msg << " #{source.lines[parser.failure_line - 1].chomp}\n #{' ' * parser.failure_column}^"
|
30
30
|
|
31
31
|
msg
|
32
32
|
end
|
@@ -2,7 +2,11 @@ module Lydown::Rendering
|
|
2
2
|
class Lyrics < Base
|
3
3
|
def translate
|
4
4
|
value = lilypond_lyrics(@event[:content])
|
5
|
-
|
5
|
+
|
6
|
+
lyrics_idx = @event[:stream_index] || 1
|
7
|
+
voice = @work['process/voice_selector'] || 'voice1'
|
8
|
+
|
9
|
+
@work.emit("lyrics/#{voice}/#{lyrics_idx}", value, ' ')
|
6
10
|
end
|
7
11
|
|
8
12
|
def lilypond_lyrics(lyrics)
|
@@ -60,7 +60,12 @@ module Lydown::Rendering
|
|
60
60
|
def add_note(event)
|
61
61
|
return add_macro_note(event) if @work['process/duration_macro']
|
62
62
|
|
63
|
-
@
|
63
|
+
if @event[:head] == '@'
|
64
|
+
# replace repeating note head
|
65
|
+
@event[:head] = @work['process/last_note_head']
|
66
|
+
else
|
67
|
+
@work['process/last_note_head'] = event[:head]
|
68
|
+
end
|
64
69
|
|
65
70
|
value = @work['process/duration_values'].first
|
66
71
|
@work['process/duration_values'].rotate!
|
@@ -87,20 +92,35 @@ module Lydown::Rendering
|
|
87
92
|
|
88
93
|
@work.emit(event[:stream] || :music, lilypond_note(event, value: value))
|
89
94
|
end
|
95
|
+
|
96
|
+
FICTA_CODE = <<EOF
|
97
|
+
\\once \\override AccidentalSuggestion #'avoid-slur = #'outside
|
98
|
+
\\once \\set suggestAccidentals = ##t
|
99
|
+
EOF
|
90
100
|
|
91
101
|
def lilypond_note(event, options = {})
|
92
102
|
head = Accidentals.translate_note_name(@work, event[:head])
|
93
103
|
if options[:head_only]
|
94
104
|
head
|
95
105
|
else
|
96
|
-
|
106
|
+
if event[:accidental_flag] == '^'
|
107
|
+
accidental_flag = ''
|
108
|
+
prefix = FICTA_CODE
|
109
|
+
else
|
110
|
+
accidental_flag = event[:accidental_flag]
|
111
|
+
prefix = ''
|
112
|
+
end
|
113
|
+
|
114
|
+
[
|
115
|
+
prefix,
|
97
116
|
head,
|
98
117
|
event[:octave],
|
99
|
-
|
118
|
+
accidental_flag,
|
100
119
|
options[:value],
|
101
120
|
lilypond_phrasing(event),
|
102
|
-
event[:expressions] ? event[:expressions].join : ''
|
103
|
-
|
121
|
+
event[:expressions] ? event[:expressions].join : '',
|
122
|
+
' '
|
123
|
+
].join
|
104
124
|
end
|
105
125
|
end
|
106
126
|
|
@@ -152,12 +172,12 @@ module Lydown::Rendering
|
|
152
172
|
]
|
153
173
|
|
154
174
|
# replace place holder and repeaters in macro group with actual note
|
155
|
-
@work['process/macro_group'].gsub!(/[_
|
175
|
+
@work['process/macro_group'].gsub!(/[_∞]/) do |match|
|
156
176
|
case match
|
157
177
|
when '_'
|
158
178
|
underscore_count += 1
|
159
179
|
underscore_count == 1 ? lydown_note : match
|
160
|
-
when '
|
180
|
+
when '∞'
|
161
181
|
underscore_count < 2 ? event[:head] : match
|
162
182
|
end
|
163
183
|
end
|
@@ -176,6 +196,34 @@ module Lydown::Rendering
|
|
176
196
|
@work['process/macro_group'] = nil
|
177
197
|
end
|
178
198
|
end
|
199
|
+
|
200
|
+
LILYPOND_EXPRESSIONS = {
|
201
|
+
'_' => '--',
|
202
|
+
'.' => '-.',
|
203
|
+
'`' => '-!'
|
204
|
+
}
|
205
|
+
|
206
|
+
def translate_expressions
|
207
|
+
return unless @event[:expressions]
|
208
|
+
|
209
|
+
@event[:expressions] = @event[:expressions].map do |expr|
|
210
|
+
if expr =~ /^(?:\\(_?))?"(.+)"$/
|
211
|
+
placement = ($1 == '_') ? '_' : '^'
|
212
|
+
"#{placement}\\markup { #{translate_string_expression($2)} }"
|
213
|
+
elsif expr =~ /^\\/
|
214
|
+
expr
|
215
|
+
elsif LILYPOND_EXPRESSIONS[expr]
|
216
|
+
LILYPOND_EXPRESSIONS[expr]
|
217
|
+
else
|
218
|
+
raise LydownError, "Invalid expression #{expr.inspect}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def translate_string_expression(expr)
|
224
|
+
expr.gsub(/__([^_]+)__/) {|m| "\\bold { #{$1} }" }.
|
225
|
+
gsub(/_([^_]+)_/) {|m| "\\italic { #{$1} }" }
|
226
|
+
end
|
179
227
|
end
|
180
228
|
|
181
229
|
LILYPOND_DURATIONS = {
|
@@ -232,7 +280,7 @@ module Lydown::Rendering
|
|
232
280
|
# look ahead and see if any beam or slur closing after note
|
233
281
|
look_ahead_idx = @idx + 1
|
234
282
|
while event = @stream[look_ahead_idx]
|
235
|
-
case
|
283
|
+
case event[:type]
|
236
284
|
when :beam_close
|
237
285
|
@event[:beam_close] = true
|
238
286
|
when :slur_close
|
@@ -245,26 +293,6 @@ module Lydown::Rendering
|
|
245
293
|
|
246
294
|
add_note(@event)
|
247
295
|
end
|
248
|
-
|
249
|
-
LILYPOND_EXPRESSIONS = {
|
250
|
-
'_' => '--',
|
251
|
-
'.' => '-.',
|
252
|
-
'`' => '-!'
|
253
|
-
}
|
254
|
-
|
255
|
-
def translate_expressions
|
256
|
-
return unless @event[:expressions]
|
257
|
-
|
258
|
-
@event[:expressions] = @event[:expressions].map do |expr|
|
259
|
-
if expr =~ /^\\/
|
260
|
-
expr
|
261
|
-
elsif LILYPOND_EXPRESSIONS[expr]
|
262
|
-
LILYPOND_EXPRESSIONS[expr]
|
263
|
-
else
|
264
|
-
raise LydownError, "Invalid expression #{expr.inspect}"
|
265
|
-
end
|
266
|
-
end
|
267
|
-
end
|
268
296
|
end
|
269
297
|
|
270
298
|
class StandAloneFigures < Base
|
@@ -327,6 +355,8 @@ module Lydown::Rendering
|
|
327
355
|
end
|
328
356
|
|
329
357
|
def translate
|
358
|
+
translate_expressions
|
359
|
+
|
330
360
|
if @event[:multiplier]
|
331
361
|
value = full_bar_value(@work[:time])
|
332
362
|
if value
|
@@ -369,7 +399,9 @@ module Lydown::Rendering
|
|
369
399
|
if macro =~ /^\{(.+)\}$/
|
370
400
|
macro = $1
|
371
401
|
end
|
372
|
-
|
402
|
+
# replace the repeating note placeholder with another sign in order to
|
403
|
+
# avoid mixing up with repeating notes from outside the macro
|
404
|
+
@work['process/duration_macro'] = macro.gsub('@', '∞')
|
373
405
|
else
|
374
406
|
raise LydownError, "Unknown macro #{@event[:macro]}"
|
375
407
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Lydown::Rendering
|
2
2
|
class Setting < Base
|
3
3
|
SETTING_KEYS = [
|
4
|
-
'key', 'time', 'pickup', 'clef', 'part', 'movement',
|
5
|
-
'accidentals', 'beams', 'end_barline', 'macros'
|
4
|
+
'key', 'time', 'pickup', 'clef', 'part', 'movement', 'tempo',
|
5
|
+
'accidentals', 'beams', 'end_barline', 'macros', 'empty_staves'
|
6
6
|
]
|
7
7
|
|
8
8
|
RENDERABLE_SETTING_KEYS = [
|
@@ -11,7 +11,8 @@ module Lydown::Rendering
|
|
11
11
|
|
12
12
|
ALLOWED_SETTING_VALUES = {
|
13
13
|
'accidentals' => ['manual', 'auto'],
|
14
|
-
'beams' => ['manual', 'auto']
|
14
|
+
'beams' => ['manual', 'auto'],
|
15
|
+
'empty_staves' => ['hide', 'show']
|
15
16
|
}
|
16
17
|
|
17
18
|
def translate
|
@@ -20,11 +21,12 @@ module Lydown::Rendering
|
|
20
21
|
level = @event[:level] || 0
|
21
22
|
|
22
23
|
unless (level > 0) || SETTING_KEYS.include?(key)
|
23
|
-
raise
|
24
|
+
raise LydownError, "Invalid setting (#{key})"
|
24
25
|
end
|
25
26
|
|
26
27
|
if level == 0
|
27
|
-
|
28
|
+
value = check_setting_value(key, value)
|
29
|
+
@work[key] = value
|
28
30
|
case key
|
29
31
|
when 'part'
|
30
32
|
# when changing parts we repeat the last set time and key signature
|
@@ -55,7 +57,13 @@ module Lydown::Rendering
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def check_setting_value(key, value)
|
58
|
-
if
|
60
|
+
if key == 'key'
|
61
|
+
# process shorthand notation
|
62
|
+
if value =~ /^([a-gA-G])[\+\-]*$/
|
63
|
+
mode = $1.downcase == $1 ? 'minor' : 'major'
|
64
|
+
value = "#{value.downcase} #{mode}"
|
65
|
+
end
|
66
|
+
elsif ALLOWED_SETTING_VALUES[key]
|
59
67
|
unless ALLOWED_SETTING_VALUES[key].include?(value)
|
60
68
|
raise LydownError, "Invalid value for setting #{key}: #{value.inspect}"
|
61
69
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Lydown::Rendering
|
2
|
+
class VoiceSelect < Base
|
3
|
+
def translate
|
4
|
+
if @event[:voice]
|
5
|
+
@work['process/voice_selector'] = "voice#{@event[:voice]}"
|
6
|
+
else
|
7
|
+
self.class.render_voices(@work)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.render_voices(work)
|
12
|
+
work['process/voice_selector'] = nil
|
13
|
+
|
14
|
+
music = Lydown::Templates.render(:multi_voice, work, part: work[:part])
|
15
|
+
|
16
|
+
work.emit(:music, music)
|
17
|
+
|
18
|
+
work['process/voices'].each_value do |stream|
|
19
|
+
if stream['lyrics']
|
20
|
+
stream['lyrics'].each do |voice, lyrics_stream|
|
21
|
+
lyrics_stream.each do |idx, content|
|
22
|
+
work.emit("lyrics/#{voice}/#{idx}", content)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
work['process/voices'] = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
VOICE_COMMANDS = {
|
32
|
+
'voice1' => '\voiceOne',
|
33
|
+
'voice2' => '\voiceTwo',
|
34
|
+
'voice3' => '\voiceThree',
|
35
|
+
'voice4' => '\voiceFour',
|
36
|
+
}
|
37
|
+
|
38
|
+
def self.voice_command(voice)
|
39
|
+
VOICE_COMMANDS[voice]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/lydown/rendering.rb
CHANGED
@@ -27,6 +27,15 @@
|
|
27
27
|
|
28
28
|
<% if score_mode %>
|
29
29
|
\score {
|
30
|
+
<% if self['empty_staves'] == 'hide' %>
|
31
|
+
\layout {
|
32
|
+
\context {
|
33
|
+
\RemoveEmptyStaffContext
|
34
|
+
\override VerticalAxisGroup #'remove-first = ##t
|
35
|
+
}
|
36
|
+
}
|
37
|
+
<% end %>
|
38
|
+
|
30
39
|
\new StaffGroup <<
|
31
40
|
\set StaffGroup.systemStartDelimiterHierarchy = <%= staff_hierarchy %>
|
32
41
|
<% end %>
|
@@ -36,7 +45,7 @@
|
|
36
45
|
\set Score.barNumberVisibility = #all-bar-numbers-visible
|
37
46
|
\bar ""
|
38
47
|
<% end %>
|
39
|
-
|
48
|
+
|
40
49
|
<% parts.each do |n, p| %>
|
41
50
|
<%= Lydown::Templates.render(:part, self,
|
42
51
|
name: n, part: p, movement: movement) %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<<
|
2
|
+
<% beaming_mode = Lydown::Rendering::Staff.beaming_mode(part) %>
|
3
|
+
<% voice_prefix = part.nil? || (part == "") ? nil : "#{part}_" %>
|
4
|
+
|
5
|
+
<% self['process/voices'].each do |voice, stream| %>
|
6
|
+
<% if voice != 'voice1' %>
|
7
|
+
\new Voice = "<%= voice_prefix %><%= voice %>"
|
8
|
+
<% end %>
|
9
|
+
{
|
10
|
+
<%= beaming_mode %>
|
11
|
+
<%= Lydown::Rendering::VoiceSelect.voice_command(voice) %>
|
12
|
+
<%= stream['music'] %>
|
13
|
+
}
|
14
|
+
<% end %>
|
15
|
+
>>
|
16
|
+
\oneVoice
|
@@ -2,8 +2,13 @@
|
|
2
2
|
title = nil
|
3
3
|
if name && (name != '')
|
4
4
|
title = Lydown::Rendering.part_title(name)
|
5
|
+
voice_prefix = "#{name}_"
|
6
|
+
else
|
7
|
+
title = nil
|
8
|
+
voice_prefix = nil
|
5
9
|
end
|
6
|
-
|
10
|
+
staff_id = "#{title && title.gsub(' ', '')}Staff"
|
11
|
+
|
7
12
|
|
8
13
|
score_mode = self['render_opts/mode'] == :score
|
9
14
|
|
@@ -18,19 +23,27 @@
|
|
18
23
|
|
19
24
|
<<
|
20
25
|
|
21
|
-
|
26
|
+
<% if self[:tempo] %>
|
27
|
+
\tempo "<%= self[:tempo] %>"
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
\new Staff = <%= staff_id %> \with {
|
22
31
|
}
|
23
32
|
|
24
|
-
\context Staff = <%=
|
33
|
+
\context Staff = <%= staff_id %> {
|
25
34
|
<% if score_mode %>\set Staff.instrumentName = #"<%= title %>"<% end %>
|
26
35
|
\relative c {
|
27
36
|
<% if clef %>
|
28
37
|
\clef "<%= clef %>"
|
29
38
|
<% end %>
|
30
|
-
<%= beaming_mode %>
|
31
39
|
<%= partial %>
|
32
|
-
|
33
|
-
|
40
|
+
|
41
|
+
<<
|
42
|
+
\new Voice = "<%= voice_prefix %>voice1" {
|
43
|
+
<%= beaming_mode %>
|
44
|
+
<%= part['music'] %>
|
45
|
+
}
|
46
|
+
>>
|
34
47
|
<% if end_barline %>
|
35
48
|
\bar "<%= end_barline %>"
|
36
49
|
<% end %>
|
@@ -38,15 +51,22 @@
|
|
38
51
|
}
|
39
52
|
|
40
53
|
<% if part['lyrics'] %>
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
54
|
+
<% multi_voice = part['lyrics'].size > 1 %>
|
55
|
+
<% part['lyrics'].each_key do |voice| %>
|
56
|
+
<% above_staff = multi_voice && ['voice1', 'voice3'].include?(voice) %>
|
57
|
+
<% part['lyrics'][voice].keys.sort.each do |idx| %>
|
58
|
+
\new Lyrics
|
59
|
+
<% if above_staff %>
|
60
|
+
\with { alignAboveContext = "<%= staff_id %>" }
|
61
|
+
<% end %>
|
62
|
+
{
|
63
|
+
\lyricsto "<%= voice_prefix %><%= voice %>" {
|
64
|
+
<%= part['lyrics'][voice][idx] %>
|
65
|
+
}
|
66
|
+
}
|
67
|
+
<% end %>
|
68
|
+
<% end %>
|
69
|
+
|
50
70
|
<% end %>
|
51
71
|
|
52
72
|
<% if part['figures'] %>
|
data/lib/lydown/version.rb
CHANGED
data/lib/lydown/work.rb
CHANGED
@@ -28,6 +28,7 @@ module Lydown
|
|
28
28
|
case mode
|
29
29
|
when :work, :movement
|
30
30
|
@context[:time] = '4/4'
|
31
|
+
@context[:tempo] = nil
|
31
32
|
@context[:cadenza_mode] = nil
|
32
33
|
@context[:key] = 'c major'
|
33
34
|
@context[:pickup] = nil
|
@@ -38,6 +39,10 @@ module Lydown
|
|
38
39
|
if @context['process/tuplet_mode']
|
39
40
|
Lydown::Rendering::TupletDuration.emit_tuplet_end(self)
|
40
41
|
end
|
42
|
+
|
43
|
+
if @context['process/voice_selector']
|
44
|
+
Lydown::Rendering::VoiceSelect.render_voices(self)
|
45
|
+
end
|
41
46
|
|
42
47
|
# reset processing variables
|
43
48
|
@context['process'] = {
|
@@ -65,16 +70,20 @@ module Lydown
|
|
65
70
|
reset_context(:part) unless opts[:no_reset]
|
66
71
|
end
|
67
72
|
|
68
|
-
def emit(
|
69
|
-
stream = current_stream(
|
73
|
+
def emit(path, *content)
|
74
|
+
stream = current_stream(path)
|
70
75
|
|
71
76
|
content.each {|c| stream << c}
|
72
77
|
end
|
73
78
|
|
74
|
-
def current_stream(
|
75
|
-
|
76
|
-
|
77
|
-
|
79
|
+
def current_stream(subpath)
|
80
|
+
if @context['process/voice_selector']
|
81
|
+
path = "process/voices/#{@context['process/voice_selector']}/#{subpath}"
|
82
|
+
else
|
83
|
+
movement = @context[:movement]
|
84
|
+
part = @context[:part]
|
85
|
+
path = "movements/#{movement}/parts/#{part}/#{subpath}"
|
86
|
+
end
|
78
87
|
@context[path] ||= ''
|
79
88
|
end
|
80
89
|
|
@@ -101,8 +110,11 @@ module Lydown
|
|
101
110
|
def filter_context(opts = {})
|
102
111
|
filtered = @context.deep_clone
|
103
112
|
|
104
|
-
|
105
|
-
|
113
|
+
if filtered['movements'].nil? || filtered['movements'].size == 0
|
114
|
+
# no movements found, so no music
|
115
|
+
raise LydownError, "No music found"
|
116
|
+
elsif filtered['movements'].size > 1
|
117
|
+
# delete default movement if other movements are present
|
106
118
|
filtered['movements'].delete('')
|
107
119
|
end
|
108
120
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lydown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: treetop
|
@@ -43,6 +43,7 @@ files:
|
|
43
43
|
- lib/lydown/parsing/nodes.rb
|
44
44
|
- lib/lydown/rendering.rb
|
45
45
|
- lib/lydown/rendering/base.rb
|
46
|
+
- lib/lydown/rendering/command.rb
|
46
47
|
- lib/lydown/rendering/comments.rb
|
47
48
|
- lib/lydown/rendering/defaults.yml
|
48
49
|
- lib/lydown/rendering/figures.rb
|
@@ -51,9 +52,11 @@ files:
|
|
51
52
|
- lib/lydown/rendering/music.rb
|
52
53
|
- lib/lydown/rendering/settings.rb
|
53
54
|
- lib/lydown/rendering/staff.rb
|
55
|
+
- lib/lydown/rendering/voices.rb
|
54
56
|
- lib/lydown/templates.rb
|
55
57
|
- lib/lydown/templates/lilypond_doc.erb
|
56
58
|
- lib/lydown/templates/movement.erb
|
59
|
+
- lib/lydown/templates/multi_voice.erb
|
57
60
|
- lib/lydown/templates/part.erb
|
58
61
|
- lib/lydown/version.rb
|
59
62
|
- lib/lydown/work.rb
|