jsound 0.1.0-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 -0
- data/LICENSE.txt +27 -0
- data/README.md +96 -0
- data/Rakefile +19 -0
- data/examples/harmonizer.rb +25 -0
- data/examples/launchpad/launchpad.rb +65 -0
- data/examples/launchpad/launchpad_generator.rb +111 -0
- data/examples/list_devices.rb +6 -0
- data/examples/monitor.rb +15 -0
- data/examples/notes.rb +90 -0
- data/examples/transposer.rb +20 -0
- data/lib/jsound.rb +4 -0
- data/lib/jsound/convert.rb +30 -0
- data/lib/jsound/midi.rb +77 -0
- data/lib/jsound/midi/device.rb +72 -0
- data/lib/jsound/midi/device_list.rb +157 -0
- data/lib/jsound/midi/devices/generator.rb +22 -0
- data/lib/jsound/midi/devices/input_device.rb +51 -0
- data/lib/jsound/midi/devices/jdevice.rb +100 -0
- data/lib/jsound/midi/devices/monitor.rb +18 -0
- data/lib/jsound/midi/devices/output_device.rb +36 -0
- data/lib/jsound/midi/devices/recorder.rb +56 -0
- data/lib/jsound/midi/devices/repeater.rb +28 -0
- data/lib/jsound/midi/devices/transformer.rb +30 -0
- data/lib/jsound/midi/message.rb +165 -0
- data/lib/jsound/midi/message_builder.rb +52 -0
- data/lib/jsound/midi/messages/channel_pressure.rb +26 -0
- data/lib/jsound/midi/messages/control_change.rb +29 -0
- data/lib/jsound/midi/messages/note_off.rb +10 -0
- data/lib/jsound/midi/messages/note_on.rb +29 -0
- data/lib/jsound/midi/messages/pitch_bend.rb +43 -0
- data/lib/jsound/midi/messages/poly_pressure.rb +29 -0
- data/lib/jsound/midi/messages/program_change.rb +27 -0
- data/lib/jsound/type_from_class_name.rb +23 -0
- data/spec/jsound/convert_spec.rb +68 -0
- data/spec/jsound/midi/device_spec.rb +75 -0
- data/spec/jsound/midi/devices/generator_spec.rb +21 -0
- data/spec/jsound/midi/devices/output_device_spec.rb +22 -0
- data/spec/jsound/midi/devices/recorder_spec.rb +88 -0
- data/spec/jsound/midi/devices/repeater_device_spec.rb +19 -0
- data/spec/jsound/midi/devices/transformer_spec.rb +20 -0
- data/spec/jsound/midi/devlice_list_spec.rb +60 -0
- data/spec/jsound/midi/message_builder_spec.rb +22 -0
- data/spec/jsound/midi/message_spec.rb +30 -0
- data/spec/jsound/midi/messages/note_off_spec.rb +62 -0
- data/spec/jsound/midi/messages/note_on_spec.rb +109 -0
- data/spec/jsound/midi/messages/pitch_bend_spec.rb +88 -0
- data/spec/jsound/midi_spec.rb +33 -0
- data/spec/jsound/type_from_class_name_spec.rb +26 -0
- data/spec/spec_helper.rb +23 -0
- metadata +103 -0
data/.yardopts
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2010-2011, Adam Murray (adam@compusition.com).
|
2
|
+
|
3
|
+
All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use of the "JSound" Ruby library, in source and binary
|
6
|
+
forms, with or without modification, are permitted provided that the
|
7
|
+
following conditions are met:
|
8
|
+
|
9
|
+
1. Redistributions of source code must retain the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
11
|
+
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright
|
13
|
+
notice, this list of conditions and the following disclaimer in
|
14
|
+
the documentation and/or other materials provided with the
|
15
|
+
distribution.
|
16
|
+
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
19
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
20
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
21
|
+
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
22
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
23
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
24
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
25
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
26
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
JSound: a Ruby wrapper for the Java sound API
|
2
|
+
=============================================
|
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)
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
Project Info
|
10
|
+
------------
|
11
|
+
|
12
|
+
Home: http://github.com/adamjmurray/jsound
|
13
|
+
|
14
|
+
Author: Adam Murray (adam@compusition.com)
|
15
|
+
|
16
|
+
License: Distributed under a permissive BSD-style license, see LICENSE.txt
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
Getting started
|
21
|
+
---------------
|
22
|
+
|
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
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
Documentation
|
48
|
+
-------------
|
49
|
+
|
50
|
+
Gem: http://rubydoc.info/gems/jsound/0.0.1/frames
|
51
|
+
|
52
|
+
Latest for source: http://rubydoc.info/github/adamjmurray/jsound/master/frames
|
53
|
+
|
54
|
+
|
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
|
+
|
76
|
+
Development Notes
|
77
|
+
-----------------
|
78
|
+
|
79
|
+
### Run Tests ###
|
80
|
+
|
81
|
+
rake spec
|
82
|
+
|
83
|
+
and to quickly check compatibility with multiple JRuby versions via rvm:
|
84
|
+
|
85
|
+
rvm jruby-1.5.6,jruby-1.6.2 rake spec:fast
|
86
|
+
|
87
|
+
|
88
|
+
### Generate Docs ###
|
89
|
+
|
90
|
+
yard
|
91
|
+
open doc/frames.html
|
92
|
+
|
93
|
+
|
94
|
+
### Project Roadmap ###
|
95
|
+
|
96
|
+
https://www.pivotaltracker.com/projects/85719
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require 'rake/clean'
|
3
|
+
|
4
|
+
task :default => :spec
|
5
|
+
|
6
|
+
CLEAN.include('doc') # clean and clobber do the same thing for now
|
7
|
+
|
8
|
+
|
9
|
+
desc "Run RSpec tests with full output"
|
10
|
+
RSpec::Core::RakeTask.new do |spec|
|
11
|
+
spec.rspec_opts = ["--color", "--format", "nested"]
|
12
|
+
end
|
13
|
+
|
14
|
+
namespace :spec do
|
15
|
+
desc "Run RSpecs tests with summary output and fast failure"
|
16
|
+
RSpec::Core::RakeTask.new(:fast) do |spec|
|
17
|
+
spec.rspec_opts = ["--color", "--fail-fast"]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
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
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env jruby --debug
|
2
|
+
require 'rubygems'
|
3
|
+
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
|
+
|
11
|
+
|
12
|
+
# A higher-level representation of the messages coming from the Launchpad
|
13
|
+
class LaunchpadMessage < Messages::Message
|
14
|
+
def initialize(button_group, position, pressed, channel=0, source=nil)
|
15
|
+
@type = :launchpad_button
|
16
|
+
@button_group = button_group
|
17
|
+
@position = position
|
18
|
+
@pressed = pressed
|
19
|
+
@data = {:button_group => button_group, :position => position, :pressed => pressed}
|
20
|
+
@channel = channel
|
21
|
+
@source = source
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# A device that converts the lower-level MIDI messages into LaunchpadMessages
|
26
|
+
class LaunchadTranslator < Devices::Device
|
27
|
+
def <=(message)
|
28
|
+
case message.type
|
29
|
+
|
30
|
+
when :control_change
|
31
|
+
button_group = :top
|
32
|
+
pressed = (message.value > 0)
|
33
|
+
position = message.control - 104
|
34
|
+
|
35
|
+
when :note_on
|
36
|
+
# everything else
|
37
|
+
pressed = (message.velocity > 0)
|
38
|
+
value = message.pitch
|
39
|
+
row = value / 16
|
40
|
+
col = value % 16
|
41
|
+
if col > 7
|
42
|
+
button_group = :right
|
43
|
+
position = row
|
44
|
+
else
|
45
|
+
button_group = :grid
|
46
|
+
position = [col,row]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
super LaunchpadMessage.new(button_group, position, pressed, message.channel, message.source)
|
51
|
+
rescue
|
52
|
+
puts $!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# And now that those classes are defined,
|
58
|
+
# just hook up the Launchpad to a monitor, with the translator in between:
|
59
|
+
INPUTS.Launchpad >> LaunchadTranslator.new >> Devices::Monitor.new
|
60
|
+
|
61
|
+
|
62
|
+
# force the script to keep running (MIDI devices run in a background thread)
|
63
|
+
while(true)
|
64
|
+
sleep 5
|
65
|
+
end
|
@@ -0,0 +1,111 @@
|
|
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
|
33
|
+
|
34
|
+
class LaunchpadGenerator < JSound::Midi::Devices::Generator
|
35
|
+
|
36
|
+
def grid(x,y,color=3)
|
37
|
+
x = clip(x,0,15)
|
38
|
+
y = clip(y,0,7)
|
39
|
+
c = color_value(color)
|
40
|
+
note_on(16*y + x, c)
|
41
|
+
end
|
42
|
+
|
43
|
+
def scene_launch(position,color=3)
|
44
|
+
grid(8,position,color)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def control_row(position,color=3)
|
49
|
+
c = color_value(color)
|
50
|
+
p = 104 + clip(position,0,7)
|
51
|
+
control_change(p,c)
|
52
|
+
end
|
53
|
+
|
54
|
+
def up( color=3); control_row(0,color) end
|
55
|
+
def down( color=3); control_row(1,color) end
|
56
|
+
def left( color=3); control_row(2,color) end
|
57
|
+
def right( color=3); control_row(3,color) end
|
58
|
+
def session(color=3); control_row(4,color) end
|
59
|
+
def user1( color=3); control_row(5,color) end
|
60
|
+
def user2( color=3); control_row(6,color) end
|
61
|
+
def mixer( color=3); control_row(7,color) end
|
62
|
+
|
63
|
+
def all_on(brightness=3)
|
64
|
+
# convert brightness values 0,1,2,3 to 0,125,126,127
|
65
|
+
# (0=off, 125=low, 126=med, 127=high)
|
66
|
+
b = clip(brightness,0,3)
|
67
|
+
b += 124 if b > 0
|
68
|
+
control_change(0,b)
|
69
|
+
end
|
70
|
+
|
71
|
+
def all_off
|
72
|
+
control_change(0,0)
|
73
|
+
end
|
74
|
+
|
75
|
+
def duty_cycle(numerator,denominator)
|
76
|
+
n = clip(numerator,1,16)
|
77
|
+
d = clip(denominator,3,18)
|
78
|
+
if n < 9
|
79
|
+
control_change(30, 16*(n-1) + (d-3))
|
80
|
+
else
|
81
|
+
control_change(31, 16*(n-9) + (d-3))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def clip(value,min,max)
|
86
|
+
value = min if value.nil? or value < min
|
87
|
+
value = max if value > max
|
88
|
+
return value
|
89
|
+
end
|
90
|
+
|
91
|
+
def color_value(color)
|
92
|
+
case color
|
93
|
+
when Array
|
94
|
+
g,r = color[0],color[1]
|
95
|
+
when Numeric
|
96
|
+
g,r = color,0
|
97
|
+
else case color.to_s
|
98
|
+
when 'green', 'g' then g,r = 3,0
|
99
|
+
when 'yellow', 'y' then g,r = 3,2
|
100
|
+
when 'amber', 'a' then g,r = 3,3
|
101
|
+
when 'orange', 'o' then g,r = 2,3
|
102
|
+
when 'red', 'r' then g,r = 0,3
|
103
|
+
else g,r = 0,0
|
104
|
+
end
|
105
|
+
end
|
106
|
+
g = clip(g.to_i,0,3)
|
107
|
+
r = clip(r.to_i,0,3)
|
108
|
+
return 16*g + r
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
data/examples/monitor.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'jsound'
|
4
|
+
include JSound::Midi
|
5
|
+
|
6
|
+
INPUTS.each do |input|
|
7
|
+
input.open
|
8
|
+
input >> Devices::Monitor.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# force the script to keep running (MIDI devices run in a background thread)
|
12
|
+
while(true)
|
13
|
+
sleep 5
|
14
|
+
end
|
15
|
+
|
data/examples/notes.rb
ADDED
@@ -0,0 +1,90 @@
|
|
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.
|