mtk 0.0.1 → 0.0.2
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.
- data/.yardopts +9 -0
- data/INTRO.md +73 -0
- data/LICENSE.txt +27 -0
- data/README.md +93 -18
- data/Rakefile +13 -1
- data/examples/crescendo.rb +20 -0
- data/examples/dynamic_pattern.rb +39 -0
- data/examples/play_midi.rb +19 -0
- data/examples/print_midi.rb +13 -0
- data/examples/random_tone_row.rb +18 -0
- data/examples/tone_row_melody.rb +21 -0
- data/lib/mtk/_constants/durations.rb +80 -0
- data/lib/mtk/_constants/intensities.rb +81 -0
- data/lib/mtk/{constants → _constants}/intervals.rb +10 -1
- data/lib/mtk/_constants/pitch_classes.rb +35 -0
- data/lib/mtk/_constants/pitches.rb +49 -0
- data/lib/mtk/{numeric_extensions.rb → _numeric_extensions.rb} +0 -0
- data/lib/mtk/event.rb +14 -5
- data/lib/mtk/helper/collection.rb +114 -0
- data/lib/mtk/helper/event_builder.rb +85 -0
- data/lib/mtk/{constants → helper}/pseudo_constants.rb +7 -6
- data/lib/mtk/lang/grammar.rb +17 -0
- data/lib/mtk/lang/mtk_grammar.citrus +60 -0
- data/lib/mtk/midi/file.rb +10 -15
- data/lib/mtk/midi/jsound_input.rb +68 -0
- data/lib/mtk/midi/jsound_output.rb +80 -0
- data/lib/mtk/note.rb +22 -3
- data/lib/mtk/pattern/abstract_pattern.rb +132 -0
- data/lib/mtk/pattern/choice.rb +25 -9
- data/lib/mtk/pattern/cycle.rb +51 -0
- data/lib/mtk/pattern/enumerator.rb +26 -0
- data/lib/mtk/pattern/function.rb +46 -0
- data/lib/mtk/pattern/lines.rb +60 -0
- data/lib/mtk/pattern/palindrome.rb +42 -0
- data/lib/mtk/pattern/sequence.rb +15 -50
- data/lib/mtk/pitch.rb +45 -6
- data/lib/mtk/pitch_class.rb +36 -35
- data/lib/mtk/pitch_class_set.rb +46 -14
- data/lib/mtk/pitch_set.rb +20 -31
- data/lib/mtk/sequencer/abstract_sequencer.rb +85 -0
- data/lib/mtk/sequencer/rhythmic_sequencer.rb +29 -0
- data/lib/mtk/sequencer/step_sequencer.rb +26 -0
- data/lib/mtk/timeline.rb +75 -22
- data/lib/mtk/transform/invertible.rb +15 -0
- data/lib/mtk/{util → transform}/mappable.rb +6 -2
- data/lib/mtk/transform/set_theory_operations.rb +34 -0
- data/lib/mtk/transform/transposable.rb +14 -0
- data/lib/mtk.rb +56 -22
- data/spec/mtk/_constants/durations_spec.rb +118 -0
- data/spec/mtk/{constants/dynamics_spec.rb → _constants/intensities_spec.rb} +48 -17
- data/spec/mtk/{constants → _constants}/intervals_spec.rb +21 -0
- data/spec/mtk/_constants/pitch_classes_spec.rb +58 -0
- data/spec/mtk/_constants/pitches_spec.rb +52 -0
- data/spec/mtk/{numeric_extensions_spec.rb → _numeric_extensions_spec.rb} +0 -0
- data/spec/mtk/event_spec.rb +19 -0
- data/spec/mtk/helper/collection_spec.rb +291 -0
- data/spec/mtk/helper/event_builder_spec.rb +92 -0
- data/spec/mtk/helper/pseudo_constants_spec.rb +20 -0
- data/spec/mtk/lang/grammar_spec.rb +100 -0
- data/spec/mtk/midi/file_spec.rb +41 -6
- data/spec/mtk/note_spec.rb +53 -3
- data/spec/mtk/pattern/abstract_pattern_spec.rb +45 -0
- data/spec/mtk/pattern/choice_spec.rb +89 -3
- data/spec/mtk/pattern/cycle_spec.rb +133 -0
- data/spec/mtk/pattern/function_spec.rb +133 -0
- data/spec/mtk/pattern/lines_spec.rb +93 -0
- data/spec/mtk/pattern/note_cycle_spec.rb.bak +116 -0
- data/spec/mtk/pattern/palindrome_spec.rb +124 -0
- data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +47 -0
- data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +37 -0
- data/spec/mtk/pattern/sequence_spec.rb +128 -31
- data/spec/mtk/pitch_class_set_spec.rb +240 -7
- data/spec/mtk/pitch_class_spec.rb +84 -18
- data/spec/mtk/pitch_set_spec.rb +45 -10
- data/spec/mtk/pitch_spec.rb +59 -0
- data/spec/mtk/sequencer/abstract_sequencer_spec.rb +159 -0
- data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +49 -0
- data/spec/mtk/sequencer/step_sequencer_spec.rb +71 -0
- data/spec/mtk/timeline_spec.rb +118 -15
- data/spec/spec_helper.rb +4 -3
- metadata +59 -22
- data/lib/mtk/chord.rb +0 -47
- data/lib/mtk/constants/dynamics.rb +0 -56
- data/lib/mtk/constants/pitch_classes.rb +0 -18
- data/lib/mtk/constants/pitches.rb +0 -24
- data/lib/mtk/pattern/note_sequence.rb +0 -60
- data/lib/mtk/pattern/pitch_sequence.rb +0 -22
- data/lib/mtk/patterns.rb +0 -4
- data/spec/mtk/chord_spec.rb +0 -74
- data/spec/mtk/constants/pitch_classes_spec.rb +0 -35
- data/spec/mtk/constants/pitches_spec.rb +0 -23
- data/spec/mtk/pattern/note_sequence_spec.rb +0 -121
- data/spec/mtk/pattern/pitch_sequence_spec.rb +0 -47
data/lib/mtk/pitch_class_set.rb
CHANGED
@@ -1,35 +1,39 @@
|
|
1
1
|
module MTK
|
2
2
|
|
3
|
-
#
|
3
|
+
# An ordered Set of PitchClasses, for 12-tone set-theory pitch analysis and manipulations
|
4
4
|
#
|
5
5
|
class PitchClassSet
|
6
6
|
|
7
|
-
include
|
7
|
+
include Helper::Collection
|
8
|
+
include Transform::Mappable
|
9
|
+
include Transform::Transposable
|
10
|
+
include Transform::Invertible
|
11
|
+
include Transform::SetTheoryOperations
|
8
12
|
|
9
13
|
attr_reader :pitch_classes
|
10
|
-
|
11
|
-
def
|
12
|
-
|
14
|
+
|
15
|
+
def self.random_row
|
16
|
+
new(PitchClasses::PITCH_CLASSES.shuffle)
|
13
17
|
end
|
14
18
|
|
15
|
-
def self.
|
16
|
-
new
|
19
|
+
def self.all
|
20
|
+
@all ||= new(PitchClasses::PITCH_CLASSES)
|
17
21
|
end
|
18
22
|
|
19
|
-
def
|
20
|
-
|
23
|
+
def initialize(pitch_classes)
|
24
|
+
@pitch_classes = pitch_classes.to_a.uniq.freeze
|
21
25
|
end
|
22
26
|
|
23
|
-
def
|
24
|
-
@pitch_classes
|
27
|
+
def elements
|
28
|
+
@pitch_classes
|
25
29
|
end
|
26
30
|
|
27
|
-
def
|
28
|
-
|
31
|
+
def self.from_a enumerable
|
32
|
+
new enumerable
|
29
33
|
end
|
30
34
|
|
31
35
|
def normal_order
|
32
|
-
ordering = Array.new(@pitch_classes)
|
36
|
+
ordering = Array.new(@pitch_classes.sort)
|
33
37
|
min_span, start_index_for_normal_order = nil, nil
|
34
38
|
|
35
39
|
# check every rotation for the minimal span:
|
@@ -86,6 +90,22 @@ module MTK
|
|
86
90
|
end
|
87
91
|
end
|
88
92
|
|
93
|
+
# Compare for equality, ignoring order
|
94
|
+
# @param other [#pitch_classes, #to_a, #sort, Array]
|
95
|
+
def =~ other
|
96
|
+
if other.is_a? Array and other.frozen?
|
97
|
+
@pitch_classes.sort == other
|
98
|
+
elsif other.respond_to? :pitch_classes
|
99
|
+
@pitch_classes.sort == other.pitch_classes.sort
|
100
|
+
elsif other.respond_to? :to_a
|
101
|
+
@pitch_classes.sort == other.to_a.sort
|
102
|
+
elsif other.respond_to? :sort
|
103
|
+
@pitch_classes.sort == other.sort
|
104
|
+
else
|
105
|
+
@pitch_classes.sort == other
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
89
109
|
def to_s
|
90
110
|
@pitch_classes.join(' ')
|
91
111
|
end
|
@@ -103,4 +123,16 @@ module MTK
|
|
103
123
|
end
|
104
124
|
|
105
125
|
end
|
126
|
+
|
127
|
+
# Construct a {PitchClassSet} from any supported type
|
128
|
+
def PitchClassSet(*anything)
|
129
|
+
anything = anything.first if anything.size == 1
|
130
|
+
case anything
|
131
|
+
when Array then PitchClassSet.new(anything.map{|elem| PitchClass(elem) })
|
132
|
+
when PitchClassSet then anything
|
133
|
+
else PitchClassSet.new([PitchClass(anything)])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
module_function :PitchClassSet
|
137
|
+
|
106
138
|
end
|
data/lib/mtk/pitch_set.rb
CHANGED
@@ -4,7 +4,10 @@ module MTK
|
|
4
4
|
#
|
5
5
|
class PitchSet
|
6
6
|
|
7
|
-
include
|
7
|
+
include Helper::Collection
|
8
|
+
include Transform::Mappable
|
9
|
+
include Transform::Transposable
|
10
|
+
include Transform::Invertible
|
8
11
|
|
9
12
|
attr_reader :pitches
|
10
13
|
|
@@ -12,16 +15,12 @@ module MTK
|
|
12
15
|
@pitches = pitches.to_a.uniq.sort.freeze
|
13
16
|
end
|
14
17
|
|
15
|
-
def
|
16
|
-
|
18
|
+
def elements
|
19
|
+
@pitches
|
17
20
|
end
|
18
21
|
|
19
|
-
def
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
def each &block
|
24
|
-
@pitches.each &block
|
22
|
+
def self.from_a enumerable
|
23
|
+
new enumerable
|
25
24
|
end
|
26
25
|
|
27
26
|
def to_pitch_class_set
|
@@ -32,18 +31,7 @@ module MTK
|
|
32
31
|
@pitch_classes ||= @pitches.map{|p| p.pitch_class }.uniq
|
33
32
|
end
|
34
33
|
|
35
|
-
|
36
|
-
each_pitch_apply :+, semitones
|
37
|
-
end
|
38
|
-
|
39
|
-
def - semitones
|
40
|
-
each_pitch_apply :-, semitones
|
41
|
-
end
|
42
|
-
|
43
|
-
def invert(center_pitch=@pitches.first)
|
44
|
-
each_pitch_apply :invert, center_pitch
|
45
|
-
end
|
46
|
-
|
34
|
+
# generate a chord inversion (positive numbers move the lowest notes up an octave, negative moves the highest notes down)
|
47
35
|
def inversion(number)
|
48
36
|
number = number.to_i
|
49
37
|
pitch_set = Array.new(@pitches)
|
@@ -61,12 +49,8 @@ module MTK
|
|
61
49
|
self.class.new pitch_set
|
62
50
|
end
|
63
51
|
|
64
|
-
def include? pitch
|
65
|
-
@pitches.include? pitch
|
66
|
-
end
|
67
|
-
|
68
52
|
def nearest(pitch_class)
|
69
|
-
self
|
53
|
+
self.transpose @pitches.first.pitch_class.distance_to(pitch_class)
|
70
54
|
end
|
71
55
|
|
72
56
|
# @param other [#pitches, #to_a, Array]
|
@@ -84,12 +68,17 @@ module MTK
|
|
84
68
|
@pitches.inspect
|
85
69
|
end
|
86
70
|
|
87
|
-
|
88
|
-
protected
|
71
|
+
end
|
89
72
|
|
90
|
-
|
91
|
-
|
73
|
+
# Construct a {PitchSet} from any supported type
|
74
|
+
def PitchSet(*anything)
|
75
|
+
anything = anything.first if anything.size == 1
|
76
|
+
case anything
|
77
|
+
when Array then PitchSet.new(anything.map{|elem| Pitch(elem) })
|
78
|
+
when PitchSet then anything
|
79
|
+
else PitchSet.new([Pitch(anything)])
|
92
80
|
end
|
93
|
-
|
94
81
|
end
|
82
|
+
module_function :PitchSet
|
83
|
+
|
95
84
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module MTK
|
2
|
+
module Sequencer
|
3
|
+
|
4
|
+
# A Sequencer produces {Timeline}s from a collection of {Pattern}s.
|
5
|
+
#
|
6
|
+
# @abstract Subclass and override {#advance} to implement a Sequencer.
|
7
|
+
#
|
8
|
+
class AbstractSequencer
|
9
|
+
|
10
|
+
# The maximum number of [time,event_list] entries that will be generated for the {Timeline}.
|
11
|
+
# nil means no maximum (be careful of infinite loops!)
|
12
|
+
attr_accessor :max_steps
|
13
|
+
|
14
|
+
# The maximum time (key) that will be generated for the {Timeline}.
|
15
|
+
# nil means no maximum (be careful of infinite loops!)
|
16
|
+
attr_accessor :max_time
|
17
|
+
|
18
|
+
# Used by {#to_timeline} to builds event lists from the results of #{Pattern::Enumerator#next} for the {Pattern}s in this Sequencer.
|
19
|
+
attr_reader :event_builder
|
20
|
+
|
21
|
+
# The current time offset for the sequencer. Used for the {Timeline} times.
|
22
|
+
attr_reader :time
|
23
|
+
|
24
|
+
# The current sequencer step index (the number of times-1 that {#next} has been called), or -1 if the sequencer has not yet started.
|
25
|
+
attr_reader :step
|
26
|
+
|
27
|
+
def initialize(patterns, options={})
|
28
|
+
@patterns = patterns
|
29
|
+
@max_steps = options[:max_steps]
|
30
|
+
@max_time = options[:max_time]
|
31
|
+
|
32
|
+
event_builder_class = options.fetch :event_builder, Helper::EventBuilder
|
33
|
+
@event_builder = event_builder_class.new(patterns, options)
|
34
|
+
rewind
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Produce a {Timeline} from the {Pattern}s in this Sequencer.
|
39
|
+
def to_timeline
|
40
|
+
rewind
|
41
|
+
timeline = Timeline.new
|
42
|
+
loop do
|
43
|
+
events = self.next
|
44
|
+
timeline[@time] = events if events
|
45
|
+
end
|
46
|
+
timeline
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# Advanced the step index and time, and return the next list of events built from the sequencer patterns.
|
51
|
+
# @note this is called automatically by {#to_timeline},
|
52
|
+
# so you can ignore this method unless you want to hack on sequencers at a lower level.
|
53
|
+
def next
|
54
|
+
if @step >= 0
|
55
|
+
advance!
|
56
|
+
raise StopIteration if @max_time and @time > @max_time
|
57
|
+
end
|
58
|
+
@step += 1
|
59
|
+
raise StopIteration if @max_steps and @step >= @max_steps
|
60
|
+
@event_builder.next
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# Reset the sequencer and all its patterns.
|
65
|
+
# @note this is called automatically at the beginning of {#to_timeline},
|
66
|
+
# so you can ignore this method unless you want to hack on sequencers at a lower level.
|
67
|
+
def rewind
|
68
|
+
@time = 0
|
69
|
+
@step = -1
|
70
|
+
event_builder.rewind
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
########################
|
75
|
+
protected
|
76
|
+
|
77
|
+
# Advance @time to the next time for the {Timeline} being produced by {#to_timeline}
|
78
|
+
def advance!
|
79
|
+
@time += 1 # default behavior simply advances one beat at a time
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MTK
|
2
|
+
module Sequencer
|
3
|
+
|
4
|
+
# A Sequencer which uses a :rhythm type {Pattern} to determine the delta times between entries in the {Timeline}.
|
5
|
+
class RhythmicSequencer < AbstractSequencer
|
6
|
+
|
7
|
+
def initialize(patterns, options={})
|
8
|
+
patterns = patterns.clone
|
9
|
+
patterns.each_with_index do |pattern, index|
|
10
|
+
if pattern.type == :rhythm
|
11
|
+
@rhythm = pattern
|
12
|
+
patterns.delete_at index # so we don't enumerate the rhythm values in EventBuilder
|
13
|
+
end
|
14
|
+
end
|
15
|
+
super(patterns, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
########################
|
19
|
+
protected
|
20
|
+
|
21
|
+
# (see AbstractSequencer#advance!)
|
22
|
+
def advance!
|
23
|
+
@time += @rhythm.next
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MTK
|
2
|
+
module Sequencer
|
3
|
+
|
4
|
+
# A Sequencer which has a constant {#step_size} time between {Timeline} entries.
|
5
|
+
class StepSequencer < AbstractSequencer
|
6
|
+
|
7
|
+
# The time between entries in the {Timeline}.
|
8
|
+
attr_accessor :step_size
|
9
|
+
|
10
|
+
def initialize(patterns, options={})
|
11
|
+
super
|
12
|
+
@step_size = options.fetch :step_size, 1
|
13
|
+
end
|
14
|
+
|
15
|
+
########################
|
16
|
+
protected
|
17
|
+
|
18
|
+
# (see AbstractSequencer#advance!)
|
19
|
+
def advance!
|
20
|
+
@time += @step_size
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/mtk/timeline.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
module MTK
|
2
2
|
|
3
|
+
# A collection of timed events. The core data structure used to interface with input and output.
|
4
|
+
#
|
3
5
|
# Maps sorted times to lists of events.
|
4
6
|
#
|
5
|
-
# Enumerable as
|
7
|
+
# Enumerable as [time,event_list] pairs.
|
6
8
|
#
|
7
9
|
class Timeline
|
8
10
|
|
9
|
-
include Mappable
|
11
|
+
include Transform::Mappable
|
10
12
|
|
11
13
|
def initialize()
|
12
14
|
@timeline = {}
|
@@ -90,37 +92,32 @@ module MTK
|
|
90
92
|
end
|
91
93
|
|
92
94
|
def each
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
yield time,event
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def each_time
|
102
|
-
times.each do |time|
|
103
|
-
events = @timeline[time]
|
104
|
-
yield time,events
|
95
|
+
# this is similar to @timeline.each, but by iterating over #times, we yield the events in chronological order
|
96
|
+
for time in times
|
97
|
+
yield time,@timeline[time]
|
105
98
|
end
|
106
99
|
end
|
107
100
|
|
108
101
|
def map! &block
|
102
|
+
# we use the enumerable_map that aliased by the Mappable module,
|
103
|
+
# because Mappable#map will create an extra timeline instance, which is unnecessary in this case
|
109
104
|
mapped = enumerable_map &block
|
110
105
|
clear
|
111
106
|
merge mapped
|
112
107
|
end
|
113
108
|
|
109
|
+
# Map every individual event, without regard for the time at which is occurs
|
114
110
|
def map_events
|
115
111
|
mapped_timeline = Timeline.new
|
116
|
-
|
112
|
+
for time,events in self
|
117
113
|
mapped_timeline[time] = events.map{|event| yield event }
|
118
114
|
end
|
119
115
|
mapped_timeline
|
120
116
|
end
|
121
117
|
|
118
|
+
# Map every individual event in place, without regard for the time at which is occurs
|
122
119
|
def map_events!
|
123
|
-
|
120
|
+
for time,events in self
|
124
121
|
self[time] = events.map{|event| yield event }
|
125
122
|
end
|
126
123
|
end
|
@@ -135,18 +132,68 @@ module MTK
|
|
135
132
|
|
136
133
|
def flatten
|
137
134
|
flattened = Timeline.new
|
138
|
-
for time,
|
139
|
-
|
140
|
-
|
141
|
-
|
135
|
+
for time,events in self
|
136
|
+
for event in events
|
137
|
+
if event.is_a? Timeline
|
138
|
+
for subtime,subevent in event.flatten
|
139
|
+
flattened.add(time+subtime, subevent)
|
140
|
+
end
|
141
|
+
else
|
142
|
+
flattened.add(time,event)
|
142
143
|
end
|
143
|
-
else
|
144
|
-
flattened.add(time,event)
|
145
144
|
end
|
146
145
|
end
|
147
146
|
flattened
|
148
147
|
end
|
149
148
|
|
149
|
+
# @return a new Timeline where all times have been quantized to multiples of the given interval
|
150
|
+
# @example timeline.quantize(0.5) # quantize to eight notes (assuming the beat is a quarter note)
|
151
|
+
# @see quantize!
|
152
|
+
def quantize interval
|
153
|
+
map{|time,events| [self.class.quantize_time(time,interval), events] }
|
154
|
+
end
|
155
|
+
|
156
|
+
def quantize! interval
|
157
|
+
map!{|time,events| [self.class.quantize_time(time,interval), events] }
|
158
|
+
end
|
159
|
+
|
160
|
+
# shifts all times by the given amount
|
161
|
+
# @see #shift!
|
162
|
+
# @see #shift_to
|
163
|
+
def shift time_delta
|
164
|
+
map{|time,events| [time+time_delta, events] }
|
165
|
+
end
|
166
|
+
|
167
|
+
# shifts all times in place by the given amount
|
168
|
+
# @see #shift
|
169
|
+
# @see #shift_to!
|
170
|
+
def shift! time_delta
|
171
|
+
map!{|time,events| [time+time_delta, events] }
|
172
|
+
end
|
173
|
+
|
174
|
+
# shifts the times so that the start of the timeline is at the given time
|
175
|
+
# @see #shift_to!
|
176
|
+
# @see #shift
|
177
|
+
def shift_to absolute_time
|
178
|
+
start = times.first
|
179
|
+
if start
|
180
|
+
shift absolute_time - start
|
181
|
+
else
|
182
|
+
clone
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# shifts the times in place so that the start of the timeline is at the given time
|
187
|
+
# @see #shift_to
|
188
|
+
# @see #shift!
|
189
|
+
def shift_to! absolute_time
|
190
|
+
start = times.first
|
191
|
+
if start
|
192
|
+
shift! absolute_time - start
|
193
|
+
end
|
194
|
+
self
|
195
|
+
end
|
196
|
+
|
150
197
|
def to_s
|
151
198
|
times.map{|t| "#{t} => #{@timeline[t].join ', '}" }.join "\n"
|
152
199
|
end
|
@@ -155,6 +202,12 @@ module MTK
|
|
155
202
|
@timeline.inspect
|
156
203
|
end
|
157
204
|
|
205
|
+
def self.quantize_time time, interval
|
206
|
+
upper = interval * (time.to_f/interval).ceil
|
207
|
+
lower = upper - interval
|
208
|
+
(time - lower) < (upper - time) ? lower : upper
|
209
|
+
end
|
210
|
+
|
158
211
|
end
|
159
212
|
|
160
213
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module MTK::Transform
|
2
|
+
|
3
|
+
# {Mappable} class whose elements can handle the :invert message
|
4
|
+
# @note Classes including this module should include either MTK::Collection or provide a #first method
|
5
|
+
module Invertible
|
6
|
+
|
7
|
+
# Invert all elements around the given inversion point
|
8
|
+
# @param inversion_point [Numeric] the value around which all elements will be inverted (defaults to the first element in the collection)
|
9
|
+
def invert(inversion_point=first)
|
10
|
+
map{|elem| elem.invert(inversion_point) }
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -1,14 +1,18 @@
|
|
1
|
-
module MTK
|
1
|
+
module MTK::Transform
|
2
2
|
|
3
3
|
# Similar to Enumerable, but relies on the including Class's from_a method to
|
4
|
-
# provide an implementation of #map which returns an object of the same type
|
4
|
+
# provide an implementation of #map which returns an object of the same type.
|
5
5
|
module Mappable
|
6
6
|
include Enumerable
|
7
7
|
|
8
|
+
# the original Enumerable#map implementation, which returns an Array
|
8
9
|
alias enumerable_map map
|
10
|
+
|
11
|
+
# the overriden #map implementation, which returns an object of the same type
|
9
12
|
def map &block
|
10
13
|
self.class.from_a(enumerable_map &block)
|
11
14
|
end
|
12
15
|
|
13
16
|
end
|
17
|
+
|
14
18
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module MTK::Transform
|
2
|
+
|
3
|
+
# {Helper::Collection} that supports set theory operations
|
4
|
+
module SetTheoryOperations
|
5
|
+
|
6
|
+
# the collection of elements present in both sets
|
7
|
+
def intersection(other)
|
8
|
+
self.class.from_a(to_a & other.to_a)
|
9
|
+
end
|
10
|
+
|
11
|
+
# the collection of all elements present in either set
|
12
|
+
def union(other)
|
13
|
+
self.class.from_a(to_a | other.to_a)
|
14
|
+
end
|
15
|
+
|
16
|
+
# the collection of elements from this set with any elements from the other set removed
|
17
|
+
def difference(other)
|
18
|
+
self.class.from_a(to_a - other.to_a)
|
19
|
+
end
|
20
|
+
|
21
|
+
# the collection of elements that are members of exactly one of the sets
|
22
|
+
def symmetric_difference(other)
|
23
|
+
union(other).difference( intersection(other) )
|
24
|
+
end
|
25
|
+
|
26
|
+
# the collection of elements that are not members of this set
|
27
|
+
# @note this method requires that the including class define the class method .all(), which returns the collection of all possible elements
|
28
|
+
def complement
|
29
|
+
self.class.all.difference(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module MTK::Transform
|
2
|
+
|
3
|
+
# {Mappable} class whose elements can handle the :transpose message
|
4
|
+
module Transposable
|
5
|
+
|
6
|
+
# Transpose all elements upward by the given interval
|
7
|
+
# @param interval_in_semitones [Numeric] an interval in semitones
|
8
|
+
def transpose interval_in_semitones
|
9
|
+
map{|elem| elem + interval_in_semitones }
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/lib/mtk.rb
CHANGED
@@ -1,4 +1,41 @@
|
|
1
|
-
|
1
|
+
##############################################
|
2
|
+
# Description of modules for documentation:
|
3
|
+
|
4
|
+
# The top level module for this library
|
5
|
+
module MTK
|
6
|
+
|
7
|
+
# Internal helper classes used to avoid duplicating code in this library.
|
8
|
+
module Helper
|
9
|
+
end
|
10
|
+
|
11
|
+
# Classes that emit elements one at a time. Used by {Sequencer}s to construct {Timeline}s.
|
12
|
+
#
|
13
|
+
# The core interface for Pattern classes is {Pattern::Enumerator#next} and {Pattern::Enumerator#rewind}.
|
14
|
+
module Pattern
|
15
|
+
end
|
16
|
+
|
17
|
+
# Classes that assemble {Pattern}s into {Timeline}s.
|
18
|
+
module Sequencer
|
19
|
+
end
|
20
|
+
|
21
|
+
# Optional classes for the "MTK language", which let's you compose music via MTK without writing any Ruby code
|
22
|
+
module Lang
|
23
|
+
end
|
24
|
+
|
25
|
+
# Optional classes for MIDI input and output.
|
26
|
+
module MIDI
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'mtk/helper/collection'
|
32
|
+
require 'mtk/helper/pseudo_constants'
|
33
|
+
|
34
|
+
require 'mtk/transform/mappable'
|
35
|
+
require 'mtk/transform/transposable'
|
36
|
+
require 'mtk/transform/invertible'
|
37
|
+
require 'mtk/transform/set_theory_operations'
|
38
|
+
|
2
39
|
require 'mtk/pitch_class'
|
3
40
|
require 'mtk/pitch_class_set'
|
4
41
|
require 'mtk/pitch'
|
@@ -6,31 +43,28 @@ require 'mtk/pitch_set'
|
|
6
43
|
|
7
44
|
require 'mtk/event'
|
8
45
|
require 'mtk/note'
|
9
|
-
require 'mtk/chord'
|
10
46
|
require 'mtk/timeline'
|
11
47
|
|
12
|
-
require 'mtk/
|
13
|
-
require 'mtk/
|
14
|
-
require 'mtk/
|
15
|
-
require 'mtk/
|
16
|
-
require 'mtk/
|
48
|
+
require 'mtk/_constants/pitch_classes'
|
49
|
+
require 'mtk/_constants/pitches'
|
50
|
+
require 'mtk/_constants/intervals'
|
51
|
+
require 'mtk/_constants/intensities'
|
52
|
+
require 'mtk/_constants/durations'
|
17
53
|
|
18
|
-
require 'mtk/
|
54
|
+
require 'mtk/pattern/enumerator'
|
55
|
+
require 'mtk/pattern/abstract_pattern'
|
56
|
+
require 'mtk/pattern/sequence'
|
57
|
+
require 'mtk/pattern/cycle'
|
58
|
+
require 'mtk/pattern/choice'
|
59
|
+
require 'mtk/pattern/function'
|
60
|
+
require 'mtk/pattern/lines'
|
61
|
+
require 'mtk/pattern/palindrome'
|
19
62
|
|
63
|
+
require 'mtk/helper/event_builder'
|
64
|
+
require 'mtk/sequencer/abstract_sequencer'
|
65
|
+
require 'mtk/sequencer/step_sequencer'
|
66
|
+
require 'mtk/sequencer/rhythmic_sequencer'
|
20
67
|
|
21
|
-
|
22
|
-
# Description of modules for documentation:
|
68
|
+
require 'mtk/_numeric_extensions'
|
23
69
|
|
24
|
-
# The top level module for this library
|
25
|
-
module MTK
|
26
|
-
|
27
|
-
# Classes for MIDI input and output
|
28
|
-
module MIDI
|
29
|
-
end
|
30
|
-
|
31
|
-
# Classes that enumerate elements
|
32
|
-
module Pattern
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
70
|
|