lydown 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +357 -0
- data/bin/lydown +149 -0
- data/lib/lydown/core_ext.rb +121 -0
- data/lib/lydown/errors.rb +2 -0
- data/lib/lydown/lilypond.rb +41 -0
- data/lib/lydown/parsing/lydown.treetop +160 -0
- data/lib/lydown/parsing/nodes.rb +191 -0
- data/lib/lydown/parsing.rb +33 -0
- data/lib/lydown/rendering/base.rb +24 -0
- data/lib/lydown/rendering/comments.rb +7 -0
- data/lib/lydown/rendering/defaults.yml +185 -0
- data/lib/lydown/rendering/figures.rb +106 -0
- data/lib/lydown/rendering/lyrics.rb +14 -0
- data/lib/lydown/rendering/movement.rb +19 -0
- data/lib/lydown/rendering/music.rb +362 -0
- data/lib/lydown/rendering/settings.rb +107 -0
- data/lib/lydown/rendering/staff.rb +83 -0
- data/lib/lydown/rendering.rb +45 -0
- data/lib/lydown/templates/lilypond_doc.erb +15 -0
- data/lib/lydown/templates/movement.erb +48 -0
- data/lib/lydown/templates/part.erb +55 -0
- data/lib/lydown/templates.rb +22 -0
- data/lib/lydown/version.rb +3 -0
- data/lib/lydown/work.rb +192 -0
- data/lib/lydown.rb +11 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 66935404398a57aa09da31ebddd911edae7227fe
|
4
|
+
data.tar.gz: 6bc6875c3b2e2187140a91bb586b53411aeb57c3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 28dcdb1e4501356d371f3ff217af806f966a37fe24611fe7f33426ec264d80b197fc609441fef6953b3929d9239436289950e05983d0bbf15f8fc58770663392
|
7
|
+
data.tar.gz: 36a98eb309977703065fb6feb0dc5284b2f78d7b241f40606f0098087980e70415258d9df13d30b06854ac40b92a93fec84bdad93cb6fe9acaea565d3d8e76c0
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Sharon Rosner
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,357 @@
|
|
1
|
+
Lydown is a language and compiler for creating music scores, parts and snippets. The lydown code is compiled to [lilypond](http://lilypond.org/) code and then compiled to PDF, PNG or MIDI files.
|
2
|
+
|
3
|
+
Lydown builds on the ideas put forth by lilypond and makes the following improvements:
|
4
|
+
|
5
|
+
- a greatly simplified syntax for entering notes, for more rapid note entry and improved legibility.
|
6
|
+
- ability to enter lyrics and bass figures interspersed with the music.
|
7
|
+
- rhythmic macros for rapid entry of repeated rhythmic patterns.
|
8
|
+
- zero lilypond boilerplate code.
|
9
|
+
- a sane file/folder structure for creating multi-part, multi-movement works with automatic part extraction.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Before installing lydown, you'll need to have installed [lilypond](http://lilypond.org/download.html).
|
14
|
+
|
15
|
+
You can verify that lilypond is correctly installed by running the following command:
|
16
|
+
|
17
|
+
lilypond --version
|
18
|
+
|
19
|
+
If everything's ok, you can proceed by installing lydown:
|
20
|
+
|
21
|
+
gem install lydown
|
22
|
+
|
23
|
+
and verifying that it too works:
|
24
|
+
|
25
|
+
lydown --version
|
26
|
+
|
27
|
+
## Hello world in lydown
|
28
|
+
|
29
|
+
// helloworld.lydown
|
30
|
+
- key: d major
|
31
|
+
- time: 2/4
|
32
|
+
4d\"Hello world!" 6c#bag 3f#gag6f#d 4e
|
33
|
+
|
34
|
+
And here's the equivalent lilypond code:
|
35
|
+
|
36
|
+
\version "2.18.2"
|
37
|
+
relative c' {
|
38
|
+
\key d major
|
39
|
+
\time 2/4
|
40
|
+
d'4\"Hello world!" cs16 b a g fs32 g a g fs16 d e4
|
41
|
+
}
|
42
|
+
|
43
|
+
## Compiling the lydown code
|
44
|
+
|
45
|
+
The lydown command line tool can compile the code into lilypond code, PDF, PNG, or MIDI. The program creates an output file with the same name as the input file and the corresponding extension. Specifiying the -O switch causes the output to be opened immediately.
|
46
|
+
|
47
|
+
To create a lilypond file:
|
48
|
+
|
49
|
+
lydown -O --ly helloworld.lydown
|
50
|
+
|
51
|
+
To create a PDF file:
|
52
|
+
|
53
|
+
lydown -O --pdf helloworld.lydown
|
54
|
+
|
55
|
+
To create a PNG file:
|
56
|
+
|
57
|
+
lydown -O --png helloworld.lydown
|
58
|
+
|
59
|
+
To create a MIDI file:
|
60
|
+
|
61
|
+
lydown -O --midi helloworld.lydown
|
62
|
+
|
63
|
+
## The lydown syntax
|
64
|
+
|
65
|
+
The lydown syntax is designed for faster note entry, better legibility and minimal boilerplate. The lydown syntax takes the basic ideas put forth by lilypond and simplifies them, with the following main differences:
|
66
|
+
|
67
|
+
- musical context (staves, parts, etc) is implicit rather than explicit.
|
68
|
+
- whitespace between notes is optional.
|
69
|
+
- durations come before notes.
|
70
|
+
- duration macros allow rapid entry of repeated rhythmic patterns (such as dotted rhythm).
|
71
|
+
|
72
|
+
It must be stressed that lydown is made to process music that is relatively simple, and it was designed to support the processing of baroque music in particular. Therefore, a lot of stuff that is possible with plain lilypond would not be possible with lydown.
|
73
|
+
|
74
|
+
For the sake of this tutorial, some familiarity with the concepts and syntax of lilypond is presumed. The lydown code will be shown alongside its lilypond equivalent.
|
75
|
+
|
76
|
+
### Notes and durations
|
77
|
+
|
78
|
+
In lydown, durations are entered before the note to which they refer, and they stay valid for subsequent notes, until a new value is entered:
|
79
|
+
|
80
|
+
4c8de2f => c4 d8 e f2
|
81
|
+
|
82
|
+
In addition to the usual values (1, 2, 4, 8, 16, 32 etc), lydown adds two shortcuts for commonly used values: 6 for 16th notes and 3 for 32th notes:
|
83
|
+
|
84
|
+
8c6de3fefefede2f => c8 d16 e f32 e f e f e d e f2
|
85
|
+
|
86
|
+
Augmentation dots are entered like in lilypond:
|
87
|
+
|
88
|
+
8.c6d 8.e6f 2g => c8. d16 e8. f16 g2
|
89
|
+
8..g 3g 4c => g8.. g32 c4
|
90
|
+
|
91
|
+
### Rests
|
92
|
+
|
93
|
+
Normal rests are written [like in lilypond](http://www.lilypond.org/doc/v2.18/Documentation/notation/writing-rests#rests):
|
94
|
+
|
95
|
+
4ce2r => c4 e r2
|
96
|
+
|
97
|
+
Full bar rests are [similar to lilypond](http://www.lilypond.org/doc/v2.18/Documentation/notation/writing-rests#full-measure-rests), except there's no need to enter the rest value (it is implicit in the time signature):
|
98
|
+
|
99
|
+
- time: 3/4
|
100
|
+
// 4 bar rest in the middle
|
101
|
+
2c4e R*4 2.g
|
102
|
+
|
103
|
+
### Accidentals
|
104
|
+
|
105
|
+
Accidentals are entered using + and -
|
106
|
+
|
107
|
+
8cgb-c2a => c8 g bb c a2
|
108
|
+
|
109
|
+
In lydown notes follow the key signature by default:
|
110
|
+
|
111
|
+
- key: g major
|
112
|
+
8g6fedcba2g => g8 fs16 e d c b a g2
|
113
|
+
|
114
|
+
The accidental mode can be changed by specifiying the manual accidental mode:
|
115
|
+
|
116
|
+
- key: g major
|
117
|
+
- accidentals: manual
|
118
|
+
8g6f+edcba2g
|
119
|
+
|
120
|
+
In the default, automatic mode, when deviating from the key signature the accidental must be repeated for each note, regardless of barlines.
|
121
|
+
|
122
|
+
In the [same manner as lilypond](http://www.lilypond.org/doc/v2.18/Documentation/notation/writing-pitches#accidentals), accidentals can be forced by following the note name and accidental with a ! (for a reminder), or ? (for a cautionary accidental in parentheses):
|
123
|
+
|
124
|
+
cc+c+!cc? => c cs cs! c c?
|
125
|
+
|
126
|
+
A ficta accidental (an non-original accidental that appears above the staff) can be entered using the ^ symbol after the accidental:
|
127
|
+
|
128
|
+
cdef+^g
|
129
|
+
|
130
|
+
### Octaves
|
131
|
+
|
132
|
+
Like in lilypond's [relative mode](http://www.lilypond.org/doc/v2.18/Documentation/notation/writing-pitches#relative-octave-entry), lydown uses ' and , for moving between octaves. The starting point is always c (that is c°).
|
133
|
+
|
134
|
+
### Barlines
|
135
|
+
|
136
|
+
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:
|
137
|
+
|
138
|
+
|: cege :|: cfaf :|
|
139
|
+
|
140
|
+
When entering unmetered music, an invisible barline can be added in order to provide line breaks:
|
141
|
+
|
142
|
+
-time: unmetered
|
143
|
+
cdef ?| gag
|
144
|
+
|
145
|
+
### Beams, slurs and ties
|
146
|
+
|
147
|
+
Lydown uses automatic beaming as the default, except in the case of vocal parts (see [document settings](#settings)). Auto beaming can be
|
148
|
+
|
149
|
+
Beaming and sluring is similar to lilypond, except the beam/slur start comes before the note:
|
150
|
+
|
151
|
+
8(cdef)g[6fe]4f => c8( d e f) g f16[ e] f4
|
152
|
+
|
153
|
+
a regular tie is written just like in lilypond:
|
154
|
+
|
155
|
+
4g~6gfed2c => g4 ~ g16 f e d c2
|
156
|
+
|
157
|
+
Lydown also supports a shortened tie form, where the tied note is not repeated:
|
158
|
+
|
159
|
+
4g6&fed2c => g4 ~ g16 f e d c2
|
160
|
+
|
161
|
+
### Articulation and expression marks
|
162
|
+
|
163
|
+
Lilypond [shorthand articulation marks](http://www.lilypond.org/doc/v2.18/Documentation/notation/expressive-marks-attached-to-notes#articulations-and-ornamentations) can be entered after a backslash
|
164
|
+
|
165
|
+
c\^ e\+ g\_ => c-^ e-+ g-_
|
166
|
+
|
167
|
+
Common articulation marks can be entered immediately after the note:
|
168
|
+
|
169
|
+
// tenuto, staccato, staccatissimo
|
170
|
+
c_e.g` => c-- e-. g-!
|
171
|
+
|
172
|
+
Other arbitrary lilypond articulations can be entered after a backslash:
|
173
|
+
|
174
|
+
c\staccato e\mordent g\turn => c\staccato e\mordent g\turn
|
175
|
+
|
176
|
+
[Dynamic marks](http://www.lilypond.org/doc/v2.18/Documentation/notation/expressive-marks-attached-to-notes#dynamics) are entered before the note to which they apply:
|
177
|
+
|
178
|
+
c\f eg
|
179
|
+
|
180
|
+
\f cege \p cfaf => c\f e g e c\p f a f
|
181
|
+
|
182
|
+
Arbitrary expression marks can be entered as a string following a backslash (for placing it under the note) or a forward slash (for placing it above the note).
|
183
|
+
|
184
|
+
### Repeated articulation and rhythmic patterns: macros
|
185
|
+
|
186
|
+
An important feature of lydown is the macro, which facilitates rapid entry of repeated rhythmic and articulative patterns.
|
187
|
+
|
188
|
+
A common scenario is repeated articulation, for example a sequence of staccato notes:
|
189
|
+
|
190
|
+
{.}cdefgabc => c-. d-. e-. f-. g-. a-. b-. c-.
|
191
|
+
|
192
|
+
A macro can also contain a fully qualified lilypond articulation specifier:
|
193
|
+
|
194
|
+
{\tenuto}cege => c\tenuto e\tenuto g\tenuto e\tenuto
|
195
|
+
|
196
|
+
Rhythmic macros can be used for repeated rhythmic patterns. A common scenario is a dotted rhythm:
|
197
|
+
|
198
|
+
// The _ symbol denotes a note placeholder
|
199
|
+
{8._6_}cdefgfed => c8. d16 e8. f16 g8. f16 e8. d16
|
200
|
+
|
201
|
+
A repeating diminution may also be expressed succintly:
|
202
|
+
|
203
|
+
// The @ symbol denotes a repeated pitch
|
204
|
+
{16_@@@}cfgf => c16 c c c f f f f g g g g f f f f
|
205
|
+
|
206
|
+
A macro can include both durations and articulation marks:
|
207
|
+
|
208
|
+
{4_~6@(_._._.)}cdefgfed => c4 ~ c16 d-.( e-. f-.) g4 ~ g16 f-.( e-. d-.)
|
209
|
+
|
210
|
+
A macro containing durations will remain valid until another duration or duration macro is encountered. A macro containing articulation only will be valid until another duration, macro or empty macro is encountered:
|
211
|
+
|
212
|
+
6{.}gg{}aa => g16-. g-. a a
|
213
|
+
|
214
|
+
### Named macros
|
215
|
+
|
216
|
+
Macros can be defined with a name and reused:
|
217
|
+
|
218
|
+
- macros:
|
219
|
+
- dotted: 8._6_
|
220
|
+
{dotted}gaba2g{dotted}abcb2a => g8. a16 b8. a16 g2 a8. b16 c8. b16 a2
|
221
|
+
|
222
|
+
### Clefs, key and time signatures
|
223
|
+
|
224
|
+
Clefs are determined automatically by lydown based on the specified part. In case no part is specified, the default clef is a treble clef. The clef values are the same as in [lilypond](http://www.lilypond.org/doc/v2.18/Documentation/notation/displaying-pitches#clef). The clef can be changed at any time with a clef setting:
|
225
|
+
|
226
|
+
- clef: bass
|
227
|
+
8cdefgabc
|
228
|
+
- clef: tenor
|
229
|
+
defedcbd
|
230
|
+
- clef: bass
|
231
|
+
1c
|
232
|
+
|
233
|
+
Key and time signatures are entered inline as document settings ([see below](#settings)). The [key](http://www.lilypond.org/doc/v2.18/Documentation/notation/displaying-pitches#key-signature) and [time](http://www.lilypond.org/doc/v2.18/Documentation/notation/displaying-rhythms#time-signature) values follow the lilypond syntax:
|
234
|
+
|
235
|
+
- key: d major
|
236
|
+
- time: 3/4
|
237
|
+
|
238
|
+
In the case of key signatures, accidentals will follow the lydown syntax:
|
239
|
+
|
240
|
+
- key: b- major
|
241
|
+
- key: f+ minor
|
242
|
+
|
243
|
+
The default key signature is C major, and the default time signature is 4/4.
|
244
|
+
|
245
|
+
Key or time signatures can be changed on the fly:
|
246
|
+
|
247
|
+
- time: 4/4
|
248
|
+
4c e g b
|
249
|
+
- time: 3/4
|
250
|
+
c e g 2.c
|
251
|
+
|
252
|
+
### Pickup bars
|
253
|
+
|
254
|
+
[Pickup bars](http://www.lilypond.org/doc/v2.18/Documentation/notation/displaying-rhythms#upbeats) (anacrusis, upbeat) are defined with the pickup setting:
|
255
|
+
|
256
|
+
- time: 3/4
|
257
|
+
- pickup: 4
|
258
|
+
4g
|
259
|
+
c8cdcb4aaa
|
260
|
+
d8dedc4bb
|
261
|
+
|
262
|
+
### Inline lyrics
|
263
|
+
|
264
|
+
Lyrics for vocal parts can be entered on separate lines prefixed by a > symbol:
|
265
|
+
|
266
|
+
4c[8de]4fd(4c[8de]2f)
|
267
|
+
> Ly-down is the bomb__
|
268
|
+
|
269
|
+
Text alignment follows the duration, beaming and slurring of the music, just like in lilypond. Sillables are expected to be separated by a dash. Melismas, i.e. a single sillable streched over multiple notes, is signified by one or more underscores.
|
270
|
+
|
271
|
+
### Stream switching
|
272
|
+
|
273
|
+
Lyrics can be entered in a block, before or after musical notation, by switching streams:
|
274
|
+
|
275
|
+
8ccg'gaa4g
|
276
|
+
8ffeedd4c
|
277
|
+
=lyrics
|
278
|
+
Twin-kle twin-kle lit-tle star,
|
279
|
+
How I won-der what you are.
|
280
|
+
=music
|
281
|
+
8g'gffeed4
|
282
|
+
...
|
283
|
+
|
284
|
+
### Figured bass
|
285
|
+
|
286
|
+
Figured bass is entered inline, following notes or even between notes, when
|
287
|
+
multiple figures align with a single note.
|
288
|
+
|
289
|
+
## Multiple parts
|
290
|
+
|
291
|
+
Multiple parts can be entered in the same file by prefixing each part's content with a -part setting:
|
292
|
+
|
293
|
+
- part: violino1
|
294
|
+
8c'cg'gaa4g
|
295
|
+
- part: continuo
|
296
|
+
4cefe
|
297
|
+
|
298
|
+
## Multiple movements
|
299
|
+
|
300
|
+
For multi-movement works, prefix each movement with a -movement setting:
|
301
|
+
|
302
|
+
- movement: Adagio
|
303
|
+
...
|
304
|
+
- movement: Allegro
|
305
|
+
...
|
306
|
+
|
307
|
+
## Multiple voices
|
308
|
+
|
309
|
+
[Multiple voices](http://www.lilypond.org/doc/v2.18/Documentation/notation/multiple-voices#single_002dstaff-polyphony) on the same staff can be easily entered using the following notation:
|
310
|
+
|
311
|
+
1: 8egfdeg4f
|
312
|
+
2: 4cded
|
313
|
+
|
314
|
+
## Piano scores
|
315
|
+
|
316
|
+
[Piano/keyboard scores](http://www.lilypond.org/doc/v2.18/Documentation/notation/common-notation-for-keyboards) can be created by using the <code>r/l</code> (right/left) prefixes:
|
317
|
+
|
318
|
+
r: 8cdeccdec
|
319
|
+
l: 4cgcg
|
320
|
+
|
321
|
+
In order to jump between staves, you can use the special commands <code>\r, \l</code>
|
322
|
+
|
323
|
+
r: 8<e'c'> \l g,f+g \r <g'' c'> \l e,,d+e \r
|
324
|
+
l: 1s
|
325
|
+
|
326
|
+
## multi-part scores and part extraction
|
327
|
+
|
328
|
+
As the example above shows, lydown supports multiple parts in the same file, but parts can also be written in separate files. This is useful for long pieces or those with a large number of parts.
|
329
|
+
|
330
|
+
For this we create a main file which defines the score structure:
|
331
|
+
|
332
|
+
// score.lydown
|
333
|
+
- score:
|
334
|
+
- [violino1, violino2]
|
335
|
+
- viola
|
336
|
+
- violoncello
|
337
|
+
- time: 4/4
|
338
|
+
- key: c major
|
339
|
+
|
340
|
+
Then we enter the music in a separate file for each part:
|
341
|
+
|
342
|
+
// violino1.lydown
|
343
|
+
8c''cg'gaa4g 8ffeedd4c
|
344
|
+
|
345
|
+
And so forth.
|
346
|
+
|
347
|
+
To compile the score, we use the following command:
|
348
|
+
|
349
|
+
lydown -o -s score.lydown
|
350
|
+
|
351
|
+
To extract a specific part, we use the -p switch:
|
352
|
+
|
353
|
+
lydown -o -p violino1 score.lydown
|
354
|
+
|
355
|
+
## Adding a front cover
|
356
|
+
|
357
|
+
When creating professional scores and parts, it is customary to add a cover page, with the title of the piece, the composer's name and other general information. Lydown includes a
|
data/bin/lydown
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'lydown'
|
5
|
+
require 'lydown/version'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
trap("INT") {exit}
|
9
|
+
trap("TERM") {exit}
|
10
|
+
|
11
|
+
help = <<HELP
|
12
|
+
Lydown is not lilypond
|
13
|
+
HELP
|
14
|
+
|
15
|
+
$options = {'format' => 'pdf'}
|
16
|
+
opts = OptionParser.new do |opts|
|
17
|
+
opts.banner = help
|
18
|
+
|
19
|
+
opts.on("-v", "--version", "Display current version") do
|
20
|
+
puts "lydown " + Lydown::VERSION
|
21
|
+
exit 0
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("-p", "--parts PART", "Parts (comma-separated)") do |v|
|
25
|
+
$options["parts"] = v.split(',')
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-m", "--mvts MVT", "Movements (comma-separated)") do |v|
|
29
|
+
$options["movements"] = v.split(',')
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on("--no-score", "Do not generate secore") do
|
33
|
+
$options["no_score"] = true
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("-s", "--score", "Generate only score, no parts") do
|
37
|
+
$options["score_only"] = true
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("-V", "--vocal", "Generate only vocal score") do
|
41
|
+
$options["vocal_only"] = true
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("--ly", "Generate Lilypond file") do
|
45
|
+
$options["format"] = 'ly'
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on("--pdf", "Generate PDF file") do
|
49
|
+
$options["format"] = 'pdf'
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("--png", "Generate PNG file") do
|
53
|
+
$options["format"] = 'png'
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on("-M", "--midi", "Generate midi file") do
|
57
|
+
$options["format"] = 'midi'
|
58
|
+
$options["score_only"] = true # implied
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("-O", "--open", "Open PDF/Midi file after processing") do
|
62
|
+
$options["open_target"] = true
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on("-o", "--output FILE", "Filename for output") do |v|
|
66
|
+
$options["output_filename"] = v
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on("-W", "--work", "Create new work") do
|
70
|
+
$options["gen"] = :work
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
opts.parse!
|
75
|
+
|
76
|
+
if $options["gen"]
|
77
|
+
# Ripple.generate($options["gen"], ARGV)
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
|
81
|
+
source = ''
|
82
|
+
if ARGV[0].nil? || ARGV[0] == '-'
|
83
|
+
# read source from stdin
|
84
|
+
source = STDIN.read
|
85
|
+
|
86
|
+
# the output defaults to a file named lydown expect if the format is ly.
|
87
|
+
# In that case the output will be sent to STDOUT.
|
88
|
+
$options["output_filename"] ||= 'lydown' unless $options["format"] == 'ly'
|
89
|
+
else
|
90
|
+
filename = ARGV[0]
|
91
|
+
$options['source_filename'] = filename
|
92
|
+
if (filename !~ /\.ld$/) and File.file?(filename + ".ld")
|
93
|
+
filename += ".ld"
|
94
|
+
end
|
95
|
+
|
96
|
+
$options["output_filename"] ||= (filename =~ /^(.+)\.ld$/) ? $1 : filename
|
97
|
+
end
|
98
|
+
|
99
|
+
# translate lydown code to lilypond code
|
100
|
+
ly = ''
|
101
|
+
begin
|
102
|
+
work = Lydown::Work.new(path: $options['source_filename'], nice_error: true)
|
103
|
+
# work.process(lydown)
|
104
|
+
ly = work.to_lilypond(mode: :score)
|
105
|
+
rescue LydownError => e
|
106
|
+
STDERR.puts e.message
|
107
|
+
exit 1
|
108
|
+
rescue => e
|
109
|
+
STDERR.puts "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
|
110
|
+
exit 1
|
111
|
+
end
|
112
|
+
|
113
|
+
if $options['format'] == 'ly'
|
114
|
+
unless $options["output_filename"]
|
115
|
+
puts ly
|
116
|
+
exit
|
117
|
+
else
|
118
|
+
begin
|
119
|
+
File.open($options["output_filename"] + '.ly', 'w+') do |f|
|
120
|
+
f.write ly
|
121
|
+
end
|
122
|
+
exit
|
123
|
+
rescue => e
|
124
|
+
STDERR.puts "#{e.class}: #{e.message}"
|
125
|
+
exit 1
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# compile lilypond code
|
131
|
+
begin
|
132
|
+
opts = {
|
133
|
+
output_filename: $options['output_filename'],
|
134
|
+
format: $options['format'],
|
135
|
+
mode: :score
|
136
|
+
}
|
137
|
+
Lydown::Lilypond.compile(ly, opts)
|
138
|
+
rescue LydownError => e
|
139
|
+
STDERR.puts e.message
|
140
|
+
exit 1
|
141
|
+
rescue => e
|
142
|
+
STDERR.puts "#{e.class}: #{e.message}"
|
143
|
+
exit 1
|
144
|
+
end
|
145
|
+
|
146
|
+
if $options['open_target']
|
147
|
+
filename = "#{$options['output_filename']}.#{$options['format']}"
|
148
|
+
system("open #{filename}")
|
149
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
class Hash
|
2
|
+
# Merges self with another hash, recursively.
|
3
|
+
#
|
4
|
+
# This code was lovingly stolen from some random gem:
|
5
|
+
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
|
6
|
+
#
|
7
|
+
# Thanks to whoever made it.
|
8
|
+
def deep_merge(hash)
|
9
|
+
target = Marshal.load(Marshal.dump(self))
|
10
|
+
target.deep_merge!(hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
def deep_merge!(hash)
|
14
|
+
hash.keys.each do |key|
|
15
|
+
if hash[key].is_a? Hash and self[key].is_a? Hash
|
16
|
+
self[key] = self[key].deep_merge!(hash[key])
|
17
|
+
next
|
18
|
+
end
|
19
|
+
|
20
|
+
self[key] = hash[key]
|
21
|
+
end
|
22
|
+
|
23
|
+
self.deep = true
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def deep_clone
|
28
|
+
dest = {}.deep!
|
29
|
+
each do |k, v|
|
30
|
+
dest[k] = case v
|
31
|
+
when Hash
|
32
|
+
v.deep_clone
|
33
|
+
when Array
|
34
|
+
v.clone
|
35
|
+
else
|
36
|
+
v
|
37
|
+
end
|
38
|
+
end
|
39
|
+
dest
|
40
|
+
end
|
41
|
+
|
42
|
+
def lookup(path)
|
43
|
+
path.split("/").inject(self) {|m,i| m[i].nil? ? (return nil) : m[i]}
|
44
|
+
end
|
45
|
+
|
46
|
+
def set(path, value)
|
47
|
+
leafs = path.split("/")
|
48
|
+
k = leafs.pop
|
49
|
+
h = leafs.inject(self) {|m, i| m[i].is_a?(Hash) ? m[i] : (m[i] = {})}
|
50
|
+
h[k] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_accessor :deep
|
54
|
+
|
55
|
+
alias_method :old_get, :[]
|
56
|
+
def [](k)
|
57
|
+
if @deep && k.is_a?(String) && k =~ /\//
|
58
|
+
lookup(k)
|
59
|
+
elsif @deep && k.is_a?(Symbol)
|
60
|
+
old_get(k.to_s)
|
61
|
+
else
|
62
|
+
old_get(k)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
alias_method :old_set, :[]=
|
67
|
+
def []=(k, v)
|
68
|
+
if @deep && k.is_a?(String) && k =~ /\//
|
69
|
+
set(k, v)
|
70
|
+
elsif @deep && k.is_a?(Symbol)
|
71
|
+
old_set(k.to_s, v)
|
72
|
+
else
|
73
|
+
old_set(k, v)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
alias_method :old_merge, :merge
|
78
|
+
def merge(hash)
|
79
|
+
if @deep || hash.deep
|
80
|
+
deep_merge(hash)
|
81
|
+
else
|
82
|
+
old_merge(hash)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
alias_method :old_merge!, :merge!
|
87
|
+
def merge!(hash)
|
88
|
+
if @deep || hash.deep
|
89
|
+
deep_merge!(hash)
|
90
|
+
else
|
91
|
+
old_merge!(hash)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def deep!
|
96
|
+
@deep = true
|
97
|
+
self
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class String
|
102
|
+
def titlize(all_capitals = false)
|
103
|
+
all_capitals ?
|
104
|
+
self.gsub("-", " ").gsub(/\b('?[a-z])/) {$1.capitalize} :
|
105
|
+
self.gsub("-", " ").capitalize
|
106
|
+
end
|
107
|
+
|
108
|
+
def camelize
|
109
|
+
split('_').collect(&:capitalize).join
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Fixnum
|
114
|
+
ROMAN = %w[0 I II III IV V VI VII VIII IX X XI XII XIII XIV XV XVI XVII XVIII XIX
|
115
|
+
XX XXI XXII XXIII XXIV XXV XXVI XXVII XXVIII XXIX XXX]
|
116
|
+
|
117
|
+
def to_roman
|
118
|
+
ROMAN[self]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'lydown/errors'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
module Lydown
|
5
|
+
module Lilypond
|
6
|
+
class << self
|
7
|
+
def tmpdir
|
8
|
+
@tmpdir ||= Dir.mktmpdir
|
9
|
+
end
|
10
|
+
|
11
|
+
def compile(source, opts = {})
|
12
|
+
opts[:output_filename] ||= 'lydown'
|
13
|
+
|
14
|
+
ly_path = File.join(tmpdir, File.basename(opts[:output_filename]))
|
15
|
+
File.open(ly_path, 'w+') do |f|
|
16
|
+
f << source
|
17
|
+
end
|
18
|
+
|
19
|
+
# Run lilypond, pipe source into its STDIN, and capture its STDERR
|
20
|
+
cmd = 'lilypond -lERROR '
|
21
|
+
cmd << "-o #{File.dirname(opts[:output_filename])} "
|
22
|
+
cmd << "--#{opts[:format]} " if opts[:format]
|
23
|
+
# cmd << '-s - 2>&1'
|
24
|
+
cmd << "-s #{ly_path} 2>&1"
|
25
|
+
|
26
|
+
err_info = ''
|
27
|
+
IO.popen(cmd, 'r+') do |f|
|
28
|
+
f.puts source
|
29
|
+
f.close_write
|
30
|
+
err_info = f.read
|
31
|
+
f.close
|
32
|
+
end
|
33
|
+
unless $?.success?
|
34
|
+
err_info = err_info.lines[0, 3].join
|
35
|
+
raise LydownError, "Lilypond compilation failed:\n#{err_info}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|