gly 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/bin/gly +11 -0
  3. data/bin/glyfy +4 -0
  4. data/lib/gly.rb +5 -0
  5. data/lib/gly.rb~ +5 -0
  6. data/lib/gly/cli.rb +94 -0
  7. data/lib/gly/cli.rb~ +13 -0
  8. data/lib/gly/document.rb +15 -0
  9. data/lib/gly/document.rb~ +8 -0
  10. data/lib/gly/gabc_convertor.rb +60 -0
  11. data/lib/gly/gabc_convertor.rb~ +5 -0
  12. data/lib/gly/headers.rb +80 -0
  13. data/lib/gly/headers.rb~ +13 -0
  14. data/lib/gly/lyrics.rb +39 -0
  15. data/lib/gly/lyrics.rb~ +21 -0
  16. data/lib/gly/parsed_score.rb +16 -0
  17. data/lib/gly/parsed_score.rb~ +6 -0
  18. data/lib/gly/parser.rb +106 -0
  19. data/lib/gly/parser.rb~ +6 -0
  20. data/lib/gly/string_helpers.rb +34 -0
  21. data/lib/gly/string_helpers.rb~ +36 -0
  22. data/tests/examples.rb +28 -0
  23. data/tests/examples.rb~ +9 -0
  24. data/tests/examples/gly/expected/block_lyrics.gabc +2 -0
  25. data/tests/examples/gly/expected/differentiae.gabc +2 -0
  26. data/tests/examples/gly/expected/differentiae.gabc~ +2 -0
  27. data/tests/examples/gly/expected/document_header.gabc +4 -0
  28. data/tests/examples/gly/expected/document_header.gabc~ +4 -0
  29. data/tests/examples/gly/expected/empty.gabc +1 -0
  30. data/tests/examples/gly/expected/explicit_lyrics.gabc +2 -0
  31. data/tests/examples/gly/expected/explicit_lyrics.gabc~ +2 -0
  32. data/tests/examples/gly/expected/explicit_score.gabc +2 -0
  33. data/tests/examples/gly/expected/force_divisio_lyrics.gabc +2 -0
  34. data/tests/examples/gly/expected/header.gabc +3 -0
  35. data/tests/examples/gly/expected/header_unofficial.gabc +4 -0
  36. data/tests/examples/gly/expected/header_unofficial.gabc~ +3 -0
  37. data/tests/examples/gly/expected/multiline.gabc +2 -0
  38. data/tests/examples/gly/expected/multiline_interlaced.gabc +2 -0
  39. data/tests/examples/gly/expected/nonlyrical_lyrics.gabc +2 -0
  40. data/tests/examples/gly/expected/nonlyrical_lyrics.gabc~ +2 -0
  41. data/tests/examples/gly/expected/notes_bracketted.gabc +2 -0
  42. data/tests/examples/gly/expected/notes_bracketted.gabc~ +1 -0
  43. data/tests/examples/gly/expected/notes_only.gabc +2 -0
  44. data/tests/examples/gly/expected/notes_with_lyrics.gabc +2 -0
  45. data/tests/examples/gly/expected/notes_with_lyrics.gabc~ +2 -0
  46. data/tests/examples/gly/expected/unsyllabified_lyrics.gabc +2 -0
  47. data/tests/examples/gly/given/block_lyrics.gly +3 -0
  48. data/tests/examples/gly/given/differentiae.gly +3 -0
  49. data/tests/examples/gly/given/differentiae.gly~ +3 -0
  50. data/tests/examples/gly/given/document_header.gly +9 -0
  51. data/tests/examples/gly/given/empty.gly +0 -0
  52. data/tests/examples/gly/given/explicit_lyrics.gly +2 -0
  53. data/tests/examples/gly/given/explicit_score.gly +2 -0
  54. data/tests/examples/gly/given/force_divisio_lyrics.gly +2 -0
  55. data/tests/examples/gly/given/header.gly +2 -0
  56. data/tests/examples/gly/given/header_unofficial.gly +3 -0
  57. data/tests/examples/gly/given/header_unofficial.gly~ +2 -0
  58. data/tests/examples/gly/given/multiline.gly +4 -0
  59. data/tests/examples/gly/given/multiline_interlaced.gly +5 -0
  60. data/tests/examples/gly/given/multiline_interlaced.gly~ +4 -0
  61. data/tests/examples/gly/given/nonlyrical_lyrics.gly +2 -0
  62. data/tests/examples/gly/given/nonlyrical_lyrics.gly~ +2 -0
  63. data/tests/examples/gly/given/notes_bracketted.gabc~ +1 -0
  64. data/tests/examples/gly/given/notes_bracketted.gly +1 -0
  65. data/tests/examples/gly/given/notes_only.gly +1 -0
  66. data/tests/examples/gly/given/notes_with_lyrics.gly +2 -0
  67. data/tests/examples/gly/given/unsyllabified_lyrics.gly +2 -0
  68. data/tests/examples/gly/no_crash/document_header.gly~ +4 -0
  69. data/tests/examples/gly/no_crash/multiple_scores.gly +10 -0
  70. data/tests/examples/gly/no_crash/multiple_scores.gly~ +7 -0
  71. data/tests/no_crash.rb +20 -0
  72. data/tests/programmed_examples.rb~ +14 -0
  73. data/tests/run.rb +3 -0
  74. data/tests/run.rb~ +3 -0
  75. data/tests/string_helpers_test.rb +19 -0
  76. data/tests/string_helpers_test.rb~ +9 -0
  77. data/tests/test_helper.rb +17 -0
  78. metadata +193 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 341817505f45deff8e6c9815061fa6cffa571858
4
+ data.tar.gz: 64ff6cb093190e69c37ed874af5c9b34e1499c3f
5
+ SHA512:
6
+ metadata.gz: 41ba343096d37f095b7e9ca1724a8692d7bf4ca6dfcac689e681e9c61c1c95067ec42ba18f6fa8de259f6bf70b1960e5108f1f2503be1044e8df64bb0ec6fa96
7
+ data.tar.gz: a14a88215832854321ecadff5fd0f0c01a057d3ad991873aede7d91b879da35edfe197bca214144a55a4ad4ff893d0a365505849ce62020b9da7361a5fe9a2e8
data/bin/gly ADDED
@@ -0,0 +1,11 @@
1
+ #!/bin/env ruby
2
+
3
+ # gly
4
+ # takes gly file, produces gabc file(s);
5
+ # optionally makes a simple tex file including it(them) and
6
+ # builds a pdf.
7
+
8
+ require 'gly'
9
+ require 'gly/cli'
10
+
11
+ Gly::CLI.start ARGV
@@ -0,0 +1,4 @@
1
+ #!/bin/env ruby
2
+
3
+ # glyfy
4
+ # takes gabc file(s), produces gly file
@@ -0,0 +1,5 @@
1
+ module Gly; end
2
+
3
+ %w(parser gabc_convertor parsed_score headers lyrics document string_helpers).each do |mod|
4
+ require "gly/#{mod}"
5
+ end
@@ -0,0 +1,5 @@
1
+ module Gly; end
2
+
3
+ %w(parser gabc_convertor).each do |mod|
4
+ require "lyv/#{mod}"
5
+ end
@@ -0,0 +1,94 @@
1
+ require 'thor'
2
+
3
+ module Gly
4
+ # implements the 'gly' executable
5
+ class CLI < Thor
6
+ desc 'gabc FILE ...', 'convert gly to gabc'
7
+ def gabc(*files)
8
+ files.each {|f| gabc_convert(parse(f)) }
9
+ end
10
+
11
+ desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
12
+ def preview(*files)
13
+ files.each {|f| make_preview f }
14
+ end
15
+
16
+ private
17
+
18
+ def parse(gly_file)
19
+ document = File.open(gly_file) do |fr|
20
+ Parser.new.parse(fr)
21
+ end
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?
33
+ 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
+
48
+ % for gregorio
49
+ \\usepackage{luatextra}
50
+ \\usepackage{graphicx}
51
+ \\usepackage{gregoriotex}
52
+
53
+ \\title{#{doc.header['title']}}
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
79
+ end
80
+
81
+ fw.puts "\\includescore{#{gtex_fname}}\n\\vspace{1cm}"
82
+ 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}"
89
+ end
90
+
91
+ system "lualatex #{tex_fname}"
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,13 @@
1
+ module Gly
2
+ class CLI < Thor
3
+ desc 'gabc FILE ...', 'convert gly to gabc'
4
+ def gabc(*files)
5
+
6
+ end
7
+
8
+ desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
9
+ def preview(*files)
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Gly
2
+ # Result of parsing of a gly file.
3
+ # Usually contains one or more scores
4
+ # and possibly a document header.
5
+ class Document
6
+ def initialize
7
+ @scores = []
8
+ @header = Headers.new
9
+ @path = nil
10
+ end
11
+
12
+ attr_reader :scores, :header
13
+ attr_accessor :path
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ module Gly
2
+ # Result of parsing of a gly file.
3
+ # Usually contains one or more scores
4
+ # and possibly a document header.
5
+ class Document
6
+
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ module Gly
2
+ # converts parsed gly to gabc
3
+ class GabcConvertor
4
+ def convert(score, out=StringIO.new)
5
+ score.headers.each_pair do |key,value|
6
+ out.print '% ' unless Headers.gregorio_supported?(key)
7
+ out.puts "#{key}: #{value};"
8
+ end
9
+
10
+ out.puts '%%'
11
+
12
+ lyric_enum = score.lyrics.each_syllable.to_enum
13
+ score.music.each_with_index do |mus_chunk,i|
14
+ begin
15
+ next_syl = lyric_enum.peek
16
+ rescue StopIteration
17
+ next_syl = ''
18
+ end
19
+
20
+ if clef?(mus_chunk) ||
21
+ (nonlyrical_chunk?(mus_chunk) && ! nonlyrical_lyrics?(next_syl))
22
+ # music chunk normally not having lyrics
23
+ out.print ' ' if i != 0
24
+ else
25
+ # regular music chunk
26
+ begin
27
+ out.print strip_directives lyric_enum.next
28
+ rescue StopIteration
29
+ out.print ' ' if i != 0
30
+ end
31
+ end
32
+ out.print "(#{mus_chunk})"
33
+ # out.puts if differentia?(mus_chunk) # newline after each differentia
34
+ end
35
+
36
+ return out
37
+ end
38
+
39
+ def clef?(chunk)
40
+ chunk =~ /\A[cf][1-4]\Z/
41
+ end
42
+
43
+ def differentia?(chunk)
44
+ chunk =~ /\A*[,;:]+\Z/ # differentia
45
+ end
46
+
47
+ # is the given music chunk capable of bearing lyrics?
48
+ def nonlyrical_chunk?(chunk)
49
+ differentia? chunk
50
+ end
51
+
52
+ def nonlyrical_lyrics?(syl)
53
+ syl =~ /\A\s*!/ || syl =~ /\A\s*\*\Z/
54
+ end
55
+
56
+ def strip_directives(syl)
57
+ syl.sub(/(\s*)!/, '\1') # exclamation mark at the beginning - place even under nonlyrical music chunk
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ module Gly
2
+ # converts parsed gly to gabc
3
+ class GabcConvertor
4
+ end
5
+ end
@@ -0,0 +1,80 @@
1
+ require 'forwardable'
2
+
3
+ module Gly
4
+ # score or document header
5
+ class Headers
6
+ extend Forwardable
7
+
8
+ # header fields supported by gregorio.
9
+ # Based on http://gregorio-project.github.io/gabc/index.html#header
10
+ # last checked 2015-12-06
11
+ GREGORIO_HEADERS = %w(
12
+ name
13
+ gabc-copyright
14
+ score-copyright
15
+ office-part
16
+ occasion
17
+ meter
18
+ commentary
19
+ arranger
20
+ gabc-version
21
+ author
22
+ date
23
+ manuscript
24
+ manuscript-reference
25
+ manuscript-storage-place
26
+ book
27
+ transcriber
28
+ transcription-date
29
+ gregoriotex-font
30
+ mode
31
+ initial-style
32
+ centering-scheme
33
+ user-notes
34
+ annotation
35
+ )
36
+
37
+ def initialize
38
+ # for quick lookup. Only holds a single value per header
39
+ @headers = {}
40
+ # holds order and as many values for a header as given in the
41
+ # score (typically annotation occurs more than once)
42
+ @pairs = []
43
+ end
44
+
45
+ def []=(key, value)
46
+ @headers[key] = value
47
+ @pairs << [key, value]
48
+ end
49
+
50
+ def_delegator :@headers, :[]
51
+ def_delegator :@pairs, :empty?
52
+
53
+ # some header fields may appear more than once;
54
+ # this method provides access to values of all occurrences
55
+ # of a given header field
56
+ def each_value(key)
57
+ return to_enum(:each_value, key) unless block_given?
58
+
59
+ each_pair do |k,v|
60
+ yield v if k == key
61
+ end
62
+ end
63
+
64
+ def each_pair
65
+ return to_enum(:each_pair) unless block_given?
66
+
67
+ @pairs.each do |k|
68
+ yield k[0], k[1]
69
+ end
70
+ end
71
+
72
+ def self.gregorio_supported?(key)
73
+ GREGORIO_HEADERS.include? key
74
+ end
75
+
76
+ def headers
77
+ self
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,13 @@
1
+ module Gly
2
+ class Headers
3
+ def initialize
4
+ @headers = {}
5
+ @keys_order = []
6
+ end
7
+
8
+ def []=(key, value)
9
+ @headers[key] = value
10
+ @keys_order << key
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ require 'forwardable'
2
+
3
+ module Gly
4
+ class Lyrics
5
+ extend Forwardable
6
+
7
+ def initialize
8
+ @words = []
9
+ end
10
+
11
+ def each_syllable
12
+ return enum_for(:each_syllable) unless block_given?
13
+
14
+ @words.each do |w|
15
+ w.each_syllable.each_with_index do |s,si|
16
+ if si == 0
17
+ yield ' ' + s
18
+ else
19
+ yield s
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def_delegator :@words, :each, :each_word
26
+ def_delegators :@words, :empty?, :<<
27
+ end
28
+
29
+ class Word
30
+ extend Forwardable
31
+
32
+ def initialize(syllables=[])
33
+ @syllables = syllables
34
+ end
35
+
36
+ def_delegators :@syllables, :<<, :push
37
+ def_delegator :@syllables, :each, :each_syllable
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ require 'forwardable'
2
+
3
+ module Gly
4
+ class Lyrics
5
+ def initialize
6
+ @words = []
7
+ end
8
+
9
+ def_delegator :@words, :each
10
+ alias_method :each_word, :each
11
+ end
12
+
13
+ class Word
14
+ def initialize
15
+ @syllables = []
16
+ end
17
+
18
+ def_delegator :@syllables, :<<, :push, :each
19
+ alias_method :each_syllable, :each
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module Gly
2
+ # result of a gly score parsing
3
+ class ParsedScore
4
+ def initialize
5
+ @headers = Headers.new
6
+ @lyrics = Lyrics.new
7
+ @music = []
8
+ end
9
+
10
+ attr_reader :headers, :lyrics, :music
11
+
12
+ def empty?
13
+ @headers.empty? && @lyrics.empty? && @music.empty?
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ # result of a gly score parsing
2
+ module Gly
3
+ class ParsedScore
4
+ attr_reader :headers, :lyrics, :notes
5
+ end
6
+ end
@@ -0,0 +1,106 @@
1
+ module Gly
2
+ # parses gly source
3
+ class Parser
4
+ SYLLABLE_SEP = '--'
5
+
6
+ def parse(io)
7
+ @doc = Document.new
8
+ @score = ParsedScore.new
9
+
10
+ if io.respond_to? :path
11
+ @doc.path = io.path
12
+ end
13
+
14
+ io.each do |line|
15
+ line = strip_comments(line)
16
+
17
+ if empty? line
18
+ next
19
+ elsif new_score? line
20
+ push_score
21
+ @score = ParsedScore.new
22
+ elsif header_start? line
23
+ push_score
24
+ @score = @doc.header
25
+ elsif header_line? line
26
+ parse_header line
27
+ elsif lyrics_line? line
28
+ parse_lyrics line
29
+ else
30
+ parse_music line
31
+ end
32
+ end
33
+
34
+ push_score
35
+
36
+ return @doc
37
+ end
38
+
39
+ private
40
+
41
+ def empty?(str)
42
+ str =~ /\A\s*\Z/
43
+ end
44
+
45
+ def new_score?(str)
46
+ str =~ /\A\s*\\score/
47
+ end
48
+
49
+ def header_start?(str)
50
+ str =~ /\A\s*\\header/
51
+ end
52
+
53
+ def strip_comments(str)
54
+ str.sub(/%.*\Z/, '')
55
+ end
56
+
57
+ def header_line?(str)
58
+ in_header_block? || @score.lyrics.empty? && @score.music.empty? && str =~ /\w+:\s*./
59
+ end
60
+
61
+ EXPLICIT_LYRICS_RE = /\A\\l(yrics)?\s+/
62
+
63
+ def lyrics_line?(str)
64
+ str =~ EXPLICIT_LYRICS_RE || str.include?(SYLLABLE_SEP) || contains_unmusical_letters?(str)
65
+ end
66
+
67
+ def in_header_block?
68
+ @score.is_a? Headers
69
+ end
70
+
71
+ def contains_unmusical_letters?(str)
72
+ letters = str.gsub(/[\W\d_]+/, '')
73
+ letters !~ /\A[a-mvwoxz]*\Z/i # incomplete gabc music letters!
74
+ end
75
+
76
+ def parse_header(str)
77
+ hid, hvalue = str.split(':').collect(&:strip)
78
+ @score.headers[hid] = hvalue
79
+ end
80
+
81
+ def parse_lyrics(str)
82
+ # words: split by whitespace not being part of syllable
83
+ # separator
84
+ str
85
+ .sub(EXPLICIT_LYRICS_RE, '')
86
+ .split(/(?<!#{SYLLABLE_SEP})\s+(?!#{SYLLABLE_SEP})/)
87
+ .each do |word|
88
+ @score.lyrics << Word.new(word.split(/\s*#{SYLLABLE_SEP}\s*/))
89
+ end
90
+ end
91
+
92
+ def parse_music(str)
93
+ # music chunks: split by whitespace out of brackets
94
+ str.split(/\s+/).each do |chunk|
95
+ chunk.sub!(/\A\((.*?)\)\Z/, '\1') # unparenthesize
96
+ @score.music << chunk
97
+ end
98
+ end
99
+
100
+ def push_score
101
+ if @score.is_a?(ParsedScore) && !@score.empty?
102
+ @doc.scores << @score
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,6 @@
1
+ module Gly
2
+ # parses gly source
3
+ class Parser
4
+
5
+ end
6
+ end
@@ -0,0 +1,34 @@
1
+ module Gly
2
+ module StringHelpers
3
+ extend self
4
+
5
+ # splits string by whitespace that is not enclosed
6
+ # in brackets.
7
+ # At the same time removes brackets.
8
+ def bracket_aware_whitespace_split(str)
9
+ str = str.strip
10
+ chunk_start = 0
11
+ chunks = []
12
+ in_brackets = false
13
+ str.chars.each_with_index do |char, chi|
14
+ if in_brackets
15
+ next if char != ')'
16
+
17
+ else
18
+ if char =~ /^\s$/
19
+ if chunk_start != chi
20
+ chunks << str[chunk_start .. chi-1]
21
+ end
22
+ chunk_start = chi+1
23
+ end
24
+ end
25
+ end
26
+
27
+ chunks << str[chunk_start .. -1]
28
+
29
+ chunks
30
+ end
31
+
32
+ alias_method :music_split, :bracket_aware_whitespace_split
33
+ end
34
+ end
@@ -0,0 +1,36 @@
1
+ module Gly
2
+ module StringHelpers
3
+ extend self
4
+
5
+ # splits string by whitespace that is not enclosed
6
+ # in brackets.
7
+ # At the same time removes brackets.
8
+ def bracket_aware_whitespace_split(str)
9
+ str = str.strip
10
+ chunk_start = 0
11
+ chunks = []
12
+ in_brackets = false
13
+ str.chars.each_with_index do |char, chi|
14
+ if in_brackets
15
+ next if char != ')'
16
+
17
+ else
18
+ if char =~ /^\s$/
19
+ if chunk_start != chi
20
+ chunks << str[chunk_start .. chi-1]
21
+ end
22
+ chunk_start = chi+1
23
+ end
24
+ end
25
+ end
26
+
27
+ if chunk_start != chi
28
+ chunks << str[chunk_start .. chi-1]
29
+ end
30
+
31
+ chunks
32
+ end
33
+
34
+ alias_method :music_split, :bracket_aware_whitespace_split
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ # test-suite dynamically created from examples in subfolders
2
+
3
+ require_relative 'test_helper'
4
+
5
+ # Each test case is defined by a pair of files
6
+ # in directories examples/given and examples/expected
7
+ # which define what is an expected gabc result of a single-score
8
+ # gly file.
9
+ class TestExamples < GlyTest
10
+ def self.example_test_case(given_file, expected_file)
11
+ # filename without extension
12
+ case_name = File.basename(given_file).sub(/\.[^\.]*\Z/, '')
13
+
14
+ define_method "test_#{case_name}" do
15
+ expected = File.read expected_file
16
+ File.open given_file do |fr|
17
+ given = gly_process(fr).string
18
+ assert_equal expected, given
19
+ end
20
+ end
21
+ end
22
+
23
+ here = File.dirname __FILE__
24
+ Dir.glob(File.join(here, 'examples/gly/given/*.gly')).each do |given|
25
+ expected = given.sub('/given', '/expected').sub('.gly', '.gabc')
26
+ example_test_case given, expected
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ # test-suite dynamically created from examples in subfolders
2
+
3
+ def create_test_suite(given_file, expected_file)
4
+
5
+ end
6
+
7
+ Dir.glob('examples/gly/**/*.gly').each do |given|
8
+ expected = given.sub('/given', '/expected').sub('.gly', '.gabc')
9
+ end
@@ -0,0 +1,2 @@
1
+ %%
2
+ a(i) a(i)
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) hej(h)hou(h) hej(h) (,) hou(f) (;) hej(h) (:) hou(f) (::) hej(h)
@@ -0,0 +1,2 @@
1
+ (c4)
2
+ hej(h) (,) hou(f) (;) hej(h) (:) hou(f) (::) hej(h)
@@ -0,0 +1,4 @@
1
+ mode: 8;
2
+ % description: Basic alleluia;
3
+ %%
4
+ (c4) A(g)lle(gh)lu(g)ia(g)
@@ -0,0 +1,4 @@
1
+ mode: 8;
2
+ % description: Basic alleluia;
3
+ %%
4
+ (c4) A(g)lle(gh)lu(g)ia(g)
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) A(h)men.(h) V.(::) Di(gf)xi(gh)
@@ -0,0 +1,3 @@
1
+ name: Nativitas gloriosae;
2
+ office-part: laudes, 1. ant.;
3
+ %%
@@ -0,0 +1,4 @@
1
+ name: Nativitas gloriosae;
2
+ office-part: laudes, 1. ant.;
3
+ % unofficial-header: value;
4
+ %%
@@ -0,0 +1,3 @@
1
+ name: Nativitas gloriosae;
2
+ office-part: laudes, 1. ant.;
3
+ %%
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) Na(g)tí(g')vi(g)tas(gd) glo(f)ri(gh)ó(g)sae(g_') *()
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) Na(g)tí(g')vi(g)tas(gd) glo(f)ri(gh)ó(g)sae(g_') *()
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) a(h)men,(h) *(,) a(h)men.(f)
@@ -0,0 +1,2 @@
1
+ %%
2
+ a(h)men,(h) *(,) a(h)men.(f)
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) (g) (g') (g) (gd) (f) (gh) (g) (g_') () (g a ij)
@@ -0,0 +1 @@
1
+ c4 g g' g gd f (gh) g g_' () (g a ij)
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) (g) (g') (g) (gd) (f) (gh) (g) (g_')
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) Na(g)tí(g')vi(g)tas(gd) glo(f)ri(gh)ó(g)sae(g_') *()
@@ -0,0 +1,2 @@
1
+ c4 g g' g gd f gh g g_' ()
2
+ Na -- tí -- vi -- tas glo -- ri -- ó -- sae *
@@ -0,0 +1,2 @@
1
+ %%
2
+ (c4) Custó(h)
@@ -0,0 +1,3 @@
1
+ a a
2
+ \lyrics
3
+ i i
@@ -0,0 +1,3 @@
1
+ c4
2
+ h h h , f ; h : f :: h
3
+ hej -- hou hej hou hej hou hej
@@ -0,0 +1,3 @@
1
+ c4
2
+ h , f ; h : f :: h
3
+ hej hou hej hou hej
@@ -0,0 +1,9 @@
1
+ \header
2
+ title: Testing document with document header
3
+ author: the author of gly
4
+
5
+ \score
6
+ mode: 8
7
+ description: Basic alleluia
8
+ c4 g gh g g
9
+ A -- lle -- lu -- ia
File without changes
@@ -0,0 +1,2 @@
1
+ a
2
+ \lyrics a
@@ -0,0 +1,2 @@
1
+ c4 h h :: gf gh
2
+ A -- men. !V. Di -- xi
@@ -0,0 +1,2 @@
1
+ name: Nativitas gloriosae
2
+ office-part: laudes, 1. ant.
@@ -0,0 +1,3 @@
1
+ name: Nativitas gloriosae
2
+ office-part: laudes, 1. ant.
3
+ unofficial-header: value
@@ -0,0 +1,2 @@
1
+ name: Nativitas gloriosae
2
+ office-part: laudes, 1. ant.
@@ -0,0 +1,4 @@
1
+ c4 g g' g gd
2
+ f gh g g_' ()
3
+ Na -- tí -- vi -- tas
4
+ glo -- ri -- ó -- sae *
@@ -0,0 +1,5 @@
1
+ c4 g g' g gd
2
+ Na -- tí -- vi -- tas
3
+
4
+ f gh g g_' ()
5
+ glo -- ri -- ó -- sae *
@@ -0,0 +1,4 @@
1
+ c4 g g' g gd
2
+ f gh g g_' ()
3
+ Na -- tí -- vi -- tas
4
+ glo -- ri -- ó -- sae *
@@ -0,0 +1,2 @@
1
+ c4 h h , h f
2
+ a -- men, * a -- men.
@@ -0,0 +1,2 @@
1
+ h h , h f
2
+ a -- men, * a -- men.
@@ -0,0 +1 @@
1
+ c4 g g' g gd f gh g g_'
@@ -0,0 +1 @@
1
+ c4 g g' g gd f (gh) g g_' () (g a ij)
@@ -0,0 +1 @@
1
+ c4 g g' g gd f gh g g_'
@@ -0,0 +1,2 @@
1
+ c4 g g' g gd f gh g g_' ()
2
+ Na -- tí -- vi -- tas glo -- ri -- ó -- sae *
@@ -0,0 +1,4 @@
1
+ \header
2
+ title: Testing document with document header
3
+ author: the author of gly
4
+
@@ -0,0 +1,10 @@
1
+ \score
2
+ mode: 8
3
+ description: Basic alleluia
4
+ c4 g gh g g
5
+ A -- lle -- lu -- ia
6
+
7
+ \score
8
+ description: Another simple alleluia
9
+ c4 g gj hig h
10
+ A -- lle -- lu -- ia
@@ -0,0 +1,7 @@
1
+ \score
2
+ c4 g gh g g
3
+ A -- lle -- lu -- ia
4
+
5
+ \score
6
+ c4 g gj hig h
7
+ A -- lle -- lu -- ia
@@ -0,0 +1,20 @@
1
+ require_relative 'test_helper'
2
+
3
+ # tests of cases that cannot be easily described by a pair
4
+ # of single gly file and resulting gabc file
5
+ class TestNoCrash < GlyTest
6
+ def self.nocrash_test_case(filename)
7
+ case_name = File.basename(filename).sub(/\.[^\.]*\Z/, '')
8
+ define_method "test_#{case_name}" do
9
+ File.open filename do |fr|
10
+ # simply let it convert to test that it does not crash
11
+ gly_process(fr).string
12
+ end
13
+ end
14
+ end
15
+
16
+ here = File.dirname __FILE__
17
+ Dir.glob(File.join(here, 'examples/gly/no_crash/*.gly')).each do |f|
18
+ nocrash_test_case f
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'test_helper'
2
+
3
+ # tests of cases that cannot be easily described by a pair
4
+ # of single gly file and resulting gabc file
5
+ class TestSpecialCases < MiniTest::Test
6
+ def self.loads_without_crash_test(filename)
7
+ case_name = File.basename(given_file).sub(/\.[^\.]*\Z/, '')
8
+ define_method "test_#{case_name}" do
9
+ File.open given_file do |fr|
10
+ gly_process(fr).string
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'examples'
2
+ require_relative 'no_crash'
3
+ require_relative 'string_helpers_test'
@@ -0,0 +1,3 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative 'examples'
@@ -0,0 +1,19 @@
1
+ require_relative 'test_helper'
2
+
3
+ class StringHelpersTest < GlyTest
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
+ ]
12
+
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
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'test_helper'
2
+
3
+ class StringHelpersTest < GlyTest
4
+ SH = Gly::StringHelpers
5
+
6
+ def test_simple_whitespace_split
7
+ assert_equal ['aa', 'aa'], SH.music_split('aa aa')
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
2
+ require 'gly'
3
+
4
+ require 'minitest/autorun'
5
+
6
+ require 'minitest/reporters'
7
+ Minitest::Reporters.use!
8
+
9
+ # parent of all gly test classes
10
+ class GlyTest < MiniTest::Test
11
+ # shortcut performing gly->gabc conversion and returning
12
+ # it's results
13
+ def gly_process(gly_io)
14
+ doc = Gly::Parser.new.parse(gly_io)
15
+ Gly::GabcConvertor.new.convert(doc.scores[0])
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jakub Pavlík
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest-reporters
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
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 crashes on headers it doesn't know)\n* several
53
+ scores per file (when compiled to gabc, each becomes\n a separate file)\n* compile
54
+ pdf preview by a single command, without writing any (La)TeX\n\n## Examples\n\nTypical
55
+ GABC source of an antiphon looks like this:\n\n name: Nativitas gloriosae;\n
56
+ \ office-part: laudes, 1. ant.;\n occasion: In Nativitate B. Mariae Virginis;\n
57
+ \ book: Antiphonale Romanum 1912, pg. 704;\n mode: 8;\n initial-style: 1;\n
58
+ \ %%\n \n (c4) NA(g)TI(g)VI(g)TAS(gd) glo(f)ri(gh)ó(g)sae(g) * (,)\n Vír(g)gi(g)nis(hi)
59
+ Ma(gh)rí(gf)ae,(f) (;)\n ex(f) sé(g)mi(h)ne(h) A(hiwji)bra(hg)hae,(g) (;)\n or(gh~)tae(g)
60
+ de(g) tri(g)bu(fe/fgf) Ju(d)da,(d) (;)\n cla(df!gh)ra(g) ex(f) stir(hg~)pe(hi)
61
+ Da(h)vid.(g) (::)\n\nCorresponding GLY may look like this:\n\n name: Nativitas
62
+ gloriosae\n office-part: laudes, 1. ant.\n occasion: In Nativitate B. Mariae
63
+ Virginis\n book: Antiphonale Romanum 1912, pg. 704\n mode: 8\n initial-style:
64
+ 1\n \n c4 g g g gd f gh g g ,\n g g hi gh gf f ;\n f g h h hiwji hg
65
+ g ;\n gh~ g g g fe/fgf d d ;\n df!gh g f hg~ hi h g ::\n \n NA -- TI
66
+ -- VI -- TAS glo -- ri -- ósae *\n Vír -- gi -- nis Ma -- rí -- ae,\n ex sé
67
+ -- mi -- ne A -- bra -- hae,\n or -- tae de tri -- bu Ju -- da,\n cla -- ra
68
+ ex stir -- pe Da -- vid.\n\nOr, with music and lyrics interlaced:\n\n name: Nativitas
69
+ gloriosae\n office-part: laudes, 1. ant.\n occasion: In Nativitate B. Mariae
70
+ Virginis\n book: Antiphonale Romanum 1912, pg. 704\n mode: 8\n initial-style:
71
+ 1\n \n c4 g g g gd f gh g g ,\n NA -- TI -- VI -- TAS glo -- ri -- ósae
72
+ *\n \n g g hi gh gf f ;\n Vír -- gi -- nis Ma -- rí -- ae,\n \n f
73
+ g h h hiwji hg g ;\n ex sé -- mi -- ne A -- bra -- hae,\n \n gh~ g g g
74
+ fe/fgf d d ;\n or -- tae de tri -- bu Ju -- da,\n \n df!gh g f hg~ hi h
75
+ 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## Usage\n\nThis
77
+ gem provides executable `gly`. Run `gly help` for full list\nof subcommands. The
78
+ most important ones are:\n\n`gly gabc FILE1 ...`\n\nconverts given gly file(s) to
79
+ one or more gabc files (one per score,\ni.e. one gly may spawn a bunch of gabcs).\n\n`gly
80
+ 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.\n\n## Tools\n\n[Emacs mode with syntax
83
+ highlighting for gly][elisp]\n\n![Editing gly in emacs](/doc/img/gly_emacs_scr.png)\n\n##
84
+ Run tests\n\nby executing `tests/run.rb`\n\n## License\n\nMIT\n\n[gregorio]: https://github.com/gregorio-project/gregorio\n[elisp]:
85
+ /tree/master/elisp\n"
86
+ email: jkb.pavlik@gmail.com
87
+ executables:
88
+ - gly
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - bin/gly
93
+ - bin/glyfy
94
+ - lib/gly.rb
95
+ - lib/gly.rb~
96
+ - lib/gly/cli.rb
97
+ - lib/gly/cli.rb~
98
+ - lib/gly/document.rb
99
+ - lib/gly/document.rb~
100
+ - lib/gly/gabc_convertor.rb
101
+ - lib/gly/gabc_convertor.rb~
102
+ - lib/gly/headers.rb
103
+ - lib/gly/headers.rb~
104
+ - lib/gly/lyrics.rb
105
+ - lib/gly/lyrics.rb~
106
+ - lib/gly/parsed_score.rb
107
+ - lib/gly/parsed_score.rb~
108
+ - lib/gly/parser.rb
109
+ - lib/gly/parser.rb~
110
+ - lib/gly/string_helpers.rb
111
+ - lib/gly/string_helpers.rb~
112
+ - tests/examples.rb
113
+ - tests/examples.rb~
114
+ - tests/examples/gly/expected/block_lyrics.gabc
115
+ - tests/examples/gly/expected/differentiae.gabc
116
+ - tests/examples/gly/expected/differentiae.gabc~
117
+ - tests/examples/gly/expected/document_header.gabc
118
+ - tests/examples/gly/expected/document_header.gabc~
119
+ - tests/examples/gly/expected/empty.gabc
120
+ - tests/examples/gly/expected/explicit_lyrics.gabc
121
+ - tests/examples/gly/expected/explicit_lyrics.gabc~
122
+ - tests/examples/gly/expected/explicit_score.gabc
123
+ - tests/examples/gly/expected/force_divisio_lyrics.gabc
124
+ - tests/examples/gly/expected/header.gabc
125
+ - tests/examples/gly/expected/header_unofficial.gabc
126
+ - tests/examples/gly/expected/header_unofficial.gabc~
127
+ - tests/examples/gly/expected/multiline.gabc
128
+ - tests/examples/gly/expected/multiline_interlaced.gabc
129
+ - tests/examples/gly/expected/nonlyrical_lyrics.gabc
130
+ - tests/examples/gly/expected/nonlyrical_lyrics.gabc~
131
+ - tests/examples/gly/expected/notes_bracketted.gabc
132
+ - tests/examples/gly/expected/notes_bracketted.gabc~
133
+ - tests/examples/gly/expected/notes_only.gabc
134
+ - tests/examples/gly/expected/notes_with_lyrics.gabc
135
+ - tests/examples/gly/expected/notes_with_lyrics.gabc~
136
+ - tests/examples/gly/expected/unsyllabified_lyrics.gabc
137
+ - tests/examples/gly/given/block_lyrics.gly
138
+ - tests/examples/gly/given/differentiae.gly
139
+ - tests/examples/gly/given/differentiae.gly~
140
+ - tests/examples/gly/given/document_header.gly
141
+ - tests/examples/gly/given/empty.gly
142
+ - tests/examples/gly/given/explicit_lyrics.gly
143
+ - tests/examples/gly/given/explicit_score.gly
144
+ - tests/examples/gly/given/force_divisio_lyrics.gly
145
+ - tests/examples/gly/given/header.gly
146
+ - tests/examples/gly/given/header_unofficial.gly
147
+ - tests/examples/gly/given/header_unofficial.gly~
148
+ - tests/examples/gly/given/multiline.gly
149
+ - tests/examples/gly/given/multiline_interlaced.gly
150
+ - tests/examples/gly/given/multiline_interlaced.gly~
151
+ - tests/examples/gly/given/nonlyrical_lyrics.gly
152
+ - tests/examples/gly/given/nonlyrical_lyrics.gly~
153
+ - tests/examples/gly/given/notes_bracketted.gabc~
154
+ - tests/examples/gly/given/notes_bracketted.gly
155
+ - tests/examples/gly/given/notes_only.gly
156
+ - tests/examples/gly/given/notes_with_lyrics.gly
157
+ - tests/examples/gly/given/unsyllabified_lyrics.gly
158
+ - tests/examples/gly/no_crash/document_header.gly~
159
+ - tests/examples/gly/no_crash/multiple_scores.gly
160
+ - tests/examples/gly/no_crash/multiple_scores.gly~
161
+ - tests/no_crash.rb
162
+ - tests/programmed_examples.rb~
163
+ - tests/run.rb
164
+ - tests/run.rb~
165
+ - tests/string_helpers_test.rb
166
+ - tests/string_helpers_test.rb~
167
+ - tests/test_helper.rb
168
+ homepage: http://github.com/igneus/gly
169
+ licenses:
170
+ - MIT
171
+ metadata: {}
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubyforge_project:
188
+ rubygems_version: 2.2.2
189
+ signing_key:
190
+ specification_version: 4
191
+ summary: Writer-friendly Gregorian notation format compiling to gabc
192
+ test_files: []
193
+ has_rdoc: