gly 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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\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
|