lygre 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 +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
|