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.
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