musicality 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +7 -1
  3. data/lib/musicality.rb +0 -1
  4. data/lib/musicality/composition/dsl/score_methods.rb +5 -6
  5. data/lib/musicality/notation/conversion/score_conversion.rb +3 -34
  6. data/lib/musicality/notation/conversion/score_converter.rb +6 -17
  7. data/lib/musicality/notation/model/mark.rb +0 -12
  8. data/lib/musicality/notation/model/marks.rb +0 -3
  9. data/lib/musicality/notation/model/note.rb +0 -8
  10. data/lib/musicality/notation/model/score.rb +14 -35
  11. data/lib/musicality/notation/model/symbols.rb +0 -2
  12. data/lib/musicality/notation/parsing/mark_parsing.rb +0 -58
  13. data/lib/musicality/notation/parsing/mark_parsing.treetop +0 -12
  14. data/lib/musicality/notation/parsing/note_node.rb +22 -27
  15. data/lib/musicality/notation/parsing/note_parsing.rb +59 -189
  16. data/lib/musicality/notation/parsing/note_parsing.treetop +2 -9
  17. data/lib/musicality/performance/conversion/note_sequence_extractor.rb +1 -20
  18. data/lib/musicality/performance/model/note_sequence.rb +1 -1
  19. data/lib/musicality/printing/lilypond/note_engraving.rb +3 -3
  20. data/lib/musicality/printing/lilypond/part_engraver.rb +16 -12
  21. data/lib/musicality/project/create_tasks.rb +1 -1
  22. data/lib/musicality/project/load_config.rb +16 -1
  23. data/lib/musicality/project/project.rb +2 -2
  24. data/lib/musicality/version.rb +1 -1
  25. data/musicality.gemspec +0 -1
  26. data/spec/composition/dsl/score_methods_spec.rb +73 -0
  27. data/spec/notation/conversion/score_conversion_spec.rb +0 -100
  28. data/spec/notation/conversion/score_converter_spec.rb +33 -33
  29. data/spec/notation/model/note_spec.rb +2 -2
  30. data/spec/notation/model/score_spec.rb +17 -87
  31. data/spec/notation/parsing/note_node_spec.rb +2 -2
  32. data/spec/notation/parsing/note_parsing_spec.rb +3 -3
  33. data/spec/performance/conversion/note_sequence_extractor_spec.rb +23 -0
  34. data/spec/performance/model/note_sequence_spec.rb +50 -6
  35. metadata +4 -19
  36. data/lib/musicality/notation/conversion/measure_note_map.rb +0 -40
  37. 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: a2ca1d5317ab9f47ee4a5a48391c529b6db3dc80
4
- data.tar.gz: 61306cd02de6089beb8ebfd71617cefbafb7a1ee
3
+ metadata.gz: 0d3af3d687ff5109e122bfa208ee1d566bc97674
4
+ data.tar.gz: 2d819234281b8639e5bb8dd58fcc4e1acb2c3834
5
5
  SHA512:
6
- metadata.gz: 48c4022b31b8fede1ecf3ae7d8a0f5ee574d02cb7c46863e746d2df11ce0a5f7eec568df3180742202c2ae11d0885c34a8df07c68d43bf17aa1af13fbeecc001
7
- data.tar.gz: f4ef016ba31054de3de408c1952e2e8d8319c7d09b6faa6dbd72d166636ac7a4fafa1c2e3ffbbf996d3edff397cdbf808d3406bdb62631994d8dc4a5100f3228
6
+ metadata.gz: a3f3dd479c5350582fbe1ff5183dd25ae006e8fa9028d071da12123d1192adfe938dea47c4705fd4c95c192c6f34abf1cf37e473ef1d1e726631dbd375d1154e
7
+ data.tar.gz: 2cf2328e11b1f5b998483c894460e95582e4635c979b310840d0a02c272beadf7bb0f53f01d74b2edca2cc6fe7f57886053d53e971ca1674d7121c737f778ecd
data/ChangeLog.md CHANGED
@@ -1,4 +1,10 @@
1
- ### 0.8.0 / 2016-01-01
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
- durs = part_notes.values.map {|notes| notes.map {|n| n.duration }.inject(0,:+) }
27
- durs_uniq = durs.uniq
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 measure-based offsets and note-based
6
- # durations to time-based. This eliminates the use of meters and tempos.
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
- mn_map = score.measure_note_map
40
- new_parts = Hash[ score.parts.map do |name,part|
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, new_tempo_changes)
54
- bdur_computer = ValueComputer.new(score.start_meter.beat_duration, new_beat_durations)
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 = new_parts
58
- @program = new_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
@@ -3,9 +3,6 @@ module Musicality
3
3
  module Marks
4
4
  BEGIN_SLUR = Mark::Slur::Begin.new
5
5
  END_SLUR = Mark::Slur::End.new
6
-
7
- BEGIN_TRIPLET = Mark::Triplet::Begin.new
8
- END_TRIPLET = Mark::Triplet::End.new
9
6
  end
10
7
 
11
8
  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 max_part_duration
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
- # Offsets are measure-based, and tempo values are in beats-per-minute.
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
- def measures_long note_dur = self.notes_long
111
- noff_end = note_dur
112
- noff_prev = 0.to_r
113
- moff_prev, mdur_prev = 0.to_r, @start_meter.measure_duration
114
-
115
- @meter_changes.sort.each do |moff,change|
116
- mdur = change.end_value.measure_duration
117
- notes_elapsed = mdur_prev * (moff - moff_prev)
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}"
@@ -20,8 +20,6 @@ LINK_SYMBOLS = {
20
20
  MARK_SYMBOLS = {
21
21
  Mark::Slur::Begin => "(",
22
22
  Mark::Slur::End => ")",
23
- Mark::Triplet::Begin => "[",
24
- Mark::Triplet::End => "]",
25
23
  }
26
24
 
27
25
  end
@@ -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
- first_pl = more.first_pl
15
- more_pl = more.more_pl
10
+ unless more.empty?
11
+ first_pl = more.first_pl
12
+ more_pl = more.more_pl
16
13
 
17
- pitches.push first_pl.pitch.to_pitch
18
- unless first_pl.the_link.empty?
19
- links[pitches[-1]] = first_pl.the_link.to_link
20
- end
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
- more_pl.elements.each do |x|
23
- pitches.push x.pl.pitch.to_pitch
24
- unless x.pl.the_link.empty?
25
- links[pitches[-1]] = x.pl.the_link.to_link
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 begin_marks.empty?
31
- marks.push begin_marks.first.to_mark
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 end_marks.empty?
38
- marks.push end_marks.first.to_mark
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