mtk 0.0.3.2 → 0.0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/.yardopts +2 -2
  2. data/DEVELOPMENT_NOTES.md +20 -0
  3. data/README.md +9 -3
  4. data/Rakefile +47 -13
  5. data/bin/mtk +55 -20
  6. data/examples/crescendo.rb +4 -4
  7. data/examples/{drum_pattern1.rb → drum_pattern.rb} +8 -8
  8. data/examples/dynamic_pattern.rb +5 -5
  9. data/examples/gets_and_play.rb +3 -2
  10. data/examples/notation.rb +3 -3
  11. data/examples/play_midi.rb +4 -4
  12. data/examples/print_midi.rb +2 -2
  13. data/examples/random_tone_row.rb +3 -3
  14. data/examples/syntax_to_midi.rb +2 -2
  15. data/examples/test_output.rb +4 -5
  16. data/examples/tone_row_melody.rb +7 -5
  17. data/lib/mtk/core/duration.rb +213 -0
  18. data/lib/mtk/core/intensity.rb +158 -0
  19. data/lib/mtk/core/interval.rb +157 -0
  20. data/lib/mtk/core/pitch.rb +154 -0
  21. data/lib/mtk/core/pitch_class.rb +194 -0
  22. data/lib/mtk/events/event.rb +4 -4
  23. data/lib/mtk/events/note.rb +12 -12
  24. data/lib/mtk/events/timeline.rb +232 -0
  25. data/lib/mtk/groups/chord.rb +56 -0
  26. data/lib/mtk/{helpers → groups}/collection.rb +33 -1
  27. data/lib/mtk/groups/melody.rb +96 -0
  28. data/lib/mtk/groups/pitch_class_set.rb +163 -0
  29. data/lib/mtk/{helpers → groups}/pitch_collection.rb +1 -1
  30. data/lib/mtk/{midi → io}/dls_synth_device.rb +3 -1
  31. data/lib/mtk/{midi → io}/dls_synth_output.rb +10 -10
  32. data/lib/mtk/{midi → io}/jsound_input.rb +2 -2
  33. data/lib/mtk/{midi → io}/jsound_output.rb +9 -9
  34. data/lib/mtk/{midi/file.rb → io/midi_file.rb} +13 -13
  35. data/lib/mtk/{midi/input.rb → io/midi_input.rb} +4 -4
  36. data/lib/mtk/{midi/output.rb → io/midi_output.rb} +8 -8
  37. data/lib/mtk/{helpers/lilypond.rb → io/notation.rb} +5 -5
  38. data/lib/mtk/{midi → io}/unimidi_input.rb +2 -2
  39. data/lib/mtk/{midi → io}/unimidi_output.rb +14 -9
  40. data/lib/mtk/{constants → lang}/durations.rb +11 -11
  41. data/lib/mtk/{constants → lang}/intensities.rb +11 -11
  42. data/lib/mtk/{constants → lang}/intervals.rb +17 -17
  43. data/lib/mtk/lang/mtk_grammar.citrus +9 -9
  44. data/lib/mtk/{constants → lang}/pitch_classes.rb +5 -5
  45. data/lib/mtk/{constants → lang}/pitches.rb +7 -7
  46. data/lib/mtk/{helpers → lang}/pseudo_constants.rb +1 -1
  47. data/lib/mtk/{variable.rb → lang/variable.rb} +1 -1
  48. data/lib/mtk/numeric_extensions.rb +40 -47
  49. data/lib/mtk/patterns/for_each.rb +1 -1
  50. data/lib/mtk/patterns/pattern.rb +3 -3
  51. data/lib/mtk/sequencers/event_builder.rb +16 -15
  52. data/lib/mtk/sequencers/legato_sequencer.rb +1 -1
  53. data/lib/mtk/sequencers/rhythmic_sequencer.rb +1 -1
  54. data/lib/mtk/sequencers/sequencer.rb +8 -8
  55. data/lib/mtk/sequencers/step_sequencer.rb +2 -2
  56. data/lib/mtk.rb +33 -39
  57. data/spec/mtk/{duration_spec.rb → core/duration_spec.rb} +3 -3
  58. data/spec/mtk/{intensity_spec.rb → core/intensity_spec.rb} +3 -3
  59. data/spec/mtk/{interval_spec.rb → core/interval_spec.rb} +1 -1
  60. data/spec/mtk/{pitch_class_spec.rb → core/pitch_class_spec.rb} +1 -1
  61. data/spec/mtk/{pitch_spec.rb → core/pitch_spec.rb} +8 -8
  62. data/spec/mtk/events/event_spec.rb +4 -4
  63. data/spec/mtk/events/note_spec.rb +8 -8
  64. data/spec/mtk/{timeline_spec.rb → events/timeline_spec.rb} +47 -47
  65. data/spec/mtk/{chord_spec.rb → groups/chord_spec.rb} +18 -16
  66. data/spec/mtk/{helpers → groups}/collection_spec.rb +3 -3
  67. data/spec/mtk/{melody_spec.rb → groups/melody_spec.rb} +36 -34
  68. data/spec/mtk/{pitch_class_set_spec.rb → groups/pitch_class_set_spec.rb} +57 -55
  69. data/spec/mtk/{midi/file_spec.rb → io/midi_file_spec.rb} +17 -17
  70. data/spec/mtk/{midi/output_spec.rb → io/midi_output_spec.rb} +6 -6
  71. data/spec/mtk/{constants → lang}/durations_spec.rb +1 -1
  72. data/spec/mtk/{constants → lang}/intensities_spec.rb +1 -1
  73. data/spec/mtk/{constants → lang}/intervals_spec.rb +1 -1
  74. data/spec/mtk/lang/parser_spec.rb +12 -6
  75. data/spec/mtk/{constants → lang}/pitch_classes_spec.rb +1 -1
  76. data/spec/mtk/{constants → lang}/pitches_spec.rb +1 -1
  77. data/spec/mtk/{helpers → lang}/pseudo_constants_spec.rb +2 -2
  78. data/spec/mtk/{variable_spec.rb → lang/variable_spec.rb} +4 -4
  79. data/spec/mtk/numeric_extensions_spec.rb +35 -55
  80. data/spec/mtk/patterns/for_each_spec.rb +1 -1
  81. data/spec/mtk/patterns/sequence_spec.rb +1 -1
  82. data/spec/mtk/sequencers/legato_sequencer_spec.rb +2 -2
  83. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +4 -4
  84. data/spec/mtk/sequencers/step_sequencer_spec.rb +5 -5
  85. data/spec/spec_helper.rb +7 -6
  86. metadata +75 -61
  87. data/ext/mkrf_conf.rb +0 -25
  88. data/lib/mtk/chord.rb +0 -55
  89. data/lib/mtk/duration.rb +0 -211
  90. data/lib/mtk/helpers/convert.rb +0 -36
  91. data/lib/mtk/helpers/output_selector.rb +0 -67
  92. data/lib/mtk/intensity.rb +0 -156
  93. data/lib/mtk/interval.rb +0 -155
  94. data/lib/mtk/melody.rb +0 -94
  95. data/lib/mtk/pitch.rb +0 -152
  96. data/lib/mtk/pitch_class.rb +0 -192
  97. data/lib/mtk/pitch_class_set.rb +0 -161
  98. data/lib/mtk/timeline.rb +0 -230
  99. data/spec/mtk/midi/jsound_input_spec.rb +0 -11
  100. data/spec/mtk/midi/jsound_output_spec.rb +0 -11
  101. data/spec/mtk/midi/unimidi_input_spec.rb +0 -11
  102. data/spec/mtk/midi/unimidi_output_spec.rb +0 -11
@@ -1,192 +0,0 @@
1
- module MTK
2
-
3
- # A set of all pitches that are an integer number of octaves apart.
4
- # A {Pitch} has the same PitchClass as the pitches one or more octaves away.
5
- # @see https://en.wikipedia.org/wiki/Pitch_class
6
- #
7
- class PitchClass
8
-
9
- # The normalized names of the 12 pitch classes in the chromatic scale.
10
- # The index of each {#name} is the pitch class's numeric {#value}.
11
- NAMES = %w( C Db D Eb E F Gb G Ab A Bb B ).freeze
12
-
13
- # All enharmonic names of the 12 pitch classes, including sharps, flats, double-sharps, and double-flats,
14
- # organized such that each index contains the allowed names of the pitch class with a {#value} equal to that index.
15
- # @see VALID_NAMES
16
- VALID_NAMES_BY_VALUE =
17
- [ # (valid names ), # value # normalized name
18
- %w( B# C Dbb ), # 0 # C
19
- %w( B## C# Db ), # 1 # Db
20
- %w( C## D Ebb ), # 2 # D
21
- %w( D# Eb Fbb ), # 3 # Eb
22
- %w( D## E Fb ), # 4 # E
23
- %w( E# F Gbb ), # 5 # F
24
- %w( E## F# Gb ), # 6 # Gb
25
- %w( F## G Abb ), # 7 # G
26
- %w( G# Ab ), # 8 # Ab
27
- %w( G## A Bbb ), # 9 # A
28
- %w( A# Bb Cbb ), # 10 # Bb
29
- %w( A## B Cb ) # 11 # B
30
- ].freeze
31
-
32
- # All valid enharmonic pitch class names in a flat list.
33
- # @see VALID_NAMES_BY_VALUE
34
- VALID_NAMES = VALID_NAMES_BY_VALUE.flatten.freeze
35
-
36
- # A mapping from valid names to the value of the pitch class with that name
37
- VALUES_BY_NAME = Hash[ # a map from a list of name,value pairs
38
- VALID_NAMES_BY_VALUE.map.with_index do |valid_names,value|
39
- valid_names.map{|name| [name,value] }
40
- end.flatten(1)
41
- ].freeze
42
-
43
-
44
- # The name of this pitch class.
45
- # One of the {NAMES} defined by this class.
46
- attr_reader :name
47
-
48
- # The value of this pitch class.
49
- # An integer from 0..11 that indexes this pitch class in {PITCH_CLASSES} and the {#name} in {NAMES}.
50
- attr_reader :value
51
-
52
-
53
- private ######
54
- # Even though new is a private_class_method, YARD gets confused so we temporarily go private
55
-
56
- def initialize(name, value)
57
- @name, @value = name, value
58
- end
59
- private_class_method :new
60
-
61
- @flyweight = {}
62
-
63
- public ######
64
-
65
-
66
- # Lookup a PitchClass by name or value.
67
- # @param name_or_value [String,Symbol,Numeric] one of {VALID_NAMES} or 0..12
68
- # @return the PitchClass representing the argument
69
- # @raise ArgumentError for arguments that cannot be converted to a PitchClass
70
- def self.[] name_or_value
71
- @flyweight[name_or_value] ||= case name_or_value
72
- when String,Symbol then from_name(name_or_value)
73
- when Numeric then from_value(name_or_value.round)
74
- else raise ArgumentError.new("PitchClass.[] doesn't understand #{name_or_value.class}")
75
- end
76
- end
77
-
78
- # Lookup a PitchClass by name.
79
- # @param name [String,#to_s] one of {VALID_NAMES} (case-insensitive)
80
- def self.from_name(name)
81
- @flyweight[name] ||= (
82
- valid_name = name.to_s.capitalize
83
- value = VALUES_BY_NAME[valid_name] or raise ArgumentError.new("Invalid PitchClass name: #{name}")
84
- new(valid_name,value)
85
- )
86
- end
87
-
88
- class << self
89
- alias from_s from_name
90
- end
91
-
92
- # All 12 pitch classes in the chromatic scale.
93
- # The index of each pitch class is the pitch class's numeric {#value}.
94
- PITCH_CLASSES = NAMES.map{|name| from_name name }.freeze
95
-
96
- # return the pitch class with the given integer value mod 12
97
- # @param value [Integer,#to_i]
98
- def self.from_value(value)
99
- PITCH_CLASSES[value.to_i % 12]
100
- end
101
-
102
- class << self
103
- alias from_i from_value
104
- end
105
-
106
- # return the pitch class with the given float rounded to the nearest integer, mod 12
107
- # @param value [Float,#to_f]
108
- def self.from_f(value)
109
- from_i value.to_f.round
110
- end
111
-
112
- # Compare 2 pitch classes for equal values.
113
- # @param other [PitchClass]
114
- # @return true if this pitch class's value is equal to the other pitch class's value
115
- def == other
116
- other.is_a? PitchClass and other.value == @value
117
- end
118
-
119
- # Compare a pitch class with another pitch class or integer value
120
- # @param other [PitchClass,#to_i]
121
- # @return -1, 0, or +1 depending on whether this pitch class's value is less than, equal to, or greater than the other object's integer value
122
- # @see http://ruby-doc.org/core-1.9.3/Comparable.html
123
- def <=> other
124
- @value <=> other.to_i
125
- end
126
-
127
- # This pitch class's normalized {#name}.
128
- # @see NAMES
129
- def to_s
130
- @name.to_s
131
- end
132
-
133
- # This pitch class's integer {#value}
134
- def to_i
135
- @value.to_i
136
- end
137
-
138
- # This pitch class's {#value} as a floating point number
139
- def to_f
140
- @value.to_f
141
- end
142
-
143
- # Transpose this pitch class by adding it's value to the value given (mod 12)
144
- # @param interval [PitchClass,Float,#to_f]
145
- def + interval
146
- new_value = (value + interval.to_f).round
147
- self.class.from_value new_value
148
- end
149
- alias transpose +
150
-
151
- # Transpose this pitch class by subtracing the given value from this value (mod 12)
152
- # @param interval [PitchClass,Float,#to_f]
153
- def - interval
154
- new_value = (value - interval.to_f).round
155
- self.class.from_value new_value
156
- end
157
-
158
- # Inverts (mirrors) the pitch class around the given center
159
- # @param center [PitchClass,Pitch,Float,#to_f] the value to "mirror" this pitch class around
160
- def invert(center)
161
- delta = (2*(center.to_f - value)).round
162
- self + delta
163
- end
164
-
165
- # the smallest interval in semitones that needs to be added to this PitchClass to reach the given PitchClass
166
- # @param pitch_class [PitchClass,#value]
167
- def distance_to(pitch_class)
168
- delta = (pitch_class.value - value) % 12
169
- if delta > 6
170
- delta -= 12
171
- elsif delta == 6 and to_i >= 6
172
- # this is a special edge case to prevent endlessly ascending pitch sequences when alternating between two pitch classes a tritone apart
173
- delta = -6
174
- end
175
- delta
176
- end
177
-
178
- end
179
-
180
- # Construct a {PitchClass} from any supported type
181
- # @param anything [PitchClass,String,Symbol,Numeric]
182
- def PitchClass(anything)
183
- case anything
184
- when Numeric then PitchClass.from_f(anything)
185
- when String, Symbol then PitchClass.from_s(anything)
186
- when PitchClass then anything
187
- else raise ArgumentError.new("PitchClass doesn't understand #{anything.class}")
188
- end
189
- end
190
- module_function :PitchClass
191
-
192
- end
@@ -1,161 +0,0 @@
1
- module MTK
2
-
3
- # An ordered collection of {PitchClass}es.
4
- #
5
- # Unlike a mathematical Set, a PitchClassSet is ordered and may contain duplicates.
6
- #
7
- # @see Melody
8
- # @see Chord
9
- #
10
- class PitchClassSet
11
- include Helpers::PitchCollection
12
-
13
- attr_reader :pitch_classes
14
-
15
- def self.random_row
16
- new(Constants::PitchClasses::PITCH_CLASSES.shuffle)
17
- end
18
-
19
- def self.all
20
- @all ||= new(Constants::PitchClasses::PITCH_CLASSES)
21
- end
22
-
23
- # @param pitch_classes [#to_a] the collection of pitch classes
24
- #
25
- # @see MTK#PitchClassSet
26
- #
27
- def initialize(pitch_classes)
28
- @pitch_classes = pitch_classes.to_a.clone.freeze
29
- end
30
-
31
- # @see Helper::Collection
32
- def elements
33
- @pitch_classes
34
- end
35
-
36
- # Convert to an Array of pitch_classes.
37
- # @note this returns a mutable copy the underlying @pitch_classes attribute, which is otherwise unmutable
38
- alias :to_pitch_classes :to_a
39
-
40
- def self.from_a enumerable
41
- new enumerable
42
- end
43
-
44
- def normal_order
45
- ordering = Array.new(@pitch_classes.uniq.sort)
46
- min_span, start_index_for_normal_order = nil, nil
47
-
48
- # check every rotation for the minimal span:
49
- size.times do |index|
50
- span = self.class.span_between ordering.first, ordering.last
51
-
52
- if min_span.nil? or span < min_span
53
- # best so far
54
- min_span = span
55
- start_index_for_normal_order = index
56
-
57
- elsif span == min_span
58
- # handle ties, minimize distance between first and second-to-last note, then first and third-to-last, etc
59
- span1, span2 = nil, nil
60
- tie_breaker = 1
61
- while span1 == span2 and tie_breaker < size
62
- span1 = self.class.span_between( ordering[0], ordering[-1 - tie_breaker] )
63
- span2 = self.class.span_between( ordering[start_index_for_normal_order], ordering[start_index_for_normal_order - tie_breaker] )
64
- tie_breaker -= 1
65
- end
66
- if span1 != span2
67
- # tie cannot be broken, pick the one starting with the lowest pitch class
68
- if ordering[0].to_i < ordering[start_index_for_normal_order].to_i
69
- start_index_for_normal_order = index
70
- end
71
- elsif span1 < span2
72
- start_index_for_normal_order = index
73
- end
74
-
75
- end
76
- ordering << ordering.shift # rotate
77
- end
78
-
79
- # we've rotated all the way around, so we now need to rotate back to the start index we just found:
80
- start_index_for_normal_order.times{ ordering << ordering.shift }
81
-
82
- ordering
83
- end
84
-
85
- def normal_form
86
- norder = normal_order
87
- first_pc_val = norder.first.to_i
88
- norder.map{|pitch_class| (pitch_class.to_i - first_pc_val) % 12 }
89
- end
90
-
91
- # the collection of elements present in both sets
92
- def intersection(other)
93
- self.class.from_a(to_a & other.to_a)
94
- end
95
-
96
- # the collection of all elements present in either set
97
- def union(other)
98
- self.class.from_a(to_a | other.to_a)
99
- end
100
-
101
- # the collection of elements from this set with any elements from the other set removed
102
- def difference(other)
103
- self.class.from_a(to_a - other.to_a)
104
- end
105
-
106
- # the collection of elements that are members of exactly one of the sets
107
- def symmetric_difference(other)
108
- union(other).difference( intersection(other) )
109
- end
110
-
111
- # the collection of elements that are not members of this set
112
- def complement
113
- self.class.all.difference(self)
114
- end
115
-
116
- # @param other [#pitch_classes, #to_a, Array]
117
- def == other
118
- if other.respond_to? :pitch_classes
119
- @pitch_classes == other.pitch_classes
120
- elsif other.respond_to? :to_a
121
- @pitch_classes == other.to_a
122
- else
123
- @pitch_classes == other
124
- end
125
- end
126
-
127
- # Compare for equality, ignoring order and duplicates
128
- # @param other [#pitch_classes, Array, #to_a]
129
- def =~ other
130
- @normalized_pitch_classes ||= @pitch_classes.uniq.sort
131
- @normalized_pitch_classes == case
132
- when other.respond_to?(:pitch_classes) then other.pitch_classes.uniq.sort
133
- when (other.is_a? Array and other.frozen?) then other
134
- when other.respond_to?(:to_a) then other.to_a.uniq.sort
135
- else other
136
- end
137
- end
138
-
139
- def to_s
140
- @pitch_classes.join(' ')
141
- end
142
-
143
- def inspect
144
- @pitch_classes.inspect
145
- end
146
-
147
- def self.span_between(pc1, pc2)
148
- (pc2.to_i - pc1.to_i) % 12
149
- end
150
-
151
- end
152
-
153
-
154
- # Construct a {PitchClassSet}
155
- # @see PitchClassSet#initialize
156
- def PitchClassSet(*anything)
157
- PitchClassSet.new Helpers::Convert.to_pitch_classes(*anything)
158
- end
159
- module_function :PitchClassSet
160
-
161
- end
data/lib/mtk/timeline.rb DELETED
@@ -1,230 +0,0 @@
1
- module MTK
2
-
3
- # A collection of timed events. The core data structure used to interface with input and output.
4
- #
5
- # Maps sorted floating point times to lists of events.
6
- #
7
- # Enumerable as [time,event_list] pairs.
8
- #
9
- class Timeline
10
- include Enumerable
11
-
12
- def initialize()
13
- @timeline = {}
14
- end
15
-
16
- class << self
17
- def from_a(enumerable)
18
- new.merge enumerable
19
- end
20
- alias from_hash from_a
21
- end
22
-
23
- def merge enumerable
24
- enumerable.each do |time,events|
25
- add(time,events)
26
- end
27
- self
28
- end
29
-
30
- def clear
31
- @timeline.clear
32
- self
33
- end
34
-
35
- def to_hash
36
- @timeline
37
- end
38
-
39
- def == other
40
- other = other.to_hash unless other.is_a? Hash
41
- @timeline == other
42
- end
43
-
44
- def [](time)
45
- @timeline[time.to_f]
46
- end
47
-
48
- def []=(time, events)
49
- time = time.to_f unless time.is_a? Numeric
50
- case events
51
- when nil?
52
- @timeline.delete time.to_f
53
- when Array
54
- @timeline[time.to_f] = events
55
- else
56
- @timeline[time.to_f] = [events]
57
- end
58
- end
59
-
60
- def add(time, event)
61
- events = @timeline[time.to_f]
62
- if events
63
- if event.is_a? Array
64
- events.concat event
65
- else
66
- events << event
67
- end
68
- else
69
- self[time] = event
70
- end
71
- end
72
-
73
- def delete(time)
74
- @timeline.delete(time.to_f)
75
- end
76
-
77
- def has_time? time
78
- @timeline.has_key? time.to_f
79
- end
80
-
81
- def times
82
- @timeline.keys.sort
83
- end
84
-
85
- def length
86
- last_time = times.last
87
- events = @timeline[last_time]
88
- last_time + events.map{|event| event.duration }.max
89
- end
90
-
91
- def empty?
92
- @timeline.empty?
93
- end
94
-
95
- def events
96
- times.map{|t| @timeline[t] }.flatten
97
- end
98
-
99
- def each
100
- # this is similar to @timeline.each, but by iterating over #times, we yield the events in chronological order
101
- times.each do |time|
102
- yield time, @timeline[time]
103
- end
104
- end
105
-
106
- # the original Enumerable#map implementation, which returns an Array
107
- alias enumerable_map map
108
-
109
- # Constructs a new Timeline by mapping each [time,event_list] pair
110
- # @see #map!
111
- def map &block
112
- self.class.from_a enumerable_map(&block)
113
- end
114
-
115
- # Perform #map in place
116
- # @see #map
117
- def map! &block
118
- mapped = enumerable_map(&block)
119
- clear
120
- merge mapped
121
- end
122
-
123
- # Map every individual event, without regard for the time at which is occurs
124
- def map_events
125
- mapped_timeline = Timeline.new
126
- self.each do |time,events|
127
- mapped_timeline[time] = events.map{|event| yield event }
128
- end
129
- mapped_timeline
130
- end
131
-
132
- # Map every individual event in place, without regard for the time at which is occurs
133
- def map_events!
134
- each do |time,events|
135
- self[time] = events.map{|event| yield event }
136
- end
137
- end
138
-
139
- def clone
140
- self.class.from_hash(to_hash)
141
- end
142
-
143
- def compact!
144
- @timeline.delete_if {|t,events| events.empty? }
145
- end
146
-
147
- def flatten
148
- flattened = Timeline.new
149
- self.each do |time,events|
150
- events.each do |event|
151
- if event.is_a? Timeline
152
- event.flatten.each do |subtime,subevent|
153
- flattened.add(time+subtime, subevent)
154
- end
155
- else
156
- flattened.add(time,event)
157
- end
158
- end
159
- end
160
- flattened
161
- end
162
-
163
- # @return a new Timeline where all times have been quantized to multiples of the given interval
164
- # @example timeline.quantize(0.5) # quantize to eight notes (assuming the beat is a quarter note)
165
- # @see quantize!
166
- def quantize interval
167
- map{|time,events| [self.class.quantize_time(time,interval), events] }
168
- end
169
-
170
- def quantize! interval
171
- map!{|time,events| [self.class.quantize_time(time,interval), events] }
172
- end
173
-
174
- # shifts all times by the given amount
175
- # @see #shift!
176
- # @see #shift_to
177
- def shift time_delta
178
- map{|time,events| [time+time_delta, events] }
179
- end
180
-
181
- # shifts all times in place by the given amount
182
- # @see #shift
183
- # @see #shift_to!
184
- def shift! time_delta
185
- map!{|time,events| [time+time_delta, events] }
186
- end
187
-
188
- # shifts the times so that the start of the timeline is at the given time
189
- # @see #shift_to!
190
- # @see #shift
191
- def shift_to absolute_time
192
- start = times.first
193
- if start
194
- shift absolute_time - start
195
- else
196
- clone
197
- end
198
- end
199
-
200
- # shifts the times in place so that the start of the timeline is at the given time
201
- # @see #shift_to
202
- # @see #shift!
203
- def shift_to! absolute_time
204
- start = times.first
205
- if start
206
- shift! absolute_time - start
207
- end
208
- self
209
- end
210
-
211
- def to_s
212
- times = self.times
213
- last = times.last
214
- width = sprintf("%d",last).length + 3 # nicely align the '=>' against the longest number
215
- times.map{|t| sprintf("%#{width}.2f",t)+" => #{@timeline[t].join ', '}" }.join "\n"
216
- end
217
-
218
- def inspect
219
- @timeline.inspect
220
- end
221
-
222
- def self.quantize_time time, interval
223
- upper = interval * (time.to_f/interval).ceil
224
- lower = upper - interval
225
- (time - lower) < (upper - time) ? lower : upper
226
- end
227
-
228
- end
229
-
230
- end
@@ -1,11 +0,0 @@
1
- begin
2
- require 'spec_helper'
3
- require 'mtk/midi/input'
4
- require 'mtk/midi/jsound_input'
5
-
6
- describe MTK::MIDI::JSoundInput do
7
-
8
- end
9
-
10
-
11
- rescue LoadError; end # only run the test when the required gems are available
@@ -1,11 +0,0 @@
1
- begin
2
- require 'spec_helper'
3
- require 'mtk/midi/output'
4
- require 'mtk/midi/jsound_output'
5
-
6
- describe MTK::MIDI::JSoundOutput do
7
-
8
- end
9
-
10
-
11
- rescue LoadError; end # only run the test when the required gems are available
@@ -1,11 +0,0 @@
1
- begin
2
- require 'spec_helper'
3
- require 'mtk/midi/input'
4
- require 'mtk/midi/unimidi_input'
5
-
6
- describe MTK::MIDI::UniMIDIInput do
7
-
8
- end
9
-
10
-
11
- rescue LoadError; end # only run the test when the required gems are available
@@ -1,11 +0,0 @@
1
- begin
2
- require 'spec_helper'
3
- require 'mtk/midi/output'
4
- require 'mtk/midi/unimidi_output'
5
-
6
- describe MTK::MIDI::UniMIDIOutput do
7
-
8
- end
9
-
10
-
11
- rescue LoadError; end # only run the test when the required gems are available