lydown 0.12.4 → 0.14.0

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