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
@@ -0,0 +1,35 @@
|
|
1
|
+
# The MIDIator driver for JRuby on OSX.
|
2
|
+
#
|
3
|
+
# == Authors
|
4
|
+
#
|
5
|
+
# * Jeremy Voorhis
|
6
|
+
#
|
7
|
+
# == Copyright
|
8
|
+
#
|
9
|
+
# Copyright (c) 2008 Jeremy Voorhis
|
10
|
+
#
|
11
|
+
# This code released under the terms of the MIT license.
|
12
|
+
#
|
13
|
+
|
14
|
+
class MIDIator::Driver::Mmj < MIDIator::Driver # :nodoc:
|
15
|
+
include Java
|
16
|
+
include_package 'de.humatic.mmj'
|
17
|
+
|
18
|
+
def outputs
|
19
|
+
MidiSystem.get_outputs.inject( [{},0] ) {|(map, i), out|
|
20
|
+
[ map.update(out => i), i + 1 ]
|
21
|
+
}.first
|
22
|
+
end
|
23
|
+
|
24
|
+
def open( output = 0 )
|
25
|
+
@out = MidiSystem.open_midi_output( output )
|
26
|
+
end
|
27
|
+
|
28
|
+
def message( *args )
|
29
|
+
@out.send_midi( args.to_java(:byte) )
|
30
|
+
end
|
31
|
+
|
32
|
+
def close
|
33
|
+
MidiSystem.close_midi_system
|
34
|
+
end
|
35
|
+
end
|
@@ -24,26 +24,26 @@ require 'midiator/driver'
|
|
24
24
|
require 'midiator/driver_registry'
|
25
25
|
|
26
26
|
class MIDIator::Driver::WinMM < MIDIator::Driver # :nodoc:
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
module C # :nodoc:
|
28
|
+
extend DL::Importable
|
29
|
+
dlload 'winmm'
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
extern "int midiOutOpen(HMIDIOUT*, int, int, int, int)"
|
32
|
+
extern "int midiOutClose(int)"
|
33
|
+
extern "int midiOutShortMsg(int, int)"
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
def open
|
37
|
+
@device = DL.malloc(DL.sizeof('I'))
|
38
|
+
C.midiOutOpen(@device, -1, 0, 0, 0)
|
39
|
+
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
def close
|
42
|
+
C.midiOutClose(@device.ptr.to_i)
|
43
|
+
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
def message(one, two=0, three=0)
|
46
|
+
message = one + (two << 8) + (three << 16)
|
47
|
+
C.midiOutShortMsg(@device.ptr.to_i, message)
|
48
|
+
end
|
49
49
|
end
|
data/lib/midiator/drums.rb
CHANGED
@@ -16,24 +16,24 @@
|
|
16
16
|
|
17
17
|
module MIDIator::Drums # this file does not rdoc well, so uhh.... :nodoc:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
19
|
+
##########################################################################
|
20
|
+
### G E N E R A L M I D I D R U M N O T E S
|
21
|
+
##########################################################################
|
22
|
+
BassDrum1 = 36 ;;; LongGuiro = 74 ;;; OpenHiHat = 46
|
23
|
+
BassDrum2 = 35 ;;; LongWhistle = 72 ;;; OpenHighConga = 63
|
24
|
+
Cabasa = 69 ;;; LowAgogo = 68 ;;; OpenTriangle = 81
|
25
|
+
ChineseCymbal = 52 ;;; LowBongo = 61 ;;; PedalHiHat = 44
|
26
|
+
Claves = 75 ;;; LowConga = 64 ;;; RideBell = 53
|
27
|
+
ClosedHiHat = 42 ;;; LowTimbale = 66 ;;; RideCymbal1 = 51
|
28
|
+
Cowbell = 56 ;;; LowTom1 = 43 ;;; RideCymbal2 = 59
|
29
|
+
CrashCymbal1 = 49 ;;; LowTom2 = 41 ;;; ShortGuiro = 73
|
30
|
+
CrashCymbal2 = 57 ;;; LowWoodBlock = 77 ;;; ShortWhistle = 71
|
31
|
+
HandClap = 39 ;;; Maracas = 70 ;;; SideStick = 37
|
32
|
+
HighAgogo = 67 ;;; MidTom1 = 47 ;;; SnareDrum1 = 38
|
33
|
+
HighBongo = 60 ;;; MidTom2 = 45 ;;; SnareDrum2 = 40
|
34
|
+
HighTimbale = 65 ;;; MuteCuica = 78 ;;; SplashCymbal = 55
|
35
|
+
HighTom1 = 50 ;;; MuteHighConga = 62 ;;; Tambourine = 54
|
36
|
+
HighTom2 = 48 ;;; MuteTriangle = 80 ;;; VibraSlap = 58
|
37
|
+
HighWoodBlock = 76 ;;; OpenCuica = 79
|
38
38
|
|
39
39
|
end
|
data/lib/midiator/interface.rb
CHANGED
@@ -22,84 +22,95 @@
|
|
22
22
|
require 'midiator'
|
23
23
|
|
24
24
|
class MIDIator::Interface
|
25
|
-
|
26
|
-
|
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
|
-
|
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
|
-
|
25
|
+
attr_reader :driver
|
26
|
+
|
27
|
+
### Automatically select a driver to use
|
28
|
+
def autodetect_driver
|
29
|
+
driver = case Platform::IMPL
|
30
|
+
when :macosx
|
31
|
+
:core_midi
|
32
|
+
when :mswin, :cygwin
|
33
|
+
:winmm
|
34
|
+
when :linux
|
35
|
+
:alsa
|
36
|
+
else
|
37
|
+
if defined?( Java ) && Java::java.lang.System.get_property('os.name') == 'Mac OS X'
|
38
|
+
:mmj
|
39
|
+
else
|
40
|
+
raise "No driver is available."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
self.use(driver)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
### Attempts to load the MIDI system driver called +driver_name+.
|
49
|
+
def use( driver_name )
|
50
|
+
driver_path = "midiator/drivers/#{driver_name.to_s}"
|
51
|
+
|
52
|
+
begin
|
53
|
+
require driver_path
|
54
|
+
rescue LoadError => e
|
55
|
+
raise LoadError,
|
56
|
+
"Could not load driver '#{driver_name}'."
|
57
|
+
end
|
58
|
+
|
59
|
+
# Fix two side-effects of the camelization process... first, change
|
60
|
+
# instances of Midi to MIDI. This fixes the acronym form but doesn't
|
61
|
+
# change, for instance, 'timidity'.
|
62
|
+
#
|
63
|
+
# Second, the require path is midiator/drivers/foo, but the module
|
64
|
+
# name is Driver singular, so fix that.
|
65
|
+
driver_class = driver_path.camelize.
|
66
|
+
gsub( /Midi/, 'MIDI' ).
|
67
|
+
sub( /::Drivers::/, '::Driver::')
|
68
|
+
|
69
|
+
# special case for the ALSA driver
|
70
|
+
driver_class.sub!( /Alsa/, 'ALSA' )
|
71
|
+
|
72
|
+
# special case for the WinMM driver
|
73
|
+
driver_class.sub!( /Winmm/, 'WinMM' )
|
74
|
+
|
75
|
+
# special case for the DLSSynth driver
|
76
|
+
driver_class.sub!( /Dls/, 'DLS' )
|
77
|
+
|
78
|
+
# this little trick stolen from ActiveSupport. It looks for a top-
|
79
|
+
# level module with the given name.
|
80
|
+
@driver = Object.module_eval( "::#{driver_class}" ).new
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
### A little shortcut method for playing the given +note+ for the
|
85
|
+
### specified +duration+. If +note+ is an array, all notes in it are
|
86
|
+
### played as a chord.
|
87
|
+
def play( note, duration = 0.1, channel = 0, velocity = 100 )
|
88
|
+
[note].flatten.each do |n|
|
89
|
+
@driver.note_on( n, channel, velocity )
|
90
|
+
end
|
91
|
+
sleep duration
|
92
|
+
[note].flatten.each do |n|
|
93
|
+
@driver.note_off( n, channel, velocity )
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
### Does nothing for +duration+ seconds.
|
99
|
+
def rest( duration = 0.1 )
|
100
|
+
sleep duration
|
101
|
+
end
|
102
|
+
|
103
|
+
#######
|
104
|
+
private
|
105
|
+
#######
|
106
|
+
|
107
|
+
### Checks to see if the currently-loaded driver knows how to do +method+ and
|
108
|
+
### passes the message on if so. Raises an exception (as normal) if not.
|
109
|
+
def method_missing( method, *args )
|
110
|
+
raise NoMethodError, "Neither MIDIator::Interface nor #{@driver.class} " +
|
111
|
+
"has a '#{method}' method." unless @driver.respond_to? method
|
112
|
+
|
113
|
+
return @driver.send( method, *args )
|
114
|
+
end
|
104
115
|
|
105
116
|
end
|
data/lib/midiator/notes.rb
CHANGED
@@ -20,71 +20,71 @@
|
|
20
20
|
|
21
21
|
module MIDIator::Notes # this file does not rdoc well, so uhh.... :nodoc:
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
23
|
+
##########################################################################
|
24
|
+
### O C T A V E - 1 ;;; O C T A V E 0 ;;; O C T A V E 1
|
25
|
+
##########################################################################
|
26
|
+
Cn1 = 0 ; Bsn1 = 0 ;;; C0 = 12 ; Bs0 = 12 ;;; C1 = 24 ; Bs1 = 24
|
27
|
+
Csn1 = 1 ; Dbn1 = 1 ;;; Cs0 = 13 ; Db0 = 13 ;;; Cs1 = 25 ; Db1 = 25
|
28
|
+
Dn1 = 2 ; ;;; D0 = 14 ; ;;; D1 = 26 ;
|
29
|
+
Dsn1 = 3 ; Ebn1 = 3 ;;; Ds0 = 15 ; Eb0 = 15 ;;; Ds1 = 27 ; Eb1 = 27
|
30
|
+
En1 = 4 ; Fbn1 = 4 ;;; E0 = 16 ; Fb0 = 16 ;;; E1 = 28 ; Fb1 = 28
|
31
|
+
Fn1 = 5 ; ;;; F0 = 17 ; ;;; F1 = 29 ;
|
32
|
+
Fsn1 = 6 ; Gbn1 = 6 ;;; Fs0 = 18 ; Gb0 = 18 ;;; Fs1 = 30 ; Gb1 = 30
|
33
|
+
Gn1 = 7 ; ;;; G0 = 19 ; ;;; G1 = 31 ;
|
34
|
+
Gsn1 = 8 ; Abn1 = 8 ;;; Gs0 = 20 ; Ab0 = 20 ;;; Gs1 = 32 ; Ab1 = 32
|
35
|
+
An1 = 9 ; ;;; A0 = 21 ; ;;; A1 = 33 ;
|
36
|
+
Asn1 = 10 ; Bbn1 = 10 ;;; As0 = 22 ; Bb0 = 22 ;;; As1 = 34 ; Bb1 = 34
|
37
|
+
Bn1 = 11 ; Cb0 = 11 ;;; B0 = 23 ; Cb1 = 23 ;;; B1 = 35 ; Cb2 = 35
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
39
|
+
##########################################################################
|
40
|
+
### O C T A V E 2 ;;; O C T A V E 3 ;;; O C T A V E 4
|
41
|
+
##########################################################################
|
42
|
+
C2 = 36 ; Bs2 = 36 ;;; C3 = 48 ; Bs3 = 48 ;;; C4 = 60 ; Bs4 = 60
|
43
|
+
Cs2 = 37 ; Db2 = 37 ;;; Cs3 = 49 ; Db3 = 49 ;;; Cs4 = 61 ; Db4 = 61
|
44
|
+
D2 = 38 ; ;;; D3 = 50 ; ;;; D4 = 62 ;
|
45
|
+
Ds2 = 39 ; Eb2 = 39 ;;; Ds3 = 51 ; Eb3 = 51 ;;; Ds4 = 63 ; Eb4 = 63
|
46
|
+
E2 = 40 ; Fb2 = 40 ;;; E3 = 52 ; Fb3 = 52 ;;; E4 = 64 ; Fb4 = 64
|
47
|
+
F2 = 41 ; ;;; F3 = 53 ; ;;; F4 = 65 ;
|
48
|
+
Fs2 = 42 ; Gb2 = 42 ;;; Fs3 = 54 ; Gb3 = 54 ;;; Fs4 = 66 ; Gb4 = 66
|
49
|
+
G2 = 43 ; ;;; G3 = 55 ; ;;; G4 = 67 ;
|
50
|
+
Gs2 = 44 ; Ab2 = 44 ;;; Gs3 = 56 ; Ab3 = 56 ;;; Gs4 = 68 ; Ab4 = 68
|
51
|
+
A2 = 45 ; ;;; A3 = 57 ; ;;; A4 = 69 ;
|
52
|
+
As2 = 46 ; Bb2 = 46 ;;; As3 = 58 ; Bb3 = 58 ;;; As4 = 70 ; Bb4 = 70
|
53
|
+
B2 = 47 ; Cb3 = 47 ;;; B3 = 59 ; Cb4 = 59 ;;; B4 = 71 ; Cb5 = 71
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
55
|
+
##########################################################################
|
56
|
+
### O C T A V E 5 ;;; O C T A V E 6 ;;; O C T A V E 7
|
57
|
+
##########################################################################
|
58
|
+
C5 = 72 ; Bs5 = 72 ;;; C6 = 84 ; Bs6 = 84 ;;; C7 = 96 ; Bs7 = 96
|
59
|
+
Cs5 = 73 ; Db5 = 73 ;;; Cs6 = 85 ; Db6 = 85 ;;; Cs7 = 97 ; Db7 = 97
|
60
|
+
D5 = 74 ; ;;; D6 = 86 ; ;;; D7 = 98 ;
|
61
|
+
Ds5 = 75 ; Eb5 = 75 ;;; Ds6 = 87 ; Eb6 = 87 ;;; Ds7 = 99 ; Eb7 = 99
|
62
|
+
E5 = 76 ; Fb5 = 76 ;;; E6 = 88 ; Fb6 = 88 ;;; E7 = 100 ; Fb7 = 100
|
63
|
+
F5 = 77 ; ;;; F6 = 89 ; ;;; F7 = 101 ;
|
64
|
+
Fs5 = 78 ; Gb5 = 78 ;;; Fs6 = 90 ; Gb6 = 90 ;;; Fs7 = 102 ; Gb7 = 102
|
65
|
+
G5 = 79 ; ;;; G6 = 91 ; ;;; G7 = 103 ;
|
66
|
+
Gs5 = 80 ; Ab5 = 80 ;;; Gs6 = 92 ; Ab6 = 92 ;;; Gs7 = 104 ; Ab7 = 104
|
67
|
+
A5 = 81 ; ;;; A6 = 93 ; ;;; A7 = 105 ;
|
68
|
+
As5 = 82 ; Bb5 = 82 ;;; As6 = 94 ; Bb6 = 94 ;;; As7 = 106 ; Bb7 = 106
|
69
|
+
B5 = 83 ; Cb6 = 83 ;;; B6 = 95 ; Cb7 = 95 ;;; B7 = 107 ; Cb8 = 107
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
71
|
+
##########################################################################
|
72
|
+
### O C T A V E 8 ;;; O C T A V E 9
|
73
|
+
##########################################################################
|
74
|
+
C8 = 108 ; Bs8 = 108 ;;; C9 = 120 ; Bs9 = 120
|
75
|
+
Cs8 = 109 ; Db8 = 109 ;;; Cs9 = 121 ; Db9 = 121
|
76
|
+
D8 = 110 ; ;;; D9 = 122 ;
|
77
|
+
Ds8 = 111 ; Eb8 = 111 ;;; Ds9 = 123 ; Eb9 = 123
|
78
|
+
E8 = 112 ; Fb8 = 112 ;;; E9 = 124 ; Fb9 = 124
|
79
|
+
F8 = 113 ; ;;; F9 = 125 ;
|
80
|
+
Fs8 = 114 ; Gb8 = 114 ;;; Fs9 = 126 ; Gb9 = 126
|
81
|
+
G8 = 115 ; ;;; G9 = 127 ;
|
82
|
+
Gs8 = 116 ; Ab8 = 116
|
83
|
+
A8 = 117 ;
|
84
|
+
As8 = 118 ; Bb8 = 118
|
85
|
+
B8 = 119 ; Cb9 = 119
|
86
86
|
|
87
|
-
|
88
|
-
|
87
|
+
# Shortcuts!
|
88
|
+
MiddleC = 84
|
89
89
|
|
90
90
|
end
|
data/lib/midiator/timer.rb
CHANGED
@@ -15,48 +15,48 @@
|
|
15
15
|
#
|
16
16
|
|
17
17
|
class MIDIator::Timer
|
18
|
-
|
18
|
+
attr_reader :resolution, :queue, :thread
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
### Create a new Timer object that ticks every +resolution+ seconds.
|
21
|
+
def initialize( resolution )
|
22
|
+
@resolution = resolution
|
23
|
+
@queue = []
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
@thread = Thread.new do
|
26
|
+
loop do
|
27
|
+
dispatch
|
28
|
+
sleep @resolution
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
32
|
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
### Empty the queue
|
35
|
+
def flush
|
36
|
+
@queue.clear
|
37
|
+
end
|
38
38
|
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
### Add a new job to be performed at +time+.
|
41
|
+
def at( time, &block )
|
42
|
+
time = time.to_f if time.kind_of? Time
|
43
|
+
@queue.push [ time, block ]
|
44
|
+
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
#######
|
47
|
+
private
|
48
|
+
#######
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
### Check to see if there is work to perform in this timeslice and
|
51
|
+
### do it if so.
|
52
|
+
def dispatch
|
53
|
+
now = Time.now.to_f
|
54
54
|
|
55
|
-
|
56
|
-
|
55
|
+
# move "ready" work out of the queue
|
56
|
+
ready, @queue = @queue.partition {|time, proc| time <= now }
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
# call all of the "ready" jobs, passing in the time
|
59
|
+
ready.each {|time, proc| proc.call( time ) }
|
60
|
+
end
|
61
61
|
|
62
62
|
end
|