lydown 0.6.5 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -3
- data/bin/lydown +16 -49
- data/lib/lydown/cli/compiler.rb +75 -0
- data/lib/lydown/cli.rb +6 -0
- data/lib/lydown/lilypond.rb +19 -7
- data/lib/lydown/parsing/lydown.treetop +3 -3
- data/lib/lydown/rendering/lib.ly +17 -0
- data/lib/lydown/rendering/movement.rb +1 -1
- data/lib/lydown/rendering/notes.rb +17 -10
- data/lib/lydown/rendering/settings.rb +2 -2
- data/lib/lydown/version.rb +1 -1
- data/lib/lydown/work.rb +13 -7
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8be82e654bd57692d8e882f1c8643bd684d637c
|
4
|
+
data.tar.gz: 80c45eb4a1a4c4eb26960356e01774150d0b2697
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 755759c7dbc352e537743f8b829f35791297baa586a89a2e80cbd6fd98aef6a48cd54bb8f2d7c26c1aa8a4568701c69d5fdf117d56a271e9a1aebdf24aecbf0c
|
7
|
+
data.tar.gz: e5e151f5f78d6b372ca5cf655aeba53dc68be083d6b34b160b0211c89a9c6765498db2878f093ebd7d3e1c9fdac9d3bd2da11ee955c43b768b944f72e81d9f8f
|
data/README.md
CHANGED
@@ -112,14 +112,18 @@ Full bar rests are [similar to lilypond](http://www.lilypond.org/doc/v2.18/Docum
|
|
112
112
|
|
113
113
|
### Accidentals
|
114
114
|
|
115
|
-
Accidentals
|
115
|
+
Accidentals can be entered using + and -
|
116
116
|
|
117
|
-
8cgb-c2a => c8 g
|
117
|
+
8cgb-c2a => c8 g bes c a2
|
118
|
+
|
119
|
+
But can also be entered using the characters # for sharp, ß for flat and h for natural:
|
120
|
+
|
121
|
+
c#dßeh => cis des e
|
118
122
|
|
119
123
|
In lydown notes follow the key signature by default:
|
120
124
|
|
121
125
|
- key: g major
|
122
|
-
|
126
|
+
ff#fhfßd#bß => fis fisis f f dis bes
|
123
127
|
|
124
128
|
The accidental mode can be changed by specifiying the manual accidental mode:
|
125
129
|
|
data/bin/lydown
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'lydown'
|
5
5
|
require 'lydown/version'
|
6
|
+
require 'lydown/cli'
|
6
7
|
require 'optparse'
|
7
8
|
|
8
9
|
trap("INT") {exit}
|
@@ -58,6 +59,10 @@ opts = OptionParser.new do |opts|
|
|
58
59
|
$options["score_only"] = true # implied
|
59
60
|
end
|
60
61
|
|
62
|
+
opts.on("-P", "--proof", "Start proofing mode") do
|
63
|
+
$options["proof_mode"] = true
|
64
|
+
end
|
65
|
+
|
61
66
|
opts.on("-O", "--open", "Open PDF/Midi file after processing") do
|
62
67
|
$options["open_target"] = true
|
63
68
|
end
|
@@ -72,6 +77,7 @@ opts = OptionParser.new do |opts|
|
|
72
77
|
end
|
73
78
|
|
74
79
|
opts.parse!
|
80
|
+
$options.deep!
|
75
81
|
|
76
82
|
if $options["gen"]
|
77
83
|
# Ripple.generate($options["gen"], ARGV)
|
@@ -79,7 +85,7 @@ if $options["gen"]
|
|
79
85
|
end
|
80
86
|
|
81
87
|
source = ''
|
82
|
-
if ARGV[0]
|
88
|
+
if ARGV[0] == '-'
|
83
89
|
# read source from stdin
|
84
90
|
source = STDIN.read
|
85
91
|
|
@@ -87,7 +93,7 @@ if ARGV[0].nil? || ARGV[0] == '-'
|
|
87
93
|
# In that case the output will be sent to STDOUT.
|
88
94
|
$options["output_filename"] ||= 'lydown' unless $options["format"] == 'ly'
|
89
95
|
else
|
90
|
-
filename = ARGV[0]
|
96
|
+
filename = ARGV[0] || '.'
|
91
97
|
$options['source_filename'] = filename
|
92
98
|
if (filename !~ /\.ld$/) and File.file?(filename + ".ld")
|
93
99
|
filename += ".ld"
|
@@ -96,54 +102,15 @@ else
|
|
96
102
|
$options["output_filename"] ||= (filename =~ /^(.+)\.ld$/) ? $1 : filename
|
97
103
|
end
|
98
104
|
|
99
|
-
#
|
100
|
-
|
101
|
-
|
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
|
105
|
+
# compile score
|
106
|
+
unless $options[:no_score] || $options[:parts]
|
107
|
+
Lydown::CLI::Compiler.process($options.merge(mode: :score, parts: nil))
|
111
108
|
end
|
112
109
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
110
|
+
# compile parts
|
111
|
+
unless $options[:score_only] || !$options[:parts]
|
112
|
+
parts = $options[:parts]
|
113
|
+
parts.each do |p|
|
114
|
+
Lydown::CLI::Compiler.process($options.merge(mode: :part, parts: p))
|
127
115
|
end
|
128
116
|
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,75 @@
|
|
1
|
+
require 'lydown'
|
2
|
+
|
3
|
+
module Lydown::CLI::Compiler
|
4
|
+
class << self
|
5
|
+
def output_filename(opts)
|
6
|
+
fn = opts[:output_filename]
|
7
|
+
if opts[:movements] && opts[:movements].size == 1
|
8
|
+
fn << "-#{opts[:movements].first}"
|
9
|
+
end
|
10
|
+
if opts[:parts] && opts[:parts].size == 1
|
11
|
+
fn << "-#{opts[:parts].first}"
|
12
|
+
end
|
13
|
+
fn
|
14
|
+
end
|
15
|
+
|
16
|
+
def process(opts)
|
17
|
+
opts = opts.deep_clone
|
18
|
+
# translate lydown code to lilypond code
|
19
|
+
ly_code = ''
|
20
|
+
begin
|
21
|
+
opts[:path] = opts[:source_filename]
|
22
|
+
opts[:nice_error] = true
|
23
|
+
work = Lydown::Work.new(opts)
|
24
|
+
ly_code = work.to_lilypond(opts)
|
25
|
+
rescue LydownError => e
|
26
|
+
STDERR.puts e.message
|
27
|
+
STDERR.puts e.backtrace.join("\n")
|
28
|
+
exit 1
|
29
|
+
rescue => e
|
30
|
+
STDERR.puts "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
opts[:output_target] = output_filename(opts)
|
35
|
+
|
36
|
+
if opts[:format] == 'ly'
|
37
|
+
unless opts[:output_target]
|
38
|
+
STDOUT << ly_code
|
39
|
+
else
|
40
|
+
begin
|
41
|
+
File.open(opts[:output_target] + '.ly', 'w+') do |f|
|
42
|
+
f.write ly_code
|
43
|
+
end
|
44
|
+
rescue => e
|
45
|
+
STDERR.puts "#{e.class}: #{e.message}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
else
|
49
|
+
compile(ly_code, opts)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def compile(ly_code, opts)
|
54
|
+
opts = opts.deep_clone
|
55
|
+
begin
|
56
|
+
STDERR.puts "Compiling => #{opts[:output_target]}"
|
57
|
+
t1 = Time.now
|
58
|
+
Lydown::Lilypond.compile(ly_code, opts)
|
59
|
+
t2 = Time.now
|
60
|
+
STDERR.puts "Elapsed: #{t2-t1}s"
|
61
|
+
rescue LydownError => e
|
62
|
+
STDERR.puts e.message
|
63
|
+
STDERR.puts e.backtrace.join("\n")
|
64
|
+
rescue => e
|
65
|
+
STDERR.puts "#{e.class}: #{e.message}"
|
66
|
+
STDERR.puts e.backtrace.join("\n")
|
67
|
+
end
|
68
|
+
|
69
|
+
if opts[:open_target]
|
70
|
+
filename = "#{opts[:output_target]}.#{opts[:format]}"
|
71
|
+
system("open #{filename}")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/lydown/cli.rb
ADDED
data/lib/lydown/lilypond.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'lydown/errors'
|
2
|
-
require '
|
2
|
+
require 'tempfile'
|
3
|
+
require 'fileutils'
|
3
4
|
|
4
5
|
module Lydown
|
5
6
|
module Lilypond
|
@@ -11,17 +12,28 @@ module Lydown
|
|
11
12
|
def compile(source, opts = {})
|
12
13
|
opts[:output_filename] ||= 'lydown'
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
target = opts[:output_filename].dup
|
16
|
+
ext = ".#{opts[:format] || 'pdf'}"
|
17
|
+
if target !~ /#{ext}$/
|
18
|
+
target << ext
|
17
19
|
end
|
18
20
|
|
21
|
+
tmp_target = Tempfile.new('lydown').path
|
22
|
+
opts[:output_filename] = tmp_target
|
23
|
+
invoke(source, opts)
|
24
|
+
FileUtils.cp(tmp_target + ext, target)
|
25
|
+
rescue => e
|
26
|
+
puts e.message
|
27
|
+
p e.backtrace
|
28
|
+
end
|
29
|
+
|
30
|
+
def invoke(source, opts = {})
|
19
31
|
# Run lilypond, pipe source into its STDIN, and capture its STDERR
|
20
32
|
cmd = 'lilypond -lERROR '
|
21
|
-
cmd << "-o #{
|
33
|
+
cmd << "-o #{opts[:output_filename]} "
|
22
34
|
cmd << "--#{opts[:format]} " if opts[:format]
|
23
|
-
|
24
|
-
cmd << "-s #{ly_path} 2>&1"
|
35
|
+
cmd << '-s - 2>&1'
|
36
|
+
# cmd << "-s #{ly_path} 2>&1"
|
25
37
|
|
26
38
|
err_info = ''
|
27
39
|
IO.popen(cmd, 'r+') do |f|
|
@@ -110,7 +110,7 @@ grammar Lydown
|
|
110
110
|
rule string
|
111
111
|
'"' ('\"' / !'"' .)* '"'
|
112
112
|
end
|
113
|
-
rule figures
|
113
|
+
rule figures
|
114
114
|
'<' figures_component? (white_space? figures_component)* '>'
|
115
115
|
end
|
116
116
|
rule figures_component
|
@@ -132,13 +132,13 @@ grammar Lydown
|
|
132
132
|
[a-g@] octave* accidental* <Note::Head>
|
133
133
|
end
|
134
134
|
rule accidental
|
135
|
-
[
|
135
|
+
[\+\-#ßh]+
|
136
136
|
end
|
137
137
|
rule octave
|
138
138
|
[\,']+ <Note::Octave>
|
139
139
|
end
|
140
140
|
rule accidental_flag
|
141
|
-
[\!\?\^] <Note::AccidentalFlag>
|
141
|
+
[\!\?\^]+ <Note::AccidentalFlag>
|
142
142
|
end
|
143
143
|
rule phrasing
|
144
144
|
beam_open / beam_close / slur_open / slur_close
|
data/lib/lydown/rendering/lib.ly
CHANGED
@@ -30,3 +30,20 @@ dalsegnoadlib = {
|
|
30
30
|
\once \override Score.RehearsalMark #'font-size = #-2
|
31
31
|
\mark \markup { \musicglyph #"scripts.segno" ad lib }
|
32
32
|
}
|
33
|
+
|
34
|
+
editF = \markup { \center-align \concat { \bold { \italic ( }
|
35
|
+
\dynamic f \bold { \italic ) } } }
|
36
|
+
editP = \markup { \center-align \concat { \bold { \italic ( }
|
37
|
+
\dynamic p \bold { \italic ) } } }
|
38
|
+
editPP = \markup { \center-align \concat { \bold { \italic ( }
|
39
|
+
\dynamic pp \bold { \italic ) } } }
|
40
|
+
|
41
|
+
doux = \markup { \center-align \bold { \italic doux }}
|
42
|
+
fort = \markup { \center-align \bold { \italic fort }}
|
43
|
+
|
44
|
+
ten = \markup { \italic ten. }
|
45
|
+
|
46
|
+
ficta = {
|
47
|
+
\once \override AccidentalSuggestion #'avoid-slur = #'outside
|
48
|
+
\once \set suggestAccidentals = ##t
|
49
|
+
}
|
@@ -11,7 +11,13 @@ module Lydown::Rendering
|
|
11
11
|
KEY_ACCIDENTALS[signature] ||= calc_accidentals_for_key_signature(signature)
|
12
12
|
end
|
13
13
|
|
14
|
+
SIGNATURE_ACCIDENTAL_TRANSLATION = {
|
15
|
+
'#' => '+',
|
16
|
+
'ß' => '-'
|
17
|
+
}
|
18
|
+
|
14
19
|
def self.calc_accidentals_for_key_signature(signature)
|
20
|
+
signature = signature.gsub(/[#ß]/) {|c| SIGNATURE_ACCIDENTAL_TRANSLATION[c]}
|
15
21
|
unless signature =~ /^([a-g][\+\-]*) (major|minor)$/
|
16
22
|
raise "Invalid key signature #{signature.inspect}"
|
17
23
|
end
|
@@ -35,13 +41,19 @@ module Lydown::Rendering
|
|
35
41
|
|
36
42
|
ACCIDENTAL_VALUES = {
|
37
43
|
'+' => 1,
|
38
|
-
'-' => -1
|
44
|
+
'-' => -1,
|
45
|
+
'#' => 1,
|
46
|
+
'ß' => -1
|
39
47
|
}
|
40
48
|
|
41
49
|
def self.lilypond_note_name(note, key_signature = 'c major')
|
50
|
+
# if the natural sign (h) is used, no need to calculate the note name
|
51
|
+
return $1 if note =~ /([a-g])h/
|
52
|
+
|
42
53
|
value = 0
|
43
54
|
# accidental value from note
|
44
|
-
note = note.gsub(/[
|
55
|
+
note = note.gsub(/[\-\+#ß]/) { |c| value += ACCIDENTAL_VALUES[c]; '' }
|
56
|
+
|
45
57
|
# add key signature value
|
46
58
|
value += accidentals_for_key_signature(key_signature)[note] || 0
|
47
59
|
|
@@ -131,19 +143,14 @@ module Lydown::Rendering
|
|
131
143
|
@work.emit(event[:stream] || :music, lilypond_chord(event, notes, options))
|
132
144
|
end
|
133
145
|
|
134
|
-
FICTA_CODE = <<EOF
|
135
|
-
\\once \\override AccidentalSuggestion #'avoid-slur = #'outside
|
136
|
-
\\once \\set suggestAccidentals = ##t
|
137
|
-
EOF
|
138
|
-
|
139
146
|
def lilypond_note(event, options = {})
|
140
147
|
head = Accidentals.translate_note_name(@work, event[:head])
|
141
148
|
if options[:head_only]
|
142
149
|
head
|
143
150
|
else
|
144
|
-
if event[:accidental_flag]
|
145
|
-
accidental_flag = ''
|
146
|
-
prefix =
|
151
|
+
if event[:accidental_flag] =~ /\^/
|
152
|
+
accidental_flag = event[:accidental_flag].gsub('^', '')
|
153
|
+
prefix = '\ficta '
|
147
154
|
else
|
148
155
|
accidental_flag = event[:accidental_flag]
|
149
156
|
prefix = ''
|
@@ -69,7 +69,7 @@ module Lydown::Rendering
|
|
69
69
|
def check_setting_value(key, value)
|
70
70
|
if key == 'key'
|
71
71
|
# process shorthand notation
|
72
|
-
if value =~ /^([a-gA-G])[
|
72
|
+
if value =~ /^([a-gA-G])[\+\-#ß]*$/
|
73
73
|
mode = $1.downcase == $1 ? 'minor' : 'major'
|
74
74
|
value = "#{value.downcase} #{mode}"
|
75
75
|
end
|
@@ -104,7 +104,7 @@ module Lydown::Rendering
|
|
104
104
|
e = next_event
|
105
105
|
return if e && (e[:type] == :setting) && (e[:key] == 'key')
|
106
106
|
|
107
|
-
unless value =~ /^([
|
107
|
+
unless value =~ /^([A-Ga-g][\+\-#ß]*) (major|minor)$/
|
108
108
|
raise LydownError, "Invalid key signature #{value.inspect}"
|
109
109
|
end
|
110
110
|
|
data/lib/lydown/version.rb
CHANGED
data/lib/lydown/work.rb
CHANGED
@@ -18,8 +18,7 @@ module Lydown
|
|
18
18
|
def initialize(opts = {})
|
19
19
|
@context = {}.deep!
|
20
20
|
reset_context(:work)
|
21
|
-
|
22
|
-
opts.each {|k, v| @context[k] = v}
|
21
|
+
@context[:options] = opts.deep_clone
|
23
22
|
|
24
23
|
process_work_files if opts[:path]
|
25
24
|
end
|
@@ -199,7 +198,7 @@ module Lydown
|
|
199
198
|
end
|
200
199
|
|
201
200
|
def process_work_files
|
202
|
-
path = @context[:path]
|
201
|
+
path = @context[:options][:path]
|
203
202
|
path += '.ld' if File.file?(path + '.ld')
|
204
203
|
|
205
204
|
if File.file?(path)
|
@@ -219,12 +218,16 @@ module Lydown
|
|
219
218
|
process_lydown_file(File.join(path, 'work.ld'))
|
220
219
|
# process movement specific code
|
221
220
|
process_lydown_file(File.join(path, 'movement.ld'))
|
222
|
-
|
221
|
+
|
222
|
+
part_filter = @context[:options][:parts]
|
223
|
+
mvmt_filter = @context[:options][:movements]
|
224
|
+
|
223
225
|
# Iterate over sorted directory entries
|
224
226
|
Dir["#{path}/*"].entries.sort.each do |entry|
|
225
227
|
if File.file?(entry) && (entry =~ /\.ld$/)
|
226
228
|
part = File.basename(entry, '.*')
|
227
|
-
|
229
|
+
skip = part_filter && !part_filter.include?(part)
|
230
|
+
unless DEFAULT_BASENAMES.include?(part) || skip
|
228
231
|
preserve_context do
|
229
232
|
process_lydown_file(entry, [
|
230
233
|
{type: :setting, key: 'part', value: part}
|
@@ -233,8 +236,11 @@ module Lydown
|
|
233
236
|
end
|
234
237
|
elsif File.directory?(entry) && recursive
|
235
238
|
movement = File.basename(entry)
|
236
|
-
|
237
|
-
|
239
|
+
skip = mvmt_filter && !mvmt_filter.include?(movement)
|
240
|
+
unless skip
|
241
|
+
process([{type: :setting, key: 'movement', value: movement}])
|
242
|
+
process_directory(entry, false)
|
243
|
+
end
|
238
244
|
end
|
239
245
|
end
|
240
246
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lydown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: treetop
|
@@ -35,6 +35,8 @@ files:
|
|
35
35
|
- README.md
|
36
36
|
- bin/lydown
|
37
37
|
- lib/lydown.rb
|
38
|
+
- lib/lydown/cli.rb
|
39
|
+
- lib/lydown/cli/compiler.rb
|
38
40
|
- lib/lydown/core_ext.rb
|
39
41
|
- lib/lydown/errors.rb
|
40
42
|
- lib/lydown/lilypond.rb
|