midiator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # A simple example that plays the chromatic scale from C4 to C6 and back
4
+ # again. Also shows how to use the MIDIator::Notes mixin.
5
+ #
6
+ # == Authors
7
+ #
8
+ # * Ben Bleything <ben@bleything.net>
9
+ #
10
+ # == Copyright
11
+ #
12
+ # Copyright (c) 2008 Ben Bleything
13
+ #
14
+ # This code released under the terms of the MIT license.
15
+ #
16
+
17
+ require 'midiator'
18
+
19
+ midi = MIDIator::Interface.new
20
+ midi.autodetect_driver
21
+
22
+ include MIDIator::Notes
23
+
24
+ scale = [ C4, Cs4, D4, Eb4, E4, F4, Fs4, G4, Gs4, A4, Bb4, B4,
25
+ C5, Cs5, D5, Eb5, E5, F5, Fs5, G5, Gs5, A5, Bb5, B5 ]
26
+
27
+ scale.each do |note|
28
+ midi.play note
29
+ end
30
+
31
+ scale.reverse.each do |note|
32
+ midi.play note
33
+ end
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # A metronome that can run at any given tempo.
4
+ #
5
+ # == Synopsis
6
+ #
7
+ # $ ./metronome
8
+ # => starts a metronome running at 120bpm
9
+ #
10
+ # $ ./metronome 180
11
+ # => starts a metronome running at 180bpm
12
+ #
13
+ # == Authors
14
+ #
15
+ # * Ben Bleything <ben@bleything.net>
16
+ #
17
+ # == Copyright
18
+ #
19
+ # Copyright (c) 2008 Ben Bleything
20
+ #
21
+ # This code released under the terms of the MIT license.
22
+ #
23
+
24
+ ########################################################################
25
+ ### S A N I T Y C H E C K S
26
+ ########################################################################
27
+
28
+ @tempo = 120.0
29
+
30
+ if input = ARGV[0]
31
+ begin
32
+ @tempo = Float( input )
33
+ rescue ArgumentError => e
34
+ $stderr.puts "'#{input}' is not a valid tempo.\n"
35
+ $stderr.puts "Please specify the tempo in beats per minute " +
36
+ "(bpm). Fractional values are allowed!"
37
+
38
+ exit 1
39
+ end
40
+ end
41
+
42
+
43
+ ########################################################################
44
+ ### M I D I A T O R S E T U P
45
+ ########################################################################
46
+
47
+ require 'midiator'
48
+ include MIDIator::Notes
49
+
50
+ @midi = MIDIator::Interface.new
51
+ @midi.autodetect_driver
52
+ @midi.program_change 0, 115 # Wood block!
53
+
54
+ # trap interrupts to properly kill the timer
55
+ Signal.trap( "INT" ) { @timer.thread.exit! }
56
+
57
+ ########################################################################
58
+ ### T I M E R S E T U P
59
+ ########################################################################
60
+
61
+ # 60 / tempo gives us the delay (in seconds) between each beat. Divide
62
+ # that again by 10 to give us our desired resolution.
63
+ @interval = 60.0 / @tempo
64
+ @timer = MIDIator::Timer.new( @interval / 10 )
65
+
66
+ def register_next_bang( time )
67
+ @timer.at( time ) do |yielded_time|
68
+ register_next_bang yielded_time + @interval
69
+ @midi.play MiddleC
70
+ end
71
+ end
72
+
73
+ puts "Starting metronome running at #{@tempo} bpm."
74
+ register_next_bang Time.now.to_f
75
+ @timer.thread.join
data/lib/midiator.rb ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # A Ruby library for interacting with system-level MIDI services.
4
+ #
5
+ # == Authors
6
+ #
7
+ # * Ben Bleything <ben@bleything.net>
8
+ #
9
+ # == Copyright
10
+ #
11
+ # Copyright (c) 2008 Ben Bleything
12
+ #
13
+ # This code released under the terms of the MIT license.
14
+ #
15
+
16
+ require 'string_extensions'
17
+
18
+ module MIDIator
19
+ VERSION = "0.1.0"
20
+ end
21
+
22
+ require 'midiator/driver'
23
+ require 'midiator/driver_registry'
24
+ require 'midiator/exceptions'
25
+ require 'midiator/interface'
26
+ require 'midiator/timer'
27
+
28
+ ##########################################################################
29
+ ### S H O R T C U T M O D U L E S
30
+ ##########################################################################
31
+ require 'midiator/drums'
32
+ require 'midiator/notes'
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # The abstractish superclass of all MIDIator MIDI drivers.
4
+ #
5
+ # == Authors
6
+ #
7
+ # * Ben Bleything <ben@bleything.net>
8
+ #
9
+ # == Copyright
10
+ #
11
+ # Copyright (c) 2008 Ben Bleything
12
+ #
13
+ # This code released under the terms of the MIT license.
14
+ #
15
+
16
+ require 'midiator'
17
+ require 'midiator/driver_registry'
18
+
19
+ class MIDIator::Driver
20
+
21
+ ##########################################################################
22
+ ### M I D I C O M M A N D C O N S T A N T S
23
+ ##########################################################################
24
+
25
+ # Note on
26
+ ON = 0x90
27
+
28
+ # Note off
29
+ OFF = 0x80
30
+
31
+ # Program change
32
+ PC = 0xc0
33
+
34
+ ##########################################################################
35
+ ### M A G I C H O O K S
36
+ ##########################################################################
37
+
38
+ ### Auto-registers subclasses of MIDIator::Driver with the driver registry.
39
+ def self::inherited( driver_class )
40
+ driver_name = driver_class.to_s.underscore
41
+ MIDIator::DriverRegistry.instance.register( driver_name, driver_class )
42
+ end
43
+
44
+
45
+ ##########################################################################
46
+ ### I N T E R F A C E A P I
47
+ ##########################################################################
48
+ # These methods are the same across all drivers and are the interface that
49
+ # MIDIator::Interface interacts with.
50
+ ##########################################################################
51
+
52
+ ### Do any pre-open setup necessary. Often will not be overridden.
53
+ def initialize
54
+ open
55
+ end
56
+
57
+
58
+ ### Shortcut to send a note_on message.
59
+ def note_on( note, channel, velocity )
60
+ message( ON | channel, note, velocity )
61
+ end
62
+
63
+
64
+ ### Shortcut to send a note_off message.
65
+ def note_off( note, channel, velocity )
66
+ message( OFF | channel, note, velocity )
67
+ end
68
+
69
+
70
+ ### Shortcut to send a program_change message.
71
+ def program_change( channel, program )
72
+ message( PC | channel, program )
73
+ end
74
+
75
+ ##########################################################################
76
+ ### D R I V E R A P I
77
+ ##########################################################################
78
+ # subclasses must implement these methods.
79
+ ##########################################################################
80
+ protected
81
+ ##########################################################################
82
+
83
+ ### Open the channel to the MIDI service.
84
+ def open
85
+ raise NotImplementedError, "You must implement #open in your driver."
86
+ end
87
+
88
+
89
+ ### Close the channel to the MIDI service.
90
+ def close
91
+ raise NotImplementedError, "You must implement #close in your driver."
92
+ end
93
+
94
+
95
+ ### Send MIDI message to the MIDI service.
96
+ def message( *args )
97
+ raise NotImplementedError, "You must implement #message in your driver."
98
+ end
99
+
100
+ end
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # A hash-like register of all loaded drivers.
4
+ #
5
+ # == Authors
6
+ #
7
+ # * Ben Bleything <ben@bleything.net>
8
+ #
9
+ # == Copyright
10
+ #
11
+ # Copyright (c) 2008 Ben Bleything
12
+ #
13
+ # This code released under the terms of the MIT license.
14
+ #
15
+
16
+ require 'singleton'
17
+ require 'midiator'
18
+
19
+ class MIDIator::DriverRegistry
20
+ include Singleton
21
+
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
+
27
+ raise ArgumentError, "Attempted to register something that is not a MIDIator::Driver" unless
28
+ klass < MIDIator::Driver
29
+
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
+
35
+ @drivers[ name ] = klass
36
+ end
37
+ alias register register_driver
38
+ alias << register_driver
39
+
40
+
41
+ ### Returns the number of drivers currently registered.
42
+ def size
43
+ return @drivers.size
44
+ end
45
+
46
+
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
+
53
+ end
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # The MIDIator driver to interact with ALSA on Linux. Taken more or less
4
+ # directly from Practical Ruby Projects.
5
+ #
6
+ # NOTE: as yet completely untested.
7
+ #
8
+ # == Authors
9
+ #
10
+ # * Topher Cyll
11
+ # * Ben Bleything <ben@bleything.net>
12
+ #
13
+ # == Copyright
14
+ #
15
+ # Copyright (c) 2008 Topher Cyll
16
+ #
17
+ # This code released under the terms of the MIT license.
18
+ #
19
+
20
+ require 'dl/import'
21
+
22
+ require 'midiator'
23
+ require 'midiator/driver'
24
+ require 'midiator/driver_registry'
25
+
26
+ class MIDIator::Driver::ALSA < MIDIator::Driver # :nodoc:
27
+ module C # :nodoc:
28
+ extend DL::Importable
29
+ dlload 'libasound.so'
30
+
31
+ extern "int snd_rawmidi_open(void*, void*, char*, int)"
32
+ extern "int snd_rawmidi_close(void*)"
33
+ extern "int snd_rawmidi_write(void*, void*, int)"
34
+ extern "int snd_rawmidi_drain(void*)"
35
+ end
36
+
37
+ def open
38
+ @output = DL::PtrData.new(nil)
39
+ C.snd_rawmidi_open(nil, @output.ref, "virtual", 0)
40
+ end
41
+
42
+ def close
43
+ C.snd_rawmidi_close(@output)
44
+ end
45
+
46
+ def message(*args)
47
+ format = "C" * args.size
48
+ bytes = args.pack(format).to_ptr
49
+ C.snd_rawmidi_write(@output, bytes, args.size)
50
+ C.snd_rawmidi_drain(@output)
51
+ end
52
+ end
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # The MIDIator driver to interact with OSX's CoreMIDI. Taken more or less
4
+ # directly from Practical Ruby Projects.
5
+ #
6
+ # == Authors
7
+ #
8
+ # * Topher Cyll
9
+ # * Ben Bleything <ben@bleything.net>
10
+ #
11
+ # == Copyright
12
+ #
13
+ # Copyright (c) 2008 Topher Cyll
14
+ #
15
+ # This code released under the terms of the MIT license.
16
+ #
17
+
18
+ require 'dl/import'
19
+
20
+ require 'midiator'
21
+ require 'midiator/driver'
22
+ require 'midiator/driver_registry'
23
+
24
+ class MIDIator::Driver::CoreMIDI < MIDIator::Driver # :nodoc:
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
+
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
+
42
+ module CF # :nodoc:
43
+ extend DL::Importable
44
+ dlload '/System/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation'
45
+
46
+ extern "void* CFStringCreateWithCString( void*, char*, int )"
47
+ end
48
+
49
+ ##########################################################################
50
+ ### D R I V E R A P I
51
+ ##########################################################################
52
+
53
+ def open
54
+ client_name = CF.cFStringCreateWithCString( nil, "RubyMIDI", 0 )
55
+ @client = DL::PtrData.new( nil )
56
+ C.mIDIClientCreate( client_name, nil, nil, @client.ref )
57
+
58
+ port_name = CF.cFStringCreateWithCString( nil, "Output", 0 )
59
+ @outport = DL::PtrData.new( nil )
60
+ C.mIDIOutputPortCreate( @client, port_name, @outport.ref )
61
+
62
+ number_of_destinations = C.mIDIGetNumberOfDestinations()
63
+ raise NoMIDIDestinations if number_of_destinations < 1
64
+ @destination = C.mIDIGetDestination( @midi_destination )
65
+ end
66
+
67
+ def close
68
+ C.mIDIClientDispose( @client )
69
+ end
70
+
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
+
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
+
80
+ C.mIDISend( @outport, @destination, packet_list )
81
+ end
82
+ end
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # The MIDIator driver to interact with Windows Multimedia. Taken more or less
4
+ # directly from Practical Ruby Projects.
5
+ #
6
+ # NOTE: as yet completely untested.
7
+ #
8
+ # == Authors
9
+ #
10
+ # * Topher Cyll
11
+ # * Ben Bleything <ben@bleything.net>
12
+ #
13
+ # == Copyright
14
+ #
15
+ # Copyright (c) 2008 Topher Cyll
16
+ #
17
+ # This code released under the terms of the MIT license.
18
+ #
19
+
20
+ require 'dl/import'
21
+
22
+ require 'midiator'
23
+ require 'midiator/driver'
24
+ require 'midiator/driver_registry'
25
+
26
+ class MIDIator::Driver::WinMM < MIDIator::Driver # :nodoc:
27
+ module C # :nodoc:
28
+ extend DL::Importable
29
+ dlload 'winmm'
30
+
31
+ extern "int midiOutOpen(HMIDIOUT*, int, int, int, int)"
32
+ extern "int midiOutClose(int)"
33
+ extern "int midiOutShortMsg(int, int)"
34
+ end
35
+
36
+ def open
37
+ @device = DL.malloc(DL.sizeof('I'))
38
+ C.midiOutOpen(@device, -1, 0, 0, 0)
39
+ end
40
+
41
+ def close
42
+ C.midiOutClose(@device.ptr.to_i)
43
+ end
44
+
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
+ end