gly 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/gly.rb +10 -1
- data/lib/gly/cli.rb +19 -72
- data/lib/gly/document_gabc_convertor.rb +34 -0
- data/lib/gly/document_gabc_convertor.rb~ +23 -0
- data/lib/gly/gabc_convertor.rb +4 -2
- data/lib/gly/lister.rb +32 -0
- data/lib/gly/lister.rb~ +29 -0
- data/lib/gly/lyrics.rb +12 -1
- data/lib/gly/parser.rb +34 -7
- data/lib/gly/preview_generator.rb +48 -0
- data/lib/gly/preview_generator.rb~ +7 -0
- data/lib/gly/string_helpers.rb +13 -5
- data/lib/gly/string_helpers.rb~ +1 -3
- data/lib/gly/templates/lualatex_document.tex +27 -0
- data/lib/gly/templates/lualatex_document.tex~ +27 -0
- data/tests/string_helpers_test.rb +2 -1
- data/tests/string_helpers_test.rb~ +12 -2
- metadata +92 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1be7280dd39aa6117888f6779c409359202d8bb
|
4
|
+
data.tar.gz: b5bc60c05106a3472b4fa296cc0787f1c804ab8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d695541f34baae56918b864e062387426889b55dbed9e1636688157c1b4eadec906556be053de0b4da8f3a588c1fe415a12104ae844a5045471bfd91dfcec32c
|
7
|
+
data.tar.gz: 8aab9514701279b9e4ab1f00282e323902924c5f9b38fcf2ab1aa2fd9e534759a8a62220a38fa72d08ec0711eb78fb78c499760b97ea901458bcf93c326d7415
|
data/lib/gly.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
module Gly; end
|
2
2
|
|
3
|
-
%w(parser
|
3
|
+
%w(parser
|
4
|
+
gabc_convertor
|
5
|
+
parsed_score
|
6
|
+
headers
|
7
|
+
lyrics
|
8
|
+
document
|
9
|
+
document_gabc_convertor
|
10
|
+
preview_generator
|
11
|
+
lister
|
12
|
+
string_helpers).each do |mod|
|
4
13
|
require "gly/#{mod}"
|
5
14
|
end
|
data/lib/gly/cli.rb
CHANGED
@@ -5,90 +5,37 @@ module Gly
|
|
5
5
|
class CLI < Thor
|
6
6
|
desc 'gabc FILE ...', 'convert gly to gabc'
|
7
7
|
def gabc(*files)
|
8
|
-
files.each {|f|
|
8
|
+
files.each {|f| DocumentGabcConvertor.new(Parser.new.parse(f)).convert }
|
9
9
|
end
|
10
10
|
|
11
11
|
desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
|
12
12
|
def preview(*files)
|
13
|
-
files.each {|f|
|
13
|
+
files.each {|f| PreviewGenerator.new.process(Parser.new.parse(f)) }
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def gabc_convert(doc)
|
25
|
-
doc.scores.each_with_index do |score, si|
|
26
|
-
score_id = score.headers['id'] || si.to_s
|
27
|
-
out_fname = File.basename(doc.path)
|
28
|
-
.sub(/\.gly\Z/i, "_#{score_id}.gabc")
|
29
|
-
File.open(out_fname, 'w') do |fw|
|
30
|
-
GabcConvertor.new.convert score, fw
|
31
|
-
end
|
32
|
-
yield score, out_fname if block_given?
|
16
|
+
desc 'list FILE ...', 'list scores contained in files'
|
17
|
+
option :recursive, type: :boolean, aliases: :r, banner: 'recursively traverse directories', default: false
|
18
|
+
def list(*files)
|
19
|
+
if files.empty?
|
20
|
+
STDERR.puts 'No file specified.'
|
21
|
+
exit 1
|
33
22
|
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def make_preview(gly_file)
|
37
|
-
doc = parse(gly_file)
|
38
|
-
|
39
|
-
tex_header = <<EOS
|
40
|
-
% LuaLaTeX
|
41
|
-
|
42
|
-
\\documentclass[a4paper, 12pt]{article}
|
43
|
-
\\usepackage[latin]{babel}
|
44
|
-
\\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
|
45
|
-
|
46
|
-
\\usepackage{fontspec}
|
47
23
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
\\begin{document}
|
56
|
-
|
57
|
-
#{doc.header['title'] && '\\maketitle'}
|
58
|
-
|
59
|
-
EOS
|
60
|
-
|
61
|
-
tex_fname = File.basename(gly_file).sub(/\.gly\Z/i, '.tex')
|
62
|
-
File.open(tex_fname, 'w') do |fw|
|
63
|
-
fw.puts tex_header
|
64
|
-
|
65
|
-
gabc_convert(doc) do |score, gabc_fname|
|
66
|
-
system "gregorio #{gabc_fname}"
|
67
|
-
gtex_fname = gabc_fname.sub /\.gabc/i, ''
|
68
|
-
piece_title = %w(book manuscript arranger author).collect do |m|
|
69
|
-
score.headers[m]
|
70
|
-
end.delete_if(&:nil?).join ', '
|
71
|
-
fw.puts "\\commentary{\\footnotesize{#{piece_title}}}\n" unless piece_title.empty?
|
72
|
-
|
73
|
-
annotations = score.headers.each_value('annotation')
|
74
|
-
begin
|
75
|
-
fw.puts "\\setfirstannotation{#{annotations.next}}"
|
76
|
-
fw.puts "\\setsecondannotation{#{annotations.next}}"
|
77
|
-
rescue StopIteration
|
78
|
-
# ok, no more annotations
|
24
|
+
if options[:recursive]
|
25
|
+
files = files.collect do |f|
|
26
|
+
if File.directory?(f)
|
27
|
+
Dir[File.join(f, '**/*.gly')]
|
28
|
+
else
|
29
|
+
f
|
79
30
|
end
|
80
|
-
|
81
|
-
fw.puts "\\includescore{#{gtex_fname}}\n\\vspace{1cm}"
|
82
31
|
end
|
83
|
-
|
84
|
-
# tagline
|
85
|
-
fw.puts "\n\\vfill\n\\begin{center}"
|
86
|
-
fw.puts "\\texttt{gly preview https://github.com/igneus/gly}"
|
87
|
-
fw.puts "\\end{center}\n"
|
88
|
-
fw.puts "\n\\end{document}"
|
32
|
+
files.flatten!
|
89
33
|
end
|
90
34
|
|
91
|
-
|
35
|
+
lister = Lister.new(files)
|
36
|
+
lister.list(STDOUT, STDERR)
|
37
|
+
|
38
|
+
exit(lister.error? ? 1 : 0)
|
92
39
|
end
|
93
40
|
end
|
94
41
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Gly
|
2
|
+
class DocumentGabcConvertor
|
3
|
+
def initialize(document)
|
4
|
+
@doc = document
|
5
|
+
end
|
6
|
+
|
7
|
+
def convert
|
8
|
+
each_score_with_gabcname do |score, out_fname|
|
9
|
+
File.open(out_fname, 'w') do |fw|
|
10
|
+
GabcConvertor.new.convert score, fw
|
11
|
+
end
|
12
|
+
yield score, out_fname if block_given?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# iterates over document scores,
|
17
|
+
# yields score and filename of it's generated gabc file
|
18
|
+
def each_score_with_gabcname
|
19
|
+
@doc.scores.each_with_index do |score, si|
|
20
|
+
yield score, output_fname(score, si)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def output_fname(score, score_index=nil)
|
27
|
+
score_id = score.headers['id'] || score_index.to_s
|
28
|
+
score_id = '_' + score_id unless score_id.empty?
|
29
|
+
|
30
|
+
File.basename(@doc.path)
|
31
|
+
.sub(/\.gly\Z/i, "#{score_id}.gabc")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Gly
|
2
|
+
class DocumentGabcConvertor
|
3
|
+
def initialize(document)
|
4
|
+
@doc = document
|
5
|
+
end
|
6
|
+
|
7
|
+
def convert
|
8
|
+
@doc.scores.each_with_index do |score, si|
|
9
|
+
score_id = score.headers['id'] || si.to_s
|
10
|
+
out_fname = File.basename(doc.path)
|
11
|
+
.sub(/\.gly\Z/i, "_#{score_id}.gabc")
|
12
|
+
File.open(out_fname, 'w') do |fw|
|
13
|
+
GabcConvertor.new.convert score, fw
|
14
|
+
end
|
15
|
+
yield score, out_fname if block_given?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def output_fname(input_fname, score_id=nil)
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/gly/gabc_convertor.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
1
3
|
module Gly
|
2
|
-
#
|
4
|
+
# takes ParsedScore, translates it to gabc
|
3
5
|
class GabcConvertor
|
4
6
|
def convert(score, out=StringIO.new)
|
5
7
|
score.headers.each_pair do |key,value|
|
@@ -41,7 +43,7 @@ module Gly
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def differentia?(chunk)
|
44
|
-
chunk =~ /\A*[
|
46
|
+
chunk =~ /\A*[,;:`]+\Z/ # differentia
|
45
47
|
end
|
46
48
|
|
47
49
|
# is the given music chunk capable of bearing lyrics?
|
data/lib/gly/lister.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Gly
|
2
|
+
class Lister
|
3
|
+
def initialize(files)
|
4
|
+
@files = files
|
5
|
+
@error = false
|
6
|
+
end
|
7
|
+
|
8
|
+
def list(io, errio)
|
9
|
+
@files.each do |f|
|
10
|
+
begin
|
11
|
+
document = Parser.new.parse(f)
|
12
|
+
rescue SystemCallError => err # Errno::WHATEVER
|
13
|
+
errio.puts "Cannot read file '#{f}': #{err.message}"
|
14
|
+
@error = true
|
15
|
+
next
|
16
|
+
end
|
17
|
+
|
18
|
+
io.puts
|
19
|
+
io.puts "== #{f}"
|
20
|
+
|
21
|
+
document.scores.each do |s|
|
22
|
+
l = s.lyrics.readable
|
23
|
+
io.puts l unless l.empty?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def error?
|
29
|
+
@error
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/gly/lister.rb~
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Gly
|
2
|
+
class Lister
|
3
|
+
def initialize(files)
|
4
|
+
@files = files
|
5
|
+
end
|
6
|
+
|
7
|
+
def list(io, errio)
|
8
|
+
was_error = false
|
9
|
+
|
10
|
+
files.each do |f|
|
11
|
+
begin
|
12
|
+
document = parse(f)
|
13
|
+
rescue SystemCallError => err # Errno::WHATEVER
|
14
|
+
errio.puts "Cannot read file '#{f}': #{err.message}"
|
15
|
+
was_error = true
|
16
|
+
next
|
17
|
+
end
|
18
|
+
|
19
|
+
puts
|
20
|
+
puts "== #{f}"
|
21
|
+
|
22
|
+
document.scores.each do |s|
|
23
|
+
l = s.lyrics.readable
|
24
|
+
puts l unless l.empty?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/gly/lyrics.rb
CHANGED
@@ -23,7 +23,11 @@ module Gly
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def_delegator :@words, :each, :each_word
|
26
|
-
def_delegators :@words, :empty
|
26
|
+
def_delegators :@words, :<<, :empty?
|
27
|
+
|
28
|
+
def readable
|
29
|
+
@words.collect(&:readable).join ' '
|
30
|
+
end
|
27
31
|
end
|
28
32
|
|
29
33
|
class Word
|
@@ -35,5 +39,12 @@ module Gly
|
|
35
39
|
|
36
40
|
def_delegators :@syllables, :<<, :push
|
37
41
|
def_delegator :@syllables, :each, :each_syllable
|
42
|
+
|
43
|
+
def readable
|
44
|
+
without_directives = @syllables.collect do |s|
|
45
|
+
s.start_with?('!') ? s[1..-1] : s
|
46
|
+
end
|
47
|
+
without_directives.join
|
48
|
+
end
|
38
49
|
end
|
39
50
|
end
|
data/lib/gly/parser.rb
CHANGED
@@ -1,9 +1,37 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
1
3
|
module Gly
|
2
4
|
# parses gly source
|
3
5
|
class Parser
|
4
|
-
|
6
|
+
def initialize(syllable_separator='--')
|
7
|
+
@syllable_separator = syllable_separator
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(source)
|
11
|
+
if source.is_a? String
|
12
|
+
if File.file? source
|
13
|
+
parse_fname source
|
14
|
+
elsif source == '-'
|
15
|
+
parse_io STDIN
|
16
|
+
else
|
17
|
+
parse_str source
|
18
|
+
end
|
19
|
+
else
|
20
|
+
parse_io source
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_fname(str)
|
25
|
+
File.open(str) do |fr|
|
26
|
+
parse_io fr
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_str(str)
|
31
|
+
parse_io(StringIO.new(source))
|
32
|
+
end
|
5
33
|
|
6
|
-
def
|
34
|
+
def parse_io(io)
|
7
35
|
@doc = Document.new
|
8
36
|
@score = ParsedScore.new
|
9
37
|
|
@@ -61,7 +89,7 @@ module Gly
|
|
61
89
|
EXPLICIT_LYRICS_RE = /\A\\l(yrics)?\s+/
|
62
90
|
|
63
91
|
def lyrics_line?(str)
|
64
|
-
str =~ EXPLICIT_LYRICS_RE || str.include?(
|
92
|
+
str =~ EXPLICIT_LYRICS_RE || str.include?(@syllable_separator) || contains_unmusical_letters?(str)
|
65
93
|
end
|
66
94
|
|
67
95
|
def in_header_block?
|
@@ -83,16 +111,15 @@ module Gly
|
|
83
111
|
# separator
|
84
112
|
str
|
85
113
|
.sub(EXPLICIT_LYRICS_RE, '')
|
86
|
-
.split(/(?<!#{
|
114
|
+
.split(/(?<!#{@syllable_separator})\s+(?!#{@syllable_separator})/)
|
87
115
|
.each do |word|
|
88
|
-
@score.lyrics << Word.new(word.split(/\s*#{
|
116
|
+
@score.lyrics << Word.new(word.split(/\s*#{@syllable_separator}\s*/))
|
89
117
|
end
|
90
118
|
end
|
91
119
|
|
92
120
|
def parse_music(str)
|
93
121
|
# music chunks: split by whitespace out of brackets
|
94
|
-
|
95
|
-
chunk.sub!(/\A\((.*?)\)\Z/, '\1') # unparenthesize
|
122
|
+
StringHelpers.music_split(str).each do |chunk|
|
96
123
|
@score.music << chunk
|
97
124
|
end
|
98
125
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Gly
|
4
|
+
# Takes Gly::Document, builds a pdf preview
|
5
|
+
# (or at least generates all necessary assets)
|
6
|
+
class PreviewGenerator
|
7
|
+
def process(document)
|
8
|
+
tpl_name = File.join(File.dirname(__FILE__), 'templates/lualatex_document.tex')
|
9
|
+
template = File.read(tpl_name)
|
10
|
+
|
11
|
+
doc_body = fw = StringIO.new
|
12
|
+
|
13
|
+
convertor = DocumentGabcConvertor.new(document)
|
14
|
+
convertor.each_score_with_gabcname do |score, gabc_fname|
|
15
|
+
system "gregorio #{gabc_fname}"
|
16
|
+
gtex_fname = gabc_fname.sub /\.gabc/i, ''
|
17
|
+
piece_title = %w(book manuscript arranger author).collect do |m|
|
18
|
+
score.headers[m]
|
19
|
+
end.delete_if(&:nil?).join ', '
|
20
|
+
fw.puts "\\commentary{\\footnotesize{#{piece_title}}}\n" unless piece_title.empty?
|
21
|
+
|
22
|
+
annotations = score.headers.each_value('annotation')
|
23
|
+
begin
|
24
|
+
fw.puts "\\setfirstannotation{#{annotations.next}}"
|
25
|
+
fw.puts "\\setsecondannotation{#{annotations.next}}"
|
26
|
+
rescue StopIteration
|
27
|
+
# ok, no more annotations
|
28
|
+
end
|
29
|
+
|
30
|
+
fw.puts "\\includescore{#{gtex_fname}}\n\\vspace{1cm}"
|
31
|
+
end
|
32
|
+
|
33
|
+
replacements = {
|
34
|
+
title: document.header['title'],
|
35
|
+
maketitle: (document.header['title'] && '\maketitle'),
|
36
|
+
body: doc_body.string
|
37
|
+
}
|
38
|
+
tex = template % replacements
|
39
|
+
|
40
|
+
out_fname = File.basename(document.path).sub(/\.gly\Z/i, '.tex')
|
41
|
+
File.open(out_fname, 'w') do |fw|
|
42
|
+
fw.puts tex
|
43
|
+
end
|
44
|
+
|
45
|
+
system "lualatex #{out_fname}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/gly/string_helpers.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Gly
|
2
2
|
module StringHelpers
|
3
3
|
extend self
|
4
|
-
|
4
|
+
|
5
5
|
# splits string by whitespace that is not enclosed
|
6
6
|
# in brackets.
|
7
7
|
# At the same time removes brackets.
|
@@ -12,10 +12,16 @@ module Gly
|
|
12
12
|
in_brackets = false
|
13
13
|
str.chars.each_with_index do |char, chi|
|
14
14
|
if in_brackets
|
15
|
-
|
16
|
-
|
15
|
+
if char == ')'
|
16
|
+
in_brackets = false
|
17
|
+
chunks << str[chunk_start .. chi-1]
|
18
|
+
chunk_start = chi + 1
|
19
|
+
end
|
17
20
|
else
|
18
|
-
if char
|
21
|
+
if char == '('
|
22
|
+
in_brackets = true
|
23
|
+
chunk_start = chi + 1
|
24
|
+
elsif char =~ /^\s$/
|
19
25
|
if chunk_start != chi
|
20
26
|
chunks << str[chunk_start .. chi-1]
|
21
27
|
end
|
@@ -24,7 +30,9 @@ module Gly
|
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
27
|
-
|
33
|
+
if chunk_start < str.size
|
34
|
+
chunks << str[chunk_start .. -1]
|
35
|
+
end
|
28
36
|
|
29
37
|
chunks
|
30
38
|
end
|
data/lib/gly/string_helpers.rb~
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
%% LuaLaTeX
|
2
|
+
|
3
|
+
\documentclass[a4paper, 12pt]{article}
|
4
|
+
\usepackage[latin]{babel}
|
5
|
+
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
|
6
|
+
|
7
|
+
\usepackage{fontspec}
|
8
|
+
|
9
|
+
%% for gregorio
|
10
|
+
\usepackage{luatextra}
|
11
|
+
\usepackage{graphicx}
|
12
|
+
\usepackage{gregoriotex}
|
13
|
+
|
14
|
+
\title{%{title}}
|
15
|
+
|
16
|
+
\begin{document}
|
17
|
+
|
18
|
+
%{maketitle}
|
19
|
+
|
20
|
+
%{body}
|
21
|
+
|
22
|
+
%% tagline
|
23
|
+
\vfill
|
24
|
+
\begin{center}
|
25
|
+
\texttt{gly preview https://github.com/igneus/gly}
|
26
|
+
\end{center}
|
27
|
+
\end{document}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
% LuaLaTeX
|
2
|
+
|
3
|
+
\documentclass[a4paper, 12pt]{article}
|
4
|
+
\usepackage[latin]{babel}
|
5
|
+
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
|
6
|
+
|
7
|
+
\usepackage{fontspec}
|
8
|
+
|
9
|
+
% for gregorio
|
10
|
+
\usepackage{luatextra}
|
11
|
+
\usepackage{graphicx}
|
12
|
+
\usepackage{gregoriotex}
|
13
|
+
|
14
|
+
\title{#{doc.header['title']}}
|
15
|
+
|
16
|
+
\begin{document}
|
17
|
+
|
18
|
+
%{maketitle}
|
19
|
+
|
20
|
+
%{body}
|
21
|
+
|
22
|
+
% tagline
|
23
|
+
\vfill
|
24
|
+
\begin{center}
|
25
|
+
\texttt{gly preview https://github.com/igneus/gly}
|
26
|
+
\end{center}
|
27
|
+
\end{document}
|
@@ -8,8 +8,9 @@ class StringHelpersTest < GlyTest
|
|
8
8
|
['simple_whitespace', 'aa aa', ['aa', 'aa']],
|
9
9
|
['leading_trailing_whitespace', ' a a ', ['a', 'a']],
|
10
10
|
['bracketted', '(a)', ['a']],
|
11
|
+
['empty', '()', ['']]
|
11
12
|
]
|
12
|
-
|
13
|
+
|
13
14
|
examples.each do |e|
|
14
15
|
name, given, expected = e
|
15
16
|
define_method "test_#{name}" do
|
@@ -2,8 +2,18 @@ require_relative 'test_helper'
|
|
2
2
|
|
3
3
|
class StringHelpersTest < GlyTest
|
4
4
|
SH = Gly::StringHelpers
|
5
|
+
|
6
|
+
examples = [
|
7
|
+
['single_chunk', 'a', ['a']],
|
8
|
+
['simple_whitespace', 'aa aa', ['aa', 'aa']],
|
9
|
+
['leading_trailing_whitespace', ' a a ', ['a', 'a']],
|
10
|
+
['bracketted', '(a)', ['a']],
|
11
|
+
]
|
5
12
|
|
6
|
-
|
7
|
-
|
13
|
+
examples.each do |e|
|
14
|
+
name, given, expected = e
|
15
|
+
define_method "test_#{name}" do
|
16
|
+
assert_equal expected, SH.music_split(given)
|
17
|
+
end
|
8
18
|
end
|
9
19
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub Pavlík
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -49,23 +49,26 @@ description: "# gly\n\nWriter-friendly Gregorian notation format compiling to ga
|
|
49
49
|
or\n lyrics alone is possible (useful for a composer)\n * syllabified lyrics
|
50
50
|
entered in a format inspired by LilyPond\n* music and lyrics can be interspersed
|
51
51
|
as needed\n* no semicolons in the header\n* custom header fields supported (commented
|
52
|
-
out in the GABC output
|
53
|
-
scores per file (when compiled to gabc, each becomes\n a separate file)\n*
|
54
|
-
pdf preview by a single command, without writing any (La)TeX\n\n##
|
55
|
-
|
56
|
-
\
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
1
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
52
|
+
out in the GABC output,\n as gregorio doesn't tolerate headers it doesn't know)\n*
|
53
|
+
several scores per file (when compiled to gabc, each becomes\n a separate file)\n*
|
54
|
+
compile pdf preview by a single command, without writing any (La)TeX\n\n## Real
|
55
|
+
world examples\n\n* [Proper Divine Office chants of Bohemian Premonstratensian houses][opraem_boh]\n\n##
|
56
|
+
Basic examples\n\nTypical GABC source of an antiphon looks like this:\n\n name:
|
57
|
+
Nativitas gloriosae;\n office-part: laudes, 1. ant.;\n occasion: In Nativitate
|
58
|
+
B. Mariae Virginis;\n book: Antiphonale Romanum 1912, pg. 704;\n mode: 8;\n
|
59
|
+
\ initial-style: 1;\n %%\n \n (c4) NA(g)TI(g)VI(g)TAS(gd) glo(f)ri(gh)ó(g)sae(g)
|
60
|
+
* (,)\n Vír(g)gi(g)nis(hi) Ma(gh)rí(gf)ae,(f) (;)\n ex(f) sé(g)mi(h)ne(h)
|
61
|
+
A(hiwji)bra(hg)hae,(g) (;)\n or(gh~)tae(g) de(g) tri(g)bu(fe/fgf) Ju(d)da,(d)
|
62
|
+
(;)\n cla(df!gh)ra(g) ex(f) stir(hg~)pe(hi) Da(h)vid.(g) (::)\n\nCorresponding
|
63
|
+
GLY may look like this:\n\n name: Nativitas gloriosae\n office-part: laudes,
|
64
|
+
1. ant.\n occasion: In Nativitate B. Mariae Virginis\n book: Antiphonale Romanum
|
65
|
+
1912, pg. 704\n mode: 8\n initial-style: 1\n \n c4 g g g gd f gh g g
|
66
|
+
,\n g g hi gh gf f ;\n f g h h hiwji hg g ;\n gh~ g g g fe/fgf d d ;\n
|
67
|
+
\ df!gh g f hg~ hi h g ::\n \n NA -- TI -- VI -- TAS glo -- ri -- ósae *\n
|
68
|
+
\ Vír -- gi -- nis Ma -- rí -- ae,\n ex sé -- mi -- ne A -- bra -- hae,\n or
|
69
|
+
-- tae de tri -- bu Ju -- da,\n cla -- ra ex stir -- pe Da -- vid.\n\nOr, with
|
70
|
+
music and lyrics interlaced\n(this arrangement may be handy for larger scores,\nlike
|
71
|
+
full-notated hymns, sequences or nocturnal responsories):\n\n name: Nativitas
|
69
72
|
gloriosae\n office-part: laudes, 1. ant.\n occasion: In Nativitate B. Mariae
|
70
73
|
Virginis\n book: Antiphonale Romanum 1912, pg. 704\n mode: 8\n initial-style:
|
71
74
|
1\n \n c4 g g g gd f gh g g ,\n NA -- TI -- VI -- TAS glo -- ri -- ósae
|
@@ -73,16 +76,70 @@ description: "# gly\n\nWriter-friendly Gregorian notation format compiling to ga
|
|
73
76
|
g h h hiwji hg g ;\n ex sé -- mi -- ne A -- bra -- hae,\n \n gh~ g g g
|
74
77
|
fe/fgf d d ;\n or -- tae de tri -- bu Ju -- da,\n \n df!gh g f hg~ hi h
|
75
78
|
g ::\n cla -- ra ex stir -- pe Da -- vid.\n\nOther arrangements are also possible.
|
76
|
-
Order of music and lyrics\nis actually ignored during processing.\n\n##
|
77
|
-
|
78
|
-
|
79
|
-
|
79
|
+
Order of music and lyrics\nis actually ignored during processing.\n\n## Installation\n\nInstall
|
80
|
+
Ruby (some 2.x version) runtime. Then install as any ruby gem:\n\n`gem install gly`\n\n##
|
81
|
+
Usage\n\nThis gem provides executable `gly`. Run `gly help` for full list\nof subcommands.
|
82
|
+
The most important ones are:\n\n`gly gabc FILE1 ...`\n\nconverts given gly file(s)
|
83
|
+
to one or more gabc files (one per score,\ni.e. one gly may spawn a bunch of gabcs).\n\n`gly
|
80
84
|
preview FILE1 ...`\n\nAttempts to create a pdf document with all scores contained
|
81
|
-
in each\ngly file. Expects gregorio and lualatex to be in PATH\nand gregoriotex
|
82
|
-
to be installed and accessible by lualatex
|
85
|
+
in each\ngly file. Expects `gregorio` and `lualatex` to be in PATH\nand `gregoriotex`
|
86
|
+
to be installed and accessible by `lualatex`.\n\n## Tools\n\n[Emacs mode with syntax
|
83
87
|
highlighting for gly][elisp]\n\n![Editing gly in emacs](/doc/img/gly_emacs_scr.png)\n\n##
|
84
|
-
|
85
|
-
|
88
|
+
Syntax reference\n\nGly syntax is line-based.\nThe interpreter reads the input line
|
89
|
+
by line,\nand depending on context it interprets each line as\ne.g. music, lyrics
|
90
|
+
or header field.\n\nThe syntax is quite permissive, not requiring a lot of delimiters\nor
|
91
|
+
hints for the parser concerning what each line means.\nMostly the parser guesses
|
92
|
+
the meaning correctly.\nWhere not, meaning of each line can be stated explicitly.\n\n###
|
93
|
+
1. Comments\n\nWhen a `%` sign is encountered, everything until the end of line\nis
|
94
|
+
considered a comment and not interpreted.\n(Comment syntax is the same as in gabc.)\n\nPlease
|
95
|
+
note, that when compiling to gabc, comments are dropped\nand don't appear in the
|
96
|
+
resulting gabc file.\n\n### 2. Whitespace\n\nEmpty lines are ignored.\n\n### 3.
|
97
|
+
Scores\n\nA new score begins at the beginning of a file or at a line containing\na
|
98
|
+
single keyword '\\score'.\n\nIt consists of\na header (optional, only permitted
|
99
|
+
at the beginning of a score)\nand music- and lyrics-lines.\nLines with music and
|
100
|
+
lyrics may appear in any order.\n\nScore ends with end of file or with explicit
|
101
|
+
beginning of a new score\nor another top-level element.\n\n#### 3.1 Score header\n\nScore
|
102
|
+
header consists of fields.\n\nEach header field is on it's own (one) line and consists
|
103
|
+
of\nidentifier, colon and value:\n\n`mode: 8`\n\nHeader field identifier may only
|
104
|
+
consist of alphanumeric characters,\nminus-sign and underscore. Value can contain
|
105
|
+
anything.\n\nScore header ends with first non-empty line identified by the parser\nas
|
106
|
+
music or lyrics.\n\nHeader field 'id' is special: if present, it is used as suffix\nof
|
107
|
+
the generated gabc file (instead of the default, which is\nnumeric position of the
|
108
|
+
score in the source document).\n\n#### 3.2 Lyrics\n\nSyntax of lyrics is inspired
|
109
|
+
by LilyPond.\nLyrics have to be manually syllabified. Default syllable delimiter\nis
|
110
|
+
double dash (minus) `--` with optional whitespace around.\n\n`cla -- ra ex stir
|
111
|
+
-- pe Da -- vid.`\n\nThe parser guesses meaning of the line by attempting to find\nsyllable
|
112
|
+
separator in it and by looking if it's alphanumeric\ncharacters contains something
|
113
|
+
that cannot be interpreted as music.\nIf any of these conditions is met, the line
|
114
|
+
is interpreted as lyrics.\n\nIf gly fails to guess your lyrics line correctly and
|
115
|
+
interprets\nit as music, place `\\lyrics` or short `\\l` at the beginning\nof the
|
116
|
+
unhappy line:\n\n`\\l a a a`\n\n#### 3.3 Music\n\nAny line appearing in a score
|
117
|
+
and not identified as header field\nor lyrics is music by default.\n\nMusic line
|
118
|
+
contains one or more music chunks separated by whitespace.\nFor music syntax see
|
119
|
+
[official gabc documentation][gabc] -\ngly doesn't change anything in this respect.\n\nMusic
|
120
|
+
chunks may be enclosed in parentheses as in gabc.\n(Of course you don't like the
|
121
|
+
parentheses and are happy that gly\nlet's you leave them out. But in some special
|
122
|
+
cases they come handy.)\n\n#### 3.4 Matching lyrics to music\n\nWhen processing
|
123
|
+
the gly source and producing gabc, music chunks\nare matched to lyric syllables.\n\nThere
|
124
|
+
are, however, a few special cases, to make it work conveniently:\n\nThese special
|
125
|
+
cases of music chunks don't get lyric syllable:\n\n* clef\n* music chunk containing
|
126
|
+
only a division - i.e. music chunk containing\n one of `,` , `;` , `:` , `::` alone\n\nException
|
127
|
+
to this rule are 'nonlyrical lyrics chunks'.\nCurrently there is only one built-in
|
128
|
+
nonlyrical lyric chunk:\nasterisk `*`.\nNormally it is treated as any other syllable,\nbut
|
129
|
+
if it meets a division, it is set as it's lyrics, while\na normal syllable wouldn't
|
130
|
+
be.\n\nIf you need to set some other syllable under a division,\nmake it 'nonlyrical'
|
131
|
+
by placing\nan exclamation mark at it's beginning, e.g. `!<i>Ps.</i>`\n\nIn the
|
132
|
+
other direction it is sometimes necessary to set a syllable\nnot matching any music
|
133
|
+
at all. In such cases empty music chunk\n`()` is what you need.\n\n### 4. Document
|
134
|
+
header\n\nEach gly document may optinally contain a document header.\nIt may appear
|
135
|
+
anywhere in the document, but best practice is to place\nit at the very beginning.\n\nDocument
|
136
|
+
header starts with keyword `\\header` and ends\nat the end of file or at the beginning
|
137
|
+
of another top-level element.\nThe syntax of it's content is the same\nas for [Score
|
138
|
+
header][].\n\nField 'title' in the document header is, if present,\nused by `gly
|
139
|
+
preview` as title of the generated pdf.\n\n## Run tests\n\nby executing `tests/run.rb`\n\n##
|
140
|
+
License\n\nMIT\n\n[gregorio]: https://github.com/gregorio-project/gregorio\n[gabc]:
|
141
|
+
http://gregorio-project.github.io/gabc/index.html\n[elisp]: /tree/master/elisp\n\n[opraem_boh]:
|
142
|
+
https://gist.github.com/igneus/1aed0b36e9b23b51526d\n"
|
86
143
|
email: jkb.pavlik@gmail.com
|
87
144
|
executables:
|
88
145
|
- gly
|
@@ -97,18 +154,26 @@ files:
|
|
97
154
|
- lib/gly/cli.rb~
|
98
155
|
- lib/gly/document.rb
|
99
156
|
- lib/gly/document.rb~
|
157
|
+
- lib/gly/document_gabc_convertor.rb
|
158
|
+
- lib/gly/document_gabc_convertor.rb~
|
100
159
|
- lib/gly/gabc_convertor.rb
|
101
160
|
- lib/gly/gabc_convertor.rb~
|
102
161
|
- lib/gly/headers.rb
|
103
162
|
- lib/gly/headers.rb~
|
163
|
+
- lib/gly/lister.rb
|
164
|
+
- lib/gly/lister.rb~
|
104
165
|
- lib/gly/lyrics.rb
|
105
166
|
- lib/gly/lyrics.rb~
|
106
167
|
- lib/gly/parsed_score.rb
|
107
168
|
- lib/gly/parsed_score.rb~
|
108
169
|
- lib/gly/parser.rb
|
109
170
|
- lib/gly/parser.rb~
|
171
|
+
- lib/gly/preview_generator.rb
|
172
|
+
- lib/gly/preview_generator.rb~
|
110
173
|
- lib/gly/string_helpers.rb
|
111
174
|
- lib/gly/string_helpers.rb~
|
175
|
+
- lib/gly/templates/lualatex_document.tex
|
176
|
+
- lib/gly/templates/lualatex_document.tex~
|
112
177
|
- tests/examples.rb
|
113
178
|
- tests/examples.rb~
|
114
179
|
- tests/examples/gly/expected/block_lyrics.gabc
|