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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -1
  3. data/bin/lydown +2 -0
  4. data/lib/lydown.rb +6 -2
  5. data/lib/lydown/cache.rb +5 -1
  6. data/lib/lydown/cli.rb +1 -1
  7. data/lib/lydown/cli/commands.rb +7 -11
  8. data/lib/lydown/cli/compiler.rb +5 -0
  9. data/lib/lydown/cli/proofing.rb +2 -2
  10. data/lib/lydown/cli/repl.rb +1 -1
  11. data/lib/lydown/cli/support.rb +0 -33
  12. data/lib/lydown/defaults.yml +50 -8
  13. data/lib/lydown/inverso.rb +84 -0
  14. data/lib/lydown/lilypond.rb +76 -10
  15. data/lib/lydown/ly_lib/lib.ly +140 -127
  16. data/lib/lydown/parsing/lydown.treetop +25 -12
  17. data/lib/lydown/parsing/nodes.rb +55 -19
  18. data/lib/lydown/rendering.rb +72 -1
  19. data/lib/lydown/rendering/base.rb +21 -0
  20. data/lib/lydown/rendering/command.rb +53 -0
  21. data/lib/lydown/rendering/layout.rb +83 -0
  22. data/lib/lydown/rendering/lyrics.rb +1 -1
  23. data/lib/lydown/rendering/markup.rb +23 -0
  24. data/lib/lydown/rendering/movement.rb +7 -4
  25. data/lib/lydown/rendering/music.rb +35 -29
  26. data/lib/lydown/rendering/notes.rb +75 -41
  27. data/lib/lydown/rendering/repeats.rb +27 -0
  28. data/lib/lydown/rendering/settings.rb +36 -9
  29. data/lib/lydown/rendering/skipping.rb +10 -2
  30. data/lib/lydown/rendering/staff.rb +38 -31
  31. data/lib/lydown/rendering/voices.rb +1 -1
  32. data/lib/lydown/templates.rb +8 -8
  33. data/lib/lydown/templates/layout.rb +40 -0
  34. data/lib/lydown/templates/lilypond_doc.rb +95 -0
  35. data/lib/lydown/templates/movement.rb +188 -0
  36. data/lib/lydown/templates/multi_voice.rb +25 -0
  37. data/lib/lydown/templates/part.rb +146 -0
  38. data/lib/lydown/templates/variables.rb +43 -0
  39. data/lib/lydown/translation/ripple.rb +1 -1
  40. data/lib/lydown/translation/ripple/nodes.rb +51 -2
  41. data/lib/lydown/translation/ripple/ripple.treetop +87 -10
  42. data/lib/lydown/version.rb +1 -1
  43. data/lib/lydown/work.rb +19 -2
  44. data/lib/lydown/work_context.rb +10 -2
  45. metadata +12 -8
  46. data/lib/lydown/cli/installer.rb +0 -175
  47. data/lib/lydown/templates/lilypond_doc.erb +0 -34
  48. data/lib/lydown/templates/movement.erb +0 -118
  49. data/lib/lydown/templates/multi_voice.erb +0 -16
  50. data/lib/lydown/templates/part.erb +0 -118
  51. 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 / barline / source_ref /
50
- grace_duration / duration / standalone_figures / chord / note / rest /
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>
@@ -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
- stream << event_hash(stream, opts, {
108
- type: :duration, value: text_value
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
@@ -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
+