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