diamond 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/README.md +126 -0
- data/lib/diamond.rb +35 -0
- data/lib/diamond/api.rb +102 -0
- data/lib/diamond/arpeggiator.rb +81 -0
- data/lib/diamond/clock.rb +88 -0
- data/lib/diamond/midi.rb +165 -0
- data/lib/diamond/osc.rb +102 -0
- data/lib/diamond/pattern.rb +95 -0
- data/lib/diamond/sequence.rb +178 -0
- data/lib/diamond/sequence_parameters.rb +164 -0
- data/test/api_test.rb +152 -0
- data/test/arpeggiator_test.rb +34 -0
- data/test/helper.rb +23 -0
- data/test/osc_test.rb +37 -0
- data/test/pattern_test.rb +63 -0
- data/test/sequence_parameters_test.rb +96 -0
- data/test/sequence_test.rb +36 -0
- metadata +222 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e4b95fbd189a76487319f77e97e6d645a75e4dac
|
4
|
+
data.tar.gz: 608fb59532ddca113ea6c29d68ffcbb34211696e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eb1157bc6dbf55bd3d2b75808a3fd5d12d75db4467d40e42b72b5e7963fe45505ffa9f294417377d354fa1f662d23a86c7e10e0c4eba64084078cf80c197a34d
|
7
|
+
data.tar.gz: fd6dddb843a56ef77845a04155cd99550e60033953fc407e15d0434fd74503777103513fb2c6f06119978a2bcb16f2f679deb9c892bae5dbf7316cefed24dd70
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2011-2014 Ari Russo
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Diamond
|
2
|
+
|
3
|
+
[MIDI arpeggiator](http://en.wikipedia.org/wiki/Arpeggiator#Arpeggiator) in Ruby
|
4
|
+
|
5
|
+
![diamond](http://img256.imageshack.us/img256/4514/diamondd.jpg)
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
* Classic arpeggiator functionality
|
10
|
+
* [OSC](http://en.wikipedia.org/wiki/Open_Sound_Control) and MIDI remote control
|
11
|
+
* MIDI clock IO
|
12
|
+
* Suited to [live coding](http://en.wikipedia.org/wiki/Live_coding)
|
13
|
+
* Multiplex clocks and arpeggiators
|
14
|
+
* Preset and curstom arpeggio pattern algorithms
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
`gem install diamond`
|
19
|
+
|
20
|
+
or with Bundler, add this to your Gemfile
|
21
|
+
|
22
|
+
`gem "diamond"`
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require "diamond"
|
28
|
+
```
|
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))
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
@output = UniMIDI::Output.gets
|
34
|
+
```
|
35
|
+
|
36
|
+
The Diamond arpeggiator has a number of [optional parameters](http://rubydoc.info/github/arirusso/diamond/master/Diamond/Arpeggiator:initialize). For this example, here's a straightforward setup
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
options = {
|
40
|
+
:gate => 90,
|
41
|
+
:interval => 7,
|
42
|
+
:midi => @output,
|
43
|
+
:pattern => "UpDown",
|
44
|
+
:range => 4,
|
45
|
+
:rate => 8
|
46
|
+
}
|
47
|
+
|
48
|
+
arpeggiator = Diamond::Arpeggiator.new(options)
|
49
|
+
```
|
50
|
+
|
51
|
+
Create a clock object, passing in a tempo value. In this case the tempo will be 138 BPM
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
clock = Diamond::Clock.new(138)
|
55
|
+
```
|
56
|
+
|
57
|
+
Point the clock to the arpeggiator
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
clock << arpeggiator
|
61
|
+
```
|
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
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
chord = ["C3", "G3", "Bb3", "A4"]
|
67
|
+
```
|
68
|
+
|
69
|
+
Use `Arpeggiator#add` and `Arpeggiator#remove` to change the notes that the arpeggiator sees. (`Arpeggiator#<<` is the same as add)
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
arpeggiator.add(chord)
|
73
|
+
arpeggiator << "C5"
|
74
|
+
```
|
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`.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
clock.start
|
82
|
+
```
|
83
|
+
|
84
|
+
All of the [arpeggiator options](http://rubydoc.info/github/arirusso/diamond/master/Diamond/Arpeggiator:initialize) can be controlled on the on the fly
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
arpeggiator.rate = 16
|
88
|
+
arpeggiator.gate = 20
|
89
|
+
arpeggiator.remove("C5", "A4")
|
90
|
+
```
|
91
|
+
|
92
|
+
[This screencast video](http://vimeo.com/25983971) shows Diamond being live coded in this way. (Note that the API has changed a bit since 2011 when the video was made).
|
93
|
+
|
94
|
+
This [blog post](http://tx81z.blogspot.com/2011/07/live-coding-with-diamond.html) explains what is happening in the video.
|
95
|
+
|
96
|
+
#### Posts
|
97
|
+
|
98
|
+
* [Introduction](http://tx81z.blogspot.com/2011/07/diamond-midi-arpeggiator-in-ruby.html)
|
99
|
+
* [Live coding Diamond and syncing multiple arpeggiators to each other](http://tx81z.blogspot.com/2011/07/live-coding-with-diamond.html)
|
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
|
+
|
102
|
+
#### Examples
|
103
|
+
|
104
|
+
* [Control via OSC](http://github.com/arirusso/diamond/blob/master/examples/osc_control.rb)
|
105
|
+
* [Define a Pattern](http://github.com/arirusso/diamond/blob/master/examples/define_pattern.rb)
|
106
|
+
* [Feed notes to Diamond using a MIDI controller or other input](http://github.com/arirusso/diamond/blob/master/examples/midi_note_input.rb)
|
107
|
+
* [Feed notes to Diamond using MIDI message objects](http://github.com/arirusso/diamond/blob/master/examples/midi_message_objects.rb)
|
108
|
+
* [Sync multiple Arpeggiator instances to each other](http://github.com/arirusso/diamond/blob/master/examples/sync_multiple_arps.rb)
|
109
|
+
* [Sync Diamond to external MIDI clock](http://github.com/arirusso/diamond/blob/master/examples/midi_clock_sync.rb)
|
110
|
+
* [Use Diamond as a master MIDI clock](http://github.com/arirusso/diamond/blob/master/examples/midi_clock_output.rb)
|
111
|
+
|
112
|
+
[More...](http://github.com/arirusso/diamond/blob/master/examples)
|
113
|
+
|
114
|
+
## Other Documentation
|
115
|
+
|
116
|
+
* [rdoc](http://rubydoc.info/github/arirusso/diamond)
|
117
|
+
|
118
|
+
## Author
|
119
|
+
|
120
|
+
* [Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
|
121
|
+
|
122
|
+
## License
|
123
|
+
|
124
|
+
Apache 2.0, See the file LICENSE
|
125
|
+
|
126
|
+
Copyright (c) 2011-2014 Ari Russo
|
data/lib/diamond.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# Diamond
|
3
|
+
#
|
4
|
+
# MIDI arpeggiator in Ruby
|
5
|
+
# (c)2011-2014 Ari Russo and licensed under the Apache 2.0 License
|
6
|
+
#
|
7
|
+
|
8
|
+
# libs
|
9
|
+
require "forwardable"
|
10
|
+
require "midi-instrument"
|
11
|
+
require "midi-message"
|
12
|
+
require "osc-ruby"
|
13
|
+
require "osc-ruby/em_server"
|
14
|
+
require "scale"
|
15
|
+
require "sequencer"
|
16
|
+
require "topaz"
|
17
|
+
require "unimidi"
|
18
|
+
|
19
|
+
# modules
|
20
|
+
require "diamond/api"
|
21
|
+
|
22
|
+
# classes
|
23
|
+
require "diamond/arpeggiator"
|
24
|
+
require "diamond/clock"
|
25
|
+
require "diamond/midi"
|
26
|
+
require "diamond/osc"
|
27
|
+
require "diamond/pattern"
|
28
|
+
require "diamond/sequence"
|
29
|
+
require "diamond/sequence_parameters"
|
30
|
+
|
31
|
+
module Diamond
|
32
|
+
|
33
|
+
VERSION = "0.5.1"
|
34
|
+
|
35
|
+
end
|
data/lib/diamond/api.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
module Diamond
|
2
|
+
|
3
|
+
# Convenience methods for the instrument
|
4
|
+
module API
|
5
|
+
|
6
|
+
# MIDI convenience methods
|
7
|
+
module MIDI
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.send(:extend, Forwardable)
|
11
|
+
base.send(:def_delegators,
|
12
|
+
:@midi,
|
13
|
+
:<<,
|
14
|
+
:add,
|
15
|
+
:mute,
|
16
|
+
:mute=,
|
17
|
+
:omni_on,
|
18
|
+
:remove,
|
19
|
+
:rx_channel,
|
20
|
+
:receive_channel,
|
21
|
+
:rx_channel=,
|
22
|
+
:receive_channel=,
|
23
|
+
:toggle_mute,
|
24
|
+
:tx_channel,
|
25
|
+
:transmit_channel,
|
26
|
+
:tx_channel=,
|
27
|
+
:transmit_channel=
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add a MIDI input
|
32
|
+
# @param [UniMIDI::Input] source
|
33
|
+
# @return [Array<UniMIDI::Input>]
|
34
|
+
def add_midi_source(source)
|
35
|
+
@midi.inputs << source
|
36
|
+
end
|
37
|
+
|
38
|
+
# Remove a MIDI input
|
39
|
+
# @param [UniMIDI::Input] source
|
40
|
+
# @return [Array<UniMIDI::Input>]
|
41
|
+
def remove_midi_source(source)
|
42
|
+
@midi.inputs.delete(source)
|
43
|
+
end
|
44
|
+
|
45
|
+
# MIDI inputs associated with this instrument
|
46
|
+
# @return [Array<UniMIDI::Input>]
|
47
|
+
def midi_sources
|
48
|
+
@midi.inputs
|
49
|
+
end
|
50
|
+
|
51
|
+
# Is the instrument MIDI output muted?
|
52
|
+
# @return [Boolean]
|
53
|
+
def mute?
|
54
|
+
@midi.output.mute?
|
55
|
+
end
|
56
|
+
alias_method :muted?, :mute?
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
# Convenience methods dealing with the sequence
|
61
|
+
module Sequence
|
62
|
+
|
63
|
+
def self.included(base)
|
64
|
+
base.send(:extend, Forwardable)
|
65
|
+
base.send(:def_delegators,
|
66
|
+
:@sequence,
|
67
|
+
:sequence,
|
68
|
+
:remove_all)
|
69
|
+
base.send(:alias_method, :clear, :remove_all)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# Convenience methods dealing with the sequence parameters
|
75
|
+
module SequenceParameters
|
76
|
+
|
77
|
+
def self.included(base)
|
78
|
+
base.send(:extend, Forwardable)
|
79
|
+
base.send(:def_delegators,
|
80
|
+
:@parameter,
|
81
|
+
:gate,
|
82
|
+
:gate=,
|
83
|
+
:interval,
|
84
|
+
:interval=,
|
85
|
+
:pattern,
|
86
|
+
:pattern=,
|
87
|
+
:range,
|
88
|
+
:range=,
|
89
|
+
:rate,
|
90
|
+
:rate=,
|
91
|
+
:pattern_offset,
|
92
|
+
:pattern_offset=,
|
93
|
+
:resolution,
|
94
|
+
:resolution=,
|
95
|
+
:transpose,
|
96
|
+
:transpose=)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Diamond
|
2
|
+
|
3
|
+
# The arpeggiator core
|
4
|
+
class Arpeggiator
|
5
|
+
|
6
|
+
include API::MIDI
|
7
|
+
include API::Sequence
|
8
|
+
include API::SequenceParameters
|
9
|
+
|
10
|
+
attr_reader :parameter, :sequence, :sequencer
|
11
|
+
|
12
|
+
# @param [Hash] options
|
13
|
+
# @option options [Fixnum] :gate Duration of the arpeggiated notes. The value is a percentage based on the rate. If the rate is 4, then a gate of 100 is equal to a quarter note. (default: 75) must be 1..500
|
14
|
+
# @option options [Fixnum] :interval Increment (pattern) over (interval) scale degrees (range) times. May be positive or negative. (default: 12)
|
15
|
+
# @option options [Array<UniMIDI::Input, UniMIDI::Output>, UniMIDI::Input, UniMIDI::Output] :midi MIDI devices to use
|
16
|
+
# @option options [Array<Hash>] :midi_control A user-defined mapping of MIDI cc to arpeggiator params
|
17
|
+
# @option options [Boolean] :midi_debug Whether to send debug output about MIDI to standard out
|
18
|
+
# @option options [Fixnum] :rx_channel (or :channel) Only respond to input messages to the given MIDI channel. will operate on all input sources. if not included, or nil the arpeggiator will work in omni mode and respond to all messages
|
19
|
+
# @option options [Fixnum] :tx_channel Send output messages to the given MIDI channel despite what channel the input notes were intended for.
|
20
|
+
# @option options [Fixnum] :pattern_offset Begin on the nth note of the sequence (but not omit any notes). (default: 0)
|
21
|
+
# @option options [String, Pattern] :pattern Computes the contour of the arpeggiated melody. Can be the name of a pattern or a pattern object.
|
22
|
+
# @option options [Fixnum] :range Increment the (pattern) over (interval) scale degrees (range) times. Must be positive (abs will be used). (default: 3)
|
23
|
+
# @option options [Fixnum] :rate How fast the arpeggios will be played. Must be positive (abs will be used). (default: 8, eighth note.) must be 0..resolution
|
24
|
+
# @option options [Fixnum] :resolution Numeric resolution for rhythm (default: 128)
|
25
|
+
# @option options [Hash] :osc_control A user-defined map of OSC addresses and properties to arpeggiator params
|
26
|
+
# @option options [Fixnum] :osc_port The port to listen for OSC on
|
27
|
+
# @option options [Boolean] :osc_debug Whether to send debug output about OSC to standard out
|
28
|
+
def initialize(options = {}, &block)
|
29
|
+
resolution = options.fetch(:resolution, 128)
|
30
|
+
|
31
|
+
@sequence = Sequence.new
|
32
|
+
@parameter = SequenceParameters.new(@sequence, resolution, options) { @sequence.mark_changed }
|
33
|
+
@sequencer = Sequencer.new
|
34
|
+
|
35
|
+
initialize_midi(options)
|
36
|
+
initialize_osc(options)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Initialize MIDI IO
|
42
|
+
# @param [Hash] options
|
43
|
+
# @option options [Array<UniMIDI::Input, UniMIDI::Output>, UniMIDI::Input, UniMIDI::Output] :midi MIDI devices to use
|
44
|
+
# @option options [Array<Hash>] :midi_control A user-defined mapping of MIDI cc to arpeggiator params
|
45
|
+
# @option options [Boolean] :midi_debug Whether to send debug output about MIDI to standard out
|
46
|
+
# @option options [Fixnum] :rx_channel (or :channel) Only respond to input messages to the given MIDI channel. will operate on all input sources. if not included, or nil the arpeggiator will work in omni mode and respond to all messages
|
47
|
+
# @option options [Fixnum] :tx_channel Send output messages to the given MIDI channel despite what channel the input notes were intended for.
|
48
|
+
# @return [MIDI::Node]
|
49
|
+
def initialize_midi(options = {})
|
50
|
+
receive_channel = options[:rx_channel] || options[:channel]
|
51
|
+
transmit_channel = options[:tx_channel]
|
52
|
+
devices = MIDIInstrument::Device.partition(options[:midi])
|
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?
|
57
|
+
@midi
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param [Hash] options
|
61
|
+
# @option options [Hash] :osc_control A map of OSC addresses and properties
|
62
|
+
# @option options [Fixnum] :osc_port The port to listen for OSC on
|
63
|
+
# @return [OSC::Node]
|
64
|
+
def initialize_osc(options = {})
|
65
|
+
@osc = OSC.new(:debug => !!options[:osc_debug], :server_port => options[:osc_port])
|
66
|
+
@osc.enable_parameter_control(@parameter, options[:osc_control]) if !options[:osc_control].nil?
|
67
|
+
@osc
|
68
|
+
end
|
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
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Diamond
|
2
|
+
|
3
|
+
# A wrapper for Sequencer::Clock (and thus Topaz::Tempo) that's geared towards the arpeggiator
|
4
|
+
class Clock
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@clock, :event, :pause, :unpause
|
9
|
+
|
10
|
+
# @param [Fixnum, UniMIDI::Input] tempo_or_input
|
11
|
+
# @param [Hash] options
|
12
|
+
# @option options [Array<UniMIDI::Output>, UniMIDI::Output] :outputs MIDI output device(s)
|
13
|
+
def initialize(*args)
|
14
|
+
@arpeggiators = []
|
15
|
+
@clock = Sequencer::Clock.new(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Start the clock
|
19
|
+
# @param [Hash] options
|
20
|
+
# @option options [Boolean] :blocking Whether to run in the foreground (also :focus, :foreground)
|
21
|
+
# @option options [Boolean] :suppress_clock Whether this clock is a sync-slave
|
22
|
+
# @return [Boolean]
|
23
|
+
def start(options = {})
|
24
|
+
begin
|
25
|
+
@clock.start(options)
|
26
|
+
rescue SystemExit, Interrupt => exception
|
27
|
+
stop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Stop the clock (and fire the arpeggiator sequencer stop event)
|
32
|
+
# @return [Boolean]
|
33
|
+
def stop
|
34
|
+
@arpeggiators.each { |arpeggiator| arpeggiator.sequencer.event.do_stop }
|
35
|
+
@clock.stop
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add an arpeggiator to this clock's control
|
40
|
+
# @param [Array<Arpeggiator>, Arpeggiator] arpeggiator
|
41
|
+
# @return [Boolean]
|
42
|
+
def add(arpeggiator)
|
43
|
+
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?
|
54
|
+
end
|
55
|
+
alias_method :<<, :add
|
56
|
+
|
57
|
+
# Remove an arpeggiator from this clock's control
|
58
|
+
# @param [Arpeggiator] arpeggiator
|
59
|
+
# @return [Boolean]
|
60
|
+
def remove(arpeggiator)
|
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?
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
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
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|