lygre 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 +4 -4
- data/lib/grely.rb +2 -5
- data/lib/lygre/gabcparser.rb +4 -0
- data/lib/lygre/gabcpitchreader.rb +5 -61
- data/lib/lygre/gabcscore.rb +2 -0
- data/lib/lygre/gabcsemantics.rb +3 -0
- data/lib/lygre/musictheory.rb +44 -0
- data/spec/gabcpitchreader_spec.rb +3 -54
- data/spec/musictheory_spec.rb +44 -0
- data/spec/spec_helper.rb +3 -9
- metadata +6 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b246c70e332e42827f7747ab0439d890b368b720
|
4
|
+
data.tar.gz: 6029592482f7d71440f9a5dd402715ad4f504f12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6bb4e396985f00612b94be4e3b53bbdc4adfdaebd2f7a950e66824aa7d979d966008fb08eb69678e2850e3680352dc562052e7646d8d841f783a77242b53c81
|
7
|
+
data.tar.gz: 2fca20a9e491d4b5d33f87d95cdd1a6239a65b49ca797f0cb236dd435595da19112b813a8dc5adc84debebad70bbb8c49ce046a789cb7b6e84bb38dd99616020
|
data/lib/grely.rb
CHANGED
@@ -4,10 +4,7 @@
|
|
4
4
|
gabcscore
|
5
5
|
gabcsemantics
|
6
6
|
gabcpitchreader
|
7
|
+
gabcparser
|
7
8
|
lilypondconvertor
|
9
|
+
musictheory
|
8
10
|
}.each {|f| require_relative File.join('lygre', f)}
|
9
|
-
|
10
|
-
# gabc parser
|
11
|
-
require 'polyglot'
|
12
|
-
require 'treetop'
|
13
|
-
Treetop.load File.expand_path('../lib/lygre/gabcgrammar', File.dirname(__FILE__))
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
require 'rb-music-theory'
|
4
|
-
|
5
3
|
# responsible for converting the 'visual pitch' information
|
6
4
|
# contained in gabc to absolute musical pitch
|
7
5
|
class GabcPitchReader
|
@@ -55,18 +53,13 @@ class NoteFactory
|
|
55
53
|
note = notesym[0]
|
56
54
|
octaves = notesym[1..-1]
|
57
55
|
|
58
|
-
n = RBMusicTheory::Note.new note.upcase
|
59
56
|
sign = 0
|
60
|
-
|
57
|
+
octave = 0
|
61
58
|
if octaves then
|
62
59
|
sign = (octaves[0] == ',' ? -1 : 1)
|
63
|
-
|
64
|
-
else
|
65
|
-
octave_shift = base_octave_shift
|
60
|
+
octave += (octaves.size * sign)
|
66
61
|
end
|
67
|
-
|
68
|
-
|
69
|
-
return n
|
62
|
+
return MusicTheory::Note.new note.to_sym, octave
|
70
63
|
end
|
71
64
|
|
72
65
|
alias :create_note :create
|
@@ -78,57 +71,8 @@ class NoteFactory
|
|
78
71
|
# #create translates lilypond pitch to Note and #lily_abs_pitch
|
79
72
|
# does the reverse translation, so maybe just the class should be renamed
|
80
73
|
def lily_abs_pitch(note)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
octave_value = RBMusicTheory::NoteInterval.octave.value
|
85
|
-
octave_shift = octave_diff.abs / octave_value
|
86
|
-
if octave_diff < 0 and (octave_diff.abs % octave_value) > 0 then
|
87
|
-
octave_shift += 1
|
88
|
-
end
|
89
|
-
|
90
|
-
octave_signs = (octave_diff >= 0 ? "'" : ",") * octave_shift
|
91
|
-
note.name.downcase + octave_signs
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# monkey-patch Note to add step arithmetics
|
97
|
-
module RBMusicTheory
|
98
|
-
|
99
|
-
class Note
|
100
|
-
|
101
|
-
def diatonic_steps(steps, scale=nil)
|
102
|
-
if scale.nil? then
|
103
|
-
scale = self.class.new('C').major_scale
|
104
|
-
end
|
105
|
-
|
106
|
-
deg = self.degree_in(scale)
|
107
|
-
|
108
|
-
return scale.degree(deg + steps)
|
109
|
-
end
|
110
|
-
|
111
|
-
# note's degree in a scale
|
112
|
-
def degree_in(scale)
|
113
|
-
degree = scale.note_names.index(self.name)
|
114
|
-
if degree.nil? then
|
115
|
-
raise ArgumentError.new("#{name} is not a member of #{scale.note_names} scale")
|
116
|
-
end
|
117
|
-
degree += 1 # degrees start with 1
|
118
|
-
|
119
|
-
in_base_octave = scale.degree(degree)
|
120
|
-
octave_steps = scale.note_names.size
|
121
|
-
octave_value = RBMusicTheory::NoteInterval.octave.value
|
122
|
-
|
123
|
-
value_difference = self.value - in_base_octave.value
|
124
|
-
octave_difference = value_difference.abs / octave_value
|
125
|
-
if value_difference < 0 then
|
126
|
-
octave_difference *= -1
|
127
|
-
end
|
128
|
-
|
129
|
-
degree += octave_difference * octave_steps
|
130
|
-
|
131
|
-
return degree
|
74
|
+
octave_signs = (note.octave >= 0 ? "'" : ",") * note.octave.abs
|
75
|
+
note.pitch.to_s + octave_signs
|
132
76
|
end
|
133
77
|
end
|
134
78
|
end
|
data/lib/lygre/gabcscore.rb
CHANGED
data/lib/lygre/gabcsemantics.rb
CHANGED
@@ -100,16 +100,19 @@ module Gabc
|
|
100
100
|
if ele.is_a? NoteNode then
|
101
101
|
arr << GabcNote.new do |n|
|
102
102
|
n.pitch = ele.note_pitch.text_value.downcase.to_sym
|
103
|
+
n.text_value = ele.text_value
|
103
104
|
end
|
104
105
|
elsif ele.is_a? DivisioNode then
|
105
106
|
arr << GabcDivisio.new do |d|
|
106
107
|
d.type = ele.text_value
|
108
|
+
d.text_value = ele.text_value
|
107
109
|
end
|
108
110
|
elsif ele.is_a? ClefNode then
|
109
111
|
arr << GabcClef.new do |c|
|
110
112
|
c.pitch = ele.clef_symbol.text_value.to_sym
|
111
113
|
c.bemol = ele.bemol.text_value == 'b'
|
112
114
|
c.line = ele.line_number.text_value.to_i
|
115
|
+
c.text_value = ele.text_value
|
113
116
|
end
|
114
117
|
else
|
115
118
|
collect_notes ele, arr
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Simple diatonic music theory necessary for parsing of Gregorian chant scores
|
2
|
+
module MusicTheory
|
3
|
+
PITCHES = [:c, :d, :e, :f, :g, :a, :b]
|
4
|
+
VALUES = [0, 2, 4, 5, 7, 9, 11]
|
5
|
+
|
6
|
+
class Note
|
7
|
+
def initialize(pitch=:c, octave=1)
|
8
|
+
@pitch = pitch
|
9
|
+
@pitch_numeric = PITCHES.index @pitch
|
10
|
+
@octave = octave
|
11
|
+
|
12
|
+
if @pitch_numeric.nil?
|
13
|
+
raise ArgumentError.new("Invalid pitch #{pitch.inspect}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :pitch, :octave
|
18
|
+
|
19
|
+
def diatonic_steps(steps)
|
20
|
+
base_steps = steps % PITCHES.size
|
21
|
+
octaves = steps / PITCHES.size
|
22
|
+
new_step = @pitch_numeric + base_steps
|
23
|
+
if new_step >= PITCHES.size
|
24
|
+
new_step -= PITCHES.size
|
25
|
+
octaves += 1
|
26
|
+
end
|
27
|
+
new_pitch = PITCHES[new_step]
|
28
|
+
self.class.new(new_pitch, @octave + octaves)
|
29
|
+
end
|
30
|
+
|
31
|
+
def value
|
32
|
+
(@octave + 4) * 12 + VALUES[@pitch_numeric]
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
other.pitch == @pitch &&
|
37
|
+
other.octave == @octave
|
38
|
+
end
|
39
|
+
|
40
|
+
def hash
|
41
|
+
[@pitch, @octave].hash
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
2
3
|
|
3
4
|
describe GabcPitchReader do
|
4
5
|
|
@@ -70,9 +71,9 @@ describe NoteFactory do
|
|
70
71
|
end
|
71
72
|
|
72
73
|
describe '.lily_abs_pitch' do
|
73
|
-
|
74
|
+
describe 'operates exactly reverse to .create' do
|
74
75
|
%w{ c d g c' f' a' b' g'' d''' c, c,, c,,, d, e, e,, b, }.each do |lypitch|
|
75
|
-
@f.lily_abs_pitch(@f[lypitch]).should eq lypitch
|
76
|
+
it { @f.lily_abs_pitch(@f[lypitch]).should eq lypitch }
|
76
77
|
end
|
77
78
|
end
|
78
79
|
end
|
@@ -82,55 +83,3 @@ describe NoteFactory do
|
|
82
83
|
it { @f["c''"].value.should eq 72 }
|
83
84
|
end
|
84
85
|
end
|
85
|
-
|
86
|
-
describe 'monkey-patched RBMusicTheory' do
|
87
|
-
|
88
|
-
before :each do
|
89
|
-
@f = NoteFactory
|
90
|
-
@s = @f["c''"].major_scale
|
91
|
-
end
|
92
|
-
|
93
|
-
describe 'Note#diatonic_steps' do
|
94
|
-
it { @f["c'"].diatonic_steps(1).should eq @f["d'"] }
|
95
|
-
it { @f["c'"].diatonic_steps(0).should eq @f["c'"] }
|
96
|
-
it { @f["c'"].diatonic_steps(7).should eq @f["c''"] }
|
97
|
-
it { @f["c'"].diatonic_steps(8).should eq @f["d''"] }
|
98
|
-
it { @f["c'"].diatonic_steps(-2).should eq @f["a"] }
|
99
|
-
it { @f["c''"].diatonic_steps(-9).should eq @f["a"] }
|
100
|
-
it { @f["c'''"].diatonic_steps(-9).should eq @f["a'"] }
|
101
|
-
it { @f["c,"].diatonic_steps(-9).should eq @f["a,,,"] }
|
102
|
-
it { @f["c,"].diatonic_steps(-10).should eq @f["g,,,"] }
|
103
|
-
end
|
104
|
-
|
105
|
-
describe 'Note#degree_in' do
|
106
|
-
it { @f["c''"].degree_in(@s).should eq 1 }
|
107
|
-
it { @f["d''"].degree_in(@s).should eq 2 }
|
108
|
-
|
109
|
-
it { @f["c'''"].degree_in(@s).should eq 8 }
|
110
|
-
it { @f["e'''"].degree_in(@s).should eq 10 }
|
111
|
-
it { @f["b'"].degree_in(@s).should eq 0 }
|
112
|
-
it { @f["a'"].degree_in(@s).should eq -1 }
|
113
|
-
it { @f["a"].degree_in(@s).should eq -8 }
|
114
|
-
|
115
|
-
it 'behaves consistently with Scale#degree' do
|
116
|
-
[2, 8, -1, -12].each do |deg|
|
117
|
-
@s.degree(deg).degree_in(@s).should eq deg
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
describe 'Scale#degree' do
|
123
|
-
describe 'for positive degrees it behaves as expected' do
|
124
|
-
it { @s.degree(1).should eq @f["c''"] }
|
125
|
-
it { @s.degree(2).should eq @f["d''"] }
|
126
|
-
it { @s.degree(8).should eq @f["c'''"] }
|
127
|
-
end
|
128
|
-
|
129
|
-
describe 'and then there is zero and negative numbers' do
|
130
|
-
it { @s.degree(0).should eq @f["b'"] }
|
131
|
-
it { @s.degree(-1).should eq @f["a'"] }
|
132
|
-
it { @s.degree(-8).should eq @f["a"] }
|
133
|
-
it { @s.degree(-15).should eq @f["a,"] }
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe MusicTheory do
|
5
|
+
describe MusicTheory::Note do
|
6
|
+
let(:c1) { described_class.new(:c) }
|
7
|
+
let(:c2) { described_class.new(:c, 2) }
|
8
|
+
|
9
|
+
describe '#diatonic_steps' do
|
10
|
+
it 'step in the base octave' do
|
11
|
+
c1.diatonic_steps(1).should eq described_class.new(:d)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'octave step' do
|
15
|
+
c1.diatonic_steps(7).should eq c2
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'single step to the next octave' do
|
19
|
+
b = described_class.new(:b)
|
20
|
+
b.diatonic_steps(1).should eq c2
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'single step to the previous octave' do
|
24
|
+
c1.diatonic_steps(-1).should eq described_class.new(:b, 0)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'preserve octave' do
|
28
|
+
c = described_class.new(:c, -1)
|
29
|
+
c.diatonic_steps(1).should eq described_class.new(:d, -1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#value' do
|
34
|
+
it { c1.value.should eq 60 }
|
35
|
+
it { described_class.new(:d).value.should eq 62 }
|
36
|
+
it { described_class.new(:e).value.should eq 64 }
|
37
|
+
it { described_class.new(:f).value.should eq 65 }
|
38
|
+
it { described_class.new(:g).value.should eq 67 }
|
39
|
+
it { described_class.new(:a).value.should eq 69 }
|
40
|
+
it { described_class.new(:b).value.should eq 71 }
|
41
|
+
it { described_class.new(:c, 2).value.should eq 72 }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -15,17 +15,8 @@ RSpec.configure do |config|
|
|
15
15
|
config.order = 'random'
|
16
16
|
end
|
17
17
|
|
18
|
-
require 'polyglot'
|
19
|
-
require 'treetop'
|
20
|
-
|
21
18
|
require_relative 'matchers'
|
22
19
|
|
23
|
-
|
24
|
-
$: << '../lib'
|
25
|
-
require 'grely'
|
26
|
-
|
27
|
-
Treetop.load File.expand_path('../lib/lygre/gabcgrammar', File.dirname(__FILE__))
|
28
|
-
|
29
20
|
def load_example(fname)
|
30
21
|
File.read(
|
31
22
|
File.expand_path(
|
@@ -34,3 +25,6 @@ def load_example(fname)
|
|
34
25
|
)
|
35
26
|
)
|
36
27
|
end
|
28
|
+
|
29
|
+
# load the tested code
|
30
|
+
require_relative '../lib/grely'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lygre
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
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: 2016-
|
11
|
+
date: 2016-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: treetop
|
@@ -38,20 +38,6 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.3'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rb-music-theory
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.1'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.1'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: rspec
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,10 +60,6 @@ description: |
|
|
74
60
|
input format (gabc) to simple LilyPond input.
|
75
61
|
|
76
62
|
In future another tool for the other direction of conversion may be added.
|
77
|
-
|
78
|
-
As rb-music-theory is not at RubyGems, either install it before
|
79
|
-
attempting to 'gem install lygre', or get lygre's source from github
|
80
|
-
and install the dependencies by 'bundle install'.
|
81
63
|
email: jkb.pavlik@gmail.com
|
82
64
|
executables:
|
83
65
|
- grely.rb
|
@@ -87,16 +69,19 @@ files:
|
|
87
69
|
- bin/grely.rb
|
88
70
|
- lib/grely.rb
|
89
71
|
- lib/lygre/gabcgrammar.treetop
|
72
|
+
- lib/lygre/gabcparser.rb
|
90
73
|
- lib/lygre/gabcpitchreader.rb
|
91
74
|
- lib/lygre/gabcscore.rb
|
92
75
|
- lib/lygre/gabcsemantics.rb
|
93
76
|
- lib/lygre/lilypondconvertor.rb
|
77
|
+
- lib/lygre/musictheory.rb
|
94
78
|
- spec/gabcgrammar_spec.rb
|
95
79
|
- spec/gabcparser_spec.rb
|
96
80
|
- spec/gabcpitchreader_spec.rb
|
97
81
|
- spec/gabcscore_spec.rb
|
98
82
|
- spec/lilypondconvertor_spec.rb
|
99
83
|
- spec/matchers.rb
|
84
|
+
- spec/musictheory_spec.rb
|
100
85
|
- spec/spec_helper.rb
|
101
86
|
homepage: http://github.com/igneus/lygre
|
102
87
|
licenses:
|
@@ -119,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
104
|
version: '0'
|
120
105
|
requirements: []
|
121
106
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.
|
107
|
+
rubygems_version: 2.2.0
|
123
108
|
signing_key:
|
124
109
|
specification_version: 4
|
125
110
|
summary: converts music formats gabc -> lilypond
|