jsound 0.1.0-java → 0.1.2-java
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.
- data/.yardopts +3 -1
- data/INTRO.md +159 -0
- data/README.md +22 -46
- data/examples/harmonize.rb +25 -0
- data/examples/harmonize2.rb +23 -0
- data/examples/launchpad/launchpad_generator.rb +37 -35
- data/examples/launchpad/{launchpad.rb → launchpad_monitor.rb} +12 -19
- data/examples/list_devices.rb +4 -3
- data/examples/monitor.rb +9 -9
- data/examples/play_notes.rb +20 -0
- data/examples/transpose.rb +21 -0
- data/lib/jsound/midi.rb +0 -1
- data/lib/jsound/midi/device.rb +9 -4
- data/lib/jsound/midi/device_list.rb +19 -0
- data/lib/jsound/midi/devices/generator.rb +1 -1
- data/lib/jsound/midi/devices/recorder.rb +4 -0
- data/lib/jsound/midi/devices/transformer.rb +35 -9
- data/lib/jsound/midi/message.rb +1 -1
- data/lib/jsound/type_from_class_name.rb +1 -1
- data/spec/jsound/midi/device_spec.rb +12 -1
- data/spec/jsound/midi/devices/recorder_spec.rb +12 -0
- data/spec/jsound/midi/devices/transformer_spec.rb +29 -0
- data/spec/jsound/midi/devlice_list_spec.rb +28 -0
- data/spec/jsound/type_from_class_name_spec.rb +1 -1
- metadata +15 -15
- data/examples/harmonizer.rb +0 -25
- data/examples/notes.rb +0 -90
- data/examples/transposer.rb +0 -20
- data/lib/jsound/midi/devices/repeater.rb +0 -28
- data/spec/jsound/midi/devices/repeater_device_spec.rb +0 -19
data/.yardopts
CHANGED
data/INTRO.md
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
Synopsis
|
2
|
+
--------
|
3
|
+
|
4
|
+
JSound provides cross-platform [MIDI](http://en.wikipedia.org/wiki/Musical_Instrument_Digital_Interface) support for Ruby.
|
5
|
+
|
6
|
+
Java's cross-platform standard library includes a
|
7
|
+
[sound API for MIDI and sampled sound](http://download.oracle.com/javase/tutorial/sound/index.html).
|
8
|
+
JSound builds on this API and provides a Ruby interface via [JRuby](http://jruby.org).
|
9
|
+
|
10
|
+
For now, JSound is focusd on use of the [javax.sound.midi API](http://download.oracle.com/javase/7/docs/api/javax/sound/midi/package-summary.html)
|
11
|
+
in Ruby scripts and applications.
|
12
|
+
Support for the [javax.sound.samples API](http://download.oracle.com/javase/7/docs/api/javax/sound/sampled/package-summary.html)
|
13
|
+
may arrive in the future.
|
14
|
+
|
15
|
+
<br>
|
16
|
+
project info @ http://github.com/adamjmurray/jsound
|
17
|
+
|
18
|
+
<br>
|
19
|
+
Core Concepts
|
20
|
+
-------------
|
21
|
+
|
22
|
+
### Messages ###
|
23
|
+
|
24
|
+
Messages repesent musical events, such as a note being played or a musical parameter changing (vibrato, FX level, pitch bend, etc).
|
25
|
+
|
26
|
+
JSound provides direct support for various MIDI message, which can be found in the {JSound::Midi::Messages} module.
|
27
|
+
|
28
|
+
Generic support for other message types is provided by the {JSound::Midi::Message} class.
|
29
|
+
|
30
|
+
<br>
|
31
|
+
### Devices ###
|
32
|
+
|
33
|
+
Devices send messages to one another to communicate musical information. There are 3 general categories:
|
34
|
+
|
35
|
+
* __Input Devices__ receive messages from external hardware (e.g. keyboards, digital drums) or other software
|
36
|
+
* __Output Devices__ send messages to external hardware (e.g. synthesizers) or other software
|
37
|
+
* __Custom Devices__ process messages in arbitrary ways (e.g. generate, record, transform, route)
|
38
|
+
|
39
|
+
JSound provides the input and output devices via the {JSound::Midi::INPUTS} and {JSound::Midi::OUTPUTS} constants.
|
40
|
+
INPUTS and OUTPUTS are {JSound::Midi::DeviceList}s, which provide methods for looking up specific devices.
|
41
|
+
|
42
|
+
You make your own custom devices with Ruby by subclassing the {JSound::Midi::Device} class,
|
43
|
+
or use some of the custom devices provided in the {JSound::Midi::Devices} module.
|
44
|
+
|
45
|
+
<br>
|
46
|
+
### Device Graphs ###
|
47
|
+
|
48
|
+
In order to do something useful, devices must be connected together. Input and Output devices interact with the
|
49
|
+
outside world but don't do anything useful by themselves. Custom devices have practically unlimited
|
50
|
+
possibilities, but are useless without input or output.
|
51
|
+
|
52
|
+
To connect devices together to form a device graph, we use the {JSound::Midi::Device#>>} operator.
|
53
|
+
|
54
|
+
Examples:
|
55
|
+
|
56
|
+
record input:
|
57
|
+
input >> recorder
|
58
|
+
|
59
|
+
generate output:
|
60
|
+
generator >> output
|
61
|
+
|
62
|
+
transform a MIDI stream:
|
63
|
+
input >> transformer >> output
|
64
|
+
|
65
|
+
|
66
|
+
<br>
|
67
|
+
Using JSound
|
68
|
+
------------
|
69
|
+
|
70
|
+
0. Install JRuby 1.5 or 1.6, either:
|
71
|
+
- via manual installation
|
72
|
+
- Download and unpack the [current binary version of JRuby](http://jruby.org/download)
|
73
|
+
- Put the JRuby bin folder on your PATH
|
74
|
+
- or via [rvm](https://rvm.beginrescueend.com/) (adjust version number as desired):
|
75
|
+
|
76
|
+
rvm install jruby-1.6.2
|
77
|
+
rvm use jruby-1.6.2
|
78
|
+
|
79
|
+
0. Install JSound
|
80
|
+
|
81
|
+
jgem install jsound
|
82
|
+
|
83
|
+
0. Try the examples (monitor.rb prints any input it receives, try playing a MIDI keyboard):
|
84
|
+
|
85
|
+
mkdir tmp
|
86
|
+
cd tmp
|
87
|
+
jgem unpack jsound
|
88
|
+
cd jsound-{version}
|
89
|
+
jruby examples/list_devices.rb
|
90
|
+
jruby examples/monitor.rb
|
91
|
+
...
|
92
|
+
|
93
|
+
0. Explore this documentation
|
94
|
+
|
95
|
+
|
96
|
+
<br>
|
97
|
+
Examples
|
98
|
+
--------
|
99
|
+
|
100
|
+
* {file:examples/list_devices.rb list_devices.rb}
|
101
|
+
|
102
|
+
* {file:examples/monitor.rb monitor.rb}
|
103
|
+
|
104
|
+
* {file:examples/play_notes.rb play_notes.rb}
|
105
|
+
|
106
|
+
* {file:examples/transpose.rb transpose.rb}
|
107
|
+
|
108
|
+
* {file:examples/harmonize.rb harmonize.rb}
|
109
|
+
|
110
|
+
* {file:examples/harmonize2.rb harmonize2.rb}
|
111
|
+
|
112
|
+
|
113
|
+
<br>
|
114
|
+
Notes
|
115
|
+
-----
|
116
|
+
|
117
|
+
### Opening Devices ###
|
118
|
+
|
119
|
+
Input and output devices will not function until they are opened via {JSound::Midi::Device#open}.
|
120
|
+
If an input or output device does not appear to be working, you probably forgot to open it.
|
121
|
+
|
122
|
+
Some of the {JSound::Midi::DeviceList} methods will automatically open the device for you.
|
123
|
+
The {JSound::Midi::DeviceList#method_missing} behavior for DeviceList provides a convenient way to
|
124
|
+
find and automatically open devices.
|
125
|
+
|
126
|
+
|
127
|
+
<br>
|
128
|
+
### OS X ###
|
129
|
+
|
130
|
+
#### IAC Driver ####
|
131
|
+
By enabling the IAC (inter-application communication) driver, you can easily interface with any MIDI-enabled application:
|
132
|
+
|
133
|
+
0. Run to /Applications/Utilities/Audio MIDI Setup
|
134
|
+
|
135
|
+
0. In the menu, select: Window → Show MIDI Window
|
136
|
+
|
137
|
+
0. Double click IAC Driver
|
138
|
+
|
139
|
+
0. Check the box where it says "Device is online"
|
140
|
+
|
141
|
+
Now JSound should be able to locate an "IAC Driver" input and output device.
|
142
|
+
|
143
|
+
You can also add additional MIDI ports here, to work with multiple applications simultaneously.
|
144
|
+
|
145
|
+
#### Output ####
|
146
|
+
|
147
|
+
OS X does not provide a default MIDI output device, so you will need to take some extra steps to hear anything.
|
148
|
+
|
149
|
+
Typically, you'll want to route MIDI to your music production environment (Logic, Reason, Live, etc) using the IAC driver.
|
150
|
+
|
151
|
+
If you don't have a music production environment, you can use the free program SimpleSynth as an output:
|
152
|
+
|
153
|
+
0. Download SimpleSynth from http://notahat.com/simplesynth
|
154
|
+
|
155
|
+
0. Run SimpleSynth
|
156
|
+
|
157
|
+
0. Select "SimpleSynth virtual input"
|
158
|
+
|
159
|
+
0. Open the SimpleSynth output device in JSound (i.e. Midi::OUTPUTS.SimpleSynth)
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
JSound: a Ruby wrapper for the Java sound API
|
2
2
|
=============================================
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
JSound provides cross-platform [MIDI](http://en.wikipedia.org/wiki/Musical_Instrument_Digital_Interface) support for Ruby.
|
5
|
+
|
6
|
+
This library requires [JRuby](http://jruby.org).
|
6
7
|
|
7
8
|
|
8
9
|
|
@@ -13,65 +14,25 @@ Home: http://github.com/adamjmurray/jsound
|
|
13
14
|
|
14
15
|
Author: Adam Murray (adam@compusition.com)
|
15
16
|
|
16
|
-
License: Distributed under a permissive BSD-style license, see LICENSE.txt
|
17
|
+
License: Distributed under a permissive BSD-style license, [see LICENSE.txt](LICENSE.txt)
|
17
18
|
|
18
19
|
|
19
20
|
|
20
21
|
Getting started
|
21
22
|
---------------
|
22
23
|
|
23
|
-
|
24
|
-
- via manual installation
|
25
|
-
- Download and unpack the [current binary version of JRuby](http://jruby.org/download)
|
26
|
-
- Put the JRuby bin folder on your PATH
|
27
|
-
- or via [rvm](https://rvm.beginrescueend.com/) (adjust version number as desired):
|
28
|
-
|
29
|
-
rvm install jruby-1.6.2
|
30
|
-
rvm use jruby-1.6.2
|
31
|
-
|
32
|
-
0. Install JSound
|
33
|
-
|
34
|
-
jgem install jsound
|
35
|
-
|
36
|
-
0. Try the examples (monitor.rb prints any input it receives, try playing a MIDI keyboard):
|
37
|
-
|
38
|
-
jruby examples/list_devices.rb
|
39
|
-
jruby examples/monitor.rb
|
40
|
-
|
41
|
-
0. Take a look at the comments in examples/notes.rb for info on:
|
42
|
-
- routing MIDI inputs to MIDI outputs
|
43
|
-
- generating MIDI events and sending them to an output
|
24
|
+
See [the documentation's introduction](INTRO.md).
|
44
25
|
|
45
26
|
|
46
27
|
|
47
28
|
Documentation
|
48
29
|
-------------
|
49
30
|
|
50
|
-
Gem: http://rubydoc.info/gems/jsound/0.
|
31
|
+
Gem: http://rubydoc.info/gems/jsound/0.1.2/frames
|
51
32
|
|
52
33
|
Latest for source: http://rubydoc.info/github/adamjmurray/jsound/master/frames
|
53
34
|
|
54
35
|
|
55
|
-
Notes
|
56
|
-
-----
|
57
|
-
|
58
|
-
### OS X ###
|
59
|
-
|
60
|
-
By enabling the IAC (inter-application communication) driver, you can easily interface with any MIDI-enabled application:
|
61
|
-
|
62
|
-
0. Run to /Applications/Utilities/Audio MIDI Setup
|
63
|
-
|
64
|
-
0. In the menu, select: Window → Show MIDI Window
|
65
|
-
|
66
|
-
0. Double click IAC Driver
|
67
|
-
|
68
|
-
0. Check the box where it says "Device is online"
|
69
|
-
|
70
|
-
Now JSound should be able to locate an "IAC Driver" input and output device.
|
71
|
-
|
72
|
-
You can also add additional MIDI ports here, to work with multiple applications simultaneously.
|
73
|
-
|
74
|
-
|
75
36
|
|
76
37
|
Development Notes
|
77
38
|
-----------------
|
@@ -90,7 +51,22 @@ and to quickly check compatibility with multiple JRuby versions via rvm:
|
|
90
51
|
yard
|
91
52
|
open doc/frames.html
|
92
53
|
|
54
|
+
or, to automatically refresh the documentation as you work:
|
55
|
+
|
56
|
+
yard server -r
|
57
|
+
open http://localhost:8808
|
58
|
+
|
93
59
|
|
94
60
|
### Project Roadmap ###
|
95
61
|
|
96
|
-
https://www.pivotaltracker.com/projects/85719
|
62
|
+
https://www.pivotaltracker.com/projects/85719
|
63
|
+
|
64
|
+
|
65
|
+
Changelog
|
66
|
+
---------
|
67
|
+
|
68
|
+
* June 18, 2011, version 0.1.2
|
69
|
+
- added a "Hash of mappings" feature to Transformer, for easy implementation of simple transformation patterns
|
70
|
+
- improved examples
|
71
|
+
- got rid of the Repeater device and built its functionality into the base Device
|
72
|
+
- added #output= and #>> to DeviceList, to construct parallel paths in device graphs
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#
|
2
|
+
# EXAMPLE: Harmonize pitches by adding a second transposed pitch
|
3
|
+
#
|
4
|
+
require 'rubygems'
|
5
|
+
require 'jsound'
|
6
|
+
include JSound
|
7
|
+
|
8
|
+
# open the input & output matching the first & second command line arg, or default to the first available
|
9
|
+
input = ARGV[0] ? Midi::INPUTS / ARGV[0] : Midi::INPUTS.open_first
|
10
|
+
output = ARGV[1] ? Midi::OUTPUTS/ ARGV[1] : Midi::OUTPUTS.open_first
|
11
|
+
|
12
|
+
harmonizer = Midi::Devices::Transformer.new do |message|
|
13
|
+
if message.respond_to? :pitch
|
14
|
+
transposed = message.clone # messages are mutable, so we have to clone them to keep the original intact
|
15
|
+
transposed.pitch += 3 # transpose up a minor third
|
16
|
+
[message, transposed]
|
17
|
+
|
18
|
+
else # pass through everything else
|
19
|
+
message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
input >> harmonizer >> output
|
24
|
+
|
25
|
+
sleep 5 while true # force script to keep running
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# EXAMPLE: Harmonize pitches by adding a second transposed pitch
|
3
|
+
#
|
4
|
+
# Alternate implementation, compare with the harmonize.rb example.
|
5
|
+
# This approach simplifies the transformer, but requires a more complex device graph.
|
6
|
+
#
|
7
|
+
require 'rubygems'
|
8
|
+
require 'jsound'
|
9
|
+
include JSound
|
10
|
+
|
11
|
+
# open the input & output matching the first & second command line arg, or default to the first available
|
12
|
+
input = ARGV[0] ? Midi::INPUTS / ARGV[0] : Midi::INPUTS.open_first
|
13
|
+
output = ARGV[1] ? Midi::OUTPUTS/ ARGV[1] : Midi::OUTPUTS.open_first
|
14
|
+
|
15
|
+
pass_through = Midi::Device.new
|
16
|
+
transposer = Midi::Devices::Transformer.new :pitch => lambda{|p| p + 3 } # transpose up a minor third
|
17
|
+
|
18
|
+
input >> Midi.DeviceList(pass_through, transposer) >> output
|
19
|
+
# connecting a custom device list creates a graph of parallel chains:
|
20
|
+
# input ==> pass_through ==> output
|
21
|
+
# input ==> transposer ==> output
|
22
|
+
|
23
|
+
sleep 5 while true # force script to keep running
|
@@ -1,37 +1,11 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require 'examples/launchpad/launchpad_generator.rb'
|
8
|
-
include JSound::Midi
|
9
|
-
lp = OUTPUTS.Launchpad
|
10
|
-
g = LaunchpadGenerator.new
|
11
|
-
g >> lp
|
12
|
-
|
13
|
-
g.all_off
|
14
|
-
|
15
|
-
# Make the top left grid button green:
|
16
|
-
g.grid(0,0,:green)
|
17
|
-
|
18
|
-
# Make the mixer button red:
|
19
|
-
g.mixer(:red)
|
20
|
-
|
21
|
-
# Make the second 'scene launch' (rightmost column of buttons) orange:
|
22
|
-
g.scene_launch(2,:orange)
|
23
|
-
|
24
|
-
# and we can also do yellow:
|
25
|
-
g.grid(1,1,:yellow)
|
26
|
-
|
27
|
-
# and arbitrary mixtures of green and red LEDs (brightness levels for each range from 0-3)
|
28
|
-
g.grid(2,2,[1,1])
|
29
|
-
|
30
|
-
at_exit { g.all_off } # this will turn everything off when you exit irb
|
31
|
-
|
32
|
-
=end
|
1
|
+
#
|
2
|
+
# EXAMPLE: A device for controlling a Novation Launchpad
|
3
|
+
#
|
4
|
+
require 'rubygems'
|
5
|
+
require 'jsound'
|
6
|
+
include JSound
|
33
7
|
|
34
|
-
class LaunchpadGenerator <
|
8
|
+
class LaunchpadGenerator < Midi::Devices::Generator
|
35
9
|
|
36
10
|
def grid(x,y,color=3)
|
37
11
|
x = clip(x,0,15)
|
@@ -107,5 +81,33 @@ class LaunchpadGenerator < JSound::Midi::Devices::Generator
|
|
107
81
|
r = clip(r.to_i,0,3)
|
108
82
|
return 16*g + r
|
109
83
|
end
|
110
|
-
|
111
|
-
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
gen = LaunchpadGenerator.new
|
89
|
+
gen >> Midi::OUTPUTS.Launchpad
|
90
|
+
|
91
|
+
# clear all the lights at the start of the script
|
92
|
+
gen.all_off
|
93
|
+
# ... and at the end
|
94
|
+
at_exit { gen.all_off }
|
95
|
+
|
96
|
+
8.times do |y|
|
97
|
+
8.times do |x|
|
98
|
+
gen.grid x, y, :green
|
99
|
+
sleep 0.02
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
8.times do |n|
|
104
|
+
gen.control_row n, :amber
|
105
|
+
gen.scene_launch n, :red
|
106
|
+
sleep 0.2
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
# Other things to try:
|
111
|
+
# g.mixer(:red) # make the mixer button red
|
112
|
+
# g.grid(2,2,[1,1]) # arbitrary mixture of green and red LEDs (brightness levels for each range from 0-3)
|
113
|
+
|
@@ -1,16 +1,14 @@
|
|
1
|
-
|
1
|
+
#
|
2
|
+
# EXAMPLE: monitoring Novation Launchpad input using custom Messages
|
3
|
+
#
|
4
|
+
# Note: I have to hit a button on the top row of my Launchpad before I can receive any input.
|
5
|
+
#
|
2
6
|
require 'rubygems'
|
3
7
|
require 'jsound'
|
4
|
-
include JSound
|
5
|
-
|
6
|
-
# A quick prototype of monitoring input from a Novation Launchpad,
|
7
|
-
# after translating it to something more meaningful than the raw MIDI messages.
|
8
|
-
|
9
|
-
# Note: I have to hit a button on the top row of my Launchpad before I can receive any input.
|
10
|
-
|
8
|
+
include JSound
|
11
9
|
|
12
10
|
# A higher-level representation of the messages coming from the Launchpad
|
13
|
-
class LaunchpadMessage <
|
11
|
+
class LaunchpadMessage < Midi::Message
|
14
12
|
def initialize(button_group, position, pressed, channel=0, source=nil)
|
15
13
|
@type = :launchpad_button
|
16
14
|
@button_group = button_group
|
@@ -23,8 +21,8 @@ class LaunchpadMessage < Messages::Message
|
|
23
21
|
end
|
24
22
|
|
25
23
|
# A device that converts the lower-level MIDI messages into LaunchpadMessages
|
26
|
-
class LaunchadTranslator <
|
27
|
-
def
|
24
|
+
class LaunchadTranslator < Midi::Device
|
25
|
+
def message(message)
|
28
26
|
case message.type
|
29
27
|
|
30
28
|
when :control_change
|
@@ -53,13 +51,8 @@ class LaunchadTranslator < Devices::Device
|
|
53
51
|
end
|
54
52
|
end
|
55
53
|
|
56
|
-
|
57
|
-
# And now that those classes are defined,
|
54
|
+
# And now that those classes are defined,
|
58
55
|
# just hook up the Launchpad to a monitor, with the translator in between:
|
59
|
-
INPUTS.Launchpad >> LaunchadTranslator.new >> Devices::Monitor.new
|
56
|
+
Midi::INPUTS.Launchpad >> LaunchadTranslator.new >> Midi::Devices::Monitor.new
|
60
57
|
|
61
|
-
|
62
|
-
# force the script to keep running (MIDI devices run in a background thread)
|
63
|
-
while(true)
|
64
|
-
sleep 5
|
65
|
-
end
|
58
|
+
sleep 5 while true # force script to keep running
|
data/examples/list_devices.rb
CHANGED
data/examples/monitor.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
|
1
|
+
#
|
2
|
+
# EXAMPLE: Monitor all MIDI inputs
|
3
|
+
#
|
2
4
|
require 'rubygems'
|
3
5
|
require 'jsound'
|
4
|
-
include JSound
|
6
|
+
include JSound
|
5
7
|
|
6
|
-
|
8
|
+
monitor = Midi::Devices::Monitor.new
|
9
|
+
|
10
|
+
for input in Midi::INPUTS
|
7
11
|
input.open
|
8
|
-
input >>
|
12
|
+
input >> monitor
|
9
13
|
end
|
10
14
|
|
11
|
-
# force
|
12
|
-
while(true)
|
13
|
-
sleep 5
|
14
|
-
end
|
15
|
-
|
15
|
+
sleep 5 while true # force script to keep running
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#
|
2
|
+
# EXAMPLE: generate and play some notes
|
3
|
+
#
|
4
|
+
require 'rubygems'
|
5
|
+
require 'jsound'
|
6
|
+
include JSound
|
7
|
+
|
8
|
+
generator = Midi::Devices::Generator.new
|
9
|
+
|
10
|
+
# open the output matching the first command line arg, or default to the first available
|
11
|
+
output = ARGV[0] ? Midi::OUTPUTS/ ARGV[0] : Midi::OUTPUTS.open_first
|
12
|
+
|
13
|
+
generator >> output
|
14
|
+
|
15
|
+
13.times do |interval|
|
16
|
+
pitch = 60+interval
|
17
|
+
generator.note_on pitch, 100
|
18
|
+
sleep 0.5
|
19
|
+
generator.note_off pitch
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# EXAMPLE: using a Transformer to transpose pitches up 2 octaves
|
3
|
+
#
|
4
|
+
require 'rubygems'
|
5
|
+
require 'jsound'
|
6
|
+
include JSound
|
7
|
+
|
8
|
+
# open the input & output matching the first & second command line arg, or default to the first available
|
9
|
+
input = ARGV[0] ? Midi::INPUTS / ARGV[0] : Midi::INPUTS.open_first
|
10
|
+
output = ARGV[1] ? Midi::OUTPUTS/ ARGV[1] : Midi::OUTPUTS.open_first
|
11
|
+
|
12
|
+
# Using a block like this is most flexible way to implement a tranformer.
|
13
|
+
# This particular transformer has a nice shortcut. See the harmonize2.rb example
|
14
|
+
transposer = Midi::Devices::Transformer.new do |message|
|
15
|
+
message.pitch += 24 if message.respond_to? :pitch # transpose up two octaves (24 semitones)
|
16
|
+
message
|
17
|
+
end
|
18
|
+
|
19
|
+
input >> transposer >> output
|
20
|
+
|
21
|
+
sleep 5 while true # force script to keep running
|
data/lib/jsound/midi.rb
CHANGED
@@ -16,7 +16,6 @@ require 'jsound/midi/devices/input_device'
|
|
16
16
|
require 'jsound/midi/devices/output_device'
|
17
17
|
require 'jsound/midi/devices/monitor'
|
18
18
|
require 'jsound/midi/devices/recorder'
|
19
|
-
require 'jsound/midi/devices/repeater'
|
20
19
|
require 'jsound/midi/devices/transformer'
|
21
20
|
|
22
21
|
require 'jsound/midi/device_list'
|
data/lib/jsound/midi/device.rb
CHANGED
@@ -4,7 +4,7 @@ module JSound
|
|
4
4
|
# A device that can transmit and/or receive messages (typically MIDI messages).
|
5
5
|
# This default implementation simply passes through all messages.
|
6
6
|
class Device
|
7
|
-
include JSound::
|
7
|
+
include JSound::TypeFromClassName
|
8
8
|
|
9
9
|
# Open the device and allocate the needed resources so that it can send and receive messages
|
10
10
|
# @note this operation is typically only relevant for Java-based devices such as {Devices::InputDevice} and {Devices::OutputDevice}
|
@@ -48,12 +48,17 @@ module JSound
|
|
48
48
|
self.output= device
|
49
49
|
end
|
50
50
|
|
51
|
-
# Send a message to this device
|
51
|
+
# Send a message to this device and pass it through to any output(s)
|
52
52
|
# @param [Message]
|
53
53
|
# @see #<=
|
54
54
|
def message(message)
|
55
|
-
|
56
|
-
|
55
|
+
if @output.respond_to? :each
|
56
|
+
for device in @output
|
57
|
+
device.message(message)
|
58
|
+
end
|
59
|
+
elsif @output
|
60
|
+
@output.message(message)
|
61
|
+
end
|
57
62
|
end
|
58
63
|
|
59
64
|
# send a message to this device. shortcut for {#message}
|
@@ -14,6 +14,12 @@ module JSound
|
|
14
14
|
@devices = list
|
15
15
|
end
|
16
16
|
|
17
|
+
def each
|
18
|
+
for device in devices
|
19
|
+
yield device
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
17
23
|
# Find the first {Device} matching the criteria
|
18
24
|
#
|
19
25
|
# @param criteria matches description against for a Regexp argument, matches the device field against the value for a Hash argument, otherwise checks for an equal description
|
@@ -103,6 +109,14 @@ module JSound
|
|
103
109
|
open @devices.first
|
104
110
|
end
|
105
111
|
|
112
|
+
# Connect the output to all devices in this device list
|
113
|
+
def output= output
|
114
|
+
for device in devices
|
115
|
+
device.output = output
|
116
|
+
end
|
117
|
+
end
|
118
|
+
alias :>> :output=
|
119
|
+
|
106
120
|
def to_s
|
107
121
|
@devices.join("\n")
|
108
122
|
end
|
@@ -153,5 +167,10 @@ module JSound
|
|
153
167
|
|
154
168
|
end
|
155
169
|
|
170
|
+
def DeviceList(*devices)
|
171
|
+
DeviceList.new(devices)
|
172
|
+
end
|
173
|
+
module_function :DeviceList
|
174
|
+
|
156
175
|
end
|
157
176
|
end
|
@@ -3,7 +3,7 @@ module JSound
|
|
3
3
|
module Devices
|
4
4
|
|
5
5
|
# A device which provides methods for generating MIDI command messages.
|
6
|
-
#
|
6
|
+
# @see JSound::Midi::MessageBuilder
|
7
7
|
class Generator < Device
|
8
8
|
|
9
9
|
# For all the methods defined in the MessageBuilder module (note_on, pitch_bend, control_change, etc),
|
@@ -4,21 +4,47 @@ module JSound
|
|
4
4
|
|
5
5
|
class Transformer < Device
|
6
6
|
|
7
|
+
# A Hash of mappings, with entries in the format {:message_attribute => lambda{|attribute| ... } }
|
8
|
+
attr_accessor :mappings
|
9
|
+
|
10
|
+
# If true, messages will be automatically cloned when received by this device.
|
11
|
+
# Messages are mutable, so in many cases it's important to clone a message before changing it.
|
12
|
+
attr_accessor :clone
|
13
|
+
|
7
14
|
# The transformation block: a lambda that takes a message and returns
|
8
15
|
# either a transformed message or an Enumerable list of messages
|
9
|
-
attr_accessor :
|
16
|
+
attr_accessor :transform
|
10
17
|
|
11
|
-
|
12
|
-
|
18
|
+
# @param mappings [Hash] a Hash of mappings (see {#mappings}).
|
19
|
+
# When not nil, {#clone} will be automatically set to true.
|
20
|
+
# Set {#clone} to false by including the entry \{:clone => false}
|
21
|
+
# @param &transform see {#transform}
|
22
|
+
def initialize(mappings=nil, &transform)
|
23
|
+
@clone = !!mappings.fetch(:clone, true) if mappings
|
24
|
+
mappings.delete :clone if mappings
|
25
|
+
@mappings = mappings
|
26
|
+
@transform = transform
|
13
27
|
end
|
14
28
|
|
15
29
|
def message(message)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
30
|
+
message = message.clone if @clone
|
31
|
+
|
32
|
+
for attr, mapping in @mappings
|
33
|
+
setter = "#{attr}="
|
34
|
+
message.send setter, mapping[message.send attr] if message.respond_to? attr and message.respond_to? setter
|
35
|
+
end if @mappings
|
36
|
+
|
37
|
+
if @output
|
38
|
+
if @transform
|
39
|
+
message = @transform[message]
|
40
|
+
if message.is_a? Enumerable
|
41
|
+
message.each{|m| @output.message(m) }
|
42
|
+
else
|
43
|
+
super(message) if message
|
44
|
+
end
|
45
|
+
|
46
|
+
elsif @clone or !@mappings.empty?
|
47
|
+
super(message)
|
22
48
|
end
|
23
49
|
end
|
24
50
|
end
|
data/lib/jsound/midi/message.rb
CHANGED
@@ -6,7 +6,7 @@ module JSound
|
|
6
6
|
# See http://www.midi.org/techspecs/midimessages.php
|
7
7
|
# for info on how the MIDI spec defines these messages.
|
8
8
|
class Message
|
9
|
-
include JSound::
|
9
|
+
include JSound::TypeFromClassName
|
10
10
|
|
11
11
|
# The MIDI input {Device} which received this message.
|
12
12
|
attr_reader :source
|
@@ -52,10 +52,21 @@ module JSound::Midi
|
|
52
52
|
output.should_receive(:message).with('the_message')
|
53
53
|
device.message 'the_message'
|
54
54
|
end
|
55
|
+
|
56
|
+
it "should pass send the messages to all connected outputs" do
|
57
|
+
output1 = mock "device1"
|
58
|
+
output2 = mock "device2"
|
59
|
+
device >> [output1, output2]
|
60
|
+
|
61
|
+
output1.should_receive(:message).once.with :the_message
|
62
|
+
output2.should_receive(:message).once.with :the_message
|
63
|
+
device.message :the_message
|
64
|
+
end
|
65
|
+
|
55
66
|
end
|
56
67
|
|
57
68
|
describe '#<=' do
|
58
|
-
it 'should behave like #
|
69
|
+
it 'should behave like #message' do
|
59
70
|
device >> output
|
60
71
|
output.should_receive(:message).with('the_message')
|
61
72
|
device <= 'the_message'
|
@@ -84,5 +84,17 @@ module JSound::Midi::Devices
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
describe "#output=" do
|
88
|
+
it "should raise an error, since outputs cannot have outputs assigned" do
|
89
|
+
lambda{ recorder.output = Device.new }.should raise_error
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#>>" do
|
94
|
+
it "should raise an error, since outputs cannot have outputs assigned" do
|
95
|
+
lambda{ device >> Device.new }.should raise_error
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
87
99
|
end
|
88
100
|
end
|
@@ -12,6 +12,35 @@ module JSound::Midi
|
|
12
12
|
output.should_receive(:message).once.with MessageBuilder.note_on(72,100)
|
13
13
|
transformer.message MessageBuilder.note_on(60,100)
|
14
14
|
end
|
15
|
+
|
16
|
+
it "modifies message properties based on the Hash of mappings passed to .new" do
|
17
|
+
transformer = Transformer.new({
|
18
|
+
:attr1 => lambda{|attr| attr+1 },
|
19
|
+
:attr2 => lambda{|attr| attr-1 },
|
20
|
+
})
|
21
|
+
message = mock 'message'
|
22
|
+
message.stub!(:attr1).and_return 1
|
23
|
+
message.stub!(:attr2).and_return 5
|
24
|
+
|
25
|
+
message.should_receive(:attr1=).once.with 2
|
26
|
+
message.should_receive(:attr2=).once.with 4
|
27
|
+
transformer.message message
|
28
|
+
end
|
29
|
+
|
30
|
+
it "automatically clones the message when constructed with a Hash of mappings" do
|
31
|
+
transformer = Transformer.new({ :attr1 => lambda{|attr| attr+1 } })
|
32
|
+
message = mock 'message'
|
33
|
+
message.should_receive :clone
|
34
|
+
transformer.message message
|
35
|
+
end
|
36
|
+
|
37
|
+
it "does not clone the message when constructed with a Hash of mappings that includes {:clone => false}" do
|
38
|
+
transformer = Transformer.new({ :attr1 => lambda{|attr| attr+1 }, :clone => false })
|
39
|
+
message = mock 'message'
|
40
|
+
message.should_not_receive :clone
|
41
|
+
transformer.message message
|
42
|
+
end
|
43
|
+
|
15
44
|
end
|
16
45
|
|
17
46
|
end
|
@@ -20,6 +20,14 @@ module JSound::Midi
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
describe "#each" do
|
24
|
+
it "yields each device" do
|
25
|
+
yielded = []
|
26
|
+
device_list.each{|device| yielded << device }
|
27
|
+
yielded.should == devices
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
23
31
|
describe "#open" do
|
24
32
|
it "acts like #find and also opens the device" do
|
25
33
|
device2.should_receive :open
|
@@ -43,6 +51,26 @@ module JSound::Midi
|
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
54
|
+
describe "#output=" do
|
55
|
+
it "passes the argument to each device#open" do
|
56
|
+
output = mock "output"
|
57
|
+
device1.should_receive(:output=).with output
|
58
|
+
device2.should_receive(:output=).with output
|
59
|
+
device3.should_receive(:output=).with output
|
60
|
+
device_list.output = output
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#>>=" do
|
65
|
+
it "acts like #output=" do
|
66
|
+
output = mock "output"
|
67
|
+
device1.should_receive(:output=).with output
|
68
|
+
device2.should_receive(:output=).with output
|
69
|
+
device3.should_receive(:output=).with output
|
70
|
+
device_list >> output
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
46
74
|
describe "#method_missing" do
|
47
75
|
it "acts like #/ for unimplemented methods" do
|
48
76
|
device1.should_receive(:open)
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: jsound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.2
|
6
6
|
platform: java
|
7
7
|
authors:
|
8
8
|
- Adam Murray
|
@@ -10,10 +10,10 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-18 00:00:00 Z
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: A Ruby interface for Java's javax.sound API. Runs on the JVM via JRuby.
|
17
17
|
email: adam@compusition.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -22,9 +22,10 @@ extensions: []
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
-
-
|
25
|
+
- INTRO.md
|
26
26
|
- README.md
|
27
27
|
- LICENSE.txt
|
28
|
+
- Rakefile
|
28
29
|
- .yardopts
|
29
30
|
- lib/jsound/convert.rb
|
30
31
|
- lib/jsound/midi/device.rb
|
@@ -35,7 +36,6 @@ files:
|
|
35
36
|
- lib/jsound/midi/devices/monitor.rb
|
36
37
|
- lib/jsound/midi/devices/output_device.rb
|
37
38
|
- lib/jsound/midi/devices/recorder.rb
|
38
|
-
- lib/jsound/midi/devices/repeater.rb
|
39
39
|
- lib/jsound/midi/devices/transformer.rb
|
40
40
|
- lib/jsound/midi/message.rb
|
41
41
|
- lib/jsound/midi/message_builder.rb
|
@@ -54,7 +54,6 @@ files:
|
|
54
54
|
- spec/jsound/midi/devices/generator_spec.rb
|
55
55
|
- spec/jsound/midi/devices/output_device_spec.rb
|
56
56
|
- spec/jsound/midi/devices/recorder_spec.rb
|
57
|
-
- spec/jsound/midi/devices/repeater_device_spec.rb
|
58
57
|
- spec/jsound/midi/devices/transformer_spec.rb
|
59
58
|
- spec/jsound/midi/devlice_list_spec.rb
|
60
59
|
- spec/jsound/midi/message_builder_spec.rb
|
@@ -65,17 +64,18 @@ files:
|
|
65
64
|
- spec/jsound/midi_spec.rb
|
66
65
|
- spec/jsound/type_from_class_name_spec.rb
|
67
66
|
- spec/spec_helper.rb
|
68
|
-
- examples/
|
69
|
-
- examples/
|
67
|
+
- examples/harmonize.rb
|
68
|
+
- examples/harmonize2.rb
|
70
69
|
- examples/launchpad/launchpad_generator.rb
|
70
|
+
- examples/launchpad/launchpad_monitor.rb
|
71
71
|
- examples/list_devices.rb
|
72
72
|
- examples/monitor.rb
|
73
|
-
- examples/
|
74
|
-
- examples/
|
73
|
+
- examples/play_notes.rb
|
74
|
+
- examples/transpose.rb
|
75
75
|
homepage: http://github.com/adamjmurray/jsound
|
76
|
-
licenses:
|
77
|
-
|
78
|
-
post_install_message:
|
76
|
+
licenses:
|
77
|
+
- BSD
|
78
|
+
post_install_message: "NOTE: this gem requires JRuby 1.5+"
|
79
79
|
rdoc_options: []
|
80
80
|
|
81
81
|
require_paths:
|
@@ -92,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
92
|
- - ">="
|
93
93
|
- !ruby/object:Gem::Version
|
94
94
|
version: "0"
|
95
|
-
requirements:
|
96
|
-
|
95
|
+
requirements:
|
96
|
+
- JRuby 1.5+
|
97
97
|
rubyforge_project:
|
98
98
|
rubygems_version: 1.8.5
|
99
99
|
signing_key:
|
data/examples/harmonizer.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
#!/usr/bin/env jruby
|
2
|
-
require 'rubygems'
|
3
|
-
require 'jsound'
|
4
|
-
include JSound::Midi
|
5
|
-
include Devices
|
6
|
-
|
7
|
-
harmonizer = Transformer.new do |message|
|
8
|
-
if message.respond_to? :pitch
|
9
|
-
transposed = message.clone # messages are mutable, so we have to clone them to keep the original in tact
|
10
|
-
transposed.pitch += 3
|
11
|
-
[message, transposed]
|
12
|
-
else
|
13
|
-
message # pass through everything else
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# Adjust the INPUTS and OUTPUTS as needed to use the devices you want:
|
18
|
-
INPUTS.open_first >> harmonizer >> OUTPUTS.open_first
|
19
|
-
# For example, to send my Akai keyboard through the harmonizer to the OS X IAC bus, I can do:
|
20
|
-
# INPUTS.Akai >> harmonizer >> OUTPUTS.IAC
|
21
|
-
|
22
|
-
# force the script to keep running (MIDI devices run in a background thread)
|
23
|
-
while(true)
|
24
|
-
sleep 5
|
25
|
-
end
|
data/examples/notes.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
#!/usr/bin/env jruby
|
2
|
-
require 'rubygems'
|
3
|
-
require 'jsound'
|
4
|
-
include JSound::Midi
|
5
|
-
|
6
|
-
puts "ALL DEVICES:"
|
7
|
-
puts DEVICES
|
8
|
-
puts
|
9
|
-
puts "DEVICE VENDORS"
|
10
|
-
puts DEVICES.list_all(:vendor).uniq # list_all will include "unknown" values
|
11
|
-
puts
|
12
|
-
puts "INPUT DESCRIPTIONS:"
|
13
|
-
puts INPUTS.list
|
14
|
-
puts
|
15
|
-
puts "OUTPUT DESCRIPTIONS:"
|
16
|
-
puts OUTPUTS.list
|
17
|
-
puts
|
18
|
-
|
19
|
-
|
20
|
-
# The following examples use SimpleSynth as an output.
|
21
|
-
# It's a free program for OS X available http://notahat.com/simplesynth
|
22
|
-
# I assume SimpleSynth's MIDI Source is set to "SimpleSynth virtual input"
|
23
|
-
|
24
|
-
# On Windows or Linux, I *think* you can just use a built-in MIDI output for your soundcard
|
25
|
-
# If you need to do anything special on Windows/Linux, let me know @ http://github.com/adamjmurray
|
26
|
-
# and I'll update this example.
|
27
|
-
|
28
|
-
|
29
|
-
#######################################################
|
30
|
-
## FINDING INPUTS AND OUTPUTS
|
31
|
-
##
|
32
|
-
## Based on the input and output descriptions,
|
33
|
-
## you can locate the first device matching a description:
|
34
|
-
#> OUTPUTS/'SimpleSynth'
|
35
|
-
#
|
36
|
-
## or just:
|
37
|
-
#> OUTPUTS/:SimpleSynth
|
38
|
-
#
|
39
|
-
## the / operator is a shortcut for
|
40
|
-
#> OUTPUTS.find /SimpleSynth/
|
41
|
-
#
|
42
|
-
## which has some more advanced options:
|
43
|
-
#> OUTPUTS.find :vendor => 'M-Audio'
|
44
|
-
#
|
45
|
-
## Use Strings for exact matches and Regexp for partial matches
|
46
|
-
## The find method returns the first match, find_all will return all of them:
|
47
|
-
#> OUTPUTS.find_all :name => /(Novation|M-Audio)/
|
48
|
-
#
|
49
|
-
## All of these lookup options work for the INPUTS collection too:
|
50
|
-
#> INPUTS/'M-Audio'
|
51
|
-
|
52
|
-
|
53
|
-
######################################################
|
54
|
-
## ROUTING INPUTS TO OUTPUTS
|
55
|
-
##
|
56
|
-
## Once you have an input and an output, just connect them like so:
|
57
|
-
#> input = INPUTS/:Akai
|
58
|
-
#> output = OUTPUTS/:SimpleSynth
|
59
|
-
#> input >> output
|
60
|
-
#
|
61
|
-
## If you are sure the inputs and outputs exist, you can do it altogether:
|
62
|
-
#> INPUTS/:Akai >> OUTPUTS/:SimpleSynth
|
63
|
-
|
64
|
-
|
65
|
-
######################################################
|
66
|
-
## MONITORING INPUT
|
67
|
-
##
|
68
|
-
## Route to an instance of JSound::Midi::Devices::Monitor
|
69
|
-
#> input >> Devcies::Monitor.new
|
70
|
-
|
71
|
-
|
72
|
-
######################################################
|
73
|
-
## GENERATING NOTES
|
74
|
-
##
|
75
|
-
## See message_builder.rb for list of messages currently supported,
|
76
|
-
## including pitch_bend, control_change, channel_pressure, and more
|
77
|
-
#
|
78
|
-
# include JSound::Midi::Messages::Builder
|
79
|
-
# output = OUTPUTS/:SimpleSynth
|
80
|
-
# output.open
|
81
|
-
# while(true)
|
82
|
-
# output <= note_on(60,70)
|
83
|
-
# sleep 1
|
84
|
-
# output <= note_off(60)
|
85
|
-
# sleep 1
|
86
|
-
# end
|
87
|
-
#
|
88
|
-
## Note that connecting to an output with >> will open the output automatically,
|
89
|
-
## but passing in Messages with << does not.
|
90
|
-
## That's why I explicitly call output.open in the above example.
|
data/examples/transposer.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
#!/usr/bin/env jruby
|
2
|
-
require 'rubygems'
|
3
|
-
require 'jsound'
|
4
|
-
include JSound::Midi
|
5
|
-
include Devices
|
6
|
-
|
7
|
-
transposer = Transformer.new do |message|
|
8
|
-
message.pitch += 24 if message.respond_to? :pitch # transpose up two octaves
|
9
|
-
message
|
10
|
-
end
|
11
|
-
|
12
|
-
# Adjust the INPUTS and OUTPUTS as needed to use the devices you want:
|
13
|
-
INPUTS.open_first >> transposer >> OUTPUTS.open_first
|
14
|
-
# For example, to send my Akai keyboard through the harmonizer to the OS X IAC bus, I can do:
|
15
|
-
# INPUTS.Akai >> transposer >> OUTPUTS.IAC
|
16
|
-
|
17
|
-
# force the script to keep running (MIDI devices run in a background thread)
|
18
|
-
while(true)
|
19
|
-
sleep 5
|
20
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module JSound
|
2
|
-
module Midi
|
3
|
-
module Devices
|
4
|
-
|
5
|
-
# A device which repeats the input message to multiple outputs.
|
6
|
-
class Repeater < Device
|
7
|
-
|
8
|
-
# connect device(s) as the outputs for this device
|
9
|
-
# @param [Enumberable, Device] the device or devices to connect, or nil to disconnect the currently connected device
|
10
|
-
# @see {#>>}
|
11
|
-
def output= device
|
12
|
-
device = [device] if not device.is_a? Enumerable
|
13
|
-
super
|
14
|
-
end
|
15
|
-
|
16
|
-
def message(message)
|
17
|
-
if @output
|
18
|
-
for device in @output
|
19
|
-
device.message(message)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module JSound::Midi::Devices
|
4
|
-
describe Repeater do
|
5
|
-
|
6
|
-
let(:repeater) { Repeater.new }
|
7
|
-
let(:output1) { mock 'device1' }
|
8
|
-
let(:output2) { mock 'device1' }
|
9
|
-
|
10
|
-
before { repeater >> [output1, output2] }
|
11
|
-
|
12
|
-
it "should pass all messages to its outputs" do
|
13
|
-
output1.should_receive(:message).once.with :the_message
|
14
|
-
output2.should_receive(:message).once.with :the_message
|
15
|
-
repeater.message :the_message
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|