jsound 0.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.yardopts +3 -0
  2. data/LICENSE.txt +27 -0
  3. data/README.md +96 -0
  4. data/Rakefile +19 -0
  5. data/examples/harmonizer.rb +25 -0
  6. data/examples/launchpad/launchpad.rb +65 -0
  7. data/examples/launchpad/launchpad_generator.rb +111 -0
  8. data/examples/list_devices.rb +6 -0
  9. data/examples/monitor.rb +15 -0
  10. data/examples/notes.rb +90 -0
  11. data/examples/transposer.rb +20 -0
  12. data/lib/jsound.rb +4 -0
  13. data/lib/jsound/convert.rb +30 -0
  14. data/lib/jsound/midi.rb +77 -0
  15. data/lib/jsound/midi/device.rb +72 -0
  16. data/lib/jsound/midi/device_list.rb +157 -0
  17. data/lib/jsound/midi/devices/generator.rb +22 -0
  18. data/lib/jsound/midi/devices/input_device.rb +51 -0
  19. data/lib/jsound/midi/devices/jdevice.rb +100 -0
  20. data/lib/jsound/midi/devices/monitor.rb +18 -0
  21. data/lib/jsound/midi/devices/output_device.rb +36 -0
  22. data/lib/jsound/midi/devices/recorder.rb +56 -0
  23. data/lib/jsound/midi/devices/repeater.rb +28 -0
  24. data/lib/jsound/midi/devices/transformer.rb +30 -0
  25. data/lib/jsound/midi/message.rb +165 -0
  26. data/lib/jsound/midi/message_builder.rb +52 -0
  27. data/lib/jsound/midi/messages/channel_pressure.rb +26 -0
  28. data/lib/jsound/midi/messages/control_change.rb +29 -0
  29. data/lib/jsound/midi/messages/note_off.rb +10 -0
  30. data/lib/jsound/midi/messages/note_on.rb +29 -0
  31. data/lib/jsound/midi/messages/pitch_bend.rb +43 -0
  32. data/lib/jsound/midi/messages/poly_pressure.rb +29 -0
  33. data/lib/jsound/midi/messages/program_change.rb +27 -0
  34. data/lib/jsound/type_from_class_name.rb +23 -0
  35. data/spec/jsound/convert_spec.rb +68 -0
  36. data/spec/jsound/midi/device_spec.rb +75 -0
  37. data/spec/jsound/midi/devices/generator_spec.rb +21 -0
  38. data/spec/jsound/midi/devices/output_device_spec.rb +22 -0
  39. data/spec/jsound/midi/devices/recorder_spec.rb +88 -0
  40. data/spec/jsound/midi/devices/repeater_device_spec.rb +19 -0
  41. data/spec/jsound/midi/devices/transformer_spec.rb +20 -0
  42. data/spec/jsound/midi/devlice_list_spec.rb +60 -0
  43. data/spec/jsound/midi/message_builder_spec.rb +22 -0
  44. data/spec/jsound/midi/message_spec.rb +30 -0
  45. data/spec/jsound/midi/messages/note_off_spec.rb +62 -0
  46. data/spec/jsound/midi/messages/note_on_spec.rb +109 -0
  47. data/spec/jsound/midi/messages/pitch_bend_spec.rb +88 -0
  48. data/spec/jsound/midi_spec.rb +33 -0
  49. data/spec/jsound/type_from_class_name_spec.rb +26 -0
  50. data/spec/spec_helper.rb +23 -0
  51. metadata +103 -0
@@ -0,0 +1,10 @@
1
+ module JSound
2
+ module Midi
3
+ module Messages
4
+
5
+ class NoteOff < NoteOn
6
+ end
7
+
8
+ end
9
+ end
10
+ end
@@ -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