musicality 0.9.0 → 0.10.0
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/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
|