Skip to content

Commit

Permalink
extract logic of "zipping" music and lyrics to a new class
Browse files Browse the repository at this point in the history
  • Loading branch information
igneus committed Jun 25, 2016
1 parent 8316896 commit 21ac1a1
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 61 deletions.
1 change: 1 addition & 0 deletions lib/gly.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Gly; end
headers
lyrics
markup
music_with_lyrics
document
document_gabc_convertor
document_ly_convertor
Expand Down
64 changes: 3 additions & 61 deletions lib/gly/gabc_convertor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,70 +11,12 @@ def convert(score, out=StringIO.new)

out.puts '%%'

lyric_enum = score.lyrics.each_syllable.to_enum

score.music.each_with_index do |mus_chunk,i|
begin
next_syl = lyric_enum.peek
out.print lyric_enum.next if next_syl == ' '
rescue StopIteration
next_syl = ''
end until next_syl != ' '

if no_lyrics? mus_chunk, next_syl
if i == score.music.size - 1
out.print ' '
end
else
# regular music chunk
begin
out.print strip_directives lyric_enum.next
rescue StopIteration
out.print ' ' if i > 0 # don't add space at the very beginning
end
end

out.print "(#{mus_chunk})"
if no_lyrics?(mus_chunk, next_syl) &&
i != (score.music.size - 1) &&
! score.lyrics.empty?
out.print ' '
end
score.music_with_lyrics.each_pair do |music_chunk, lyric_chunk|
out.print lyric_chunk if lyric_chunk
out.print "(#{music_chunk})" if music_chunk
end

return out
end

def no_lyrics?(music_chunk, syllable)
clef?(music_chunk) ||
(nonlyrical_chunk?(music_chunk) &&
! nonlyrical_lyrics?(syllable))
end

def clef?(chunk)
chunk =~ /\A[cf][1-4]\Z/
end

def without_differentiae(chunk)
chunk.gsub /(([,`])|(:[:']?)|(;[1-6]?))/, ''
end

def without_breaks(chunk)
chunk.gsub /[zZ]/, ''
end

# is the given music chunk capable of bearing lyrics?
def nonlyrical_chunk?(chunk)
chunk.size > 0 &&
without_breaks(without_differentiae(chunk)).empty?
end

def nonlyrical_lyrics?(syl)
syl =~ /\A\s*!/ || syl =~ /\A\s*\*\Z/
end

def strip_directives(syl)
syl.sub(/(\s*)!/, '\1') # exclamation mark at the beginning - place even under nonlyrical music chunk
end
end
end
78 changes: 78 additions & 0 deletions lib/gly/music_with_lyrics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
module Gly
# knows how to "zip" music and lyric chunks together
class MusicWithLyrics
def initialize(music, lyrics)
@music = music
@lyrics = lyrics
end

def each_pair
return enum_for(:each_pair) unless block_given?

lyric_enum = @lyrics.each_syllable.to_enum

@music.each_with_index do |mus_chunk,i|
begin
next_syl = lyric_enum.peek
yield nil, lyric_enum.next if next_syl == ' '
rescue StopIteration
next_syl = ''
end until next_syl != ' '

if no_lyrics? mus_chunk, next_syl
if i == @music.size - 1
yield nil, ' '
end
else
# regular music chunk
begin
lyr = strip_directives lyric_enum.next
rescue StopIteration
lyr = ' ' if i > 0 # don't add space at the very beginning
end
end

yield mus_chunk, lyr
if no_lyrics?(mus_chunk, next_syl) &&
i != (@music.size - 1) &&
! @lyrics.empty?
yield nil, ' '
end
end
end

private

def no_lyrics?(music_chunk, syllable)
clef?(music_chunk) ||
(nonlyrical_chunk?(music_chunk) &&
! nonlyrical_lyrics?(syllable))
end

def clef?(chunk)
chunk =~ /\A[cf][1-4]\Z/
end

def without_differentiae(chunk)
chunk.gsub /(([,`])|(:[:']?)|(;[1-6]?))/, ''
end

def without_breaks(chunk)
chunk.gsub /[zZ]/, ''
end

# is the given music chunk capable of bearing lyrics?
def nonlyrical_chunk?(chunk)
chunk.size > 0 &&
without_breaks(without_differentiae(chunk)).empty?
end

def nonlyrical_lyrics?(syl)
syl =~ /\A\s*!/ || syl =~ /\A\s*\*\Z/
end

def strip_directives(syl)
syl.sub(/(\s*)!/, '\1') # exclamation mark at the beginning - place even under nonlyrical music chunk
end
end
end
4 changes: 4 additions & 0 deletions lib/gly/score.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@ def initialize
def empty?
@headers.empty? && @lyrics.empty? && @music.empty?
end

def music_with_lyrics
MusicWithLyrics.new(music, lyrics)
end
end
end
28 changes: 28 additions & 0 deletions tests/music_with_lyrics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require_relative 'test_helper'

class TestMusicWithLyrics < GlyTest
include Gly

def assert_translates(gly, expected)
doc = Parser.new.parse(StringIO.new(gly))
score = doc.scores[0]
zipped = MusicWithLyrics.new(score.music, score.lyrics)
assert_equal zipped.each_pair.to_a, expected
end

def two_monosyllabic_words
assert_translates("a a\n\\l i i", [['a', 'i'], [nil, ' '], ['a', 'i']])
end

def test_one_word_and_divisio
assert_translates("a a ::\nA -- men", [['a', 'A'], ['a', 'men'], [nil, ' '], ['::', nil]])
end

def test_divisio
assert_translates("a , a\n\\l x x", [['a', 'x'], [nil, ' '], [',', nil], [nil, ' '], ['a', 'x']])
end

def test_force_under_divisio
assert_translates("a , a\n\\l x !forced x", [['a', 'x'], [nil, ' '], [',', 'forced'], [nil, ' '], ['a', 'x']])
end
end
1 change: 1 addition & 0 deletions tests/run.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
require_relative 'no_crash'
require_relative 'string_helpers_test'
require_relative 'parser'
require_relative 'music_with_lyrics'

0 comments on commit 21ac1a1

Please sign in to comment.