gly 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1be7280dd39aa6117888f6779c409359202d8bb
4
- data.tar.gz: b5bc60c05106a3472b4fa296cc0787f1c804ab8f
3
+ metadata.gz: c2057f1f3a833d1b6bb382573386d7519fd1f83a
4
+ data.tar.gz: 2c770e056bc7c3c0a6a7aeb689a9888615334f17
5
5
  SHA512:
6
- metadata.gz: d695541f34baae56918b864e062387426889b55dbed9e1636688157c1b4eadec906556be053de0b4da8f3a588c1fe415a12104ae844a5045471bfd91dfcec32c
7
- data.tar.gz: 8aab9514701279b9e4ab1f00282e323902924c5f9b38fcf2ab1aa2fd9e534759a8a62220a38fa72d08ec0711eb78fb78c499760b97ea901458bcf93c326d7415
6
+ metadata.gz: 6860d75f1e55af109e10535f76b404d074b6bc9bafad0b13784f98ca706f448705a1116216598052b364802e6c7e3b3b4dde87557d5926d13417dbb69ab488ba
7
+ data.tar.gz: fd1df56ea96ca42c779086e8b495fcb4da4dddeb77fa13a87238506dd5044802f9486ba220a617efef925671fee73afa7cb98f620ea9f508c7df70c0a7df0180
data/lib/gly.rb CHANGED
@@ -7,7 +7,9 @@ headers
7
7
  lyrics
8
8
  document
9
9
  document_gabc_convertor
10
+ document_ly_convertor
10
11
  preview_generator
12
+ preview_builder
11
13
  lister
12
14
  string_helpers).each do |mod|
13
15
  require "gly/#{mod}"
@@ -1,16 +1,38 @@
1
1
  require 'thor'
2
2
 
3
+ begin
4
+ require 'grely'
5
+ rescue LoadError
6
+ end
7
+
3
8
  module Gly
4
9
  # implements the 'gly' executable
5
10
  class CLI < Thor
11
+ class_option :separator, aliases: :s, banner: 'syllable separator (default is double dash "--")'
12
+
6
13
  desc 'gabc FILE ...', 'convert gly to gabc'
7
14
  def gabc(*files)
8
- files.each {|f| DocumentGabcConvertor.new(Parser.new.parse(f)).convert }
15
+ files.each do |f|
16
+ DocumentGabcConvertor.new(parser.parse(f)).convert
17
+ end
9
18
  end
10
19
 
11
20
  desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
21
+ option :no_build, type: :boolean, aliases: :B, banner: 'only generate preview assets, don\'t compile them'
22
+ option :no_document, type: :boolean, aliases: :D, banner: 'produce main LaTeX file without document definition; in this case --no-build is applied automatically'
23
+ option :full_headers, type: :boolean, aliases: :h, banner: 'include full document and score headers'
24
+ option :template, aliases: :t, banner: 'use custom document template'
12
25
  def preview(*files)
13
- files.each {|f| PreviewGenerator.new.process(Parser.new.parse(f)) }
26
+ tpl = nil
27
+ tpl = File.read(options[:template]) if options[:template]
28
+
29
+ opts = options.to_h
30
+ opts[:suffix_always] = true
31
+
32
+ files.each do |f|
33
+ gen = PreviewGenerator.new template: tpl, options: opts
34
+ gen.process(parser.parse(f))
35
+ end
14
36
  end
15
37
 
16
38
  desc 'list FILE ...', 'list scores contained in files'
@@ -37,5 +59,34 @@ module Gly
37
59
 
38
60
  exit(lister.error? ? 1 : 0)
39
61
  end
62
+
63
+ desc 'ly FILE ...', 'transform gly document to lilypond document'
64
+ def ly(*files)
65
+ unless defined? LilypondConvertor
66
+ STDERR.puts "'lygre' gem not found. Please, install lygre in order to run 'gly ly'."
67
+ exit 1
68
+ end
69
+
70
+ files.each do |f|
71
+ DocumentLyConvertor.new(parser.parse(f)).convert
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def parser
78
+ Parser.new options[:separator]
79
+ end
80
+
81
+ class << self
82
+ # override Thor's default handler
83
+ def handle_no_command_error(command, has_namespace=$thor_runner)
84
+ if has_namespace
85
+ fail Thor::UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace."
86
+ else
87
+ fail Thor::UndefinedCommandError, "Could not find command #{command.inspect}. Did you mean 'gly preview #{command}' ?"
88
+ end
89
+ end
90
+ end
40
91
  end
41
92
  end
@@ -5,11 +5,35 @@ module Gly
5
5
  class Document
6
6
  def initialize
7
7
  @scores = []
8
+ @scores_by_id = {}
8
9
  @header = Headers.new
9
10
  @path = nil
10
11
  end
11
12
 
12
13
  attr_reader :scores, :header
13
14
  attr_accessor :path
15
+
16
+ def <<(score)
17
+ @scores << score
18
+
19
+ sid = score.headers['id']
20
+ if sid
21
+ if @scores_by_id.has_key? sid
22
+ raise ArgumentError.new("More than one score with id '#{sid}'.")
23
+ end
24
+
25
+ @scores_by_id[sid] = score
26
+ end
27
+
28
+ self
29
+ end
30
+
31
+ def [](key)
32
+ if key.is_a? Integer
33
+ @scores[key]
34
+ else
35
+ @scores_by_id[key]
36
+ end
37
+ end
14
38
  end
15
39
  end
@@ -1,7 +1,8 @@
1
1
  module Gly
2
2
  class DocumentGabcConvertor
3
- def initialize(document)
3
+ def initialize(document, **options)
4
4
  @doc = document
5
+ @options = options
5
6
  end
6
7
 
7
8
  def convert
@@ -24,8 +25,11 @@ module Gly
24
25
  private
25
26
 
26
27
  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?
28
+ if @doc.scores.size == 1 && !@options[:suffix_always]
29
+ score_id = ''
30
+ else
31
+ score_id = '_' + (score.headers['id'] || score_index.to_s)
32
+ end
29
33
 
30
34
  File.basename(@doc.path)
31
35
  .sub(/\.gly\Z/i, "#{score_id}.gabc")
@@ -0,0 +1,58 @@
1
+ require 'stringio'
2
+
3
+ module Gly
4
+ # Converts gly to modern-notation LilyPond
5
+ #
6
+ # expects the 'lygre' gem
7
+ # (which is only an optional dependency of gly)
8
+ class DocumentLyConvertor
9
+ def initialize(document)
10
+ @doc = document
11
+ end
12
+
13
+ def convert
14
+ gabcor = GabcConvertor.new
15
+ parser = GabcParser.new
16
+ lilyor = LilypondConvertor.new cadenza: true, version: false
17
+
18
+ ly_output = StringIO.new
19
+
20
+ ly_output.puts '\version "2.18.0"'
21
+ ly_output.puts
22
+ ly_output.puts header @doc.header
23
+ ly_output.puts
24
+ ly_output.puts default_style
25
+ ly_output.puts
26
+
27
+ @doc.scores.each do |score|
28
+ gabc = gabcor.convert(score).string
29
+ parsed_score = parser.parse gabc
30
+ begin
31
+ ly_output.puts lilyor.convert parsed_score.create_score
32
+ rescue NoMethodError
33
+ ly_output.puts "\\markup{error processing score \\italic{#{score.lyrics.readable}}}"
34
+ end
35
+
36
+ ly_output.puts
37
+ end
38
+
39
+ out_fname = File.basename(@doc.path) + '.ly'
40
+ File.open(out_fname, 'w') do |fw|
41
+ fw.puts ly_output.string
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def header(h)
48
+ fields = h.each_pair.collect do |k,v|
49
+ " #{k} = \"#{v}\""
50
+ end
51
+ "\\header {\n#{fields.join("\n")}\n}\n"
52
+ end
53
+
54
+ def default_style
55
+ '\layout { \context Score \override TimeSignature #\'stencil = ##f }'
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,37 @@
1
+ module Gly
2
+ class DocumentLyConvertor < 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
+ if @doc.scores.size == 1
28
+ score_id = ''
29
+ else
30
+ score_id = '_' + (score.headers['id'] || score_index.to_s)
31
+ end
32
+
33
+ File.basename(@doc.path)
34
+ .sub(/\.gly\Z/i, "#{score_id}.gabc")
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ require 'forwardable'
2
+
3
+ module Gly
4
+ # wraps the options Hash provided by Thor,
5
+ # adds querying methods
6
+ class Options
7
+ extend Forwardable
8
+
9
+ def initialize(opts)
10
+ @opts = opts
11
+ end
12
+
13
+
14
+ end
15
+ end
@@ -3,8 +3,8 @@ require 'stringio'
3
3
  module Gly
4
4
  # parses gly source
5
5
  class Parser
6
- def initialize(syllable_separator='--')
7
- @syllable_separator = syllable_separator
6
+ def initialize(syllable_separator=nil)
7
+ @syllable_separator = syllable_separator || '--'
8
8
  end
9
9
 
10
10
  def parse(source)
@@ -28,7 +28,7 @@ module Gly
28
28
  end
29
29
 
30
30
  def parse_str(str)
31
- parse_io(StringIO.new(source))
31
+ parse_io(StringIO.new(str))
32
32
  end
33
33
 
34
34
  def parse_io(io)
@@ -52,6 +52,10 @@ module Gly
52
52
  @score = @doc.header
53
53
  elsif header_line? line
54
54
  parse_header line
55
+ elsif explicit_lyrics? line
56
+ parse_lyrics line
57
+ elsif explicit_music? line
58
+ parse_music line
55
59
  elsif lyrics_line? line
56
60
  parse_lyrics line
57
61
  else
@@ -88,8 +92,18 @@ module Gly
88
92
 
89
93
  EXPLICIT_LYRICS_RE = /\A\\l(yrics)?\s+/
90
94
 
95
+ def explicit_lyrics?(str)
96
+ str =~ EXPLICIT_LYRICS_RE
97
+ end
98
+
99
+ EXPLICIT_MUSIC_RE = /\A\\m(usic)?\s+/
100
+
101
+ def explicit_music?(str)
102
+ str =~ EXPLICIT_MUSIC_RE
103
+ end
104
+
91
105
  def lyrics_line?(str)
92
- str =~ EXPLICIT_LYRICS_RE || str.include?(@syllable_separator) || contains_unmusical_letters?(str)
106
+ !contains_square_brackets?(str) && (str.include?(@syllable_separator) || contains_unmusical_letters?(str))
93
107
  end
94
108
 
95
109
  def in_header_block?
@@ -98,7 +112,11 @@ module Gly
98
112
 
99
113
  def contains_unmusical_letters?(str)
100
114
  letters = str.gsub(/[\W\d_]+/, '')
101
- letters !~ /\A[a-mvwoxz]*\Z/i # incomplete gabc music letters!
115
+ letters !~ /\A[a-morsvwxz]*\Z/i # incomplete gabc music letters!
116
+ end
117
+
118
+ def contains_square_brackets?(str)
119
+ str.include? '['
102
120
  end
103
121
 
104
122
  def parse_header(str)
@@ -118,6 +136,8 @@ module Gly
118
136
  end
119
137
 
120
138
  def parse_music(str)
139
+ str = str.sub(EXPLICIT_MUSIC_RE, '')
140
+
121
141
  # music chunks: split by whitespace out of brackets
122
142
  StringHelpers.music_split(str).each do |chunk|
123
143
  @score.music << chunk
@@ -126,7 +146,7 @@ module Gly
126
146
 
127
147
  def push_score
128
148
  if @score.is_a?(ParsedScore) && !@score.empty?
129
- @doc.scores << @score
149
+ @doc << @score
130
150
  end
131
151
  end
132
152
  end
@@ -0,0 +1,38 @@
1
+ module Gly
2
+ # *builds* the pdf preview from assets prepared
3
+ # by PreviewGenerator
4
+ class PreviewBuilder
5
+ def initialize
6
+ @gabcs = []
7
+ @main_tex = nil
8
+ end
9
+
10
+ def add_gabc(path)
11
+ @gabcs << path
12
+ end
13
+
14
+ attr_accessor :main_tex
15
+
16
+ def build
17
+ @gabcs.each do |g|
18
+ exec 'gregorio', g
19
+ end
20
+
21
+ exec 'lualatex', @main_tex
22
+ end
23
+
24
+ private
25
+
26
+ def exec(progname, *args)
27
+ ok = system progname, *args
28
+ unless ok
29
+ case $?.exitstatus
30
+ when 127
31
+ STDERR.puts "'#{progname}' is required for this gly command to work, but it was not found. Please, ensure that '#{progname}' is installed in one of the directories listed in your PATH and try again."
32
+ else
33
+ STDERR.puts "'#{progname}' exited with exit code #{$?.to_i}. Let's continue and see what happens ..."
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ module Gly
2
+ # *builds* the pdf preview from assets prepared
3
+ # by PreviewGenerator
4
+ class PreviewBuilder
5
+ def initialize
6
+ @gabcs = []
7
+ @main_tex = nil
8
+ end
9
+
10
+ def add_gabc(path)
11
+ @gabcs << path
12
+ end
13
+
14
+ attr_writer :main_tex
15
+
16
+ def build
17
+ @gabcs.each do |g|
18
+ exec 'gregorio', g
19
+ end
20
+
21
+ exec 'lualatex', @main_tex
22
+ end
23
+
24
+ private
25
+
26
+ def exec(progname, *args)
27
+ ok = system progname, *args
28
+ unless ok
29
+ case $?.exitstatus
30
+ when 127
31
+ STDERR.puts "'#{progname}' is required for this gly command to work, but it was not found. Please, ensure that '#{progname}' is installed in one of the directories listed in your PATH and try again."
32
+ else
33
+ STDERR.puts "'#{progname}' exited with exit code #{$?.to_i}. Let's continue and see what happens ..."
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -4,18 +4,38 @@ module Gly
4
4
  # Takes Gly::Document, builds a pdf preview
5
5
  # (or at least generates all necessary assets)
6
6
  class PreviewGenerator
7
+ def initialize(**options)
8
+ @preview_dest = nil
9
+
10
+ @template = options.delete(:template) || default_template
11
+ @builder = options.delete(:builder) || PreviewBuilder.new
12
+ @options = options.delete(:options) || {}
13
+ end
14
+
15
+ # IO to which the main LaTeX document should be written.
16
+ # If not set, a file will be created with name based on
17
+ # the source file name.
18
+ attr_accessor :preview_dest
19
+
7
20
  def process(document)
8
- tpl_name = File.join(File.dirname(__FILE__), 'templates/lualatex_document.tex')
9
- template = File.read(tpl_name)
21
+ convertor = DocumentGabcConvertor.new(document, **@options)
22
+ convertor.convert
10
23
 
11
24
  doc_body = fw = StringIO.new
12
25
 
13
- convertor = DocumentGabcConvertor.new(document)
26
+ if @options[:full_headers]
27
+ fw.puts header_table document.header
28
+ end
29
+
14
30
  convertor.each_score_with_gabcname do |score, gabc_fname|
15
- system "gregorio #{gabc_fname}"
31
+ @builder.add_gabc gabc_fname
32
+
33
+ if @options[:full_headers]
34
+ fw.puts header_table score.headers
35
+ end
36
+
16
37
  gtex_fname = gabc_fname.sub /\.gabc/i, ''
17
- piece_title = %w(book manuscript arranger author).collect do |m|
18
- score.headers[m]
38
+ piece_title = %w(book manuscript arranger author).collect do |m| score.headers[m]
19
39
  end.delete_if(&:nil?).join ', '
20
40
  fw.puts "\\commentary{\\footnotesize{#{piece_title}}}\n" unless piece_title.empty?
21
41
 
@@ -30,19 +50,70 @@ module Gly
30
50
  fw.puts "\\includescore{#{gtex_fname}}\n\\vspace{1cm}"
31
51
  end
32
52
 
33
- replacements = {
34
- title: document.header['title'],
35
- maketitle: (document.header['title'] && '\maketitle'),
36
- body: doc_body.string
37
- }
38
- tex = template % replacements
53
+ if @options['no_document']
54
+ tex = doc_body.string
55
+ else
56
+ replacements = {
57
+ glyvars: header_variables(document.header),
58
+ body: doc_body.string
59
+ }
60
+ tex = @template % replacements
61
+ end
62
+
63
+ with_preview_io(document.path) do |fw|
64
+ @builder.main_tex = fw.path if fw.respond_to? :path
39
65
 
40
- out_fname = File.basename(document.path).sub(/\.gly\Z/i, '.tex')
41
- File.open(out_fname, 'w') do |fw|
42
66
  fw.puts tex
43
67
  end
44
68
 
45
- system "lualatex #{out_fname}"
69
+ build_disabled = @options['no_build'] || @options['no_document']
70
+ if @builder.main_tex && !build_disabled
71
+ @builder.build
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def with_preview_io(src_name)
78
+ if @preview_dest
79
+ yield @preview_dest
80
+ return
81
+ end
82
+
83
+ File.open(preview_fname(src_name), 'w') do |fw|
84
+ yield fw
85
+ end
86
+ end
87
+
88
+ def preview_fname(src_name)
89
+ File.basename(src_name).sub(/\.gly\Z/i, '.tex')
90
+ end
91
+
92
+ def default_template
93
+ File.read(File.join(File.dirname(__FILE__), 'templates/lualatex_document.tex'))
94
+ end
95
+
96
+ # full header of a score/file as table
97
+ def header_table(header)
98
+ return '' if header.empty?
99
+
100
+ cols = header.each_pair.collect {|k,v| "#{k} & #{v} \\\\" }
101
+
102
+ "\\begin{tabular}{ | r | l | } \\hline %s \\hline \\end{tabular}\n\n" % cols.join("\\hline\n")
103
+ end
104
+
105
+ # transforms header to LaTeX command definitions
106
+ def header_variables(header)
107
+ header.each_pair.collect do |k,v|
108
+ '\newcommand{\%s}{%s}' % [latex_cmd_name(k), v]
109
+ end.join("\n")
110
+ end
111
+
112
+ def latex_cmd_name(header_name)
113
+ sanitized = header_name
114
+ .gsub(/\s+/, '')
115
+ .gsub(/[-_]+(\w)/) {|m| m[1].upcase }
116
+ prefixed = 'gly' + sanitized[0].upcase + sanitized[1..-1]
46
117
  end
47
118
  end
48
119
  end
@@ -5,23 +5,47 @@
5
5
  \usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
6
6
 
7
7
  \usepackage{fontspec}
8
+ \usepackage[hidelinks]{hyperref}
9
+ \usepackage{etoolbox}
8
10
 
9
11
  %% for gregorio
10
12
  \usepackage{luatextra}
11
13
  \usepackage{graphicx}
12
14
  \usepackage{gregoriotex}
13
15
 
14
- \title{%{title}}
16
+ %{glyvars}
17
+
18
+ %% header fields like title, transcription-date etc.
19
+ %% are translated by 'gly preview' to latex commands like
20
+ %% \glyTitle \glyTranscriptionDate etc.
21
+ %% (All of your document header fields are handled this way.)
22
+ %% These commands can be used for various purposes,
23
+ %% but we will use them only to assemble document title:
24
+ \newcommand{\glyPreviewTitle}{
25
+ \begin{center}
26
+ \ifdef{\glyTitle}{{\huge \glyTitle}}{}
27
+
28
+ \ifdef{\glyAuthor}{\emph{\glyAuthor}}{}
29
+
30
+ \ifdef{\glyYear}{\glyYear}{}
31
+
32
+ \vspace{6mm}
33
+ \end{center}
34
+ }
35
+
36
+ \newcommand{\glyPreviewTagline}{
37
+ \begin{center}
38
+ \texttt{\href{https://github.com/igneus/gly}{gly preview} \today}
39
+ \end{center}
40
+ }
15
41
 
16
42
  \begin{document}
17
43
 
18
- %{maketitle}
44
+ \glyPreviewTitle
19
45
 
20
46
  %{body}
21
47
 
22
48
  %% tagline
23
49
  \vfill
24
- \begin{center}
25
- \texttt{gly preview https://github.com/igneus/gly}
26
- \end{center}
50
+ \glyPreviewTagline
27
51
  \end{document}
@@ -0,0 +1,2 @@
1
+ %%
2
+ (č) (ř) (š)
@@ -0,0 +1 @@
1
+ \music č ř š
@@ -0,0 +1,3 @@
1
+ % the result will be invalid gabc, but gly shouldn't care
2
+ % if it is explicitly told to handle the line as music
3
+ \music č ř š
@@ -0,0 +1,3 @@
1
+ % the result will be invalid gabc, but gly shouldn't care
2
+ % if it is explicitly told to handle the line as music
3
+ \music č ř š
@@ -7,8 +7,10 @@ class StringHelpersTest < GlyTest
7
7
  ['single_chunk', 'a', ['a']],
8
8
  ['simple_whitespace', 'aa aa', ['aa', 'aa']],
9
9
  ['leading_trailing_whitespace', ' a a ', ['a', 'a']],
10
+ ['repeated_inner_whitespace', 'a a', ['a', 'a']],
10
11
  ['bracketted', '(a)', ['a']],
11
- ['empty', '()', ['']]
12
+ ['empty', '()', ['']],
13
+ ['nested', '(a(b))', ['a(b', ')']] # nesting isn't supported
12
14
  ]
13
15
 
14
16
  examples.each do |e|
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.2
4
+ version: 0.0.3
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-16 00:00:00.000000000 Z
11
+ date: 2016-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -38,108 +38,7 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1'
41
- description: "# gly\n\nWriter-friendly Gregorian notation format compiling to gabc.\n\n*GLY*
42
- is an acronym of \"Gregorio for liLYponders\" or\n\"Gregorio with separate LYrics.\n\n##
43
- Why\n\nOne of the most popular solutions for typesetting quadratic\nnotation used
44
- for the Gregorian chant is [Gregorio][gregorio].\n\nHowever, I have several objections
45
- against it's input format.\nThat led me to designing an alternative, Gregorio-inspired\nnotation
46
- format, which compiles to pure Gregorio GABC\n(which I don't like writing manually).\n\n##
47
- Features\n\n* music separated from lyrics\n * no need of the ubiquitous and tedious
48
- parentheses\n * separation of \"material and form\" -> easy copying of the music
49
- or\n lyrics alone is possible (useful for a composer)\n * syllabified lyrics
50
- entered in a format inspired by LilyPond\n* music and lyrics can be interspersed
51
- as needed\n* no semicolons in the header\n* custom header fields supported (commented
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
72
- gloriosae\n office-part: laudes, 1. ant.\n occasion: In Nativitate B. Mariae
73
- Virginis\n book: Antiphonale Romanum 1912, pg. 704\n mode: 8\n initial-style:
74
- 1\n \n c4 g g g gd f gh g g ,\n NA -- TI -- VI -- TAS glo -- ri -- ósae
75
- *\n \n g g hi gh gf f ;\n Vír -- gi -- nis Ma -- rí -- ae,\n \n f
76
- g h h hiwji hg g ;\n ex sé -- mi -- ne A -- bra -- hae,\n \n gh~ g g g
77
- fe/fgf d d ;\n or -- tae de tri -- bu Ju -- da,\n \n df!gh g f hg~ hi h
78
- g ::\n cla -- ra ex stir -- pe Da -- vid.\n\nOther arrangements are also possible.
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
84
- preview FILE1 ...`\n\nAttempts to create a pdf document with all scores contained
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
87
- highlighting for gly][elisp]\n\n![Editing gly in emacs](/doc/img/gly_emacs_scr.png)\n\n##
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"
41
+ description:
143
42
  email: jkb.pavlik@gmail.com
144
43
  executables:
145
44
  - gly
@@ -156,6 +55,8 @@ files:
156
55
  - lib/gly/document.rb~
157
56
  - lib/gly/document_gabc_convertor.rb
158
57
  - lib/gly/document_gabc_convertor.rb~
58
+ - lib/gly/document_ly_convertor.rb
59
+ - lib/gly/document_ly_convertor.rb~
159
60
  - lib/gly/gabc_convertor.rb
160
61
  - lib/gly/gabc_convertor.rb~
161
62
  - lib/gly/headers.rb
@@ -164,10 +65,13 @@ files:
164
65
  - lib/gly/lister.rb~
165
66
  - lib/gly/lyrics.rb
166
67
  - lib/gly/lyrics.rb~
68
+ - lib/gly/options.rb~
167
69
  - lib/gly/parsed_score.rb
168
70
  - lib/gly/parsed_score.rb~
169
71
  - lib/gly/parser.rb
170
72
  - lib/gly/parser.rb~
73
+ - lib/gly/preview_builder.rb
74
+ - lib/gly/preview_builder.rb~
171
75
  - lib/gly/preview_generator.rb
172
76
  - lib/gly/preview_generator.rb~
173
77
  - lib/gly/string_helpers.rb
@@ -181,9 +85,10 @@ files:
181
85
  - tests/examples/gly/expected/differentiae.gabc~
182
86
  - tests/examples/gly/expected/document_header.gabc
183
87
  - tests/examples/gly/expected/document_header.gabc~
184
- - tests/examples/gly/expected/empty.gabc
185
88
  - tests/examples/gly/expected/explicit_lyrics.gabc
186
89
  - tests/examples/gly/expected/explicit_lyrics.gabc~
90
+ - tests/examples/gly/expected/explicit_music.gabc
91
+ - tests/examples/gly/expected/explicit_music.gabc~
187
92
  - tests/examples/gly/expected/explicit_score.gabc
188
93
  - tests/examples/gly/expected/force_divisio_lyrics.gabc
189
94
  - tests/examples/gly/expected/header.gabc
@@ -203,8 +108,9 @@ files:
203
108
  - tests/examples/gly/given/differentiae.gly
204
109
  - tests/examples/gly/given/differentiae.gly~
205
110
  - tests/examples/gly/given/document_header.gly
206
- - tests/examples/gly/given/empty.gly
207
111
  - tests/examples/gly/given/explicit_lyrics.gly
112
+ - tests/examples/gly/given/explicit_music.gly
113
+ - tests/examples/gly/given/explicit_music.gly~
208
114
  - tests/examples/gly/given/explicit_score.gly
209
115
  - tests/examples/gly/given/force_divisio_lyrics.gly
210
116
  - tests/examples/gly/given/header.gly
File without changes