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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f12383235250037392916c2a69ff03fe5f366100
4
- data.tar.gz: cf26fbac37decc03b9a271e4f3dc50340e7cb4a6
3
+ metadata.gz: e53564b3a5ed77a00ed654832969bb2002936153
4
+ data.tar.gz: 306631f5b426ae1ae60ce2e078e20496f2220dad
5
5
  SHA512:
6
- metadata.gz: fe50eac747f03ee7afd072b8fccaab45664e6503f76a6413d40044f2e86a233ae5168ffd9d4b9e4bf8b44c3d4736a1a64bbc629de943ec1445ec2002527b4f95
7
- data.tar.gz: fe3e4da8045b8b7aca9ba8e287568a8a1f9e5081af05901475654b8e5135aca4271aee51f87d79dbe6d577f2269301cb921809f6678c912773d2a931c957fa87
6
+ metadata.gz: 0c7bd65c2523aab457281ca7626ee0b22402cb3c58a361b9d325f9e63afea6ee8c47a9672a18c206d0c7f0cc718d88e2eb92d3a03bbdf8edab17ff7ceaa11b99
7
+ data.tar.gz: 6284022bcc43a640c6fc099dc92bc21675938ab186b9bc63fc8272c17afd32af1ffe996b03184743348c8cf959e84760b633621a2b5bfc43423470fb9052ac20
data/README.md CHANGED
@@ -12,8 +12,10 @@ Lydown is a language and compiler for creating music scores, parts and snippets.
12
12
  - [The lydown syntax](#the-lydown-syntax)
13
13
  - [Notes and durations](#notes-and-durations)
14
14
  - [Accidentals](#accidentals)
15
- - [Octaves](#otaves)
15
+ - [Octaves](#octaves)
16
+ - [Grace notes](#grace-notes)
16
17
  - [Barlines](#barlines)
18
+ - [Repeats with alternatives](#repeats-with-alternatives)
17
19
  - [Rests](#rests)
18
20
  - [Beams, slurs and ties](#beams-slurs-and-ties)
19
21
  - [Articulation and expression marks](#articulation-and-expression-marks)
@@ -279,6 +281,23 @@ c''' // third octave
279
281
  For the following notes, the octave markers <code>'</code> and <code>,</code> are used for jumping between octaves, using the same rules as
280
282
  lilypond's [relative mode](http://www.lilypond.org/doc/v2.18/Documentation/notation/writing-pitches#relative-octave-entry):
281
283
 
284
+ ### Grace notes
285
+
286
+ Grace notes are signified by appending a grace kind after the the duration value:
287
+
288
+ ```lydown
289
+ // grace
290
+ 6°d8edcd2c
291
+
292
+ // appoggiatura
293
+ 6^d8cbag
294
+
295
+ // acciaccatura
296
+ 6`dc8bcde
297
+ ```
298
+
299
+ The grace holds until the next change of duration.
300
+
282
301
  ### Barlines
283
302
 
284
303
  Just like in lilypond, barlines are taken care of automatically according to the time signature. Final bar lines and repeat bar lines can be entered explicitly by using shorthand syntax:
@@ -294,6 +313,19 @@ When entering unmetered music, an invisible barline can be added in order to pro
294
313
  cdef ?| gag
295
314
  ```
296
315
 
316
+ ### Repeats with alternatives
317
+
318
+ Repeats with alternatives can be written by using the following syntax:
319
+
320
+ ```lydown
321
+ |:*2 // start a repeat
322
+ |* // start a volta
323
+ *| // end the repeat
324
+
325
+ // example: 2 repeats
326
+ |:*2 1c d |* e f |* g a *| b c
327
+ ```
328
+
297
329
  ### Rests and silences
298
330
 
299
331
  Normal rests are written [like in lilypond](http://www.lilypond.org/doc/v2.18/Documentation/notation/writing-rests#rests):
@@ -814,6 +846,14 @@ To place includes at the beginning of the lilypond document, includes should be
814
846
 
815
847
  Pathnames are relative to the lydown source file.
816
848
 
849
+ ## Requiring packages
850
+
851
+ [Lyp](https://github.com/noteflakes/lyp) packages can be loaded by using `-include` settings, usually in `work.ld`:
852
+
853
+ ```lydown
854
+ - require: better-slurs
855
+ ```
856
+
817
857
  ### Including templates
818
858
 
819
859
  In addition to including normal lilypond files, Lydown also supports the rendering of templates into the generated lilypond doc, by using the <code>.ely</code> extension. The templates should be written using Ruby's [ERB](http://www.stuartellis.eu/articles/erb/) syntax, and are supplied with the lydown context object as <code>self</code>. A trivial example:
data/bin/lydown CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ $LOAD_PATH.unshift File.expand_path('../lib', File.dirname(__FILE__))
4
+
3
5
  require 'lydown/cli'
4
6
 
5
7
  if ARGV.empty?
data/lib/lydown.rb CHANGED
@@ -1,15 +1,19 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
2
+
1
3
  require 'lydown/core_ext'
2
4
  require 'lydown/cache'
3
5
 
4
6
  module Lydown
5
7
  require 'yaml'
6
- DEFAULTS = YAML.load(IO.read(File.join(File.dirname(__FILE__),
7
- 'lydown/defaults.yml'))).deep!
8
+
9
+ DEFAUTLS_FILENAME = File.expand_path('lydown/defaults.yml', File.dirname(__FILE__))
10
+ DEFAULTS = YAML.load(IO.read(DEFAUTLS_FILENAME)).deep!
8
11
  end
9
12
 
10
13
  require 'lydown/errors'
11
14
  require 'lydown/parsing'
12
15
  require 'lydown/templates'
16
+ require 'lydown/inverso'
13
17
  require 'lydown/rendering'
14
18
  require 'lydown/lilypond'
15
19
  require 'lydown/work_context'
data/lib/lydown/cache.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'digest/md5'
2
+ require 'fileutils'
2
3
 
3
4
  module Cache
4
5
  class << self
@@ -46,8 +47,11 @@ module Cache
46
47
  end
47
48
  end
48
49
 
50
+ CACHE_DIR = "/tmp/lydown/cache"
51
+ FileUtils.mkdir_p(CACHE_DIR)
52
+
49
53
  def filename(cache_key)
50
- "/tmp/lydown-cache-#{cache_key}"
54
+ "#{CACHE_DIR}/#{cache_key}"
51
55
  end
52
56
  end
53
57
  end
data/lib/lydown/cli.rb CHANGED
@@ -10,6 +10,6 @@ require 'lydown/cli/diff'
10
10
  require 'lydown/cli/translation'
11
11
  require 'lydown/cli/output'
12
12
  require 'lydown/cli/repl'
13
- require 'lydown/cli/installer'
13
+ require 'lydown/lilypond'
14
14
  require 'lydown/cli/commands'
15
15
  require 'lydown/cli/signals'
@@ -2,12 +2,16 @@ require 'thor'
2
2
 
3
3
  module Lydown::CLI
4
4
  class Commands < Thor
5
+ package_name "lydown"
6
+ map "-v" => :version
7
+ class_option :verbose, aliases: '-V', :type => :boolean, desc: 'show verbose output'
8
+
5
9
  desc "version", "show Lydown version"
6
10
  def version
7
11
  require 'lydown/version'
8
12
  $stderr.puts "Lydown version #{Lydown::VERSION}"
9
13
 
10
- lilypond_version = Lydown::CLI::Support.detect_lilypond_version(false)
14
+ lilypond_version = Lydown::Lilypond.detect_lilypond_version(false)
11
15
  if lilypond_version
12
16
  $stderr.puts "Lilypond version #{lilypond_version}"
13
17
  end
@@ -37,7 +41,8 @@ module Lydown::CLI
37
41
  desc: 'Open output file after compilation'
38
42
  method_option :separate, type: :boolean, aliases: '-S',
39
43
  desc: 'Create separate file for each movement'
40
- method_option :verbose, type: :boolean
44
+ method_option :verbose, aliases: '-V', type: :boolean
45
+ method_option :edition, aliases: '-e', type: :string
41
46
  def compile(*args)
42
47
  path = args.first || '.'
43
48
 
@@ -46,8 +51,6 @@ module Lydown::CLI
46
51
  return send($1)
47
52
  end
48
53
 
49
- Lydown::CLI::Support.detect_lilypond_version(true)
50
-
51
54
  require 'lydown'
52
55
 
53
56
  opts = Lydown::CLI::Support.copy_options(options)
@@ -88,8 +91,6 @@ module Lydown::CLI
88
91
  enum: %w{pdf png ly}
89
92
  method_option :include_parts, aliases: '-i', desc: 'Include parts (comma separated)'
90
93
  def proof(*args)
91
- Lydown::CLI::Support.detect_lilypond_version(true)
92
-
93
94
  require 'lydown'
94
95
 
95
96
  opts = Lydown::CLI::Support.copy_options(options)
@@ -115,11 +116,6 @@ module Lydown::CLI
115
116
  Lydown::CLI::Translation.process(opts)
116
117
  end
117
118
 
118
- desc "install [PACKAGE] [VERSION]", "install a package"
119
- def install(*args)
120
- Lydown::CLI::Installer.install(*args)
121
- end
122
-
123
119
  def method_missing(method, *args)
124
120
  args = ["compile", method.to_s] + args
125
121
  self.class.start(args)
@@ -165,6 +165,11 @@ module Lydown::CLI::Compiler
165
165
 
166
166
  def output_filename(opts)
167
167
  fn = opts[:output_filename].dup
168
+
169
+ if opts[:edition]
170
+ fn << "-#{opts[:edition]}"
171
+ end
172
+
168
173
  if opts[:movements] && opts[:movements].size == 1
169
174
  fn << "-#{opts[:movements].first}"
170
175
  end
@@ -3,7 +3,7 @@ require 'directory_watcher'
3
3
  module Lydown::CLI::Proofing
4
4
  class << self
5
5
  def start_proofing(opts)
6
- $stderr.puts "Proof mode: #{source} -> #{opts[:output_filename]}"
6
+ $stderr.puts "Proof mode: #{opts[:source_filename]} -> #{opts[:output_filename]}"
7
7
 
8
8
  trap("INT") {return}
9
9
  watch(opts)
@@ -65,7 +65,7 @@ module Lydown::CLI::Proofing
65
65
  if dir =~ /^\.\/(.+)$/
66
66
  dir = $1
67
67
  end
68
- if dir =~ /#{base}\/([^\/]+)/
68
+ if File.expand_path(dir) =~ /#{base}\/([^\/]+)/
69
69
  mvt = $1
70
70
  else
71
71
  mvt = nil
@@ -9,7 +9,7 @@ module Lydown::CLI::REPL
9
9
  require 'lydown/version'
10
10
  puts "Lydown version #{Lydown::VERSION}"
11
11
 
12
- lilypond_version = Lydown::CLI::Support.detect_lilypond_version(true)
12
+ lilypond_version = Lydown::Lilypond.detect_lilypond_version(true)
13
13
  if lilypond_version
14
14
  puts "Lilypond version #{lilypond_version}"
15
15
  end
@@ -49,39 +49,6 @@ module Lydown::CLI::Support
49
49
  opts[:path] = parent_dir
50
50
  end
51
51
  end
52
-
53
- MINIMAL_LILYPOND_VERSION = Gem::Version.new('2.18')
54
-
55
- # detect the lilypond version. If lilypond is not found, or the version is
56
- # less than the minimal supported version, display an error message.
57
- def self.detect_lilypond_version(exit_on_error)
58
- msg = `lilypond --version`
59
- version = nil
60
- if msg.lines.first =~ /LilyPond ([\d\.]+)/
61
- version = $1
62
- end
63
- unless version && Gem::Version.new(version) >= MINIMAL_LILYPOND_VERSION
64
- display_lilypond_version_error_msg(version)
65
- exit!(1) if exit_on_error
66
- version = nil
67
- end
68
- version
69
- rescue => e
70
- display_lilypond_version_error_msg(nil)
71
- exit!(1) if exit_on_error
72
- end
73
-
74
- def self.display_lilypond_version_error_msg(version)
75
- if version
76
- STDERR.puts "ERROR: The installed lilypond (version #{version}) is too old.
77
- You can install lilypond by running `lydown install lilypond` or by
78
- downloading a recent version from http://lilypond.org/"
79
- else
80
- STDERR.puts "ERROR: No copy of lilypond found.
81
- You can install lilypond by running `lydown install lilypond` or by
82
- downloading a recent version from http://lilypond.org/"
83
- end
84
- end
85
52
  end
86
53
 
87
54
 
@@ -1,8 +1,15 @@
1
- ly_dir: ./_ly
2
- pdf_dir: ./_pdf
3
- midi_dir: ./_midi
4
- lilypond_cmd: lilypond
1
+ layout:
2
+ paper: A4 portrait
3
+ staff_size: 6mm
4
+ margin_inner: 12mm
5
+ margin_outer: 18mm
6
+ margin_top: 10mm
7
+ margin_bottom: 10mm
8
+ margin_top_content: 24mm
9
+ margin_bottom_content: 24mm
5
10
  parts:
11
+ layout:
12
+ staff_size: 7mm
6
13
  soprano:
7
14
  clef: treble
8
15
  beaming: manual
@@ -102,6 +109,12 @@ parts:
102
109
  violino2:
103
110
  clef: treble
104
111
  midi_instrument: violin
112
+ violon1:
113
+ clef: treble
114
+ midi_instrument: violin
115
+ violon2:
116
+ clef: treble
117
+ midi_instrument: violin
105
118
  violini:
106
119
  clef: treble
107
120
  midi_instrument: violin
@@ -132,6 +145,15 @@ parts:
132
145
  gamba2:
133
146
  clef: alto
134
147
  midi_instrument: viola
148
+ viole:
149
+ clef: alto
150
+ midi_instrument: viola
151
+ viole1:
152
+ clef: alto
153
+ midi_instrument: viola
154
+ viole2:
155
+ clef: alto
156
+ midi_instrument: viola
135
157
  fagott:
136
158
  clef: bass
137
159
  midi_instrument: bassoon
@@ -176,13 +198,33 @@ parts:
176
198
  midi_instrument: flute
177
199
  flute2:
178
200
  midi_instrument: flute
201
+ trumpet:
202
+ midi_instrument: trumpet
203
+ tromba:
204
+ midi_instrument: trumpet
205
+ trumpet1:
206
+ midi_instrument: trumpet
207
+ trumpet2:
208
+ midi_instrument: trumpet
209
+ trumpet3:
210
+ midi_instrument: trumpet
211
+ tromba1:
212
+ midi_instrument: trumpet
213
+ tromba2:
214
+ midi_instrument: trumpet
215
+ tromba3:
216
+ midi_instrument: trumpet
217
+
179
218
  score:
219
+ layout:
220
+ staff_size: 4.8mm
180
221
  order:
222
+ - [trumpet, tromba, trumpet1, trumpet2, trumpet3, tromba1, tromba2, tromba3]
181
223
  - [flute, flauto1, flauto2, flauto3, flute1, flute2, flute3]
182
224
  - [oboe, oboe1, oboe2, oboe3]
183
- - [violino, violino1, violino2, violino3, violin1, violin2, violin3]
184
- - viola
185
- - [gamba, gamba1, gamba2]
225
+ - [violino, violino1, violino2, violino3, violin1, violin2, violin3, viola, violon1, violon2]
226
+ - [viola1, viola2]
227
+ - [gamba, viole, gamba1, gamba2, viole1, viole2]
186
228
  - [soprano1, soprano2, soprano, alto1, alto2, alto, tenore1, tenore2, tenore, basso1, basso2, basso]
187
- - continuo
229
+ - [fagotto, continuo, "basse-continue"]
188
230
  end_barline: "|."
@@ -0,0 +1,84 @@
1
+ module Inverso
2
+ class Template
3
+ EMIT_DELIMITER = '`'.freeze
4
+ EMIT_RE = /#{EMIT_DELIMITER}(((?!#{EMIT_DELIMITER}).)*)#{EMIT_DELIMITER}/m
5
+
6
+ INTERPOLATION_START = "{{".freeze
7
+ INTERPOLATION_END = "}}".freeze
8
+ INTERPOLATION_RE = /#{INTERPOLATION_START}([^\?\/](?:(?!#{INTERPOLATION_END}).)*)#{INTERPOLATION_END}/m
9
+
10
+ IF_START = "{{?".freeze
11
+ IF_END = "{{/}}".freeze
12
+ IF_RE = /\{\{\?((?:(?!#{INTERPOLATION_END}).)*)#{INTERPOLATION_END}((?:(?!#{IF_END}).)*)#{IF_END}/m
13
+
14
+ ESCAPED_QUOTE = '\\"'.freeze
15
+ QUOTE = '"'.freeze
16
+
17
+ # From the metaid gem
18
+ def metaclass; class << self; self; end; end
19
+
20
+ def initialize(templ)
21
+ templ = templ.gsub(EMIT_RE) {|m| convert_literal($1)}
22
+ method_str = <<EOF
23
+ define_method(:render) do |_ = {}, env = {}|
24
+ _.each {|k, v| metaclass.instance_eval {define_method(k) {v}}}
25
+ __buffer__ = env[:buffer] ||= ''
26
+ __emit__ = env[:emit] ||= lambda {|*args| args.each {|s| __buffer__ << s}}
27
+ __render_l__ = env[:render] ||= lambda {|n, o| Template.render(n, o, env)}
28
+ metaclass.instance_eval "define_method(:__render__) {|n, o| __render_l__[n, o]}"
29
+ begin
30
+ #{templ}
31
+ end
32
+ __buffer__.gsub(/^\s+$/, '').gsub(/\n+/m, "\n")
33
+ end
34
+ EOF
35
+
36
+ metaclass.instance_eval method_str
37
+ end
38
+
39
+ def convert_interpolation(s)
40
+ s.gsub(INTERPOLATION_RE) do
41
+ code = $1.gsub(ESCAPED_QUOTE, QUOTE)
42
+ "\#{#{code}}"
43
+ end
44
+ end
45
+
46
+ def convert_literal(s)
47
+ # look for interpolated values, wrap them with #{}
48
+ s = s.inspect.gsub(INTERPOLATION_RE) do
49
+ code = $1.gsub(ESCAPED_QUOTE, QUOTE)
50
+ "\#{#{code}}"
51
+ end
52
+
53
+ s = s.gsub(IF_RE) do
54
+ test, code = $1, $2
55
+ test = test.strip.gsub(ESCAPED_QUOTE, QUOTE)
56
+ "\#\{if #{test}; \"#{code}\"; end}"
57
+ end
58
+
59
+ "__emit__[#{s}]"
60
+ end
61
+
62
+ # Global template registry
63
+ @@templates = {}
64
+
65
+ def self.load_templates(path)
66
+ Dir["#{path}/*.rb"].each do |fn|
67
+ File.basename(fn) =~ /^(.+)\.rb$/
68
+ name = $1
69
+ set(name, IO.read(fn))
70
+ end
71
+ end
72
+
73
+ def self.set(name, templ)
74
+ @@templates[name.to_sym] = new(templ)
75
+ end
76
+
77
+ def self.render(name, arg = {}, env = {})
78
+ raise unless @@templates[name.to_sym]
79
+ @@templates[name.to_sym].render(arg, env)
80
+ end
81
+ end
82
+ end
83
+
84
+ Inverso::Template.load_templates(File.expand_path('templates', File.dirname(__FILE__)))