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
@@ -0,0 +1,29 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
module Messages
|
4
|
+
|
5
|
+
class NoteOn < Message
|
6
|
+
|
7
|
+
def initialize(pitch, velocity=127, channel=0, options={})
|
8
|
+
super([pitch,velocity], channel, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
alias pitch data1
|
12
|
+
alias pitch= data1=
|
13
|
+
|
14
|
+
alias velocity data2
|
15
|
+
alias velocity= data2=
|
16
|
+
|
17
|
+
def self.from_java(java_message, options={})
|
18
|
+
new java_message.data1, java_message.data2, java_message.channel, options.merge({:java_message => java_message})
|
19
|
+
end
|
20
|
+
|
21
|
+
def clone
|
22
|
+
self.class.new(pitch,velocity,@channel)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
module Messages
|
4
|
+
|
5
|
+
class PitchBend < Message
|
6
|
+
|
7
|
+
attr_reader :value
|
8
|
+
|
9
|
+
def initialize(value, channel=0, options={})
|
10
|
+
super(nil, channel, options)
|
11
|
+
self.value = value # we set #data as a side-effect here
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param value [Fixnum] an 14-bit int in the range 0-16383 (8192 is no bend)
|
15
|
+
def value= value
|
16
|
+
@value = value
|
17
|
+
self.data = Convert.to_7bit(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def data= data
|
21
|
+
super
|
22
|
+
@value = Convert.from_7bit(*data)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param f [Float] in the range -1.0 to 1.0
|
26
|
+
def self.from_f(f, channel=0, options={})
|
27
|
+
new Convert.normalized_float_to_14bit(f), channel=0, options
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.from_java(java_message, options={})
|
31
|
+
value = Convert.from_7bit(java_message.data1, java_message.data2)
|
32
|
+
new value, java_message.channel, options.merge({:java_message => java_message})
|
33
|
+
end
|
34
|
+
|
35
|
+
def clone
|
36
|
+
self.class.new(@value,@channel)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
module Messages
|
4
|
+
|
5
|
+
class PolyPressure < Message
|
6
|
+
|
7
|
+
def initialize(pitch, pressure, channel=0, options={})
|
8
|
+
super([pitch,pressure], channel, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
alias pitch data1
|
12
|
+
alias pitch= data1=
|
13
|
+
|
14
|
+
alias pressure data2
|
15
|
+
alias pressure= data2=
|
16
|
+
|
17
|
+
def self.from_java(java_message, options={})
|
18
|
+
new java_message.data1, java_message.data2, java_message.channel, options.merge({:java_message => java_message})
|
19
|
+
end
|
20
|
+
|
21
|
+
def clone
|
22
|
+
self.class.new(pitch,pressure,@channel)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module JSound
|
2
|
+
module Midi
|
3
|
+
module Messages
|
4
|
+
|
5
|
+
class ProgramChange < Message
|
6
|
+
|
7
|
+
def initialize(program, channel=0, options={})
|
8
|
+
super([program,0], channel, options)
|
9
|
+
@program = program
|
10
|
+
end
|
11
|
+
|
12
|
+
alias program data1
|
13
|
+
alias program= data1=
|
14
|
+
|
15
|
+
def self.from_java(java_message, options={})
|
16
|
+
new java_message.data1, java_message.channel, options.merge({:java_message => java_message})
|
17
|
+
end
|
18
|
+
|
19
|
+
def clone
|
20
|
+
self.class.new(program,@channel)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module JSound::Mixins
|
2
|
+
|
3
|
+
# Provides a default implementation of .type for the Class, based on the class name.
|
4
|
+
module TypeFromClassName
|
5
|
+
|
6
|
+
# Add ClassMethods to the including class.
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Class-level methods added when including TypeFromClassName
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
# Extract the class name (from fully qualified Module::Class string)
|
15
|
+
# and convert camel case to snake case.
|
16
|
+
# @example JSound::Midi::Messages::NoteOn => :note_on
|
17
|
+
def type
|
18
|
+
name.split('::').last.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module JSound
|
4
|
+
describe Convert do
|
5
|
+
|
6
|
+
it 'should define MAX_14BIT_VALUE as the maximum value for 14-bit integers' do
|
7
|
+
Convert::MAX_14BIT_VALUE.should == (127 + (127 << 7))
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'with Convert included' do
|
11
|
+
include Convert
|
12
|
+
|
13
|
+
describe '#to_7bit' do
|
14
|
+
it 'should convert positive integers to 7-bit [least_significant,most_significant]' do
|
15
|
+
to_7bit( 0).should == [ 0, 0]
|
16
|
+
to_7bit( 1).should == [ 1, 0]
|
17
|
+
to_7bit( 127).should == [127, 0]
|
18
|
+
to_7bit( 128).should == [ 0, 1]
|
19
|
+
to_7bit( 129).should == [ 1, 1]
|
20
|
+
to_7bit(16255).should == [127,126]
|
21
|
+
to_7bit(16256).should == [ 0,127]
|
22
|
+
to_7bit(16257).should == [ 1,127]
|
23
|
+
to_7bit(16383).should == [127,127]
|
24
|
+
end
|
25
|
+
it 'should be the inverse of #from_7bit' do
|
26
|
+
for lsb in 0..7
|
27
|
+
for msb in 0..7
|
28
|
+
to_7bit(from_7bit(lsb,msb)).should == [lsb,msb]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#from_7bit' do
|
35
|
+
it 'should convert 7-bit [least_significant,most_significant] to an integer' do
|
36
|
+
from_7bit( 0, 0).should == 0
|
37
|
+
from_7bit( 1, 0).should == 1
|
38
|
+
from_7bit(127, 0).should == 127
|
39
|
+
from_7bit( 0, 1).should == 128
|
40
|
+
from_7bit( 1, 1).should == 129
|
41
|
+
from_7bit(127,126).should == 16255
|
42
|
+
from_7bit( 0,127).should == 16256
|
43
|
+
from_7bit( 1,127).should == 16257
|
44
|
+
from_7bit(127,127).should == 16383
|
45
|
+
end
|
46
|
+
it 'should be the inverse of #to_7bit' do
|
47
|
+
for i in 0..16383
|
48
|
+
from_7bit(*to_7bit(i)).should == i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#normalized_float_to_14bit' do
|
54
|
+
it 'should scale floats in the range [-1.0, 1.0] to [0, 16383]' do
|
55
|
+
normalized_float_to_14bit(-1.0).should == 0
|
56
|
+
normalized_float_to_14bit(-0.5).should == 4096
|
57
|
+
normalized_float_to_14bit( 0.0).should == 8192
|
58
|
+
normalized_float_to_14bit( 0.5).should == 12287
|
59
|
+
normalized_float_to_14bit( 1.0).should == 16383
|
60
|
+
end
|
61
|
+
it 'should convert 1.0 to MAX_14BIT_VALUE' do
|
62
|
+
normalized_float_to_14bit(1.0).should == Convert::MAX_14BIT_VALUE
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module JSound::Midi
|
4
|
+
|
5
|
+
describe Device do
|
6
|
+
let(:device) { Device.new }
|
7
|
+
let(:output) { mock('device') }
|
8
|
+
|
9
|
+
describe '#open' do
|
10
|
+
it "does nothing" do
|
11
|
+
# just checking that device implements the method
|
12
|
+
device.open
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#open?' do
|
17
|
+
it "is true, because the abstract Device is always open" do
|
18
|
+
device.open?.should be_true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#close' do
|
23
|
+
it "does nothing" do
|
24
|
+
# just checking that device implements the method
|
25
|
+
device.close
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#type" do
|
30
|
+
it "is :pass_through" do
|
31
|
+
device.type.should == :pass_through
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#output' do
|
36
|
+
it "is the output of this device's messages" do
|
37
|
+
device.output = output
|
38
|
+
device.output.should == output
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#>>' do
|
43
|
+
it 'should connect to a output' do
|
44
|
+
device >> output
|
45
|
+
device.output.should == output
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#message' do
|
50
|
+
it 'should send messages to the connected output' do
|
51
|
+
device >> output
|
52
|
+
output.should_receive(:message).with('the_message')
|
53
|
+
device.message 'the_message'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#<=' do
|
58
|
+
it 'should behave like #send_message' do
|
59
|
+
device >> output
|
60
|
+
output.should_receive(:message).with('the_message')
|
61
|
+
device <= 'the_message'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should pass through messages by default' do
|
66
|
+
pass_through = Device.new
|
67
|
+
device >> pass_through >> output
|
68
|
+
device.output.should == pass_through
|
69
|
+
pass_through.output.should == output
|
70
|
+
output.should_receive(:message).with('message')
|
71
|
+
device <= 'message'
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module JSound::Midi::Devices
|
4
|
+
describe Generator do
|
5
|
+
|
6
|
+
let(:generator) { Generator.new }
|
7
|
+
let(:output) { mock('device') }
|
8
|
+
before { generator >> output }
|
9
|
+
|
10
|
+
it "should generate note_on messages" do
|
11
|
+
output.should_receive(:message).once.with note_on_message(0)
|
12
|
+
generator.note_on(0)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should generate note_off messages" do
|
16
|
+
output.should_receive(:message).once.with note_off_message(0)
|
17
|
+
generator.note_off(0)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module JSound::Midi::Devices
|
4
|
+
describe OutputDevice do
|
5
|
+
|
6
|
+
let(:device) { OutputDevice.new }
|
7
|
+
let(:another_device) { mock 'device' }
|
8
|
+
|
9
|
+
describe "#output=" do
|
10
|
+
it "should raise an error, since outputs cannot have outputs assigned" do
|
11
|
+
lambda{ device.output = another_device }.should raise_error
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#>>" do
|
16
|
+
it "should raise an error, since outputs cannot have outputs assigned" do
|
17
|
+
lambda{ device >> another_device }.should raise_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module JSound::Midi::Devices
|
4
|
+
describe Recorder do
|
5
|
+
let(:recorder) { Recorder.new }
|
6
|
+
|
7
|
+
context 'initial state' do
|
8
|
+
it 'should be recording by default' do
|
9
|
+
recorder <= :message
|
10
|
+
recorder.messages.should == [:message]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should not record anything when the constructor argument is false' do
|
14
|
+
recorder = Recorder.new(false)
|
15
|
+
recorder <= :message
|
16
|
+
recorder.messages.should be_empty
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#start' do
|
21
|
+
it 'should start recording' do
|
22
|
+
recorder.start
|
23
|
+
recorder <= :message
|
24
|
+
recorder.messages.should == [:message]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#messages' do
|
29
|
+
it 'should return all recorded messages in an Array' do
|
30
|
+
recorder.start
|
31
|
+
recorder <= :one
|
32
|
+
recorder <= 2
|
33
|
+
recorder <= 'three'
|
34
|
+
recorder.messages.should == [:one, 2, 'three']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#messages_with_timestamps' do
|
39
|
+
it 'should return all recorded [message,timestamp] pairs in an Array' do
|
40
|
+
recorder.start
|
41
|
+
time = Time.now.to_i
|
42
|
+
recorder <= :one
|
43
|
+
recorder <= 2
|
44
|
+
recorder <= 'three'
|
45
|
+
# assuming this test all happens within a small fraction of a second:
|
46
|
+
recorder.messages_with_timestamps.should == [[:one,time], [2,time], ['three',time]]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#stop' do
|
51
|
+
it 'should stop recording' do
|
52
|
+
recorder.start
|
53
|
+
recorder <= :message_while_started
|
54
|
+
recorder.stop
|
55
|
+
recorder <= :message_while_stopped
|
56
|
+
recorder.messages.should == [:message_while_started]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#clear' do
|
61
|
+
it 'should clear the recorded messages' do
|
62
|
+
recorder.start
|
63
|
+
recorder <= :message
|
64
|
+
recorder.clear
|
65
|
+
recorder.messages.should be_empty
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#recording?' do
|
70
|
+
it 'should be true in the initial state' do
|
71
|
+
recorder.recording?.should be_true
|
72
|
+
end
|
73
|
+
it 'should be false in the initial state when given a false constructor argument' do
|
74
|
+
Recorder.new(false).recording?.should be_false
|
75
|
+
end
|
76
|
+
it 'should be true after #start' do
|
77
|
+
recorder.start
|
78
|
+
recorder.recording?.should be_true
|
79
|
+
end
|
80
|
+
it 'should be false after #end' do
|
81
|
+
recorder.start
|
82
|
+
recorder.stop
|
83
|
+
recorder.recording?.should be_false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|