lydown 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +159 -2
  3. data/lib/lydown.rb +8 -2
  4. data/lib/lydown/cache.rb +54 -0
  5. data/lib/lydown/cli.rb +1 -0
  6. data/lib/lydown/cli/commands.rb +27 -9
  7. data/lib/lydown/cli/compiler.rb +218 -54
  8. data/lib/lydown/cli/diff.rb +1 -1
  9. data/lib/lydown/cli/proofing.rb +3 -3
  10. data/lib/lydown/cli/signals.rb +23 -0
  11. data/lib/lydown/cli/support.rb +23 -1
  12. data/lib/lydown/core_ext.rb +41 -5
  13. data/lib/lydown/{rendering/defaults.yml → defaults.yml} +3 -3
  14. data/lib/lydown/errors.rb +3 -0
  15. data/lib/lydown/lilypond.rb +73 -31
  16. data/lib/lydown/ly_lib/lib.ly +297 -0
  17. data/lib/lydown/parsing.rb +1 -2
  18. data/lib/lydown/parsing/lydown.treetop +16 -10
  19. data/lib/lydown/parsing/nodes.rb +29 -5
  20. data/lib/lydown/rendering.rb +32 -6
  21. data/lib/lydown/rendering/command.rb +79 -2
  22. data/lib/lydown/rendering/figures.rb +29 -8
  23. data/lib/lydown/rendering/literal.rb +7 -0
  24. data/lib/lydown/rendering/movement.rb +61 -0
  25. data/lib/lydown/rendering/music.rb +37 -5
  26. data/lib/lydown/rendering/notes.rb +26 -8
  27. data/lib/lydown/rendering/settings.rb +41 -13
  28. data/lib/lydown/rendering/skipping.rb +43 -10
  29. data/lib/lydown/rendering/staff.rb +72 -16
  30. data/lib/lydown/templates.rb +8 -2
  31. data/lib/lydown/templates/lilypond_doc.erb +10 -1
  32. data/lib/lydown/templates/movement.erb +87 -34
  33. data/lib/lydown/templates/multi_voice.erb +1 -1
  34. data/lib/lydown/templates/part.erb +83 -55
  35. data/lib/lydown/templates/variables.erb +38 -0
  36. data/lib/lydown/version.rb +1 -1
  37. data/lib/lydown/work.rb +39 -26
  38. data/lib/lydown/work_context.rb +252 -14
  39. metadata +138 -8
  40. data/lib/lydown/rendering/lib.ly +0 -88
@@ -8,7 +8,9 @@ module Lydown
8
8
 
9
9
  def self.render(name, ctx, locals = {})
10
10
  _binding = ctx.respond_to?(:template_binding) ? ctx.template_binding(locals) : binding
11
- template(name).result(_binding)
11
+
12
+ # remove trailing white space and superfluous new lines
13
+ template(name).result(_binding).gsub(/^\s+$/, '').gsub(/\n+/m, "\n")
12
14
  end
13
15
 
14
16
  def self.template(name)
@@ -16,7 +18,11 @@ module Lydown
16
18
  end
17
19
 
18
20
  def self.load_template(name)
19
- ERB.new IO.read(File.join(TEMPLATES_DIR, "#{name}.erb")), 0, '<>'
21
+ fn = name.is_a?(Symbol) ?
22
+ File.join(TEMPLATES_DIR, "#{name}.erb") :
23
+ name
24
+
25
+ ERB.new IO.read(fn), 0, '<>'
20
26
  end
21
27
  end
22
28
 
@@ -1,20 +1,29 @@
1
1
  \version "2.18.2"
2
2
 
3
- <% unless self['render_opts']['no_lib'] %>
3
+ <% unless self['render_opts/no_lib'] %>
4
4
  \include "<%= File.join(LY_LIB_DIR, 'lib.ly') %>"
5
5
  <% end %>
6
6
 
7
+ <% if self.render_mode == :proof %>
8
+ <%= Lydown::Rendering::PROOFING_LY_SETTINGS %>
9
+ <% end %>
10
+
7
11
  <% if self['layout'] %>
8
12
  \layout {
9
13
  }
10
14
  <% end %>
11
15
 
16
+ <%= Lydown::Templates.render(:variables, self) %>
17
+
12
18
  \book {
13
19
  \header {
14
20
  }
15
21
 
22
+ \bookpart {
16
23
  <% self['movements'].each do |n, m| %>
17
24
  <%= Lydown::Templates.render(:movement, self, name: n, movement: m)
18
25
  %>
19
26
  <% end %>
27
+
28
+ }
20
29
  }
@@ -1,7 +1,7 @@
1
1
  <%
2
- score_mode = self['render_opts/mode'] == :score
2
+ render_mode = self.render_mode
3
3
  staff_groups = Lydown::Rendering::Staff.staff_groups(
4
- self, movement, movement['parts'].keys)
4
+ self, {movement: name}, movement['parts'].keys)
5
5
  parts_in_order = staff_groups.flatten
6
6
  staff_hierarchy = Lydown::Rendering::Staff.staff_hierarchy(staff_groups)
7
7
 
@@ -9,25 +9,62 @@
9
9
  m[p] = movement['parts'][p]
10
10
  m
11
11
  end
12
+
13
+ score_mode = (render_mode == :score) ||
14
+ (render_mode == :proof) || (parts.size > 1)
12
15
 
16
+ tacet = Lydown::Rendering::Movement.tacet?(self, name)
13
17
  movement_title = Lydown::Rendering::Movement.movement_title(self, name)
18
+ movement_source = self.get_setting(:movement_source , movement: name)
19
+
20
+ format = self['options/format'] || self['render_opts/format'] || :pdf
21
+ midi_mode = (format == :midi) || (format == :mp3)
22
+
23
+ empty_staves = self.get_setting(:empty_staves, movement: name)
24
+
25
+ page_breaks = Lydown::Rendering::Movement.page_breaks(self, movement: name)
26
+
27
+ includes = Lydown::Rendering::Movement.include_files(self, movement: name)
28
+
29
+ hide_bar_numbers = Lydown::Rendering::Movement.hide_bar_numbers?(
30
+ self, movement: name
31
+ )
14
32
  %>
15
33
 
16
- \bookpart {
17
- <% if movement_title %>
18
- \header {
19
- piece = \markup {
20
- \column {
21
- \fill-line {\bold \large "<%= movement_title %>"}
22
-
34
+ <% if page_breaks[:blank_page_before] %>
35
+ } \bookpart {
36
+ \markup \column {
37
+ \null \null \null \null \null \null
38
+ \null \null \null \null \null \null
39
+ \null \null \null \null \null \null
40
+ \null \null \null \null \null \null
41
+ \fill-line { "(this page has been left blank to facilitate page turning)" }
42
+ }
43
+ } \bookpart {
44
+ \pageBreak
45
+ <% elsif page_breaks[:before] %>
46
+ } \bookpart {
47
+ <% end %>
48
+
49
+ <% includes.each do |i| %>
50
+ <%= i %>
51
+ <% end %>
52
+
53
+ <% unless tacet %>
54
+ \score {
55
+ <% if movement_title && render_mode != :proof %>
56
+ \header {
57
+ piece = \markup {
58
+ \bold \large { <%= movement_title %> }
59
+ <% if movement_source %>
60
+ \hspace #1 \italic { <%= movement_source %> }
61
+ <% end %>
23
62
  }
24
63
  }
25
- }
26
- <% end %>
64
+ <% end %>
27
65
 
28
- <% if score_mode %>
29
- \score {
30
- <% if self['empty_staves'] == 'hide' %>
66
+ <% if score_mode %>
67
+ <% if empty_staves == 'hide' %>
31
68
  \layout {
32
69
  \context {
33
70
  \RemoveEmptyStaffContext
@@ -35,31 +72,47 @@
35
72
  }
36
73
  }
37
74
  <% end %>
38
-
75
+
39
76
  \new StaffGroup <<
40
77
  \set StaffGroup.systemStartDelimiterHierarchy = <%= staff_hierarchy %>
41
- <% end %>
78
+ <% end %>
42
79
 
43
- <% if n = movement['bar_number'] %>
44
- \set Score.currentBarNumber = #<%= n %>
45
- \set Score.barNumberVisibility = #all-bar-numbers-visible
46
- \bar ""
47
- <% end %>
80
+ <% if n = movement['bar_number'] %>
81
+ \set Score.currentBarNumber = #<%= n %>
82
+ \set Score.barNumberVisibility = #all-bar-numbers-visible
83
+ \bar ""
84
+ <% end %>
48
85
 
49
- <% parts.each do |n, p| %>
50
- <%= Lydown::Templates.render(:part, self,
51
- name: n, part: p, movement: movement) %>
52
- <% end %>
86
+ <% parts.each do |n, p| %>
87
+ <%= Lydown::Templates.render(:part, self,
88
+ name: n, part: p, movement: movement, movement_name: name) %>
89
+ <% end %>
53
90
 
54
- <% if score_mode %>
91
+ <% if score_mode %>
55
92
  >>
56
- <% if self['render_opts']['format'] == 'midi' %>
57
- \midi {
58
- <% if tempo = movement['settings/midi_tempo'] %>
59
- \tempo <%= tempo %>
60
- <% end %>
93
+ <% end %>
94
+ <% if midi_mode %>
95
+ \midi {
96
+ <% if tempo = self.get_setting(:midi_tempo, movement: name) %>
97
+ \tempo <%= tempo %>
98
+ <% end %>
99
+ }
100
+ <% end %>
101
+
102
+ <% if hide_bar_numbers %>
103
+ \layout {
104
+ \context {
105
+ \Score
106
+ \omit BarNumber
61
107
  }
62
- <% end %>
63
108
  }
64
- <% end %>
65
- }
109
+ <% end %>
110
+ }
111
+ <% else # tacet %>
112
+ \markup {
113
+ \line { \bold \large { <%= movement_title %> } }
114
+ \line { \pad-markup #3 " " }
115
+
116
+ }
117
+ <% end %>
118
+ <% if page_breaks[:after] %>\pageBreak <% end %>
@@ -1,5 +1,5 @@
1
1
  <<
2
- <% beaming_mode = Lydown::Rendering::Staff.beaming_mode(part) %>
2
+ <% beaming_mode = Lydown::Rendering::Staff.beaming_mode(self, self.current_setting_opts) %>
3
3
  <% voice_prefix = part.nil? || (part == "") ? nil : "#{part}_" %>
4
4
 
5
5
  <% self['process/voices'].each do |voice, stream| %>
@@ -1,90 +1,118 @@
1
1
  <%
2
- title = nil
2
+ render_mode = self.render_mode
3
+
4
+ setting_opts = {movement: movement_name, part: name}
5
+
6
+ inline_part_title = nil
3
7
  if name && (name != '')
4
- title = Lydown::Rendering.part_title(name)
8
+ part_title = Lydown::Rendering::Staff.part_title(self, setting_opts)
5
9
  voice_prefix = "#{name}_"
6
10
  else
7
- title = nil
11
+ part_title = "#\"\""
8
12
  voice_prefix = nil
9
13
  end
10
- staff_id = "#{title && title.gsub(' ', '')}Staff"
11
-
14
+ staff_id = Lydown::Rendering::Staff.staff_id(name)
12
15
 
13
- score_mode = self['render_opts/mode'] == :score
16
+ score_mode = render_mode == :score
14
17
 
15
- part['settings'] ||= {
16
- 'pickup' => self[:pickup],
17
- 'key' => self[:key],
18
- 'tempo' => self[:tempo]
19
- }.deep!
20
-
21
- clef = Lydown::Rendering::Staff.clef(name)
18
+ clef = Lydown::Rendering::Staff.clef(self, setting_opts)
19
+ prevent_remove_empty = Lydown::Rendering::Staff.prevent_remove_empty(self, setting_opts)
22
20
 
23
- midi_mode = self['render_opts']['format'] == 'midi'
21
+ midi_mode = (self['render_opts/format'] == 'midi') ||
22
+ (self['render_opts/format'] == 'mp3')
24
23
  midi_instrument = midi_mode && Lydown::Rendering::Staff.midi_instrument(name)
25
24
 
26
- beaming_mode = Lydown::Rendering::Staff.beaming_mode(name)
27
- partial = part['settings'][:pickup] ? "\\partial #{part['settings'][:pickup]}" : ""
25
+ beaming_mode = Lydown::Rendering::Staff.beaming_mode(self, setting_opts)
26
+ partial = self.get_setting(:pickup, setting_opts)
27
+ partial = partial ? "\\partial #{partial}" : ""
28
28
 
29
- end_barline = Lydown::Rendering::Staff.end_barline(self, movement)
29
+ end_barline = Lydown::Rendering::Staff.end_barline(self, setting_opts)
30
30
 
31
- cadenza = part['settings'][:time] == 'unmetered'
31
+ time = self.get_setting(:time, setting_opts)
32
+ cadenza = time == 'unmetered'
33
+
34
+ skip_bars = (render_mode == :proof) || (render_mode == :part)
35
+
36
+ instrument_names = self.get_setting(:instrument_names, setting_opts)
37
+ hide_instrument_names =
38
+ (instrument_names == 'hide') ||
39
+ (instrument_names =~ /^inline\-?(.+)?$/)
40
+
41
+ if instrument_names =~ /^inline\-?(.+)?$/
42
+ alignment = $1 && "\\#{$1}"
43
+ inline_part_title = Lydown::Rendering::Staff.inline_part_title(
44
+ self, setting_opts.merge(alignment: alignment)
45
+ )
46
+ end
32
47
  %>
33
48
 
34
49
  <<
35
50
 
36
51
  \new Staff = <%= staff_id %> \with {
52
+ <% if prevent_remove_empty %>
53
+ \override VerticalAxisGroup.remove-empty = ##f
54
+ <% end %>
55
+ <% if size = self.get_setting(:staff_size, setting_opts) %>
56
+ fontSize = #<%= size %>
57
+ \override StaffSymbol.staff-space = #(magstep <%= size %>)
58
+ <% end %>
37
59
  }
38
60
 
39
61
  \context Staff = <%= staff_id %> {
40
- <% if part['settings'][:tempo] %>
41
- \tempo "<%= part['settings'][:tempo] %>"
62
+ <% if skip_bars %>
63
+ \set Score.skipBars = ##t
64
+ <% end %>
65
+
66
+
67
+ <% if tempo = self.get_setting(:tempo, setting_opts) %>
68
+ \tempo "<%= tempo %>"
42
69
  <% end %>
43
70
 
44
- <% if score_mode %>\set Staff.instrumentName = #"<%= title %>"<% end %>
71
+ <% if score_mode && !hide_instrument_names %>
72
+ \set Staff.instrumentName = <%= part_title %>
73
+ <% end %>
45
74
 
46
75
  <% if midi_instrument %>
47
76
  \set Staff.midiInstrument = #"<%= midi_instrument %>"
48
77
  <% end %>
49
78
 
50
- \relative c {
51
- <% if clef %>
52
- \clef "<%= clef %>"
53
- <% end %>
54
- <%= partial %>
55
-
56
- <<
57
- \new Voice = "<%= voice_prefix %>voice1" {
58
- <%= beaming_mode %>
59
- <%= part['music'] %>
60
- }
61
- >>
62
- <% if end_barline %>
63
- \bar "<%= end_barline %>"
64
- <% end %>
65
- }
79
+ <% if clef %>
80
+ \clef "<%= clef %>"
81
+ <% end %>
82
+ <%= partial %>
83
+
84
+ <% if score_mode && inline_part_title %>
85
+ <%= inline_part_title %>
86
+ <% end %>
87
+
88
+ <%= beaming_mode %>
89
+ \<%= Lydown::Rendering.variable_name(setting_opts.merge(stream: :music)) %>
90
+
91
+ <% if end_barline %>
92
+ \bar "<%= end_barline %>"
93
+ <% end %>
66
94
  }
67
95
 
68
- <% if part['lyrics'] %>
69
- <% multi_voice = part['lyrics'].size > 1 %>
70
- <% part['lyrics'].each_key do |voice| %>
71
- <% above_staff = multi_voice && ['voice1', 'voice3'].include?(voice) %>
72
- <% part['lyrics'][voice].keys.sort.each do |idx| %>
73
- \new Lyrics
74
- <% if above_staff %>
75
- \with { alignAboveContext = "<%= staff_id %>" }
76
- <% end %>
77
- {
78
- \lyricsto "<%= voice_prefix %><%= voice %>" {
79
- <%= part['lyrics'][voice][idx] %>
80
- }
81
- }
96
+ <% if part['lyrics'] %>
97
+ <% multi_voice = part['lyrics'].size > 1 %>
98
+ <% part['lyrics'].each_key do |voice| %>
99
+ <% above_staff = multi_voice && ['voice1', 'voice3'].include?(voice) %>
100
+ <% part['lyrics'][voice].keys.sort.each do |idx| %>
101
+ \new Lyrics
102
+ <% if above_staff %>
103
+ \with { alignAboveContext = "<%= staff_id %>" }
82
104
  <% end %>
105
+ {
106
+ \lyricsto "<%= voice_prefix %><%= voice %>" {
107
+ \<%= Lydown::Rendering.variable_name(setting_opts.merge(stream: :lyrics, voice: voice, idx: idx)) %>
108
+ }
109
+ }
83
110
  <% end %>
84
-
85
111
  <% end %>
86
112
 
87
- <% if part['figures'] %>
88
- \figures { <%= part['figures'] %> }
89
- <% end %>
113
+ <% end %>
114
+
115
+ <% if part['figures'] %>
116
+ \new FiguredBass { \<%= Lydown::Rendering.variable_name(setting_opts.merge(stream: :figures)) %> }
117
+ <% end %>
90
118
  >>
@@ -0,0 +1,38 @@
1
+ <% var_name = lambda {|opts| Lydown::Rendering.variable_name(opts)} %>
2
+ <% self['movements'].each do |mvt_name, mvt|
3
+ mvt['parts'].each do |part_name, part|
4
+ var_opts = {movement: mvt_name, part: part_name}
5
+ if part_name && !part_name.empty?
6
+ voice_prefix = "#{part_name}_"
7
+ else
8
+ voice_prefix = nil
9
+ end
10
+ %>
11
+ <%= var_name[var_opts.merge(stream: :music)] %> = \relative c {
12
+ <<
13
+ \new Voice = "<%= voice_prefix %>voice1" {
14
+ <%= part['music'] %>
15
+ }
16
+ >>
17
+ }
18
+
19
+ <% if part['lyrics']
20
+ part['lyrics'].each do |voice, voice_lyrics|
21
+ voice_lyrics.each do |idx, lyrics|
22
+ %>
23
+ <%= var_name[var_opts.merge(stream: :lyrics, voice: voice, idx: idx)]%> = \lyricmode { <%= lyrics %> }
24
+ <%
25
+ end
26
+ end
27
+ end
28
+ %>
29
+ <% if part['figures']
30
+ %>
31
+ <%= var_name[var_opts.merge(stream: :figures)]%> = \figuremode { <%= part['figures'] %> }
32
+ <%
33
+ end
34
+ %>
35
+ <%
36
+ end
37
+ end
38
+ %>
@@ -1,3 +1,3 @@
1
1
  module Lydown
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
data/lib/lydown/work.rb CHANGED
@@ -25,8 +25,8 @@ module Lydown
25
25
  end
26
26
 
27
27
  def to_lilypond(opts = {})
28
- @context['render_opts'] = opts
29
- ly_code = ''
28
+ @context[:render_opts] = opts.stringify_keys
29
+ @context[:variables] = {}
30
30
 
31
31
  if opts[:stream_path]
32
32
  unless @context[opts[:stream_path]]
@@ -34,14 +34,13 @@ module Lydown
34
34
  end
35
35
  @context[opts[:stream_path]].strip
36
36
  else
37
- @original_context = @context
38
- begin
39
- filtered = @context.filter(opts)
40
- filtered.extend(TemplateBinding)
41
- Lydown::Templates.render(:lilypond_doc, filtered)
42
- ensure
43
- @context = @original_context
44
- end
37
+ filtered = @context.filter(opts)
38
+
39
+ # the filtered context is to the template's self
40
+ filtered.extend(TemplateBinding)
41
+
42
+ # Remove empty lines from the rendered code
43
+ Lydown::Templates.render(:lilypond_doc, filtered)
45
44
  end
46
45
  end
47
46
 
@@ -77,11 +76,20 @@ module Lydown
77
76
  DEFAULT_BASENAMES = %w{work movement}
78
77
 
79
78
  def process_directory(path)
79
+ parts_filter = @context[:options][:parts]
80
+ if @context[:options] && @context[:options][:include_parts]
81
+ if parts_filter
82
+ parts_filter += @context[:options][:include_parts]
83
+ else
84
+ parts_filter = @context[:options][:include_parts]
85
+ end
86
+ end
87
+
80
88
  state = {
81
89
  streams: {},
82
90
  movements: Hash.new {|h, k| h[k] = {}},
83
91
  current_movement: nil,
84
- part_filter: @context[:options][:parts],
92
+ part_filter: parts_filter,
85
93
  mvmt_filter: @context[:options][:movements]
86
94
  }
87
95
 
@@ -110,7 +118,9 @@ module Lydown
110
118
  state[:movements][state[:current_movement]][part] = entry
111
119
  end
112
120
  elsif File.directory?(entry) && recursive
121
+ # handle movement subdirectory
113
122
  movement = File.basename(entry)
123
+ state[:movements][movement] ||= {}.deep!
114
124
  unless skip_movement?(movement, state)
115
125
  state[:current_movement] = movement
116
126
  read_directory(entry, false, state)
@@ -119,8 +129,7 @@ module Lydown
119
129
  end
120
130
 
121
131
  def skip_part?(part, state)
122
- DEFAULT_BASENAMES.include?(part) ||
123
- state[:part_filter] && !state[:part_filter].include?(part)
132
+ DEFAULT_BASENAMES.include?(part)
124
133
  end
125
134
 
126
135
  def skip_movement?(mvmt, state)
@@ -136,7 +145,7 @@ module Lydown
136
145
 
137
146
  PARALLEL_PROCESS_OPTIONS = {
138
147
  progress: {
139
- title: 'Process',
148
+ title: 'Render',
140
149
  format: Lydown::CLI::PROGRESS_FORMAT
141
150
  }
142
151
  }
@@ -145,15 +154,17 @@ module Lydown
145
154
  streams = state[:streams]
146
155
  proof_mode = @context['options/proof_mode']
147
156
  paths = streams.keys
148
-
149
- processed_streams = Parallel.map(paths, PARALLEL_PARSE_OPTIONS) do |path|
157
+
158
+ processed_streams = Parallel.map(paths, PARALLEL_PARSE_OPTIONS.clone) do |path|
150
159
  content = IO.read(path)
151
- LydownParser.parse(content, {
152
- filename: File.expand_path(path),
153
- source: content,
154
- proof_mode: proof_mode,
155
- no_progress: true
156
- })
160
+ Cache.hit(content) do
161
+ LydownParser.parse(content, {
162
+ filename: File.expand_path(path),
163
+ source: content,
164
+ proof_mode: proof_mode,
165
+ no_progress: true
166
+ })
167
+ end
157
168
  end
158
169
  processed_streams.each_with_index {|s, idx| streams[paths[idx]] = s}
159
170
  end
@@ -169,12 +180,14 @@ module Lydown
169
180
 
170
181
  def translate_movement_files(state)
171
182
  stream_entries = prepare_work_stream_array(state)
172
- processed_contexts = Parallel.map(stream_entries, PARALLEL_PROCESS_OPTIONS) do |entry|
183
+ processed_contexts = Parallel.map(stream_entries, PARALLEL_PROCESS_OPTIONS.clone) do |entry|
173
184
  mvmt_stream, stream = *entry
174
185
  ctx = @context.clone_for_translation
175
- ctx.translate(mvmt_stream)
176
- ctx.translate(stream)
177
- ctx
186
+ Cache.hit(ctx, mvmt_stream, stream) do
187
+ ctx.translate(mvmt_stream)
188
+ ctx.translate(stream)
189
+ ctx
190
+ end
178
191
  end
179
192
 
180
193
  processed_contexts.each {|ctx| @context.merge_movements(ctx)}