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