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