diamond 0.5.1 → 0.5.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e4b95fbd189a76487319f77e97e6d645a75e4dac
4
- data.tar.gz: 608fb59532ddca113ea6c29d68ffcbb34211696e
3
+ metadata.gz: 30e670f044ab17c64cedf2c71573a1a510928cd2
4
+ data.tar.gz: 0b91f502f5952b2df09f311f1569eb5aa6b061bb
5
5
  SHA512:
6
- metadata.gz: eb1157bc6dbf55bd3d2b75808a3fd5d12d75db4467d40e42b72b5e7963fe45505ffa9f294417377d354fa1f662d23a86c7e10e0c4eba64084078cf80c197a34d
7
- data.tar.gz: fd6dddb843a56ef77845a04155cd99550e60033953fc407e15d0434fd74503777103513fb2c6f06119978a2bcb16f2f679deb9c892bae5dbf7316cefed24dd70
6
+ metadata.gz: 60f2d5c041101b1bece73255f6cb4416ed5a254afcb971cda4f7de68f1a962f6f1ce8d3ff99fb34fef43b41c404d42581599ee9a45ca74aa71041fa17b5c320f
7
+ data.tar.gz: d782c0fa28658fd8c8521ae20f17b763e7bc22f9df7a0202ae67711e0494480eee2842ff0f782045c7c3d722fa13d6b39fdc00742f3ed4072f6da74a8e8cdcf4
data/README.md CHANGED
@@ -1,19 +1,19 @@
1
- # Diamond
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
- ## Features
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
- * Preset and curstom arpeggio pattern algorithms
13
+ * Suited to [live coding](http://en.wikipedia.org/wiki/Live_coding)
14
+ * Generative arpeggio patterns
15
15
 
16
- ## Installation
16
+ ##Installation
17
17
 
18
18
  `gem install diamond`
19
19
 
@@ -21,13 +21,13 @@
21
21
 
22
22
  `gem "diamond"`
23
23
 
24
- ## Usage
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...](http://tx81z.blogspot.com/2011/10/selecting-midi-device-with-unimidi.html))
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
- Of course, an arpeggiator needs notes to work with. You can use a MIDI input for that. ([see example](http://github.com/arirusso/diamond/blob/master/examples/midi_note_input.rb)). However, again for the sake of simplicity here's a chord in Ruby
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
- Now we'll start the clock and arpeggiator.
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
- All of the [arpeggiator options](http://rubydoc.info/github/arirusso/diamond/master/Diamond/Arpeggiator:initialize) can be controlled on the on the fly
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
- #### Posts
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
- #### Examples
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
- ## Other Documentation
114
+ ##Other Documentation
115
115
 
116
116
  * [rdoc](http://rubydoc.info/github/arirusso/diamond)
117
117
 
118
- ## Author
118
+ ##Author
119
119
 
120
120
  * [Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
121
121
 
122
- ## License
122
+ ##License
123
123
 
124
124
  Apache 2.0, See the file LICENSE
125
125
 
data/lib/diamond.rb CHANGED
@@ -30,6 +30,6 @@ require "diamond/sequence_parameters"
30
30
 
31
31
  module Diamond
32
32
 
33
- VERSION = "0.5.1"
33
+ VERSION = "0.5.3"
34
34
 
35
35
  end
@@ -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(@sequencer)
55
- @midi.enable_note_control(@sequence)
56
- @midi.enable_parameter_control(@parameter, options[:midi_control]) if !options[:midi_control].nil?
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 Sequencer::Clock (and thus Topaz::Tempo) that's geared towards the arpeggiator
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] :outputs MIDI output device(s)
13
- def initialize(*args)
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
- @clock = Sequencer::Clock.new(*args)
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 an arpeggiator to this clock's control
47
+ # Add arpeggiator(s) to this clock's control
40
48
  # @param [Array<Arpeggiator>, Arpeggiator] arpeggiator
41
- # @return [Boolean]
49
+ # @return [Array<Arpeggiator>]
42
50
  def add(arpeggiator)
43
51
  arpeggiators = [arpeggiator].flatten
44
- result = arpeggiators.map do |arpeggiator|
45
- unless @arpeggiators.include?(arpeggiator)
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 an arpeggiator from this clock's control
58
- # @param [Arpeggiator] arpeggiator
59
- # @return [Boolean]
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
- result = arpeggiators.map do |arpeggiator|
63
- if @arpeggiators.include?(arpeggiator)
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
- # Reset the clock's tick event (used when arpeggiators are added or removed)
77
- # @return [Array<Arpeggiator>] Arpeggiators that are now actively controlled by this clock
78
- def reset_tick
79
- @clock.event.tick.clear
80
- @arpeggiators.map do |arpeggiator|
81
- @clock.event.tick << proc { arpeggiator.sequencer.exec(arpeggiator.sequence) }
82
- arpeggiator.sequencer.event.stop { @clock.stop }
83
- arpeggiator
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 [Sequence] sequence
38
+ # @param [Arpeggiator] arpeggiator
39
39
  # @return [Boolean]
40
- def enable_note_control(sequence)
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 [SequenceParameters] parameters
59
+ # @param [Arpeggiator] arpeggiator
60
60
  # @param [Array<Hash>] map
61
61
  # @return [Boolean]
62
- def enable_parameter_control(parameters, map)
63
- from_range = 0..127
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
- to_range = SequenceParameters::RANGE[property]
70
+ parameter_range = apeggiator.parameter.constraints(property)
71
71
  value = message.value
72
- value = Scale.transform(value).from(from_range).to(to_range)
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
- parameters.send("#{property}=", value)
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 [Sequencer::Core] sequencer
109
+ # @param [Arpeggiator] arpeggiator
110
110
  # @return [Boolean]
111
- def enable_output(sequencer)
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 [Object] subject The object to operate on when messages are received
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(subject, map)
22
+ def enable_parameter_control(arpeggiator, map)
23
23
  start_server
24
24
  maps = map.map do |item|
25
25
  property = item[:property]
26
- from_range = item[:value] || (0..1.0)
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
- value = Scale.transform(value).from(from_range).to(to_range)
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
- subject.send("#{property}=", value)
32
+ arpeggiator.parameter.send("#{property}=", value)
33
33
  true
34
34
  end
35
35
  true
@@ -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
- # Set the gate property
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
- @gate = constrain(num, :range => RANGE[:gate])
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
- @interval = num
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
- @range = constrain(num, :range => RANGE[:range])
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
- @rate = constrain(num, :range => RANGE[:rate].begin..@resolution)
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 => RANGE[:interval])
128
- @range = constrain((options[:range] || 3), :range => RANGE[:range])
129
- @pattern_offset = constrain((options[:pattern_offset] || 0),:range => RANGE[:pattern_offset])
130
- @rate = constrain((options[:rate] || 8), :range => 0..@resolution)
131
- @gate = constrain((options[:gate] || 75), :range => RANGE[:gate])
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
@@ -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
@@ -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, 16) { @sequence.mark_changed }
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.1
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-18 00:00:00.000000000 Z
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