musicality 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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