gly 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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