Revisions

gist: 189610 Download_button fork
public
Description:
basic MIDI I/O for JRuby
Public Clone URL: git://gist.github.com/189610.git
Embed All Files: show embed
jr_midi.rb #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
104
105
106
107
108
# --- Very basic Midi-Implementation for JRuby (note on/off, pitchbend, controller, aftertouch), especially for use with ruby-processing, http://wiki.github.com/jashkenas/ruby-processing ---
module JavaMidi
  midi = javax.sound.midi
  import midi.MidiSystem
  import midi.MidiDevice
  import midi.MidiEvent
  import midi.ShortMessage
  import midi.Receiver
end
 
class JRmidi # Java midi Interface for JRuby (ruby-processing and others)
  # --- If you want to extent this class, use send(), send_note_on() and other send_.*-methods as a "blueprint" ---
  # --- For details on the use of Java-MIDI see: http://java.sun.com/j2se/1.5.0/docs/api/javax/sound/midi/MidiSystem.html ---
  @@RPmidiIn = Hash.new # class variable to monitor number of initialisations...
  @@RPmidiOut = Hash.new # class variable to monitor number of initialisations...
  
  def initialize( processing_instance, instance_name, params ) # establish new MIDI-in or out-connections to be used in the application
    @processing_instance = processing_instance
    @name = instance_name
    @verbose = params[:verbose]
    @midi_in_port = params[:in_port]
    @midi_out_port = params[:out_port]
 
    @midiMsg = JavaMidi::ShortMessage.new
    
    if( @@RPmidiIn[instance_name] || @@RPmidiOut[instance_name] ) # true if initialized before: avoid multiple instances with identical functionality, to be called by Processing for instance...
      @outputDevice = @@RPmidiOut[instance_name] # "reload" midi-out handle as remembered be previous instance...
      @inputDevice = @@RPmidiIn[instance_name] # "reload" midi-in handle as remembered be previous instance...
    else
      if( @midi_in_port ) # use MIDI-in for this instance?
        in_port = JavaMidi::MidiSystem.getMidiDeviceInfo[@midi_in_port]
        puts "INport: " + in_port.to_s
        @inputDevice = JavaMidi::MidiSystem.getMidiDevice(in_port)
        @inputDevice.open()
        transmit = @inputDevice.getTransmitter()
        puts transmit.to_s if @verbose
        transmit.setReceiver(self)
        @@RPmidiIn[instance_name] = @inputDevice # remember "handle" associated to the given MIDI-out, in case if instanciated more than once for midi-out-methods later
      end
      if( @midi_out_port ) # use MIDI-out for this instance?
        out_port = JavaMidi::MidiSystem.getMidiDeviceInfo[@midi_out_port]
        puts "OUTport: " + out_port.to_s
        outputDevice = JavaMidi::MidiSystem.getMidiDevice(out_port)
        outputDevice.open
        @outputDevice = outputDevice.getReceiver()
        puts @outputDevice.to_s if @verbose
        @@RPmidiOut[instance_name] = @outputDevice # remember "handle" associated to the given MIDI-out, in case if instanciated more than once for midi-out-methods later
      end
    end
  end
  
  def name; @name.to_s; end # just in case, somebody wants to retreive the "process_name" of a connected "MIDI-out-handle"...
  
  def send(msg, time_stamp) # "Interface"-method for javax.sound.midi.Receiver, "callback-method" for MIDI-input
    case msg.getCommand
      when 0x90
        @processing_instance.note_on( msg.getChannel, msg.getData1, msg.getData2 )
      when 0x80
        @processing_instance.note_off( msg.getChannel, msg.getData1, msg.getData2 )
      when 0xe0
        @processing_instance.pitch_bend( msg.getChannel, ((msg.getData2 << 7 ) | msg.getData1)-8192 )
      when 0xb0
        @processing_instance.control_change( msg.getChannel, msg.getData1, msg.getData2 )
      when 0xd0
        @processing_instance.channel_pressure( msg.getChannel, msg.getData1 ) # single data-byte message!
      else
        # puts "new MIDI-message 0x%x, channel: %d, data1: %d, data2: %d" % [msg.getCommand, msg.getChannel, msg.getData1, msg.getData2]
        return # we got a MIDI-message here, that we do not want to process...
    end
    rescue # We mainly get here, if a supported message comes in and its "callback-function" is not implemented [correctly] in the user-application
      if( @verbose )
        puts "MIDI-message 0x%x, channel: %d, data1: %d, data2: %d, not processed..." %
                                            [msg.getCommand, msg.getChannel+1, msg.getData1, msg.getData2]
      end
  end
  
  def close # "Interface"-method for javax.sound.midi.Receiver, can be called remotely to close the device
    @inputDevice.close
  end
  
  # === User-functions for MIDI-output ===
  def send_note_on( channel, note, velocity )
    @midiMsg.setMessage( 0x90, channel, note, velocity )
    @outputDevice.send(@midiMsg, -1) # timestamp is second parameter, set to "immediate"
  end
  
  def send_note_off( channel, note, velocity )
    @midiMsg.setMessage( 0x80, channel, note, velocity )
    @outputDevice.send(@midiMsg, -1)
  end
  
  def send_pitch_bend( channel, pitch )
    pitch += 8192
    @midiMsg.setMessage( 0xe0, channel, pitch & 0x7f, pitch >> 7 )
    @outputDevice.send(@midiMsg, -1)
  end
  
  def send_control_change( channel, controller, value )
    @midiMsg.setMessage( 0xb0, channel, controller, value )
    @outputDevice.send(@midiMsg, -1)
  end
   
  def send_channel_pressure( channel, touch_value )
    @midiMsg.setMessage( 0xd0, channel, touch_value, 0 )
    @outputDevice.send(@midiMsg, -1)
  end
end # class JRmidi