musicality 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog.md +7 -1
- data/lib/musicality.rb +0 -1
- data/lib/musicality/composition/dsl/score_methods.rb +5 -6
- data/lib/musicality/notation/conversion/score_conversion.rb +3 -34
- data/lib/musicality/notation/conversion/score_converter.rb +6 -17
- data/lib/musicality/notation/model/mark.rb +0 -12
- data/lib/musicality/notation/model/marks.rb +0 -3
- data/lib/musicality/notation/model/note.rb +0 -8
- data/lib/musicality/notation/model/score.rb +14 -35
- data/lib/musicality/notation/model/symbols.rb +0 -2
- data/lib/musicality/notation/parsing/mark_parsing.rb +0 -58
- data/lib/musicality/notation/parsing/mark_parsing.treetop +0 -12
- data/lib/musicality/notation/parsing/note_node.rb +22 -27
- data/lib/musicality/notation/parsing/note_parsing.rb +59 -189
- data/lib/musicality/notation/parsing/note_parsing.treetop +2 -9
- data/lib/musicality/performance/conversion/note_sequence_extractor.rb +1 -20
- data/lib/musicality/performance/model/note_sequence.rb +1 -1
- data/lib/musicality/printing/lilypond/note_engraving.rb +3 -3
- data/lib/musicality/printing/lilypond/part_engraver.rb +16 -12
- data/lib/musicality/project/create_tasks.rb +1 -1
- data/lib/musicality/project/load_config.rb +16 -1
- data/lib/musicality/project/project.rb +2 -2
- data/lib/musicality/version.rb +1 -1
- data/musicality.gemspec +0 -1
- data/spec/composition/dsl/score_methods_spec.rb +73 -0
- data/spec/notation/conversion/score_conversion_spec.rb +0 -100
- data/spec/notation/conversion/score_converter_spec.rb +33 -33
- data/spec/notation/model/note_spec.rb +2 -2
- data/spec/notation/model/score_spec.rb +17 -87
- data/spec/notation/parsing/note_node_spec.rb +2 -2
- data/spec/notation/parsing/note_parsing_spec.rb +3 -3
- data/spec/performance/conversion/note_sequence_extractor_spec.rb +23 -0
- data/spec/performance/model/note_sequence_spec.rb +50 -6
- metadata +4 -19
- data/lib/musicality/notation/conversion/measure_note_map.rb +0 -40
- data/spec/notation/conversion/measure_note_map_spec.rb +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d3af3d687ff5109e122bfa208ee1d566bc97674
|
4
|
+
data.tar.gz: 2d819234281b8639e5bb8dd58fcc4e1acb2c3834
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3f3dd479c5350582fbe1ff5183dd25ae006e8fa9028d071da12123d1192adfe938dea47c4705fd4c95c192c6f34abf1cf37e473ef1d1e726631dbd375d1154e
|
7
|
+
data.tar.gz: 2cf2328e11b1f5b998483c894460e95582e4635c979b310840d0a02c272beadf7bb0f53f01d74b2edca2cc6fe7f57886053d53e971ca1674d7121c737f778ecd
|
data/ChangeLog.md
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
### 0.
|
1
|
+
### 0.10.0 / 2016-01-08
|
2
|
+
* Remove uneeded gem dependency
|
3
|
+
* Treat all offsets and durations in Score::Tempo as note offsets. Don't check if meter change offsets are integer anymore.
|
4
|
+
* Remove triplet marks. Determine if note is triplet based on duration.
|
5
|
+
* Make project 'scores' selector glob configurable in config.yml and as an ENV
|
6
|
+
|
7
|
+
### 0.9.0 / 2016-01-01
|
2
8
|
* Require ruby v2.0 or greater.
|
3
9
|
* Remove Score::Unmeasured and change Score::Measured to Score::Tempo
|
4
10
|
* Add more convenience methods for note creation, and alter their interface to take a variable number of args, each being interpreted as a pitch group (which may also be a single pitch)
|
data/lib/musicality.rb
CHANGED
@@ -59,7 +59,6 @@ require 'musicality/notation/util/value_computer'
|
|
59
59
|
require 'musicality/notation/conversion/tempo_conversion'
|
60
60
|
require 'musicality/notation/conversion/change_conversion'
|
61
61
|
require 'musicality/notation/conversion/note_time_converter'
|
62
|
-
require 'musicality/notation/conversion/measure_note_map'
|
63
62
|
require 'musicality/notation/conversion/score_converter'
|
64
63
|
require 'musicality/notation/conversion/score_conversion'
|
65
64
|
|
@@ -23,14 +23,13 @@ class Score
|
|
23
23
|
def notes part_notes
|
24
24
|
raise ArgumentError, "No part notes given" if part_notes.empty?
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
durs_uniq = part_notes.values.map do |notes|
|
27
|
+
notes.map {|n| n.duration }.inject(0,:+)
|
28
|
+
end.uniq
|
28
29
|
raise DurationMismatchError, "New part note durations do not all match" if durs_uniq.size > 1
|
29
30
|
dur = durs_uniq.first
|
30
|
-
raise NonPositiveError, "Part note durations are not positive" if dur <= 0
|
31
31
|
|
32
|
-
a = self.duration
|
33
|
-
starting_part_dur = self.max_part_duration
|
32
|
+
a = starting_part_dur = self.duration
|
34
33
|
part_notes.each do |part,notes|
|
35
34
|
unless parts.has_key? part
|
36
35
|
parts[part] = Part.new DEFAULT_START_DYNAMIC
|
@@ -46,7 +45,7 @@ class Score
|
|
46
45
|
|
47
46
|
b = self.duration
|
48
47
|
program.push a...b
|
49
|
-
a...b
|
48
|
+
a...b
|
50
49
|
end
|
51
50
|
|
52
51
|
def dynamic_change new_dynamic, transition_dur: 0, offset: 0
|
@@ -2,27 +2,12 @@ module Musicality
|
|
2
2
|
|
3
3
|
class Score
|
4
4
|
class Tempo < Score
|
5
|
-
# Convert to timed score by converting
|
6
|
-
#
|
5
|
+
# Convert to timed score by converting note-based offsets and durations
|
6
|
+
# to time-based. This eliminates the use of meters and tempos.
|
7
7
|
def to_timed tempo_sample_rate
|
8
8
|
ScoreConverter.new(self, tempo_sample_rate).convert_score
|
9
9
|
end
|
10
|
-
|
11
|
-
def measure_note_map
|
12
|
-
Conversion::measure_note_map(measure_offsets,measure_durations)
|
13
|
-
end
|
14
|
-
|
15
|
-
def measure_offsets
|
16
|
-
moffs = Set.new([0.to_r])
|
17
|
-
@tempo_changes.each {|moff,change| moffs += change.offsets(moff) }
|
18
|
-
moffs += @meter_changes.keys
|
19
|
-
@parts.values.each do |part|
|
20
|
-
part.dynamic_changes.each {|moff,change| moffs += change.offsets(moff) }
|
21
|
-
end
|
22
|
-
moffs += @program.map {|seg| [seg.first, seg.last] }.flatten
|
23
|
-
return moffs.sort
|
24
|
-
end
|
25
|
-
|
10
|
+
|
26
11
|
def beat_durations
|
27
12
|
bdurs = @meter_changes.map do |offset,change_or_meter|
|
28
13
|
if change_or_meter.is_a? Meter
|
@@ -38,22 +23,6 @@ class Score
|
|
38
23
|
|
39
24
|
return Hash[ bdurs ]
|
40
25
|
end
|
41
|
-
|
42
|
-
def measure_durations
|
43
|
-
mdurs = @meter_changes.map do |offset,change_or_meter|
|
44
|
-
if change_or_meter.is_a? Meter
|
45
|
-
[ offset, change_or_meter.measure_duration ]
|
46
|
-
else
|
47
|
-
[ offset, change_or_meter.end_value.measure_duration ]
|
48
|
-
end
|
49
|
-
end.sort
|
50
|
-
|
51
|
-
if mdurs.empty? || mdurs[0][0] != 0
|
52
|
-
mdurs.unshift([0.to_r,@start_meter.measure_duration])
|
53
|
-
end
|
54
|
-
|
55
|
-
return Hash[ mdurs ]
|
56
|
-
end
|
57
26
|
end
|
58
27
|
end
|
59
28
|
|
@@ -36,26 +36,15 @@ class ScoreConverter
|
|
36
36
|
if score.invalid?
|
37
37
|
raise NotValidError, "Errors detected given score: #{score.errors}"
|
38
38
|
end
|
39
|
-
|
40
|
-
|
41
|
-
new_dcs = ScoreConverter.convert_changes(part.dynamic_changes, mn_map)
|
42
|
-
new_notes = part.notes.map {|n| n.clone } # note duration is already note-based
|
43
|
-
new_part = part.clone
|
44
|
-
new_part.notes = new_notes
|
45
|
-
new_part.dynamic_changes = new_dcs
|
46
|
-
[name, new_part]
|
47
|
-
end]
|
48
|
-
new_program = ScoreConverter.convert_program(score.program, mn_map)
|
49
|
-
new_tempo_changes = ScoreConverter.convert_changes(score.tempo_changes, mn_map)
|
50
|
-
new_beat_durations = Hash[ score.beat_durations.map do |moff,bdur|
|
51
|
-
[mn_map[moff], Change::Immediate.new(bdur) ]
|
39
|
+
beat_duration_changes = Hash[ score.beat_durations.map do |noff,bdur|
|
40
|
+
[noff, Change::Immediate.new(bdur) ]
|
52
41
|
end]
|
53
|
-
tempo_computer = ValueComputer.new(score.start_tempo,
|
54
|
-
bdur_computer = ValueComputer.new(score.start_meter.beat_duration,
|
42
|
+
tempo_computer = ValueComputer.new(score.start_tempo, score.tempo_changes)
|
43
|
+
bdur_computer = ValueComputer.new(score.start_meter.beat_duration, beat_duration_changes)
|
55
44
|
ntc = NoteTimeConverter.new(tempo_computer, bdur_computer, tempo_sample_rate)
|
56
45
|
|
57
|
-
@parts =
|
58
|
-
@program =
|
46
|
+
@parts = Hash[ score.parts.map {|name,part| [name, part.clone] }]
|
47
|
+
@program = score.program.clone
|
59
48
|
@note_time_map = ntc.note_time_map(note_offsets)
|
60
49
|
end
|
61
50
|
|
@@ -26,18 +26,6 @@ class Mark
|
|
26
26
|
def ends?; true; end
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
30
|
-
class Triplet < Mark
|
31
|
-
class Begin < Triplet
|
32
|
-
def begins?; true; end
|
33
|
-
def ends?; false; end
|
34
|
-
end
|
35
|
-
|
36
|
-
class End < Triplet
|
37
|
-
def begins?; false; end
|
38
|
-
def ends?; true; end
|
39
|
-
end
|
40
|
-
end
|
41
29
|
end
|
42
30
|
|
43
31
|
end
|
@@ -75,14 +75,6 @@ class Note
|
|
75
75
|
marks.count {|m| m.is_a?(Mark::Slur::End) } > 0
|
76
76
|
end
|
77
77
|
|
78
|
-
def begins_triplet?
|
79
|
-
marks.count {|m| m.is_a?(Mark::Triplet::Begin) } > 0
|
80
|
-
end
|
81
|
-
|
82
|
-
def ends_triplet?
|
83
|
-
marks.count {|m| m.is_a?(Mark::Triplet::End) } > 0
|
84
|
-
end
|
85
|
-
|
86
78
|
def to_s
|
87
79
|
d = @duration.to_r
|
88
80
|
if d.denominator == 1
|
@@ -57,7 +57,7 @@ class Score
|
|
57
57
|
@start_key == other.start_key && @key_changes == other.key_changes
|
58
58
|
end
|
59
59
|
|
60
|
-
def
|
60
|
+
def duration
|
61
61
|
@parts.map {|name,part| part.duration }.max || 0.to_r
|
62
62
|
end
|
63
63
|
|
@@ -66,14 +66,14 @@ class Score
|
|
66
66
|
end
|
67
67
|
|
68
68
|
class Timed < Score
|
69
|
-
def seconds_long
|
70
|
-
max_part_duration
|
71
|
-
end
|
72
|
-
alias duration seconds_long
|
73
69
|
end
|
74
70
|
|
75
71
|
# Tempo-based score with meter, bar lines, and a fixed pulse (beat).
|
76
|
-
#
|
72
|
+
#
|
73
|
+
# Offsets and durations are based on note duration, but note duration is
|
74
|
+
# determined by the tempo, which can change.
|
75
|
+
#
|
76
|
+
# Tempo values are in beats-per-minute.
|
77
77
|
class Tempo < Score
|
78
78
|
attr_accessor :start_tempo, :tempo_changes, :start_meter, :meter_changes
|
79
79
|
|
@@ -102,34 +102,18 @@ class Score
|
|
102
102
|
@start_meter == other.start_meter &&
|
103
103
|
@meter_changes == other.meter_changes
|
104
104
|
end
|
105
|
-
|
106
|
-
def notes_long
|
107
|
-
max_part_duration
|
108
|
-
end
|
109
105
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
noff = noff_prev + notes_elapsed
|
119
|
-
|
120
|
-
if noff >= noff_end
|
121
|
-
break
|
122
|
-
else
|
123
|
-
noff_prev = noff
|
124
|
-
end
|
125
|
-
|
126
|
-
moff_prev, mdur_prev = moff, mdur
|
106
|
+
# Returns the measure duration of the most recent meter duration since the given note offset,
|
107
|
+
# or of the start meter if there are no meter changes.
|
108
|
+
def measure_duration note_offset = self.duration
|
109
|
+
if meter_changes.any?
|
110
|
+
candidates = meter_changes.select {|noff,change| noff <= note_offset }
|
111
|
+
candidates.max[1].end_value.measure_duration
|
112
|
+
else
|
113
|
+
start_meter.measure_duration
|
127
114
|
end
|
128
|
-
return moff_prev + Rational(noff_end - noff_prev, mdur_prev)
|
129
115
|
end
|
130
116
|
|
131
|
-
alias duration measures_long
|
132
|
-
|
133
117
|
private
|
134
118
|
|
135
119
|
def check_start_tempo
|
@@ -162,11 +146,6 @@ class Score
|
|
162
146
|
raise TypeError, "Found meter change values that are not Meter objects: #{badtypes}"
|
163
147
|
end
|
164
148
|
|
165
|
-
badoffsets = @meter_changes.select {|k,v| k != k.to_i }
|
166
|
-
if badoffsets.any?
|
167
|
-
raise NonIntegerError, "Found meter changes at non-integer offsets: #{badoffsets}"
|
168
|
-
end
|
169
|
-
|
170
149
|
nonzero_duration = @meter_changes.select {|k,v| !v.is_a?(Change::Immediate) }
|
171
150
|
if nonzero_duration.any?
|
172
151
|
raise NonZeroError, "Found meter changes that are not immediate: #{nonzero_duration}"
|
@@ -69,64 +69,6 @@ module Mark
|
|
69
69
|
r0
|
70
70
|
end
|
71
71
|
|
72
|
-
module BeginTriplet0
|
73
|
-
def to_mark; return Musicality::Mark::Triplet::Begin.new; end
|
74
|
-
end
|
75
|
-
|
76
|
-
def _nt_begin_triplet
|
77
|
-
start_index = index
|
78
|
-
if node_cache[:begin_triplet].has_key?(index)
|
79
|
-
cached = node_cache[:begin_triplet][index]
|
80
|
-
if cached
|
81
|
-
node_cache[:begin_triplet][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
82
|
-
@index = cached.interval.end
|
83
|
-
end
|
84
|
-
return cached
|
85
|
-
end
|
86
|
-
|
87
|
-
if (match_len = has_terminal?("[", false, index))
|
88
|
-
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
89
|
-
r0.extend(BeginTriplet0)
|
90
|
-
@index += match_len
|
91
|
-
else
|
92
|
-
terminal_parse_failure('"["')
|
93
|
-
r0 = nil
|
94
|
-
end
|
95
|
-
|
96
|
-
node_cache[:begin_triplet][start_index] = r0
|
97
|
-
|
98
|
-
r0
|
99
|
-
end
|
100
|
-
|
101
|
-
module EndTriplet0
|
102
|
-
def to_mark; return Musicality::Mark::Triplet::End.new; end
|
103
|
-
end
|
104
|
-
|
105
|
-
def _nt_end_triplet
|
106
|
-
start_index = index
|
107
|
-
if node_cache[:end_triplet].has_key?(index)
|
108
|
-
cached = node_cache[:end_triplet][index]
|
109
|
-
if cached
|
110
|
-
node_cache[:end_triplet][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
111
|
-
@index = cached.interval.end
|
112
|
-
end
|
113
|
-
return cached
|
114
|
-
end
|
115
|
-
|
116
|
-
if (match_len = has_terminal?("]", false, index))
|
117
|
-
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
118
|
-
r0.extend(EndTriplet0)
|
119
|
-
@index += match_len
|
120
|
-
else
|
121
|
-
terminal_parse_failure('"]"')
|
122
|
-
r0 = nil
|
123
|
-
end
|
124
|
-
|
125
|
-
node_cache[:end_triplet][start_index] = r0
|
126
|
-
|
127
|
-
r0
|
128
|
-
end
|
129
|
-
|
130
72
|
end
|
131
73
|
|
132
74
|
class MarkParser < Treetop::Runtime::CompiledParser
|
@@ -13,18 +13,6 @@ grammar Mark
|
|
13
13
|
def to_mark; return Musicality::Mark::Slur::End.new; end
|
14
14
|
}
|
15
15
|
end
|
16
|
-
|
17
|
-
rule begin_triplet
|
18
|
-
"[" {
|
19
|
-
def to_mark; return Musicality::Mark::Triplet::Begin.new; end
|
20
|
-
}
|
21
|
-
end
|
22
|
-
|
23
|
-
rule end_triplet
|
24
|
-
"]" {
|
25
|
-
def to_mark; return Musicality::Mark::Triplet::End.new; end
|
26
|
-
}
|
27
|
-
end
|
28
16
|
end
|
29
17
|
|
30
18
|
end
|
@@ -3,46 +3,41 @@ module Parsing
|
|
3
3
|
class NoteNode < Treetop::Runtime::SyntaxNode
|
4
4
|
def to_note
|
5
5
|
dur = duration.to_r
|
6
|
-
|
7
|
-
if more.empty?
|
8
|
-
return Musicality::Note.new(dur)
|
9
|
-
end
|
10
|
-
|
11
6
|
pitches = []
|
12
7
|
links = {}
|
8
|
+
articulation = Articulations::NORMAL
|
13
9
|
|
14
|
-
|
15
|
-
|
10
|
+
unless more.empty?
|
11
|
+
first_pl = more.first_pl
|
12
|
+
more_pl = more.more_pl
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
pitches.push first_pl.pitch.to_pitch
|
15
|
+
unless first_pl.the_link.empty?
|
16
|
+
links[pitches[-1]] = first_pl.the_link.to_link
|
17
|
+
end
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
more_pl.elements.each do |x|
|
20
|
+
pitches.push x.pl.pitch.to_pitch
|
21
|
+
unless x.pl.the_link.empty?
|
22
|
+
links[pitches[-1]] = x.pl.the_link.to_link
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
unless more.art.empty?
|
27
|
+
articulation = more.art.to_articulation
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
29
31
|
marks = []
|
30
|
-
unless
|
31
|
-
marks.push
|
32
|
-
unless begin_marks.second.empty?
|
33
|
-
marks.push begin_marks.second.to_mark
|
34
|
-
end
|
32
|
+
unless begin_slur.empty?
|
33
|
+
marks.push Musicality::Marks::BEGIN_SLUR
|
35
34
|
end
|
36
35
|
|
37
|
-
unless
|
38
|
-
marks.push
|
39
|
-
unless end_marks.second.empty?
|
40
|
-
marks.push end_marks.second.to_mark
|
41
|
-
end
|
36
|
+
unless end_slur.empty?
|
37
|
+
marks.push Musicality::Marks::END_SLUR
|
42
38
|
end
|
43
39
|
|
44
|
-
Musicality::Note.new(dur, pitches, links: links, marks: marks,
|
45
|
-
articulation: more.art.empty? ? Articulations::NORMAL : more.art.to_articulation)
|
40
|
+
Musicality::Note.new(dur, pitches, links: links, marks: marks, articulation: articulation)
|
46
41
|
end
|
47
42
|
end
|
48
43
|
end
|