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 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