diamond 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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