diamond 0.5.1 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -18
- data/lib/diamond.rb +1 -1
- data/lib/diamond/arpeggiator.rb +3 -12
- data/lib/diamond/clock.rb +45 -38
- data/lib/diamond/midi.rb +24 -14
- data/lib/diamond/osc.rb +6 -6
- data/lib/diamond/sequence.rb +1 -1
- data/lib/diamond/sequence_parameters.rb +57 -24
- data/test/arpeggiator_test.rb +2 -0
- data/test/clock_test.rb +70 -0
- data/test/sequence_parameters_test.rb +29 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30e670f044ab17c64cedf2c71573a1a510928cd2
|
4
|
+
data.tar.gz: 0b91f502f5952b2df09f311f1569eb5aa6b061bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60f2d5c041101b1bece73255f6cb4416ed5a254afcb971cda4f7de68f1a962f6f1ce8d3ff99fb34fef43b41c404d42581599ee9a45ca74aa71041fa17b5c320f
|
7
|
+
data.tar.gz: d782c0fa28658fd8c8521ae20f17b763e7bc22f9df7a0202ae67711e0494480eee2842ff0f782045c7c3d722fa13d6b39fdc00742f3ed4072f6da74a8e8cdcf4
|
data/README.md
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
#
|
1
|
+
#Diamond
|
2
2
|
|
3
3
|
[MIDI arpeggiator](http://en.wikipedia.org/wiki/Arpeggiator#Arpeggiator) in Ruby
|
4
4
|
|
5
5
|
![diamond](http://img256.imageshack.us/img256/4514/diamondd.jpg)
|
6
6
|
|
7
|
-
##
|
7
|
+
##Features
|
8
8
|
|
9
|
-
* Classic arpeggiator functionality
|
9
|
+
* Classic arpeggiator functionality and patterns
|
10
10
|
* [OSC](http://en.wikipedia.org/wiki/Open_Sound_Control) and MIDI remote control
|
11
11
|
* MIDI clock IO
|
12
|
-
* Suited to [live coding](http://en.wikipedia.org/wiki/Live_coding)
|
13
12
|
* Multiplex clocks and arpeggiators
|
14
|
-
*
|
13
|
+
* Suited to [live coding](http://en.wikipedia.org/wiki/Live_coding)
|
14
|
+
* Generative arpeggio patterns
|
15
15
|
|
16
|
-
##
|
16
|
+
##Installation
|
17
17
|
|
18
18
|
`gem install diamond`
|
19
19
|
|
@@ -21,13 +21,13 @@
|
|
21
21
|
|
22
22
|
`gem "diamond"`
|
23
23
|
|
24
|
-
##
|
24
|
+
##Usage
|
25
25
|
|
26
26
|
```ruby
|
27
27
|
require "diamond"
|
28
28
|
```
|
29
29
|
|
30
|
-
First, select a MIDI output using [unimidi](https://github.com/arirusso/unimidi). ([more about that
|
30
|
+
First, select a MIDI output using [unimidi](https://github.com/arirusso/unimidi). ([more about that here](http://tx81z.blogspot.com/2011/10/selecting-midi-device-with-unimidi.html))
|
31
31
|
|
32
32
|
```ruby
|
33
33
|
@output = UniMIDI::Output.gets
|
@@ -60,7 +60,7 @@ Point the clock to the arpeggiator
|
|
60
60
|
clock << arpeggiator
|
61
61
|
```
|
62
62
|
|
63
|
-
|
63
|
+
The arpeggiator will play based on inputted notes or chords; a MIDI input can be used for that. ([see example](http://github.com/arirusso/diamond/blob/master/examples/midi_note_input.rb)). It's also possible to enter notes in Ruby:
|
64
64
|
|
65
65
|
```ruby
|
66
66
|
chord = ["C3", "G3", "Bb3", "A4"]
|
@@ -73,15 +73,15 @@ arpeggiator.add(chord)
|
|
73
73
|
arpeggiator << "C5"
|
74
74
|
```
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
Note that by default, the clock will run in a background thread. If you're working in a [PRY](http://pryrepl.org)/[IRB](http://en.wikipedia.org/wiki/Interactive_Ruby_Shell)/etc this will allow you to continue to code while the arpeggiator runs. To start in the *foreground*, pass `:focus => true` to `Clock#start`.
|
76
|
+
Starting the clock will also start the arpeggiator:
|
79
77
|
|
80
78
|
```ruby
|
81
79
|
clock.start
|
82
80
|
```
|
83
81
|
|
84
|
-
|
82
|
+
Note that by default, the clock will run in a background thread. If you're working in a [PRY](http://pryrepl.org)/[IRB](http://en.wikipedia.org/wiki/Interactive_Ruby_Shell)/etc this will allow you to continue to code while the arpeggiator runs. To start in the *foreground*, pass `:focus => true` to `Clock#start`.
|
83
|
+
|
84
|
+
All of the [arpeggiator options](http://rubydoc.info/github/arirusso/diamond/master/Diamond/Arpeggiator:initialize) can be controlled while the arpeggiator is running.
|
85
85
|
|
86
86
|
```ruby
|
87
87
|
arpeggiator.rate = 16
|
@@ -93,13 +93,13 @@ arpeggiator.remove("C5", "A4")
|
|
93
93
|
|
94
94
|
This [blog post](http://tx81z.blogspot.com/2011/07/live-coding-with-diamond.html) explains what is happening in the video.
|
95
95
|
|
96
|
-
####
|
96
|
+
####Posts
|
97
97
|
|
98
98
|
* [Introduction](http://tx81z.blogspot.com/2011/07/diamond-midi-arpeggiator-in-ruby.html)
|
99
99
|
* [Live coding Diamond and syncing multiple arpeggiators to each other](http://tx81z.blogspot.com/2011/07/live-coding-with-diamond.html)
|
100
100
|
* [A note about live coding in IRB with OSX](http://tx81z.blogspot.com/2011/09/note-about-live-coding-in-irb-with-osx.html)
|
101
101
|
|
102
|
-
####
|
102
|
+
####Examples
|
103
103
|
|
104
104
|
* [Control via OSC](http://github.com/arirusso/diamond/blob/master/examples/osc_control.rb)
|
105
105
|
* [Define a Pattern](http://github.com/arirusso/diamond/blob/master/examples/define_pattern.rb)
|
@@ -111,15 +111,15 @@ This [blog post](http://tx81z.blogspot.com/2011/07/live-coding-with-diamond.html
|
|
111
111
|
|
112
112
|
[More...](http://github.com/arirusso/diamond/blob/master/examples)
|
113
113
|
|
114
|
-
##
|
114
|
+
##Other Documentation
|
115
115
|
|
116
116
|
* [rdoc](http://rubydoc.info/github/arirusso/diamond)
|
117
117
|
|
118
|
-
##
|
118
|
+
##Author
|
119
119
|
|
120
120
|
* [Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
|
121
121
|
|
122
|
-
##
|
122
|
+
##License
|
123
123
|
|
124
124
|
Apache 2.0, See the file LICENSE
|
125
125
|
|
data/lib/diamond.rb
CHANGED
data/lib/diamond/arpeggiator.rb
CHANGED
@@ -51,9 +51,9 @@ module Diamond
|
|
51
51
|
transmit_channel = options[:tx_channel]
|
52
52
|
devices = MIDIInstrument::Device.partition(options[:midi])
|
53
53
|
@midi = MIDI.new(devices, :debug => !!options[:midi_debug], :receive_channel => receive_channel, :transmit_channel => transmit_channel)
|
54
|
-
@midi.enable_output(
|
55
|
-
@midi.enable_note_control(
|
56
|
-
@midi.enable_parameter_control(
|
54
|
+
@midi.enable_output(self)
|
55
|
+
@midi.enable_note_control(self)
|
56
|
+
@midi.enable_parameter_control(self, options[:midi_control]) if !options[:midi_control].nil?
|
57
57
|
@midi
|
58
58
|
end
|
59
59
|
|
@@ -67,15 +67,6 @@ module Diamond
|
|
67
67
|
@osc
|
68
68
|
end
|
69
69
|
|
70
|
-
# Emit any note off messages that are currently pending in the queue. The clock triggers this
|
71
|
-
# when stopping or pausing
|
72
|
-
# @return [Array<MIDIMessage::NoteOff>]
|
73
|
-
def emit_pending_note_offs
|
74
|
-
messages = @sequence.pending_note_offs
|
75
|
-
@midi.output.puts(*messages)
|
76
|
-
messages
|
77
|
-
end
|
78
|
-
|
79
70
|
end
|
80
71
|
|
81
72
|
end
|
data/lib/diamond/clock.rb
CHANGED
@@ -1,18 +1,26 @@
|
|
1
1
|
module Diamond
|
2
2
|
|
3
|
-
# A wrapper for
|
3
|
+
# A wrapper for Topaz::Tempo that's geared towards the arpeggiator
|
4
4
|
class Clock
|
5
5
|
|
6
6
|
extend Forwardable
|
7
7
|
|
8
|
-
def_delegators :@clock, :event, :pause, :unpause
|
8
|
+
def_delegators :@clock, :midi_output, :event, :pause, :running?, :tempo, :tempo=, :unpause
|
9
9
|
|
10
10
|
# @param [Fixnum, UniMIDI::Input] tempo_or_input
|
11
11
|
# @param [Hash] options
|
12
|
-
# @option options [Array<UniMIDI::Output>, UniMIDI::Output] :
|
13
|
-
def initialize(
|
12
|
+
# @option options [Array<UniMIDI::Output>, UniMIDI::Output] :output MIDI output device(s) (also: :outputs)
|
13
|
+
def initialize(tempo_or_input, options = {})
|
14
14
|
@arpeggiators = []
|
15
|
-
|
15
|
+
output = options[:output] || options[:outputs] || options[:midi]
|
16
|
+
initialize_clock(tempo_or_input, :output => output)
|
17
|
+
initialize_events
|
18
|
+
end
|
19
|
+
|
20
|
+
# Shortcut to the clock's MIDI output devices
|
21
|
+
# @return [Array<UniMIDI::Output>]
|
22
|
+
def midi_outputs
|
23
|
+
@clock.midi_output.devices
|
16
24
|
end
|
17
25
|
|
18
26
|
# Start the clock
|
@@ -36,53 +44,52 @@ module Diamond
|
|
36
44
|
true
|
37
45
|
end
|
38
46
|
|
39
|
-
# Add
|
47
|
+
# Add arpeggiator(s) to this clock's control
|
40
48
|
# @param [Array<Arpeggiator>, Arpeggiator] arpeggiator
|
41
|
-
# @return [
|
49
|
+
# @return [Array<Arpeggiator>]
|
42
50
|
def add(arpeggiator)
|
43
51
|
arpeggiators = [arpeggiator].flatten
|
44
|
-
|
45
|
-
|
46
|
-
@arpeggiators << arpeggiator
|
47
|
-
reset_tick
|
48
|
-
true
|
49
|
-
else
|
50
|
-
false
|
51
|
-
end
|
52
|
-
end
|
53
|
-
result.any?
|
52
|
+
@arpeggiators += arpeggiators
|
53
|
+
@arpeggiators
|
54
54
|
end
|
55
55
|
alias_method :<<, :add
|
56
56
|
|
57
|
-
# Remove
|
58
|
-
# @param [Arpeggiator] arpeggiator
|
59
|
-
# @return [
|
57
|
+
# Remove arpeggiator(s) from this clock's control
|
58
|
+
# @param [Array<Arpeggiator>, Arpeggiator] arpeggiator
|
59
|
+
# @return [Array<Arpeggiator>]
|
60
60
|
def remove(arpeggiator)
|
61
61
|
arpeggiators = [arpeggiator].flatten
|
62
|
-
|
63
|
-
|
64
|
-
@arpeggiators.delete(arpeggiator)
|
65
|
-
reset_tick
|
66
|
-
true
|
67
|
-
else
|
68
|
-
false
|
69
|
-
end
|
70
|
-
end
|
71
|
-
result.any?
|
62
|
+
@arpeggiators.delete_if? { |arpeggiator| arpeggiators.include?(arpeggiator) }
|
63
|
+
@arpeggiators
|
72
64
|
end
|
73
65
|
|
74
66
|
private
|
75
67
|
|
76
|
-
#
|
77
|
-
# @
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
68
|
+
# @param [Fixnum, UniMIDI::Input] tempo_or_input
|
69
|
+
# @param [Hash] options
|
70
|
+
# @option options [Array<UniMIDI::Output>, UniMIDI::Output] :output MIDI output device(s)
|
71
|
+
# @option options [Fixnum] :resolution
|
72
|
+
# @return [Topaz::Clock]
|
73
|
+
def initialize_clock(tempo_or_input, options = {})
|
74
|
+
@clock = Topaz::Clock.new(tempo_or_input, :midi => options[:output])
|
75
|
+
resolution = options.fetch(:resolution, 128)
|
76
|
+
@clock.interval = @clock.interval * (resolution / @clock.interval)
|
77
|
+
@clock
|
78
|
+
end
|
79
|
+
|
80
|
+
# Initialize the tick event
|
81
|
+
# @return [Boolean]
|
82
|
+
def initialize_events
|
83
|
+
@clock.event.tick << proc do
|
84
|
+
@arpeggiators.each do |arpeggiator|
|
85
|
+
arpeggiator.sequencer.exec(arpeggiator.sequence)
|
86
|
+
arpeggiator.sequencer.event.stop { @clock.stop }
|
87
|
+
arpeggiator
|
88
|
+
end
|
84
89
|
end
|
90
|
+
true
|
85
91
|
end
|
92
|
+
|
86
93
|
end
|
87
94
|
|
88
95
|
end
|
data/lib/diamond/midi.rb
CHANGED
@@ -35,43 +35,43 @@ module Diamond
|
|
35
35
|
end
|
36
36
|
|
37
37
|
# Initialize adding and removing MIDI notes from the sequence
|
38
|
-
# @param [
|
38
|
+
# @param [Arpeggiator] arpeggiator
|
39
39
|
# @return [Boolean]
|
40
|
-
def enable_note_control(
|
40
|
+
def enable_note_control(arpeggiator)
|
41
41
|
@midi.input.receive(:class => MIDIMessage::NoteOn) do |event|
|
42
42
|
message = event[:message]
|
43
43
|
if @midi.input.channel.nil? || @midi.input.channel == message.channel
|
44
44
|
puts "[DEBUG] MIDI: add note from input #{message.name} channel: #{message.channel}" if @debug
|
45
|
-
sequence.add(message)
|
45
|
+
arpeggiator.sequence.add(message)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
@midi.input.receive(:class => MIDIMessage::NoteOff) do |event|
|
49
49
|
message = event[:message]
|
50
50
|
if @midi.input.channel.nil? || @midi.input.channel == message.channel
|
51
51
|
puts "[DEBUG] MIDI: remove note from input #{message.name} channel: #{message.channel}" if @debug
|
52
|
-
sequence.remove(message)
|
52
|
+
arpeggiator.sequence.remove(message)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
true
|
56
56
|
end
|
57
57
|
|
58
58
|
# Initialize a user-defined map of control change messages
|
59
|
-
# @param [
|
59
|
+
# @param [Arpeggiator] arpeggiator
|
60
60
|
# @param [Array<Hash>] map
|
61
61
|
# @return [Boolean]
|
62
|
-
def enable_parameter_control(
|
63
|
-
|
62
|
+
def enable_parameter_control(arpeggiator, map)
|
63
|
+
midi_cc_range = 0..127
|
64
64
|
@midi.input.receive(:class => MIDIMessage::ControlChange) do |event|
|
65
65
|
message = event[:message]
|
66
66
|
if @midi.input.channel.nil? || @midi.input.channel == message.channel
|
67
67
|
index = message.index
|
68
68
|
mapping = map.find { |mapping| mapping[:index] == index }
|
69
69
|
property = mapping[:property]
|
70
|
-
|
70
|
+
parameter_range = apeggiator.parameter.constraints(property)
|
71
71
|
value = message.value
|
72
|
-
value = Scale.transform(value).from(
|
72
|
+
value = Scale.transform(value).from(midi_cc_range).to(parameter_range)
|
73
73
|
puts "[DEBUG] MIDI: #{property}= #{value} channel: #{message.channel}" if @debug
|
74
|
-
|
74
|
+
arpeggiator.parameter.send("#{property}=", value)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -106,10 +106,10 @@ module Diamond
|
|
106
106
|
end
|
107
107
|
|
108
108
|
# Initialize MIDI output, enabling the sequencer to emit notes
|
109
|
-
# @param [
|
109
|
+
# @param [Arpeggiator] arpeggiator
|
110
110
|
# @return [Boolean]
|
111
|
-
def enable_output(
|
112
|
-
sequencer.event.perform << proc do |bucket|
|
111
|
+
def enable_output(arpeggiator)
|
112
|
+
arpeggiator.sequencer.event.perform << proc do |bucket|
|
113
113
|
unless bucket.empty?
|
114
114
|
if @debug
|
115
115
|
bucket.each do |message|
|
@@ -119,12 +119,22 @@ module Diamond
|
|
119
119
|
@midi.output.puts(bucket)
|
120
120
|
end
|
121
121
|
end
|
122
|
-
sequencer.event.stop << proc { emit_pending_note_offs }
|
122
|
+
arpeggiator.sequencer.event.stop << proc { emit_pending_note_offs(arpeggiator) }
|
123
123
|
true
|
124
124
|
end
|
125
125
|
|
126
126
|
private
|
127
127
|
|
128
|
+
# Emit any note off messages that are currently pending in the queue. The clock triggers this
|
129
|
+
# when stopping or pausing
|
130
|
+
# @param [Arpeggiator] arpeggiator
|
131
|
+
# @return [Array<MIDIMessage::NoteOff>]
|
132
|
+
def emit_pending_note_offs(arpeggiator)
|
133
|
+
messages = arpeggiator.sequence.pending_note_offs
|
134
|
+
@midi.output.puts(*messages)
|
135
|
+
messages
|
136
|
+
end
|
137
|
+
|
128
138
|
# Initialize MIDI output
|
129
139
|
# @param [Array<UniMIDI::Output>] outputs
|
130
140
|
# @param [Hash] options
|
data/lib/diamond/osc.rb
CHANGED
@@ -16,20 +16,20 @@ module Diamond
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# Enable controlling the instrument via OSC
|
19
|
-
# @param [
|
19
|
+
# @param [Arpeggiator] arpeggiator The arpeggiator to operate on when messages are received
|
20
20
|
# @param [Array<Hash>] map
|
21
21
|
# @return [Boolean]
|
22
|
-
def enable_parameter_control(
|
22
|
+
def enable_parameter_control(arpeggiator, map)
|
23
23
|
start_server
|
24
24
|
maps = map.map do |item|
|
25
25
|
property = item[:property]
|
26
|
-
|
27
|
-
to_range = SequenceParameters::RANGE[property]
|
26
|
+
osc_range = item[:value] || (0..1.0)
|
28
27
|
@server.add_method(item[:address]) do |message|
|
29
28
|
value = message.to_a[0]
|
30
|
-
|
29
|
+
parameter_range = arpeggiator.parameter.constraints(property)
|
30
|
+
value = Scale.transform(value).from(osc_range).to(parameter_range)
|
31
31
|
puts "[DEBUG]: OSC: #{property}= #{value}" if @debug
|
32
|
-
|
32
|
+
arpeggiator.parameter.send("#{property}=", value)
|
33
33
|
true
|
34
34
|
end
|
35
35
|
true
|
data/lib/diamond/sequence.rb
CHANGED
@@ -21,7 +21,6 @@ module Diamond
|
|
21
21
|
def at(pointer)
|
22
22
|
if changed? && (pointer % @parameter.rate == 0)
|
23
23
|
update
|
24
|
-
@changed = false
|
25
24
|
end
|
26
25
|
enqueue_next(pointer)
|
27
26
|
messages = @queue.shift || []
|
@@ -122,6 +121,7 @@ module Diamond
|
|
122
121
|
notes = get_note_sequence
|
123
122
|
initialize_sequence(notes.length)
|
124
123
|
populate_sequence(notes) unless notes.empty?
|
124
|
+
@changed = false
|
125
125
|
@sequence
|
126
126
|
end
|
127
127
|
|
@@ -3,15 +3,6 @@ module Diamond
|
|
3
3
|
# User-controller parameters that are used to formulate the note event sequence
|
4
4
|
class SequenceParameters
|
5
5
|
|
6
|
-
RANGE = {
|
7
|
-
:gate => 1..500,
|
8
|
-
:interval => -48..48,
|
9
|
-
:pattern_offset => -16..16,
|
10
|
-
:range => 0..10,
|
11
|
-
:rate => 0..64,
|
12
|
-
:transpose => -64..64
|
13
|
-
}
|
14
|
-
|
15
6
|
attr_reader :gate,
|
16
7
|
:interval,
|
17
8
|
:pattern,
|
@@ -38,20 +29,41 @@ module Diamond
|
|
38
29
|
sequence.send(:use_parameters, self)
|
39
30
|
end
|
40
31
|
|
41
|
-
#
|
32
|
+
# Dynamically produce an acceptable range for the given param
|
33
|
+
# @param [Symbol] param
|
34
|
+
# @return [Range]
|
35
|
+
def constraints(param)
|
36
|
+
ranges = {
|
37
|
+
:gate => proc { get_lowest_gate..500 },
|
38
|
+
:interval => proc { -48..48 },
|
39
|
+
:pattern_offset => proc { -16..16 },
|
40
|
+
:range => proc { 0..10 },
|
41
|
+
:rate => proc { 0..@resolution },
|
42
|
+
:transpose => proc { -72..72 }
|
43
|
+
}
|
44
|
+
ranges[param].call
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set the gate property. Is constrained to go only as low as the rate and resolution allow.
|
42
48
|
# @param [Fixnum] num
|
49
|
+
# @param [Hash] options
|
50
|
+
# @option options [Boolean] :constrain When false, will skip constraints (default: true)
|
43
51
|
# @return [Fixnum]
|
44
|
-
def gate=(num)
|
45
|
-
|
52
|
+
def gate=(num, options = {})
|
53
|
+
should_constrain = options.fetch(:constrain, true)
|
54
|
+
@gate = should_constrain ? constrain(num, :range => constraints(:gate)) : num
|
46
55
|
mark_changed
|
47
56
|
@gate
|
48
57
|
end
|
49
58
|
|
50
59
|
# Set the interval property
|
51
60
|
# @param [Fixnum] num
|
61
|
+
# @param [Hash] options
|
62
|
+
# @option options [Boolean] :constrain When false, will skip constraints (default: true)
|
52
63
|
# @param [Fixnum]
|
53
|
-
def interval=(num)
|
54
|
-
|
64
|
+
def interval=(num, options = {})
|
65
|
+
should_constrain = options.fetch(:constrain, true)
|
66
|
+
@interval = should_constrain ? constrain(num, :range => constraints(:interval)) : num
|
55
67
|
mark_changed
|
56
68
|
@interval
|
57
69
|
end
|
@@ -67,18 +79,32 @@ module Diamond
|
|
67
79
|
|
68
80
|
# Set the range property
|
69
81
|
# @param [Fixnum] range
|
82
|
+
# @param [Hash] options
|
83
|
+
# @option options [Boolean] :constrain When false, will skip constraints (default: true)
|
70
84
|
# @return [Fixnum]
|
71
|
-
def range=(num)
|
72
|
-
|
85
|
+
def range=(num, options = {})
|
86
|
+
should_constrain = options.fetch(:constrain, true)
|
87
|
+
@range = should_constrain ? constrain(num, :range => constraints(:range)) : num
|
73
88
|
mark_changed
|
74
89
|
@range
|
75
90
|
end
|
76
91
|
|
77
|
-
# Set the rate property
|
92
|
+
# Set the rate property. This will also scale the gate accordingly
|
78
93
|
# @param [Fixnum] num
|
94
|
+
# @param [Hash] options
|
95
|
+
# @option options [Boolean] :constrain When false, will skip constraints (default: true)
|
79
96
|
# @return [Fixnum]
|
80
|
-
def rate=(num)
|
81
|
-
|
97
|
+
def rate=(num, options = {})
|
98
|
+
# Scale the gate according to the new gate
|
99
|
+
change = num / @rate.to_f
|
100
|
+
new_gate = (@gate * change).ceil
|
101
|
+
if options.fetch(:constrain, true)
|
102
|
+
@rate = constrain(num, :range => constraints(:rate))
|
103
|
+
@gate = constrain(new_gate, :range => constraints(:gate))
|
104
|
+
else
|
105
|
+
@rate = num
|
106
|
+
@gate = new_gate
|
107
|
+
end
|
82
108
|
mark_changed
|
83
109
|
@rate
|
84
110
|
end
|
@@ -116,6 +142,13 @@ module Diamond
|
|
116
142
|
|
117
143
|
private
|
118
144
|
|
145
|
+
# Find the lowest gate value possible for the rate and resolution
|
146
|
+
# @return [Float]
|
147
|
+
def get_lowest_gate
|
148
|
+
factor = @resolution.to_f / @rate
|
149
|
+
(100.0 / factor).ceil
|
150
|
+
end
|
151
|
+
|
119
152
|
# Mark that there's been a change in the sequence
|
120
153
|
def mark_changed
|
121
154
|
@callback.call
|
@@ -124,11 +157,11 @@ module Diamond
|
|
124
157
|
# @param [Hash] options
|
125
158
|
# @return [ArpeggiatorSequence::Parameters]
|
126
159
|
def apply_options(options)
|
127
|
-
@interval = constrain((options[:interval] || 12), :range =>
|
128
|
-
@range = constrain((options[:range] || 3), :range =>
|
129
|
-
@pattern_offset = constrain((options[:pattern_offset] || 0),:range =>
|
130
|
-
@rate = constrain((options[:rate] || 8), :range =>
|
131
|
-
@gate = constrain((options[:gate] || 75), :range =>
|
160
|
+
@interval = constrain((options[:interval] || 12), :range => constraints(:interval))
|
161
|
+
@range = constrain((options[:range] || 3), :range => constraints(:range))
|
162
|
+
@pattern_offset = constrain((options[:pattern_offset] || 0),:range => constraints(:pattern_offset))
|
163
|
+
@rate = constrain((options[:rate] || 8), :range => constraints(:rate))
|
164
|
+
@gate = constrain((options[:gate] || 75), :range => constraints(:gate))
|
132
165
|
@pattern = get_pattern(options[:pattern])
|
133
166
|
self
|
134
167
|
end
|
data/test/arpeggiator_test.rb
CHANGED
@@ -8,6 +8,7 @@ class Diamond::ApeggiatorTest < Test::Unit::TestCase
|
|
8
8
|
|
9
9
|
should "have defaults" do
|
10
10
|
@arpeggiator = Diamond::Arpeggiator.new
|
11
|
+
assert_equal 128, @arpeggiator.resolution
|
11
12
|
assert_equal 8, @arpeggiator.rate
|
12
13
|
assert_equal 3, @arpeggiator.range
|
13
14
|
assert_equal 12, @arpeggiator.interval
|
@@ -15,6 +16,7 @@ class Diamond::ApeggiatorTest < Test::Unit::TestCase
|
|
15
16
|
|
16
17
|
should "allow setting params" do
|
17
18
|
@arpeggiator = Diamond::Arpeggiator.new(:interval => 7, :range => 4, :rate => 16)
|
19
|
+
assert_equal 128, @arpeggiator.resolution
|
18
20
|
assert_equal 16, @arpeggiator.rate
|
19
21
|
assert_equal 4, @arpeggiator.range
|
20
22
|
assert_equal 7, @arpeggiator.interval
|
data/test/clock_test.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class Diamond::ClockTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Clock" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@clock = Diamond::Clock.new(120)
|
9
|
+
end
|
10
|
+
|
11
|
+
context "Clock#initialize" do
|
12
|
+
|
13
|
+
should "get MIDI output" do
|
14
|
+
output = Object.new
|
15
|
+
@clock = Diamond::Clock.new(120, :output => output)
|
16
|
+
assert_not_nil @clock.midi_outputs
|
17
|
+
assert_not_empty @clock.midi_outputs
|
18
|
+
assert @clock.midi_outputs.include?(output)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
context "Clock#add_midi_output" do
|
24
|
+
|
25
|
+
should "add MIDI output" do
|
26
|
+
output = Object.new
|
27
|
+
refute @clock.midi_outputs.include?(output)
|
28
|
+
@clock.midi_output.devices << output
|
29
|
+
assert_not_nil @clock.midi_outputs
|
30
|
+
assert_not_empty @clock.midi_outputs
|
31
|
+
assert @clock.midi_outputs.include?(output)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context "Clock#remove_midi_output" do
|
37
|
+
|
38
|
+
should "remove MIDI output" do
|
39
|
+
output = Object.new
|
40
|
+
refute @clock.midi_outputs.include?(output)
|
41
|
+
@clock.midi_output.devices << output
|
42
|
+
assert_not_nil @clock.midi_outputs
|
43
|
+
assert_not_empty @clock.midi_outputs
|
44
|
+
assert @clock.midi_outputs.include?(output)
|
45
|
+
|
46
|
+
@clock.midi_output.devices.delete(output)
|
47
|
+
refute @clock.midi_outputs.include?(output)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
context "Clock#tempo" do
|
53
|
+
|
54
|
+
should "get tempo" do
|
55
|
+
assert_equal 120, @clock.tempo
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
context "Clock#tempo=" do
|
61
|
+
|
62
|
+
should "set tempo" do
|
63
|
+
@clock.tempo = 58
|
64
|
+
assert_equal 58, @clock.tempo
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -6,7 +6,7 @@ class Diamond::SequenceParametersTest < Test::Unit::TestCase
|
|
6
6
|
|
7
7
|
setup do
|
8
8
|
@sequence = Diamond::Sequence.new
|
9
|
-
@params = Diamond::SequenceParameters.new(@sequence,
|
9
|
+
@params = Diamond::SequenceParameters.new(@sequence, 128) { @sequence.mark_changed }
|
10
10
|
end
|
11
11
|
|
12
12
|
context "#rate=" do
|
@@ -17,6 +17,28 @@ class Diamond::SequenceParametersTest < Test::Unit::TestCase
|
|
17
17
|
assert_equal 16, @params.rate
|
18
18
|
end
|
19
19
|
|
20
|
+
should "scale gate down" do
|
21
|
+
@params.rate = 16
|
22
|
+
@params.gate = 50
|
23
|
+
assert_equal 16, @params.rate
|
24
|
+
assert_equal 50, @params.gate
|
25
|
+
|
26
|
+
@params.rate = 8
|
27
|
+
assert_equal 8, @params.rate
|
28
|
+
assert_equal 25, @params.gate
|
29
|
+
end
|
30
|
+
|
31
|
+
should "scale gate up" do
|
32
|
+
@params.rate = 8
|
33
|
+
@params.gate = 50
|
34
|
+
assert_equal 8, @params.rate
|
35
|
+
assert_equal 50, @params.gate
|
36
|
+
|
37
|
+
@params.rate = 16
|
38
|
+
assert_equal 16, @params.rate
|
39
|
+
assert_equal 100, @params.gate
|
40
|
+
end
|
41
|
+
|
20
42
|
end
|
21
43
|
|
22
44
|
context "#range=" do
|
@@ -53,6 +75,12 @@ class Diamond::SequenceParametersTest < Test::Unit::TestCase
|
|
53
75
|
assert_equal 125, @params.gate
|
54
76
|
end
|
55
77
|
|
78
|
+
should "constrain gate to what rate and resolution allow for" do
|
79
|
+
@params.rate = 16
|
80
|
+
@params.gate = 10
|
81
|
+
assert_equal 13, @params.gate
|
82
|
+
end
|
83
|
+
|
56
84
|
end
|
57
85
|
|
58
86
|
context "#pattern_offset=" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: diamond
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ari Russo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: analog
|
@@ -190,6 +190,7 @@ files:
|
|
190
190
|
- lib/diamond/sequence_parameters.rb
|
191
191
|
- test/api_test.rb
|
192
192
|
- test/arpeggiator_test.rb
|
193
|
+
- test/clock_test.rb
|
193
194
|
- test/helper.rb
|
194
195
|
- test/osc_test.rb
|
195
196
|
- test/pattern_test.rb
|