jsound 0.1.0-java → 0.1.2-java

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts CHANGED
@@ -1,3 +1,5 @@
1
- --readme README.md
1
+ --readme INTRO.md
2
2
  --title "JSound: a Ruby wrapper for Java's sound API"
3
3
  lib/**/*.rb
4
+ - INTRO.md
5
+ - examples/*.rb
@@ -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 &rarr; 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
- This library requires [JRuby](http://jruby.org)
5
- and currently only supports the [MIDI API](http://java.sun.com/j2se/1.5.0/docs/api/javax/sound/midi/package-summary.html)
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
- 0. Install JRuby 1.5 or 1.6, either:
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.0.1/frames
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 &rarr; 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
- # A device for controlling a Novation Launchpad
2
- # To try this example, do this from the top-level project source folder
3
- =begin
4
-
5
- jirb -I lib
6
- require 'jsound'
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 < JSound::Midi::Devices::Generator
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
- #!/usr/bin/env jruby --debug
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::Midi
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 < Messages::Message
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 < Devices::Device
27
- def <=(message)
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
@@ -1,6 +1,7 @@
1
- #!/usr/bin/env jruby
1
+ #
2
+ # EXAMPLE: list all available devices
3
+ #
2
4
  require 'rubygems'
3
5
  require 'jsound'
4
- include JSound::Midi
5
6
 
6
- puts DEVICES
7
+ puts JSound::Midi::DEVICES
@@ -1,15 +1,15 @@
1
- #!/usr/bin/env jruby
1
+ #
2
+ # EXAMPLE: Monitor all MIDI inputs
3
+ #
2
4
  require 'rubygems'
3
5
  require 'jsound'
4
- include JSound::Midi
6
+ include JSound
5
7
 
6
- INPUTS.each do |input|
8
+ monitor = Midi::Devices::Monitor.new
9
+
10
+ for input in Midi::INPUTS
7
11
  input.open
8
- input >> Devices::Monitor.new
12
+ input >> monitor
9
13
  end
10
14
 
11
- # force the script to keep running (MIDI devices run in a background thread)
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
@@ -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'
@@ -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::Mixins::TypeFromClassName
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
- # default behavior is to pass the message to any connected output
56
- @output.message(message) if @output
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
- # See JSound::Midi::Messages::Builder for the available methods.
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),
@@ -45,6 +45,10 @@ module JSound
45
45
  end
46
46
  alias recording? open?
47
47
 
48
+ def output= device
49
+ raise "#{self.class} cannot be assigned an output"
50
+ end
51
+
48
52
  def message(message)
49
53
  @messages_with_timestamps << [message, Time.now.to_i] if recording?
50
54
  end
@@ -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 :message_processor
16
+ attr_accessor :transform
10
17
 
11
- def initialize(&message_processor)
12
- @message_processor = message_processor
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
- if @output and @message_processor
17
- transformed_message = @message_processor.call(message)
18
- if transformed_message.is_a? Enumerable
19
- transformed_message.each{|m| @output.message(m) }
20
- else
21
- @output.message(transformed_message) if transformed_message
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
@@ -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::Mixins::TypeFromClassName
9
+ include JSound::TypeFromClassName
10
10
 
11
11
  # The MIDI input {Device} which received this message.
12
12
  attr_reader :source
@@ -1,4 +1,4 @@
1
- module JSound::Mixins
1
+ module JSound
2
2
 
3
3
  # Provides a default implementation of .type for the Class, based on the class name.
4
4
  module TypeFromClassName
@@ -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 #send_message' do
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)
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- module JSound::Mixins
3
+ module JSound
4
4
  describe TypeFromClassName do
5
5
 
6
6
  context "including_class#type" do
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: jsound
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
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-16 00:00:00 Z
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
- - Rakefile
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/harmonizer.rb
69
- - examples/launchpad/launchpad.rb
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/notes.rb
74
- - examples/transposer.rb
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:
@@ -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
@@ -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.
@@ -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