midiator 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|