lydown 0.12.4 → 0.14.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 +41 -1
- data/bin/lydown +2 -0
- data/lib/lydown.rb +6 -2
- data/lib/lydown/cache.rb +5 -1
- data/lib/lydown/cli.rb +1 -1
- data/lib/lydown/cli/commands.rb +7 -11
- data/lib/lydown/cli/compiler.rb +5 -0
- data/lib/lydown/cli/proofing.rb +2 -2
- data/lib/lydown/cli/repl.rb +1 -1
- data/lib/lydown/cli/support.rb +0 -33
- data/lib/lydown/defaults.yml +50 -8
- data/lib/lydown/inverso.rb +84 -0
- data/lib/lydown/lilypond.rb +76 -10
- data/lib/lydown/ly_lib/lib.ly +140 -127
- data/lib/lydown/parsing/lydown.treetop +25 -12
- data/lib/lydown/parsing/nodes.rb +55 -19
- data/lib/lydown/rendering.rb +72 -1
- data/lib/lydown/rendering/base.rb +21 -0
- data/lib/lydown/rendering/command.rb +53 -0
- data/lib/lydown/rendering/layout.rb +83 -0
- data/lib/lydown/rendering/lyrics.rb +1 -1
- data/lib/lydown/rendering/markup.rb +23 -0
- data/lib/lydown/rendering/movement.rb +7 -4
- data/lib/lydown/rendering/music.rb +35 -29
- data/lib/lydown/rendering/notes.rb +75 -41
- data/lib/lydown/rendering/repeats.rb +27 -0
- data/lib/lydown/rendering/settings.rb +36 -9
- data/lib/lydown/rendering/skipping.rb +10 -2
- data/lib/lydown/rendering/staff.rb +38 -31
- data/lib/lydown/rendering/voices.rb +1 -1
- data/lib/lydown/templates.rb +8 -8
- data/lib/lydown/templates/layout.rb +40 -0
- data/lib/lydown/templates/lilypond_doc.rb +95 -0
- data/lib/lydown/templates/movement.rb +188 -0
- data/lib/lydown/templates/multi_voice.rb +25 -0
- data/lib/lydown/templates/part.rb +146 -0
- data/lib/lydown/templates/variables.rb +43 -0
- data/lib/lydown/translation/ripple.rb +1 -1
- data/lib/lydown/translation/ripple/nodes.rb +51 -2
- data/lib/lydown/translation/ripple/ripple.treetop +87 -10
- data/lib/lydown/version.rb +1 -1
- data/lib/lydown/work.rb +19 -2
- data/lib/lydown/work_context.rb +10 -2
- metadata +12 -8
- data/lib/lydown/cli/installer.rb +0 -175
- data/lib/lydown/templates/lilypond_doc.erb +0 -34
- data/lib/lydown/templates/movement.erb +0 -118
- data/lib/lydown/templates/multi_voice.erb +0 -16
- data/lib/lydown/templates/part.erb +0 -118
- data/lib/lydown/templates/variables.erb +0 -43
@@ -34,7 +34,7 @@ grammar Lydown
|
|
34
34
|
comment? <Setting>
|
35
35
|
end
|
36
36
|
rule setting_key
|
37
|
-
[a-z0-9_]+ <Setting::Key>
|
37
|
+
[a-z0-9_\-]+ <Setting::Key>
|
38
38
|
end
|
39
39
|
rule setting_value
|
40
40
|
(!"\n" !"//" .)* <Setting::Value>
|
@@ -46,27 +46,31 @@ grammar Lydown
|
|
46
46
|
[ \t]+
|
47
47
|
end
|
48
48
|
rule event
|
49
|
-
(inline_command / inline_lyrics / voice_selector /
|
50
|
-
|
49
|
+
(inline_command / inline_lyrics / voice_selector / repeat_start / volta /
|
50
|
+
repeat_end / barline / source_ref /
|
51
|
+
duration / standalone_figures / chord / note / rest /
|
51
52
|
silence / phrasing / tie) white_space*
|
52
53
|
end
|
54
|
+
rule repeat_start
|
55
|
+
'|:*' [0-9]+ <Repeat::Start>
|
56
|
+
end
|
57
|
+
rule volta
|
58
|
+
'|*' <Repeat::Volta>
|
59
|
+
end
|
60
|
+
rule repeat_end
|
61
|
+
'*|' <Repeat::End>
|
62
|
+
end
|
53
63
|
rule barline
|
54
|
-
('?|' / ':|][|:' / '[|:' / ':|]' / [\|\.\:]+) <Barline>
|
64
|
+
('?|' / ':|][|:' / '[|:' / ':|]' / [\|\.\:']+) <Barline>
|
55
65
|
end
|
56
66
|
rule duration
|
57
67
|
tuplet_value / duration_value / duration_macro
|
58
68
|
end
|
59
|
-
rule grace_duration
|
60
|
-
'$' grace_duration_type* number <GraceDuration>
|
61
|
-
end
|
62
|
-
rule grace_duration_type
|
63
|
-
[\/\^]
|
64
|
-
end
|
65
69
|
rule tuplet_value
|
66
70
|
number '%' (number '/' number)? <TupletValue>
|
67
71
|
end
|
68
72
|
rule duration_value
|
69
|
-
duration_number dots* multiplier? <DurationValue>
|
73
|
+
duration_number dots* cross_bar_dotting? multiplier? grace? <DurationValue>
|
70
74
|
end
|
71
75
|
rule duration_number
|
72
76
|
[0-9]+ / 'l'
|
@@ -77,9 +81,15 @@ grammar Lydown
|
|
77
81
|
rule dots
|
78
82
|
'.'+
|
79
83
|
end
|
84
|
+
rule cross_bar_dotting
|
85
|
+
'!'
|
86
|
+
end
|
80
87
|
rule multiplier
|
81
88
|
'*' number ('/' number)*
|
82
89
|
end
|
90
|
+
rule grace
|
91
|
+
[°^`]
|
92
|
+
end
|
83
93
|
rule duration_macro
|
84
94
|
'{' duration_macro__expression '}'
|
85
95
|
end
|
@@ -194,7 +204,10 @@ grammar Lydown
|
|
194
204
|
[\<\>\|\\]? [a-zA-Z_\-\.0-9]+ <Command::Key>
|
195
205
|
end
|
196
206
|
rule inline_command_argument
|
197
|
-
(string / [^\s\t\n\:]+) <Command::Argument>
|
207
|
+
(string / parenthesized_command_argument / [^\s\t\n\:]+)+ <Command::Argument>
|
208
|
+
end
|
209
|
+
rule parenthesized_command_argument
|
210
|
+
'(' (parenthesized_command_argument / [^\)]+) ')'
|
198
211
|
end
|
199
212
|
rule voice_selector
|
200
213
|
[1234u] ':' <VoiceSelector>
|
data/lib/lydown/parsing/nodes.rb
CHANGED
@@ -103,10 +103,31 @@ module Lydown::Parsing
|
|
103
103
|
end
|
104
104
|
|
105
105
|
class DurationValue < Root
|
106
|
+
GRACE_KIND = {
|
107
|
+
'°' => :grace,
|
108
|
+
'`' => :acciaccatura,
|
109
|
+
'^' => :appoggiatura
|
110
|
+
}
|
111
|
+
|
106
112
|
def to_stream(stream, opts)
|
107
|
-
|
108
|
-
|
109
|
-
|
113
|
+
if text_value.strip =~ /^(.+)([°`^])$/
|
114
|
+
event = {
|
115
|
+
type: :grace,
|
116
|
+
value: $1,
|
117
|
+
kind: GRACE_KIND[$2]
|
118
|
+
}
|
119
|
+
else
|
120
|
+
event = {
|
121
|
+
type: :duration,
|
122
|
+
value: text_value.strip
|
123
|
+
}
|
124
|
+
if event[:value] =~ /\!/
|
125
|
+
event[:value].gsub!('!', '')
|
126
|
+
event[:cross_bar_dotting] = true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
stream << event_hash(stream, opts, event)
|
110
131
|
end
|
111
132
|
end
|
112
133
|
|
@@ -129,22 +150,6 @@ module Lydown::Parsing
|
|
129
150
|
end
|
130
151
|
end
|
131
152
|
|
132
|
-
class GraceDuration < Root
|
133
|
-
GRACE_KIND = {
|
134
|
-
nil => :grace,
|
135
|
-
'/' => :acciaccatura,
|
136
|
-
'^' => :appoggiatura
|
137
|
-
}
|
138
|
-
|
139
|
-
def to_stream(stream, opts)
|
140
|
-
if text_value =~ /^\$([\/\^])?(\d+)$/
|
141
|
-
stream << event_hash(stream, opts, {
|
142
|
-
type: :grace, value: $2, kind: GRACE_KIND[$1]
|
143
|
-
})
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
153
|
class Note < Root
|
149
154
|
def to_stream(stream, opts)
|
150
155
|
note = event_hash(stream, opts, {
|
@@ -413,4 +418,35 @@ module Lydown::Parsing
|
|
413
418
|
end
|
414
419
|
end
|
415
420
|
end
|
421
|
+
|
422
|
+
module Repeat
|
423
|
+
class Start < Root
|
424
|
+
def to_stream(stream, opts)
|
425
|
+
ref = {type: :repeat_start, raw: text_value, count: 2}
|
426
|
+
if text_value =~ /(\d+)$/
|
427
|
+
ref[:count] = $1.to_i
|
428
|
+
end
|
429
|
+
|
430
|
+
stream << event_hash(stream, opts, ref)
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
class Volta < Root
|
435
|
+
def to_stream(stream, opts)
|
436
|
+
stream << event_hash(stream, opts, {
|
437
|
+
type: :repeat_volta,
|
438
|
+
raw: text_value
|
439
|
+
})
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
class End < Root
|
444
|
+
def to_stream(stream, opts)
|
445
|
+
stream << event_hash(stream, opts, {
|
446
|
+
type: :repeat_end,
|
447
|
+
raw: text_value
|
448
|
+
})
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
416
452
|
end
|
data/lib/lydown/rendering.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'lydown/templates'
|
2
|
+
require 'lydown/inverso'
|
2
3
|
require 'lydown/work'
|
3
4
|
require 'lydown/rendering/base'
|
4
5
|
require 'lydown/rendering/literal'
|
@@ -6,6 +7,7 @@ require 'lydown/rendering/comments'
|
|
6
7
|
require 'lydown/rendering/lyrics'
|
7
8
|
require 'lydown/rendering/notes'
|
8
9
|
require 'lydown/rendering/music'
|
10
|
+
require 'lydown/rendering/repeats'
|
9
11
|
require 'lydown/rendering/settings'
|
10
12
|
require 'lydown/rendering/staff'
|
11
13
|
require 'lydown/rendering/movement'
|
@@ -13,6 +15,8 @@ require 'lydown/rendering/command'
|
|
13
15
|
require 'lydown/rendering/voices'
|
14
16
|
require 'lydown/rendering/source_ref'
|
15
17
|
require 'lydown/rendering/skipping'
|
18
|
+
require 'lydown/rendering/layout'
|
19
|
+
require 'lydown/rendering/markup'
|
16
20
|
|
17
21
|
module Lydown::Rendering
|
18
22
|
class << self
|
@@ -70,6 +74,10 @@ module Lydown::Rendering
|
|
70
74
|
end
|
71
75
|
|
72
76
|
def include_files(context, opts)
|
77
|
+
if context.render_mode == :part
|
78
|
+
opts = opts.merge(part: context['render_opts/parts'])
|
79
|
+
end
|
80
|
+
|
73
81
|
filenames = []
|
74
82
|
if opts.has_key?(:movement)
|
75
83
|
add_includes(filenames, context, :includes, opts)
|
@@ -93,9 +101,72 @@ module Lydown::Rendering
|
|
93
101
|
when '.ely'
|
94
102
|
Lydown::Templates.render(fn, context)
|
95
103
|
else
|
96
|
-
"\\include \"#{fn}\""
|
104
|
+
"\\include \"#{File.expand_path(fn)}\""
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_requires(packages, context, key, opts)
|
110
|
+
requires = context.get_setting(key, opts)
|
111
|
+
packages.concat(requires) if requires
|
112
|
+
end
|
113
|
+
|
114
|
+
def packages_to_load(context, opts)
|
115
|
+
packages = []
|
116
|
+
if opts.has_key?(:movement)
|
117
|
+
add_requires(packages, context, :requires, opts)
|
118
|
+
case context.render_mode
|
119
|
+
when :score
|
120
|
+
add_requires(packages, context, 'score/requires', opts)
|
121
|
+
when :part
|
122
|
+
add_requires(packages, context, 'parts/requires', opts)
|
123
|
+
if opts[:part]
|
124
|
+
add_requires(packages, context, "parts/#{opts[:part]}/requires", opts)
|
125
|
+
end
|
97
126
|
end
|
127
|
+
else
|
128
|
+
# paths to be included at top of lilypond doc should be defined under
|
129
|
+
# document/requires
|
130
|
+
add_requires(packages, context, 'document/requires', opts)
|
98
131
|
end
|
132
|
+
|
133
|
+
packages.uniq
|
134
|
+
end
|
135
|
+
|
136
|
+
def layout_info(context, opts = {})
|
137
|
+
layout = (context.get_merged_setting_tree(:layout, opts) || {}).deep!
|
138
|
+
|
139
|
+
case context.render_mode
|
140
|
+
when :score
|
141
|
+
layout.deep_merge!(context.get_merged_setting_tree('score/layout', opts) || {})
|
142
|
+
when :part
|
143
|
+
layout.deep_merge!(context.get_merged_setting_tree('parts/layout', opts) || {})
|
144
|
+
layout.deep_merge!(context.get_merged_setting_tree("parts/#{context['render_opts/parts']}/layout", opts) || {})
|
145
|
+
end
|
146
|
+
|
147
|
+
layout
|
148
|
+
end
|
149
|
+
|
150
|
+
def get_set_variables(context, opts = {})
|
151
|
+
set = (context.get_merged_setting_tree(:set, opts) || {}).deep!
|
152
|
+
|
153
|
+
case context.render_mode
|
154
|
+
when :score
|
155
|
+
set.deep_merge!(context.get_merged_setting_tree('score/set', opts) || {})
|
156
|
+
when :part
|
157
|
+
set.deep_merge!(context.get_merged_setting_tree('parts/set', opts) || {})
|
158
|
+
set.deep_merge!(context.get_merged_setting_tree("parts/#{context['render_opts/parts']}/set", opts) || {})
|
159
|
+
end
|
160
|
+
|
161
|
+
set
|
162
|
+
end
|
163
|
+
|
164
|
+
def lyrics_markup(context, opts = {})
|
165
|
+
if context.render_mode == :part
|
166
|
+
opts = opts.merge(part: context['render_opts/parts'])
|
167
|
+
end
|
168
|
+
|
169
|
+
context.get_setting('lyrics_markup', opts)
|
99
170
|
end
|
100
171
|
end
|
101
172
|
end
|
@@ -11,6 +11,27 @@ module Lydown::Rendering
|
|
11
11
|
# do nothing by default
|
12
12
|
end
|
13
13
|
|
14
|
+
def prev_event
|
15
|
+
idx = @idx - 1
|
16
|
+
while idx >= 0
|
17
|
+
e = @stream[idx]
|
18
|
+
return e if e && e[:type] != :comment
|
19
|
+
idx -= 1
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_prev_event(filter)
|
25
|
+
prev = prev_event
|
26
|
+
return nil unless prev
|
27
|
+
|
28
|
+
filter.each do |k, v|
|
29
|
+
return nil unless prev[k] == v
|
30
|
+
end
|
31
|
+
|
32
|
+
prev
|
33
|
+
end
|
34
|
+
|
14
35
|
def next_event
|
15
36
|
idx = @idx + 1
|
16
37
|
while idx < @stream.size
|
@@ -97,5 +97,58 @@ module Lydown::Rendering
|
|
97
97
|
def cmd_partBreak
|
98
98
|
@context.emit(:music, "\\break ") if (@context.render_mode == :part)
|
99
99
|
end
|
100
|
+
|
101
|
+
def cmd_scoreBreak
|
102
|
+
@context.emit(:music, "\\break ") if (@context.render_mode == :score)
|
103
|
+
end
|
104
|
+
|
105
|
+
def cmd_partPageBreak
|
106
|
+
@context.emit(:music, "\\pageBreak ") if (@context.render_mode == :part)
|
107
|
+
end
|
108
|
+
|
109
|
+
def cmd_scorePageBreak
|
110
|
+
@context.emit(:music, "\\pageBreak ") if (@context.render_mode == :score)
|
111
|
+
end
|
112
|
+
|
113
|
+
def transform_slur_arguments(args)
|
114
|
+
case args.size
|
115
|
+
when 4
|
116
|
+
args = args.map do |a|
|
117
|
+
case a
|
118
|
+
when '_'
|
119
|
+
'#f'
|
120
|
+
when /\(([0-9\.\-]*),([0-9\.\-]*)\)/
|
121
|
+
"(#{$1.empty? ? '0' : $1} . #{$2.empty? ? '0' : $2})"
|
122
|
+
else
|
123
|
+
a
|
124
|
+
end
|
125
|
+
end
|
126
|
+
args.join(" ")
|
127
|
+
when 1
|
128
|
+
args[0].gsub(/_/, ' (0 . 0) ').
|
129
|
+
gsub (/\(([0-9\.\-]*),([0-9\.\-]*)\)/) do |m|
|
130
|
+
"(#{$1.empty? ? '0' : $1} . #{$2.empty? ? '0' : $2})"
|
131
|
+
end
|
132
|
+
else
|
133
|
+
raise "Invalid slur shape arguments (#{args.inspect})"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def cmd_slur
|
138
|
+
arguments = transform_slur_arguments(@event[:arguments])
|
139
|
+
@context.emit(:music, "\\sS #'(#{arguments}) ")
|
140
|
+
end
|
141
|
+
|
142
|
+
# height-limit + eccentricity
|
143
|
+
def cmd_sHLE
|
144
|
+
args = @event[:arguments]
|
145
|
+
@context.emit(:music, "\\sHL #{args[0]} \\sE #{args[1]} ")
|
146
|
+
end
|
147
|
+
|
148
|
+
# positions
|
149
|
+
def cmd_sP
|
150
|
+
args = @event[:arguments]
|
151
|
+
@context.emit(:music, "\\sP #{args.join(' ')} ")
|
152
|
+
end
|
100
153
|
end
|
101
154
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module Lydown::Rendering
|
7
|
+
module Layout
|
8
|
+
def self.define_paper_size(layout)
|
9
|
+
return "" unless layout[:paper]
|
10
|
+
|
11
|
+
if layout[:paper] =~ /([^\s]+)\s?(portrait|landscape)?/
|
12
|
+
size = $1
|
13
|
+
orientation = $2 || 'portrait'
|
14
|
+
else
|
15
|
+
size = layout[:paper]
|
16
|
+
orientation = 'portrait'
|
17
|
+
end
|
18
|
+
|
19
|
+
"#(set-paper-size \"#{size.downcase}\" '#{orientation})"
|
20
|
+
end
|
21
|
+
|
22
|
+
POINT_DIV = {
|
23
|
+
mm: 0.352778,
|
24
|
+
cm: 3.52778
|
25
|
+
}
|
26
|
+
|
27
|
+
MEASUREMENT_RE = /^([0-9\-\.]+)(mm|cm)$/
|
28
|
+
|
29
|
+
def self.to_pp(v)
|
30
|
+
if v =~ MEASUREMENT_RE
|
31
|
+
value = $1
|
32
|
+
unit = $2
|
33
|
+
|
34
|
+
factor = POINT_DIV[unit.to_sym]
|
35
|
+
"%.1f" % (value.to_f / factor)
|
36
|
+
else
|
37
|
+
raise "Invalid measurement #{v}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.fmt(v)
|
42
|
+
if v =~ MEASUREMENT_RE
|
43
|
+
"#{$1}\\#{$2}"
|
44
|
+
else
|
45
|
+
raise "Invalid measurement #{v}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.to_v(v)
|
50
|
+
if v =~ MEASUREMENT_RE
|
51
|
+
$1.to_f
|
52
|
+
else
|
53
|
+
raise "Invalid measurement #{v}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# in points
|
58
|
+
def self.staff_space(info)
|
59
|
+
to_v(info[:staff_size]) / 4
|
60
|
+
end
|
61
|
+
|
62
|
+
# calculates inner vertical margins - that is, the space between the defined
|
63
|
+
# actual margin (used for header/footer) and the music/markup.
|
64
|
+
# an array of four values is returned: top-content, bottom-content
|
65
|
+
def self.calculate_vertical_margins(info)
|
66
|
+
ss = staff_space(info)
|
67
|
+
|
68
|
+
dist = lambda do |k|
|
69
|
+
(to_v(info["#{k}_content"]) - to_v(info[k])) / ss
|
70
|
+
end
|
71
|
+
|
72
|
+
staff_dist = lambda {|k| dist[k] + 2}
|
73
|
+
|
74
|
+
[
|
75
|
+
"%.1f" % staff_dist[:margin_top],
|
76
|
+
"%.1f" % dist[:margin_top],
|
77
|
+
"%.1f" % staff_dist[:margin_bottom]
|
78
|
+
]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|