midiator 0.1.0

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