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