midiator 0.3.0 → 0.3.1
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/examples/amen_break.rb +157 -0
- data/examples/chords.rb +34 -0
- data/examples/chromatic_scale.rb +2 -2
- data/examples/drum_chords.rb +49 -0
- data/examples/metronome.rb +12 -12
- data/examples/synth.rb +34 -0
- data/examples/twinkle.rb +2 -2
- data/lib/midiator.rb +4 -4
- data/lib/midiator/driver.rb +87 -87
- data/lib/midiator/driver_registry.rb +24 -24
- data/lib/midiator/drivers/alsa.rb +36 -36
- data/lib/midiator/drivers/core_midi.rb +46 -46
- data/lib/midiator/drivers/dls_synth.rb +97 -97
- data/lib/midiator/drivers/mmj.rb +35 -0
- data/lib/midiator/drivers/winmm.rb +18 -18
- data/lib/midiator/drums.rb +19 -19
- data/lib/midiator/interface.rb +90 -79
- data/lib/midiator/notes.rb +62 -62
- data/lib/midiator/timer.rb +33 -33
- data/lib/string_extensions.rb +39 -39
- data/misc/rake/packaging.rb +20 -20
- data/misc/rake/rdoc.rb +16 -21
- data/misc/rake/testing.rb +9 -9
- data/spec/driver_registry_spec.rb +96 -96
- data/spec/driver_spec.rb +8 -8
- data/spec/interface_spec.rb +114 -114
- data/spec/string_extensions_spec.rb +12 -12
- metadata +7 -2
@@ -17,37 +17,37 @@ require 'singleton'
|
|
17
17
|
require 'midiator'
|
18
18
|
|
19
19
|
class MIDIator::DriverRegistry
|
20
|
-
|
20
|
+
include Singleton
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
### Stores the given +klass+ in the <tt>@drivers</tt> hash, keyed by +name+.
|
23
|
+
### Typically called via MIDIator::Driver's +inherited+ hook.
|
24
|
+
def register_driver( name, klass )
|
25
|
+
@drivers ||= {}
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
raise ArgumentError, "Attempted to register something that is not a MIDIator::Driver" unless
|
28
|
+
klass < MIDIator::Driver
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
@drivers.each do |existing_name, existing_klass|
|
31
|
+
raise ArgumentError, "Already registered #{existing_klass.to_s} as '#{existing_name}'." if
|
32
|
+
existing_klass == klass
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
@drivers[ name ] = klass
|
36
|
+
end
|
37
|
+
alias register register_driver
|
38
|
+
alias << register_driver
|
39
39
|
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
### Returns the number of drivers currently registered.
|
42
|
+
def size
|
43
|
+
return @drivers.size
|
44
|
+
end
|
45
45
|
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
### Included to make the registry quack like a hash. Delegates to the
|
48
|
+
### <tt>@drivers</tt> hash.
|
49
|
+
def []( name )
|
50
|
+
return @drivers[ name ]
|
51
|
+
end
|
52
52
|
|
53
53
|
end
|
@@ -24,40 +24,40 @@ require 'midiator/driver'
|
|
24
24
|
require 'midiator/driver_registry'
|
25
25
|
|
26
26
|
class MIDIator::Driver::ALSA < MIDIator::Driver # :nodoc:
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
27
|
+
# tell the user they need to connect to their output
|
28
|
+
def instruct_user!
|
29
|
+
$stderr.puts "[MIDIator] Please connect the MIDIator output port to your input"
|
30
|
+
$stderr.puts "[MIDIator] of choice. You can use qjackctl or aconnect to do so."
|
31
|
+
$stderr.puts "[MIDIator]"
|
32
|
+
$stderr.puts "[MIDIator] Press enter when you're done."
|
33
|
+
|
34
|
+
gets # wait for the enter
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
module C # :nodoc:
|
39
|
+
extend DL::Importable
|
40
|
+
dlload 'libasound.so'
|
41
|
+
|
42
|
+
extern "int snd_rawmidi_open(void*, void*, char*, int)"
|
43
|
+
extern "int snd_rawmidi_close(void*)"
|
44
|
+
extern "int snd_rawmidi_write(void*, void*, int)"
|
45
|
+
extern "int snd_rawmidi_drain(void*)"
|
46
|
+
end
|
47
|
+
|
48
|
+
def open
|
49
|
+
@output = DL::PtrData.new(nil)
|
50
|
+
C.snd_rawmidi_open(nil, @output.ref, "virtual", 0)
|
51
|
+
end
|
52
|
+
|
53
|
+
def close
|
54
|
+
C.snd_rawmidi_close(@output)
|
55
|
+
end
|
56
|
+
|
57
|
+
def message(*args)
|
58
|
+
format = "C" * args.size
|
59
|
+
bytes = args.pack(format).to_ptr
|
60
|
+
C.snd_rawmidi_write(@output, bytes, args.size)
|
61
|
+
C.snd_rawmidi_drain(@output)
|
62
|
+
end
|
63
63
|
end
|
@@ -22,61 +22,61 @@ require 'midiator/driver'
|
|
22
22
|
require 'midiator/driver_registry'
|
23
23
|
|
24
24
|
class MIDIator::Driver::CoreMIDI < MIDIator::Driver # :nodoc:
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
##########################################################################
|
26
|
+
### S Y S T E M I N T E R F A C E
|
27
|
+
##########################################################################
|
28
|
+
module C # :nodoc:
|
29
|
+
extend DL::Importable
|
30
|
+
dlload '/System/Library/Frameworks/CoreMIDI.framework/Versions/Current/CoreMIDI'
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
32
|
+
extern "int MIDIClientCreate( void*, void*, void*, void* )"
|
33
|
+
extern "int MIDIClientDispose( void* )"
|
34
|
+
extern "int MIDIGetNumberOfDestinations()"
|
35
|
+
extern "void* MIDIGetDestination( int )"
|
36
|
+
extern "int MIDIOutputPortCreate( void*, void*, void* )"
|
37
|
+
extern "void* MIDIPacketListInit( void* )"
|
38
|
+
extern "void* MIDIPacketListAdd( void*, int, void*, int, int, int, void* )"
|
39
|
+
extern "int MIDISend( void*, void*, void* )"
|
40
|
+
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
module CF # :nodoc:
|
43
|
+
extend DL::Importable
|
44
|
+
dlload '/System/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation'
|
45
45
|
|
46
|
-
|
47
|
-
|
46
|
+
extern "void* CFStringCreateWithCString( void*, char*, int )"
|
47
|
+
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
##########################################################################
|
50
|
+
### D R I V E R A P I
|
51
|
+
##########################################################################
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
def open
|
54
|
+
client_name = CF.cFStringCreateWithCString( nil, "MIDIator", 0 )
|
55
|
+
@client = DL::PtrData.new( nil )
|
56
|
+
C.mIDIClientCreate( client_name, nil, nil, @client.ref )
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
port_name = CF.cFStringCreateWithCString( nil, "Output", 0 )
|
59
|
+
@outport = DL::PtrData.new( nil )
|
60
|
+
C.mIDIOutputPortCreate( @client, port_name, @outport.ref )
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
number_of_destinations = C.mIDIGetNumberOfDestinations
|
63
|
+
raise MIDIator::NoMIDIDestinations if number_of_destinations < 1
|
64
|
+
@destination = C.mIDIGetDestination( 0 )
|
65
|
+
end
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
def close
|
68
|
+
C.mIDIClientDispose( @client )
|
69
|
+
end
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
71
|
+
def message( *args )
|
72
|
+
format = "C" * args.size
|
73
|
+
bytes = args.pack( format ).to_ptr
|
74
|
+
packet_list = DL.malloc( 256 )
|
75
|
+
packet_ptr = C.mIDIPacketListInit( packet_list )
|
76
76
|
|
77
|
-
|
78
|
-
|
77
|
+
# Pass in two 32-bit 0s for the 64 bit time
|
78
|
+
packet_ptr = C.mIDIPacketListAdd( packet_list, 256, packet_ptr, 0, 0, args.size, bytes )
|
79
79
|
|
80
|
-
|
81
|
-
|
80
|
+
C.mIDISend( @outport, @destination, packet_list )
|
81
|
+
end
|
82
82
|
end
|
@@ -15,105 +15,105 @@ require 'dl/import'
|
|
15
15
|
require 'dl/struct'
|
16
16
|
|
17
17
|
class String
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
def to_bytes
|
19
|
+
bytes = 0
|
20
|
+
self.each_byte do |byte|
|
21
|
+
bytes <<= 8
|
22
|
+
bytes += byte
|
23
|
+
end
|
24
|
+
return bytes
|
25
|
+
end
|
26
26
|
end
|
27
27
|
|
28
28
|
class MIDIator::Driver::DLSSynth < MIDIator::Driver # :nodoc:
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
30
|
+
attr_accessor :synth
|
31
|
+
|
32
|
+
module AudioToolbox
|
33
|
+
extend DL::Importable
|
34
|
+
dlload '/System/Library/Frameworks/AudioToolbox.framework/Versions/Current/AudioToolbox'
|
35
|
+
|
36
|
+
ComponentDescription = struct [
|
37
|
+
"int componentType",
|
38
|
+
"int componentSubType",
|
39
|
+
"int componentManufacturer",
|
40
|
+
"int componentFlags",
|
41
|
+
"int componentFlagsMask"
|
42
|
+
]
|
43
|
+
|
44
|
+
# to_bytes may not be strictly necessary but these are supposed to be 4 byte numbers
|
45
|
+
AudioUnitManufacturer_Apple = 'appl'.to_bytes
|
46
|
+
AudioUnitType_MusicDevice = 'aumu'.to_bytes
|
47
|
+
AudioUnitSubType_DLSSynth = 'dls '.to_bytes
|
48
|
+
AudioUnitType_Output = 'auou'.to_bytes
|
49
|
+
AudioUnitSubType_DefaultOutput = 'def '.to_bytes
|
50
|
+
|
51
|
+
extern 'int NewAUGraph(void *)'
|
52
|
+
extern 'int AUGraphAddNode(void *, ComponentDescription *, void *)'
|
53
|
+
extern 'int AUGraphOpen(void *)'
|
54
|
+
extern 'int AUGraphConnectNodeInput(void *, void *, int, void *, int)'
|
55
|
+
extern 'int AUGraphNodeInfo(void *, void *, ComponentDescription *, void *)'
|
56
|
+
extern 'int AUGraphInitialize(void *)'
|
57
|
+
extern 'int AUGraphStart(void *)'
|
58
|
+
extern 'int AUGraphStop(void *)'
|
59
|
+
extern 'int DisposeAUGraph(void *)'
|
60
|
+
|
61
|
+
extern 'void * CAShow(void *)'
|
62
|
+
|
63
|
+
extern 'void * MusicDeviceMIDIEvent(void *, int, int, int, int)'
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def require_noerr(action_description, &block)
|
69
|
+
if block.call != 0
|
70
|
+
fail "Failed to #{action_description}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def open
|
75
|
+
@synth = DL::PtrData.new(nil)
|
76
|
+
@graph = DL::PtrData.new(nil)
|
77
|
+
synthNode = DL::PtrData.new(nil)
|
78
|
+
outNode = DL::PtrData.new(nil)
|
79
|
+
|
80
|
+
cd = AudioToolbox::ComponentDescription.malloc()
|
81
|
+
cd.componentManufacturer = AudioToolbox::AudioUnitManufacturer_Apple
|
82
|
+
cd.componentFlags = 0
|
83
|
+
cd.componentFlagsMask = 0
|
84
|
+
|
85
|
+
require_noerr('create AUGraph') { AudioToolbox.newAUGraph(@graph.ref) }
|
86
|
+
|
87
|
+
cd.componentType = AudioToolbox::AudioUnitType_MusicDevice
|
88
|
+
cd.componentSubType = AudioToolbox::AudioUnitSubType_DLSSynth
|
89
|
+
require_noerr('add synthNode') { AudioToolbox.aUGraphAddNode(@graph, cd, synthNode.ref) }
|
90
|
+
|
91
|
+
cd.componentType = AudioToolbox::AudioUnitType_Output
|
92
|
+
cd.componentSubType = AudioToolbox::AudioUnitSubType_DefaultOutput
|
93
|
+
require_noerr('add outNode') { AudioToolbox.aUGraphAddNode(@graph, cd, outNode.ref) }
|
94
|
+
|
95
|
+
require_noerr('open graph') { AudioToolbox.aUGraphOpen(@graph) }
|
96
|
+
|
97
|
+
require_noerr('connect synth to out') { AudioToolbox.aUGraphConnectNodeInput(@graph, synthNode, 0, outNode, 0) }
|
98
|
+
|
99
|
+
require_noerr('graph info') { AudioToolbox.aUGraphNodeInfo(@graph, synthNode, nil, @synth.ref) }
|
100
|
+
require_noerr('init graph') { AudioToolbox.aUGraphInitialize(@graph) }
|
101
|
+
require_noerr('start graph') { AudioToolbox.aUGraphStart(@graph) }
|
102
|
+
|
103
|
+
AudioToolbox.cAShow(@graph) if $DEBUG
|
104
|
+
end
|
105
|
+
|
106
|
+
def message(*args)
|
107
|
+
arg0 = args[0] || 0
|
108
|
+
arg1 = args[1] || 0
|
109
|
+
arg2 = args[2] || 0
|
110
|
+
AudioToolbox.musicDeviceMIDIEvent(@synth, arg0, arg1, arg2, 0)
|
111
|
+
end
|
112
|
+
|
113
|
+
def close
|
114
|
+
if @graph
|
115
|
+
AudioToolbox.aUGraphStop(@graph)
|
116
|
+
AudioToolbox.disposeAUGraph(@graph)
|
117
|
+
end
|
118
|
+
end
|
119
119
|
end
|