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