musicality 0.8.0 → 0.9.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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +27 -1
  3. data/README.md +153 -10
  4. data/bin/collidify +102 -0
  5. data/bin/lilify +57 -29
  6. data/bin/midify +64 -24
  7. data/bin/musicality +39 -0
  8. data/examples/composition/auto_counterpoint.rb +4 -5
  9. data/examples/composition/part_generator.rb +8 -2
  10. data/examples/composition/scale_exercise.rb +1 -1
  11. data/examples/notation/notes.rb +27 -0
  12. data/examples/notation/parts.rb +51 -0
  13. data/examples/notation/scores.rb +38 -0
  14. data/examples/notation/twinkle.rb +34 -0
  15. data/examples/notation/twinkle.score +33 -0
  16. data/lib/musicality.rb +46 -11
  17. data/lib/musicality/composition/dsl/score_dsl.rb +2 -2
  18. data/lib/musicality/composition/dsl/score_methods.rb +10 -7
  19. data/lib/musicality/notation/conversion/change_conversion.rb +1 -1
  20. data/lib/musicality/notation/conversion/note_time_converter.rb +6 -23
  21. data/lib/musicality/notation/conversion/score_conversion.rb +15 -15
  22. data/lib/musicality/notation/conversion/score_converter.rb +50 -67
  23. data/lib/musicality/notation/model/articulations.rb +3 -2
  24. data/lib/musicality/notation/model/change.rb +15 -6
  25. data/lib/musicality/notation/model/dynamics.rb +11 -8
  26. data/lib/musicality/notation/model/instrument.rb +61 -0
  27. data/lib/musicality/notation/model/instruments.rb +111 -0
  28. data/lib/musicality/notation/model/key.rb +137 -0
  29. data/lib/musicality/notation/model/keys.rb +37 -0
  30. data/lib/musicality/notation/model/link.rb +6 -19
  31. data/lib/musicality/notation/model/mark.rb +43 -0
  32. data/lib/musicality/notation/model/marks.rb +11 -0
  33. data/lib/musicality/notation/model/meter.rb +4 -0
  34. data/lib/musicality/notation/model/note.rb +42 -28
  35. data/lib/musicality/notation/model/part.rb +18 -5
  36. data/lib/musicality/notation/model/pitch.rb +13 -4
  37. data/lib/musicality/notation/model/score.rb +104 -66
  38. data/lib/musicality/notation/model/symbols.rb +16 -11
  39. data/lib/musicality/notation/parsing/articulation_parsing.rb +38 -38
  40. data/lib/musicality/notation/parsing/articulation_parsing.treetop +14 -14
  41. data/lib/musicality/notation/parsing/link_parsing.rb +6 -6
  42. data/lib/musicality/notation/parsing/link_parsing.treetop +3 -3
  43. data/lib/musicality/notation/parsing/mark_parsing.rb +138 -0
  44. data/lib/musicality/notation/parsing/mark_parsing.treetop +31 -0
  45. data/lib/musicality/notation/parsing/note_node.rb +19 -12
  46. data/lib/musicality/notation/parsing/note_parsing.rb +218 -87
  47. data/lib/musicality/notation/parsing/note_parsing.treetop +9 -5
  48. data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.rb +7 -2
  49. data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.treetop +1 -1
  50. data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.rb +6 -4
  51. data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.treetop +1 -1
  52. data/lib/musicality/notation/util/function.rb +41 -18
  53. data/lib/musicality/packable.rb +156 -0
  54. data/lib/musicality/performance/conversion/glissando_converter.rb +2 -2
  55. data/lib/musicality/performance/conversion/note_sequence_extractor.rb +223 -70
  56. data/lib/musicality/performance/conversion/portamento_converter.rb +5 -2
  57. data/lib/musicality/performance/conversion/score_collator.rb +70 -64
  58. data/lib/musicality/performance/midi/midi_events.rb +3 -3
  59. data/lib/musicality/performance/midi/midi_settings.rb +127 -0
  60. data/lib/musicality/performance/midi/midi_util.rb +8 -2
  61. data/lib/musicality/performance/midi/part_sequencer.rb +19 -18
  62. data/lib/musicality/performance/midi/score_sequencer.rb +13 -9
  63. data/lib/musicality/performance/midi/score_sequencing.rb +5 -5
  64. data/lib/musicality/performance/model/attack.rb +8 -0
  65. data/lib/musicality/performance/model/duration_functions.rb +23 -0
  66. data/lib/musicality/performance/model/note_sequence.rb +52 -95
  67. data/lib/musicality/performance/model/separation.rb +10 -0
  68. data/lib/musicality/performance/supercollider/add_actions.rb +13 -0
  69. data/lib/musicality/performance/supercollider/bundle.rb +18 -0
  70. data/lib/musicality/performance/supercollider/conductor.rb +125 -0
  71. data/lib/musicality/performance/supercollider/group.rb +71 -0
  72. data/lib/musicality/performance/supercollider/message.rb +26 -0
  73. data/lib/musicality/performance/supercollider/node.rb +122 -0
  74. data/lib/musicality/performance/supercollider/performer.rb +123 -0
  75. data/lib/musicality/performance/supercollider/score_conducting.rb +17 -0
  76. data/lib/musicality/performance/supercollider/server.rb +8 -0
  77. data/lib/musicality/performance/supercollider/synth.rb +43 -0
  78. data/lib/musicality/performance/supercollider/synthdef.rb +57 -0
  79. data/lib/musicality/performance/supercollider/synthdef_settings.rb +23 -0
  80. data/lib/musicality/performance/supercollider/synthdefs.rb +1654 -0
  81. data/lib/musicality/{composition/model/pitch_class.rb → pitch_class.rb} +1 -1
  82. data/lib/musicality/{composition/model/pitch_classes.rb → pitch_classes.rb} +9 -9
  83. data/lib/musicality/printing/lilypond/clef.rb +12 -0
  84. data/lib/musicality/printing/lilypond/key_engraving.rb +9 -0
  85. data/lib/musicality/printing/lilypond/lilypond_settings.rb +105 -0
  86. data/lib/musicality/printing/lilypond/meter_engraving.rb +1 -1
  87. data/lib/musicality/printing/lilypond/note_engraving.rb +112 -30
  88. data/lib/musicality/printing/lilypond/part_engraver.rb +114 -3
  89. data/lib/musicality/printing/lilypond/pitch_class_engraving.rb +22 -0
  90. data/lib/musicality/printing/lilypond/pitch_engraving.rb +2 -15
  91. data/lib/musicality/printing/lilypond/score_engraver.rb +44 -73
  92. data/lib/musicality/printing/lilypond/score_engraving.rb +3 -3
  93. data/lib/musicality/project/create_tasks.rb +31 -0
  94. data/lib/musicality/project/file_cleaner.rb +19 -0
  95. data/lib/musicality/project/file_raker.rb +107 -0
  96. data/lib/musicality/project/load_config.rb +43 -0
  97. data/lib/musicality/project/project.rb +64 -0
  98. data/lib/musicality/version.rb +1 -1
  99. data/musicality.gemspec +3 -0
  100. data/spec/composition/util/random_sampler_spec.rb +1 -1
  101. data/spec/notation/conversion/measure_note_map_spec.rb +1 -1
  102. data/spec/notation/conversion/note_time_converter_spec.rb +5 -85
  103. data/spec/notation/conversion/score_conversion_spec.rb +6 -41
  104. data/spec/notation/conversion/score_converter_spec.rb +19 -137
  105. data/spec/notation/model/change_spec.rb +55 -0
  106. data/spec/notation/model/key_spec.rb +171 -0
  107. data/spec/notation/model/link_spec.rb +34 -5
  108. data/spec/notation/model/meter_spec.rb +15 -0
  109. data/spec/notation/model/note_spec.rb +33 -27
  110. data/spec/notation/model/part_spec.rb +53 -4
  111. data/spec/notation/model/pitch_spec.rb +15 -0
  112. data/spec/notation/model/score_spec.rb +64 -72
  113. data/spec/notation/parsing/link_nodes_spec.rb +3 -3
  114. data/spec/notation/parsing/link_parsing_spec.rb +6 -6
  115. data/spec/notation/parsing/note_node_spec.rb +34 -9
  116. data/spec/notation/parsing/note_parsing_spec.rb +11 -9
  117. data/spec/notation/parsing/numbers/nonnegative_integer_spec.rb +4 -0
  118. data/spec/notation/parsing/pitch_node_spec.rb +0 -1
  119. data/spec/notation/util/value_computer_spec.rb +2 -2
  120. data/spec/performance/conversion/glissando_converter_spec.rb +9 -9
  121. data/spec/performance/conversion/note_sequence_extractor_spec.rb +48 -53
  122. data/spec/performance/conversion/portamento_converter_spec.rb +11 -9
  123. data/spec/performance/conversion/score_collator_spec.rb +59 -63
  124. data/spec/performance/midi/midi_util_spec.rb +22 -8
  125. data/spec/performance/midi/part_sequencer_spec.rb +2 -2
  126. data/spec/performance/midi/score_sequencer_spec.rb +12 -10
  127. data/spec/performance/midi/score_sequencing_spec.rb +2 -2
  128. data/spec/performance/model/note_sequence_spec.rb +41 -134
  129. data/spec/printing/note_engraving_spec.rb +204 -0
  130. data/spec/printing/score_engraver_spec.rb +40 -0
  131. data/spec/spec_helper.rb +1 -0
  132. metadata +69 -23
  133. data/examples/notation/hip.rb +0 -32
  134. data/examples/notation/missed_connection.rb +0 -26
  135. data/examples/notation/song1.rb +0 -33
  136. data/examples/notation/song2.rb +0 -32
  137. data/lib/musicality/notation/model/links.rb +0 -11
  138. data/lib/musicality/notation/packing/change_packing.rb +0 -56
  139. data/lib/musicality/notation/packing/part_packing.rb +0 -31
  140. data/lib/musicality/notation/packing/score_packing.rb +0 -123
  141. data/lib/musicality/performance/model/note_attacks.rb +0 -19
  142. data/lib/musicality/performance/util/note_linker.rb +0 -28
  143. data/spec/notation/packing/change_packing_spec.rb +0 -304
  144. data/spec/notation/packing/part_packing_spec.rb +0 -66
  145. data/spec/notation/packing/score_packing_spec.rb +0 -255
  146. data/spec/performance/util/note_linker_spec.rb +0 -68
@@ -12,8 +12,8 @@ class ScoreDSL
12
12
  @score = nil
13
13
  end
14
14
 
15
- def measured_score start_meter, start_tempo, &block
16
- @score = Score::Measured.new(start_meter,start_tempo)
15
+ def tempo_score start_meter, start_tempo, &block
16
+ @score = Score::Tempo.new(start_meter,start_tempo)
17
17
  @score.instance_eval(&block)
18
18
  end
19
19
  end
@@ -51,13 +51,14 @@ class Score
51
51
 
52
52
  def dynamic_change new_dynamic, transition_dur: 0, offset: 0
53
53
  if transition_dur == 0
54
- tempo_changes[self.duration + offset] = Change::Immediate.new(new_tempo)
55
- else
56
- tempo_changes[self.duration + offset] = Change::Gradual.linear(new_tempo, transition_dur)
54
+ change = (transition_dur == 0) ? Change::Immediate.new(new_tempo) : Change::Gradual.linear(new_tempo, transition_dur)
55
+ parts.values.each do |part|
56
+ part.tempo_changes[self.duration + offset] = change
57
+ end
57
58
  end
58
59
  end
59
60
 
60
- class TempoBased < Score
61
+ class Tempo < Score
61
62
  def tempo_change new_tempo, transition_dur: 0, offset: 0
62
63
  if transition_dur == 0
63
64
  tempo_changes[self.duration + offset] = Change::Immediate.new(new_tempo)
@@ -65,12 +66,14 @@ class Score
65
66
  tempo_changes[self.duration + offset] = Change::Gradual.linear(new_tempo, transition_dur)
66
67
  end
67
68
  end
68
- end
69
-
70
- class Measured < Score::TempoBased
69
+
71
70
  def meter_change new_meter, offset: 0
72
71
  meter_changes[self.duration + offset] = Change::Immediate.new(new_meter)
73
72
  end
73
+
74
+ def key_change new_key, offset: 0
75
+ key_changes[self.duration + offset] = Change::Immediate.new(new_key)
76
+ end
74
77
  end
75
78
  end
76
79
 
@@ -11,7 +11,7 @@ class Change
11
11
  end
12
12
 
13
13
  def to_transition offset, value
14
- Transition::new(Function::Constant.new(@end_value), offset..offset)
14
+ Transition.new(Function::Constant.new(@end_value), offset..offset)
15
15
  end
16
16
  end
17
17
 
@@ -1,36 +1,19 @@
1
1
  module Musicality
2
2
 
3
- # Convert offsets in unmeasured note time to just plain time.
3
+ # Convert offsets in measured note time to just plain time.
4
4
  class NoteTimeConverter
5
5
  # @param [ValueComputer] tempo_computer Given an offset, returns tempo
6
6
  # value in quarter-notes-per-minute
7
7
  # @param [Numeric] sample_rate Rate at which tempo values are sampled
8
8
  # in the conversion (samples/sec).
9
- def initialize sample_rate
9
+ def initialize tempo_computer, bdur_computer, sample_rate
10
+ @tempo_computer = tempo_computer
11
+ @bdur_computer = bdur_computer
10
12
  @sample_period = Rational(1,sample_rate)
11
13
  end
12
14
 
13
- class Unmeasured < NoteTimeConverter
14
- def initialize tempo_computer, sample_rate
15
- @tempo_computer = tempo_computer
16
- super(sample_rate)
17
- end
18
-
19
- def notes_per_second_at offset
20
- Tempo::QNPM.to_nps(@tempo_computer.at offset)
21
- end
22
- end
23
-
24
- class Measured < NoteTimeConverter
25
- def initialize tempo_computer, bdur_computer, sample_rate
26
- @tempo_computer = tempo_computer
27
- @bdur_computer = bdur_computer
28
- super(sample_rate)
29
- end
30
-
31
- def notes_per_second_at offset
32
- Tempo::BPM.to_nps(@tempo_computer.at(offset), @bdur_computer.at(offset))
33
- end
15
+ def notes_per_second_at offset
16
+ Tempo::BPM.to_nps(@tempo_computer.at(offset), @bdur_computer.at(offset))
34
17
  end
35
18
 
36
19
  # Calculate the time elapsed between given start/end note offset. Using the
@@ -1,19 +1,11 @@
1
1
  module Musicality
2
2
 
3
3
  class Score
4
- class Unmeasured < TempoBased
5
- # Convert to timed score by converting measure-based offsets and note-based
6
- # durations to time-based. This eliminates the use of tempos.
7
- def to_timed tempo_sample_rate
8
- ScoreConverter::Unmeasured.new(self, tempo_sample_rate).convert_score
9
- end
10
- end
11
-
12
- class Measured < TempoBased
4
+ class Tempo < Score
13
5
  # Convert to timed score by converting measure-based offsets and note-based
14
6
  # durations to time-based. This eliminates the use of meters and tempos.
15
7
  def to_timed tempo_sample_rate
16
- ScoreConverter::Measured.new(self, tempo_sample_rate).convert_score
8
+ ScoreConverter.new(self, tempo_sample_rate).convert_score
17
9
  end
18
10
 
19
11
  def measure_note_map
@@ -23,7 +15,7 @@ class Score
23
15
  def measure_offsets
24
16
  moffs = Set.new([0.to_r])
25
17
  @tempo_changes.each {|moff,change| moffs += change.offsets(moff) }
26
- @meter_changes.keys.each {|moff| moffs.add(moff) }
18
+ moffs += @meter_changes.keys
27
19
  @parts.values.each do |part|
28
20
  part.dynamic_changes.each {|moff,change| moffs += change.offsets(moff) }
29
21
  end
@@ -32,8 +24,12 @@ class Score
32
24
  end
33
25
 
34
26
  def beat_durations
35
- bdurs = @meter_changes.map do |offset,change|
36
- [ offset, change.end_value.beat_duration ]
27
+ bdurs = @meter_changes.map do |offset,change_or_meter|
28
+ if change_or_meter.is_a? Meter
29
+ [ offset, change_or_meter.beat_duration ]
30
+ else
31
+ [ offset, change_or_meter.end_value.beat_duration ]
32
+ end
37
33
  end.sort
38
34
 
39
35
  if bdurs.empty? || bdurs[0][0] != 0
@@ -44,8 +40,12 @@ class Score
44
40
  end
45
41
 
46
42
  def measure_durations
47
- mdurs = @meter_changes.map do |offset,change|
48
- [ offset, change.end_value.measure_duration ]
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
49
  end.sort
50
50
 
51
51
  if mdurs.empty? || mdurs[0][0] != 0
@@ -1,5 +1,7 @@
1
1
  module Musicality
2
2
 
3
+ # Converts tempo-based score to timed score, by converting note-based offsets
4
+ # and durations to time-based, and eliminating the use of tempo and meters.
3
5
  class ScoreConverter
4
6
  def self.convert_changes changes, offset_map
5
7
  Hash[ changes.map do |off,change|
@@ -14,13 +16,13 @@ class ScoreConverter
14
16
  starttime = offset_map[offset]
15
17
  endtime = offset_map[offset + note.duration]
16
18
  offset += note.duration
17
- newnote = note.clone
18
- newnote.duration = endtime - starttime
19
- newnote
19
+ note.resize(endtime - starttime)
20
20
  end
21
21
  new_dcs = convert_changes(part.dynamic_changes, offset_map)
22
- [name, Part.new(part.start_dynamic,
23
- notes: new_notes, dynamic_changes: new_dcs)]
22
+ new_part = part.clone
23
+ new_part.notes = new_notes
24
+ new_part.dynamic_changes = new_dcs
25
+ [name, new_part]
24
26
  end]
25
27
  end
26
28
 
@@ -30,75 +32,56 @@ class ScoreConverter
30
32
  end
31
33
  end
32
34
 
33
- class TempoBased
34
- def initialize parts, program, note_time_converter
35
- @parts = parts
36
- @program = program
37
- @note_time_map = note_time_converter.note_time_map(note_offsets)
38
- end
39
-
40
- def note_offsets
41
- noffs = Set.new([0.to_r])
42
- @parts.values.each do |part|
43
- noff = 0.to_r
44
- part.notes.each {|note| noffs.add(noff += note.duration) }
45
- part.dynamic_changes.each {|noff2,change| noffs += change.offsets(noff2) }
46
- end
47
- noffs += @program.map {|seg| [seg.first, seg.last] }.flatten
48
- return noffs.sort
35
+ def initialize score, tempo_sample_rate
36
+ if score.invalid?
37
+ raise NotValidError, "Errors detected given score: #{score.errors}"
49
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) ]
52
+ 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)
55
+ ntc = NoteTimeConverter.new(tempo_computer, bdur_computer, tempo_sample_rate)
50
56
 
51
- def convert_score
52
- Score::Timed.new(parts: convert_parts, program: convert_program)
53
- end
54
-
55
- # Convert note-based offsets & durations to time-based.
56
- def convert_parts
57
- ScoreConverter.convert_parts(@parts, @note_time_map)
58
- end
59
-
60
- # Convert note-based offsets & durations to time-based.
61
- def convert_program
62
- ScoreConverter.convert_program(@program, @note_time_map)
63
- end
57
+ @parts = new_parts
58
+ @program = new_program
59
+ @note_time_map = ntc.note_time_map(note_offsets)
64
60
  end
65
61
 
66
- # Converts unmeasured score to timed score, by converting note-based offsets
67
- # and durations to time-based, and eliminating the use of tempo.
68
- class Unmeasured < TempoBased
69
- def initialize score, tempo_sample_rate
70
- if score.invalid?
71
- raise NotValidError, "Errors detected given score: #{score.errors}"
72
- end
73
- tempo_computer = ValueComputer.new(score.start_tempo, score.tempo_changes)
74
- ntc = NoteTimeConverter::Unmeasured.new(tempo_computer, tempo_sample_rate)
75
- super(score.parts, score.program, ntc)
62
+ def note_offsets
63
+ noffs = Set.new([0.to_r])
64
+ @parts.values.each do |part|
65
+ noff = 0.to_r
66
+ part.notes.each {|note| noffs.add(noff += note.duration) }
67
+ part.dynamic_changes.each {|noff2,change| noffs += change.offsets(noff2) }
76
68
  end
69
+ noffs += @program.map {|seg| [seg.first, seg.last] }.flatten
70
+ return noffs.sort
71
+ end
72
+
73
+ def convert_score
74
+ Score::Timed.new(parts: convert_parts, program: convert_program)
75
+ end
76
+
77
+ # Convert note-based offsets & durations to time-based.
78
+ def convert_parts
79
+ ScoreConverter.convert_parts(@parts, @note_time_map)
77
80
  end
78
81
 
79
- # Converts measured score to timed score, by converting note-based offsets
80
- # and durations to time-based, and eliminating the use of tempo and meters.
81
- class Measured < TempoBased
82
- def initialize score, tempo_sample_rate
83
- if score.invalid?
84
- raise NotValidError, "Errors detected given score: #{score.errors}"
85
- end
86
- mn_map = score.measure_note_map
87
- new_parts = Hash[ score.parts.map do |name,part|
88
- new_dcs = ScoreConverter.convert_changes(part.dynamic_changes, mn_map)
89
- new_notes = part.notes.map {|n| n.clone } # note duration is already note-based
90
- [name, Part.new(part.start_dynamic, notes: new_notes, dynamic_changes: new_dcs)]
91
- end]
92
- new_program = ScoreConverter.convert_program(score.program, mn_map)
93
- new_tempo_changes = ScoreConverter.convert_changes(score.tempo_changes, mn_map)
94
- new_beat_durations = Hash[ score.beat_durations.map do |moff,bdur|
95
- [mn_map[moff], Change::Immediate.new(bdur) ]
96
- end]
97
- tempo_computer = ValueComputer.new(score.start_tempo, new_tempo_changes)
98
- bdur_computer = ValueComputer.new(score.start_meter.beat_duration, new_beat_durations)
99
- ntc = NoteTimeConverter::Measured.new(tempo_computer, bdur_computer, tempo_sample_rate)
100
- super(new_parts, new_program, ntc)
101
- end
82
+ # Convert note-based offsets & durations to time-based.
83
+ def convert_program
84
+ ScoreConverter.convert_program(@program, @note_time_map)
102
85
  end
103
86
  end
104
87
 
@@ -1,10 +1,11 @@
1
1
  module Musicality
2
2
 
3
3
  module Articulations
4
- NORMAL = :normal
5
- SLUR = :slur
6
4
  LEGATO = :legato
5
+ NORMAL = :normal
7
6
  TENUTO = :tenuto
7
+ ACCENT = :accent
8
+ MARCATO = :marcato
8
9
  PORTATO = :portato
9
10
  STACCATO = :staccato
10
11
  STACCATISSIMO = :staccatissimo
@@ -1,6 +1,8 @@
1
1
  module Musicality
2
2
 
3
3
  class Change
4
+ include Packable
5
+
4
6
  attr_reader :end_value
5
7
 
6
8
  def initialize end_value
@@ -13,12 +15,12 @@ class Change
13
15
  end
14
16
 
15
17
  class Immediate < Change
16
- def initialize value
17
- super(value)
18
+ def initialize end_value
19
+ super(end_value)
18
20
  end
19
21
 
20
22
  def clone
21
- Immediate.new(@end_value)
23
+ Immediate.new(block_given? ? yield(@end_value) : @end_value)
22
24
  end
23
25
 
24
26
  def duration; 0; end
@@ -59,7 +61,12 @@ class Change
59
61
  @start_value == other.start_value
60
62
  end
61
63
 
62
- def clone; Gradual.new(@end_value, @duration, @transition); end
64
+ def clone
65
+ ev = block_given? ? yield(@end_value) : @end_value
66
+ sv = (block_given? && !@start_value.nil?) ? yield(@start_value) : @start_value
67
+ Gradual.new(ev, @duration, @transition, start_value: sv)
68
+ end
69
+
63
70
  def relative?; @start_value.nil?; end
64
71
  def absolute?; !@start_value.nil?; end
65
72
 
@@ -102,8 +109,10 @@ class Change
102
109
  end
103
110
 
104
111
  def clone
105
- Trimmed.new(@end_value, @duration, @transition, start_value: @start_value,
106
- preceding: @preceding, remaining: @remaining)
112
+ ev = block_given? ? yield(@end_value) : @end_value
113
+ sv = (block_given? && !@start_value.nil?) ? yield(@start_value) : @start_value
114
+ Trimmed.new(ev, @duration, @transition, start_value: sv,
115
+ preceding: @preceding, remaining: @remaining)
107
116
  end
108
117
  end
109
118
 
@@ -1,12 +1,15 @@
1
1
  module Musicality
2
2
  module Dynamics
3
- PPP = 0.125
4
- PP = 0.25
5
- P = 0.375
6
- MP = 0.5
7
- MF = 0.625
8
- F = 0.75
9
- FF = 0.875
10
- FFF = 1.0
3
+ PPP = 0.05
4
+ FFF = 0.5
5
+ DYNAMIC_RATIO = (FFF/PPP)**(1.0/7.0) # 7 ratios between the 8 dynamic levels
6
+
7
+ PP = PPP*DYNAMIC_RATIO
8
+ P = PP*DYNAMIC_RATIO
9
+ MP = P*DYNAMIC_RATIO
10
+ MF = MP*DYNAMIC_RATIO
11
+ F = MF*DYNAMIC_RATIO
12
+ FF = F*DYNAMIC_RATIO
13
+
11
14
  end
12
15
  end
@@ -0,0 +1,61 @@
1
+ module Musicality
2
+
3
+ class Instrument
4
+ TREBLE = :treble
5
+ BASS = :bass
6
+ TENOR = :tenor
7
+ ALTO = :alto
8
+ CLEFS = [TREBLE, ALTO, BASS, TENOR]
9
+
10
+ DEFAULT_MIDI_OPT = :default
11
+
12
+ attr_accessor :name, :clefs, :midi_num, :transpose_interval
13
+
14
+ def initialize name, clefs, midi_num, transpose_interval = 0
15
+ @name = name
16
+ @clefs = clefs
17
+ @midi_num = midi_num
18
+ @transpose_interval = transpose_interval
19
+ end
20
+
21
+ def ==(other)
22
+ begin
23
+ return name == other.name &&
24
+ Set.new(clefs) == Set.new(other.clefs) &&
25
+ midi_num == other.midi_num &&
26
+ transpose_interval == other.transpose_interval
27
+ rescue # if other object doesn't have the right methods
28
+ return false
29
+ end
30
+ end
31
+
32
+ def self.treble name, midi_num, transpose_interval = 0
33
+ Instrument.new(name, [TREBLE], midi_num, transpose_interval)
34
+ end
35
+
36
+ def self.bass name, midi_num, transpose_interval = 0
37
+ Instrument.new(name, [BASS], midi_num, transpose_interval)
38
+ end
39
+
40
+ def self.treble_bass name, midi_num, transpose_interval = 0
41
+ Instrument.new(name, [TREBLE,BASS], midi_num, transpose_interval)
42
+ end
43
+
44
+ def self.treble_alto name, midi_num, transpose_interval = 0
45
+ Instrument.new(name, [TREBLE,ALTO], midi_num, transpose_interval)
46
+ end
47
+
48
+ def self.tenor_bass name, midi_num, transpose_interval = 0
49
+ Instrument.new(name, [TENOR, BASS], midi_num, transpose_interval)
50
+ end
51
+
52
+ def self.guitar name, midi_num
53
+ Instrument.new(name, [TENOR], midi_num, 12)
54
+ end
55
+
56
+ def self.bass_guitar name, midi_num
57
+ Instrument.new(name, [BASS], midi_num, 12)
58
+ end
59
+ end
60
+
61
+ end