midi-winmm 0.1.10
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/LICENSE +13 -0
- data/README.rdoc +46 -0
- data/lib/midi-winmm.rb +15 -0
- data/lib/midi-winmm/device.rb +81 -0
- data/lib/midi-winmm/input.rb +155 -0
- data/lib/midi-winmm/map.rb +229 -0
- data/lib/midi-winmm/output.rb +139 -0
- data/test/config.rb +12 -0
- data/test/helper.rb +37 -0
- data/test/test_input_buffer.rb +45 -0
- data/test/test_io.rb +82 -0
- metadata +67 -0
    
        data/LICENSE
    ADDED
    
    | @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            Copyright 2010-2011 Ari Russo
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 4 | 
            +
            you may not use this file except in compliance with the License.
         | 
| 5 | 
            +
            You may obtain a copy of the License at
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            http://www.apache.org/licenses/LICENSE-2.0
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Unless required by applicable law or agreed to in writing, software
         | 
| 10 | 
            +
            distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 11 | 
            +
            WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 12 | 
            +
            See the License for the specific language governing permissions and
         | 
| 13 | 
            +
            limitations under the License.
         | 
    
        data/README.rdoc
    ADDED
    
    | @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            = midi-winmm
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            == Summary
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Realtime MIDI input and output with Ruby for Windows/Cygwin.  Uses the WinMM system API
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Note that in the interest of allowing people on other platforms to utilize your code, you should consider using {unimidi}[http://github.com/arirusso/unimidi].  Unimidi is a platform independent wrapper which implements midi-winmm.  
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            == Features
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            * Input and output on multiple devices concurrently
         | 
| 12 | 
            +
            * Agnostically handle different MIDI Message types (including SysEx)
         | 
| 13 | 
            +
            * Timestamped input events
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            == Requirements
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            * {ffi}[http://github.com/ffi/ffi] (gem install ffi)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            == Install
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              gem install midi-winmm
         | 
| 22 | 
            +
            	
         | 
| 23 | 
            +
            == Examples
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            * {input}[http://github.com/arirusso/midi-winmm/blob/master/examples/input.rb]
         | 
| 26 | 
            +
            * {output}[http://github.com/arirusso/midi-winmm/blob/master/examples/output.rb]
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            ({more}[http://github.com/arirusso/midi-winmm/blob/master/examples])
         | 
| 29 | 
            +
            	
         | 
| 30 | 
            +
            == Tests
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            please see {test/config.rb}[http://github.com/arirusso/midi-winmm/blob/master/test/config.rb] before running tests
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            == Documentation
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            {rdoc}[http://rdoc.info/gems/midi-winmm]
         | 
| 37 | 
            +
            	
         | 
| 38 | 
            +
            == Author 
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            {Ari Russo}[http://github.com/arirusso] <ari.russo at gmail.com>
         | 
| 41 | 
            +
            		
         | 
| 42 | 
            +
            == License
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            Apache 2.0, See the file LICENSE
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            Copyright (c) 2010-2011 Ari Russo  
         | 
    
        data/lib/midi-winmm.rb
    ADDED
    
    | @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Set of modules and classes for interacting with MIDI functions 
         | 
| 4 | 
            +
            # of the WinMM System API
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            module MIDIWinMM
         | 
| 7 | 
            +
              VERSION = "0.1.10"
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            require 'ffi'
         | 
| 11 | 
            +
             
         | 
| 12 | 
            +
            require 'midi-winmm/device'
         | 
| 13 | 
            +
            require 'midi-winmm/input'
         | 
| 14 | 
            +
            require 'midi-winmm/map'
         | 
| 15 | 
            +
            require 'midi-winmm/output'
         | 
| @@ -0,0 +1,81 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            module MIDIWinMM
         | 
| 3 | 
            +
              
         | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # Module containing methods used by both input and output devices when using the
         | 
| 6 | 
            +
              # WinMM driver interface 
         | 
| 7 | 
            +
              #
         | 
| 8 | 
            +
              module Device
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                attr_reader :enabled, :id, :info, :name, :type
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                alias_method :enabled?, :enabled
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                def initialize(id, name, options = {}, &block)
         | 
| 15 | 
            +
                  @id = id
         | 
| 16 | 
            +
                  @info = options[:info]
         | 
| 17 | 
            +
                  @type = self.class.name.split('::').last.downcase.to_sym
         | 
| 18 | 
            +
                  @name = name
         | 
| 19 | 
            +
                end    
         | 
| 20 | 
            +
                
         | 
| 21 | 
            +
                def self.all
         | 
| 22 | 
            +
                  all_by_type.values.flatten
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
                
         | 
| 25 | 
            +
                def self.all_by_type
         | 
| 26 | 
            +
                  types = { :input => Input,
         | 
| 27 | 
            +
                          :output => Output }
         | 
| 28 | 
            +
                  available_devices = { :input => [], :output => [] }
         | 
| 29 | 
            +
                  count = 0
         | 
| 30 | 
            +
                  types.each do |type, klass|
         | 
| 31 | 
            +
                   (0..(Map::cfunc(type, :getNumDevs)-1)).each do |i|
         | 
| 32 | 
            +
                      data = Map::DeviceInfo[type].new
         | 
| 33 | 
            +
                      Map::cfunc(type, :getDevCapsA, i, data.to_ptr, Map::DeviceInfo[type].size)
         | 
| 34 | 
            +
                      name = data[:szPname].to_s
         | 
| 35 | 
            +
                      dev = klass.new(i, name, :info => data)
         | 
| 36 | 
            +
                      available_devices[type] << dev
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                  available_devices
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
                
         | 
| 42 | 
            +
                # select the first device of type <em>type</em>
         | 
| 43 | 
            +
                def self.first(type)
         | 
| 44 | 
            +
                  all_by_type[type].first
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
                
         | 
| 47 | 
            +
                # select the last device of type <em>type</em>
         | 
| 48 | 
            +
                def self.last(type)
         | 
| 49 | 
            +
                  all_by_type[type].last
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
                
         | 
| 52 | 
            +
                WinmmCallbackFlag = 0x30000 # we plan to use a callback to collect events
         | 
| 53 | 
            +
                
         | 
| 54 | 
            +
                private
         | 
| 55 | 
            +
                
         | 
| 56 | 
            +
                # High word 
         | 
| 57 | 
            +
                #   High-order byte: Not used.
         | 
| 58 | 
            +
                #   Low-order byte: Contains a second byte of MIDI data (when needed).
         | 
| 59 | 
            +
                # Low word  
         | 
| 60 | 
            +
                #   High-order byte:  Contains the first byte of MIDI data (when needed).
         | 
| 61 | 
            +
                #   Low-order byte: Contains the MIDI status.
         | 
| 62 | 
            +
                #
         | 
| 63 | 
            +
                def dwmsg_to_array_of_bytes(m)
         | 
| 64 | 
            +
                  # there has to be a better way to do this
         | 
| 65 | 
            +
                  s = []
         | 
| 66 | 
            +
                  m = m.to_s(16)
         | 
| 67 | 
            +
                  (1..m.length).step(2) { |i| s << m[(m.length-i)-1, 2].hex }
         | 
| 68 | 
            +
                  s
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
                
         | 
| 71 | 
            +
                def error?(num)
         | 
| 72 | 
            +
                  Map::error?(num)
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
                
         | 
| 75 | 
            +
                def error(num)
         | 
| 76 | 
            +
                  Map::error(num)
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
                
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
              
         | 
| 81 | 
            +
            end
         | 
| @@ -0,0 +1,155 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            module MIDIWinMM
         | 
| 3 | 
            +
              
         | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # Input device class for the WinMM driver interface 
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              class Input
         | 
| 8 | 
            +
                
         | 
| 9 | 
            +
                include Device
         | 
| 10 | 
            +
                
         | 
| 11 | 
            +
                BufferSize = 256
         | 
| 12 | 
            +
                
         | 
| 13 | 
            +
                attr_reader :buffer
         | 
| 14 | 
            +
                
         | 
| 15 | 
            +
                # initializes this device
         | 
| 16 | 
            +
                def enable(options = {}, &block)
         | 
| 17 | 
            +
                  init_input_buffer
         | 
| 18 | 
            +
                  handle_ptr = FFI::MemoryPointer.new(FFI.type_size(:int))
         | 
| 19 | 
            +
                  initialize_local_buffer
         | 
| 20 | 
            +
                  @event_callback = get_event_callback
         | 
| 21 | 
            +
                  
         | 
| 22 | 
            +
                  Map.winmm_func(:midiInOpen, handle_ptr, @id, @event_callback, 0, Device::WinmmCallbackFlag)
         | 
| 23 | 
            +
                  
         | 
| 24 | 
            +
                  @handle = handle_ptr.read_int
         | 
| 25 | 
            +
                  
         | 
| 26 | 
            +
                  Map.winmm_func(:midiInPrepareHeader, @handle, @header.pointer, @header.size)      
         | 
| 27 | 
            +
                  Map.winmm_func(:midiInAddBuffer, @handle, @header.pointer, @header.size)
         | 
| 28 | 
            +
                  Map.winmm_func(:midiInStart, @handle)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  @enabled = true
         | 
| 31 | 
            +
                  
         | 
| 32 | 
            +
                  unless block.nil?
         | 
| 33 | 
            +
                    begin
         | 
| 34 | 
            +
                      yield(self)
         | 
| 35 | 
            +
                    ensure
         | 
| 36 | 
            +
                      close
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  else
         | 
| 39 | 
            +
                    self
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                  
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
                alias_method :start, :enable
         | 
| 44 | 
            +
                alias_method :open, :enable
         | 
| 45 | 
            +
                
         | 
| 46 | 
            +
                #
         | 
| 47 | 
            +
                # returns an array of MIDI event hashes as such:
         | 
| 48 | 
            +
                # [ 
         | 
| 49 | 
            +
                #   { :data => [144, 90, 100], :timestamp => 1024 },
         | 
| 50 | 
            +
                #   { :data => [128, 90, 100], :timestamp => 1100 },
         | 
| 51 | 
            +
                #   { :data => [146, 60, 120], :timestamp => 1200 }
         | 
| 52 | 
            +
                # ]
         | 
| 53 | 
            +
                #
         | 
| 54 | 
            +
                # message data is an array of Numeric bytes
         | 
| 55 | 
            +
                #
         | 
| 56 | 
            +
                def gets
         | 
| 57 | 
            +
                  until queued_messages?
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                  msgs = queued_messages
         | 
| 60 | 
            +
                  @pointer = @buffer.length
         | 
| 61 | 
            +
                  msgs
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
                
         | 
| 64 | 
            +
                # same as gets but returns message data as string of hex digits as such:
         | 
| 65 | 
            +
                # [ 
         | 
| 66 | 
            +
                #   { :data => "904060", :timestamp => 904 },
         | 
| 67 | 
            +
                #   { :data => "804060", :timestamp => 1150 },
         | 
| 68 | 
            +
                #   { :data => "90447F", :timestamp => 1300 }
         | 
| 69 | 
            +
                # ]
         | 
| 70 | 
            +
                #
         | 
| 71 | 
            +
                #
         | 
| 72 | 
            +
                def gets_s
         | 
| 73 | 
            +
                  msgs = gets
         | 
| 74 | 
            +
                  msgs.each { |msg| msg[:data] = numeric_bytes_to_hex_string(msg[:data]) }
         | 
| 75 | 
            +
                  msgs	
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
                alias_method :gets_bytestr, :gets_s
         | 
| 78 | 
            +
                alias_method :gets_hex, :gets_s
         | 
| 79 | 
            +
                
         | 
| 80 | 
            +
                # close the device
         | 
| 81 | 
            +
                def close
         | 
| 82 | 
            +
                  Map.winmm_func(:midiInUnprepareHeader, @handle, @header.pointer, @header.size)
         | 
| 83 | 
            +
                  Map.winmm_func(:midiInStop, @handle)
         | 
| 84 | 
            +
                  Map.winmm_func(:midiInClose, @handle)
         | 
| 85 | 
            +
                  @enabled = false
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
                
         | 
| 88 | 
            +
                def self.first
         | 
| 89 | 
            +
                  Device.first(:input)
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                def self.last
         | 
| 93 | 
            +
                  Device.last(:input)
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
                
         | 
| 96 | 
            +
                def self.all
         | 
| 97 | 
            +
                  Device.all_by_type[:input]
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
                    
         | 
| 100 | 
            +
                private
         | 
| 101 | 
            +
                
         | 
| 102 | 
            +
                def queued_messages
         | 
| 103 | 
            +
                  @buffer.slice(@pointer, @buffer.length - @pointer)
         | 
| 104 | 
            +
                end
         | 
| 105 | 
            +
                
         | 
| 106 | 
            +
                def queued_messages?
         | 
| 107 | 
            +
                  @pointer < @buffer.length
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
                        
         | 
| 110 | 
            +
                # prepare the header struct where input event information is held
         | 
| 111 | 
            +
                def init_input_buffer
         | 
| 112 | 
            +
                  @header = Map::MIDIHdr.new
         | 
| 113 | 
            +
                  @header.write_data(Input::BufferSize)
         | 
| 114 | 
            +
                  @header[:dwBytesRecorded] = 0
         | 
| 115 | 
            +
                  @header[:dwFlags] = 0
         | 
| 116 | 
            +
                  @header[:dwUser] = 0
         | 
| 117 | 
            +
                  @header[:dwBufferLength] = Input::BufferSize
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
                
         | 
| 120 | 
            +
                # returns a Proc that is called when the device receives a message
         | 
| 121 | 
            +
                def get_event_callback
         | 
| 122 | 
            +
                  Proc.new do |hMidiIn,wMsg,dwInstance,dwParam1,dwParam2|
         | 
| 123 | 
            +
                    msg_type = Map::CallbackMessageTypes[wMsg] || ''
         | 
| 124 | 
            +
                    case msg_type
         | 
| 125 | 
            +
                      when :input_data then 
         | 
| 126 | 
            +
                    	  msg = { :data => dwmsg_to_array_of_bytes(dwParam1), :timestamp => dwParam2 }
         | 
| 127 | 
            +
                    	  @buffer << msg
         | 
| 128 | 
            +
                      when :input_long_data then
         | 
| 129 | 
            +
                    	  @receiving_sysex = true
         | 
| 130 | 
            +
            			      data = @header[:lpData].read_string(Input::BufferSize).gsub(/ /, '')
         | 
| 131 | 
            +
            			      unless data.eql?("")
         | 
| 132 | 
            +
            			        str = data.unpack(("C" * (data.length-1)))
         | 
| 133 | 
            +
            			        msg = { :data => str, :timestamp => dwParam2 }
         | 
| 134 | 
            +
                    	    @buffer << msg
         | 
| 135 | 
            +
                    	  end      		
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
                
         | 
| 140 | 
            +
                def initialize_local_buffer
         | 
| 141 | 
            +
                  @pointer = 0
         | 
| 142 | 
            +
                  @buffer = []
         | 
| 143 | 
            +
                  def @buffer.clear
         | 
| 144 | 
            +
                    super
         | 
| 145 | 
            +
                    @pointer = 0
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
                
         | 
| 149 | 
            +
                def numeric_bytes_to_hex_string(bytes)
         | 
| 150 | 
            +
                  bytes.map { |b| s = b.to_s(16).upcase; b < 16 ? s = "0" + s : s; s }.join
         | 
| 151 | 
            +
                end 
         | 
| 152 | 
            +
                
         | 
| 153 | 
            +
              end
         | 
| 154 | 
            +
              
         | 
| 155 | 
            +
            end
         | 
| @@ -0,0 +1,229 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            module MIDIWinMM
         | 
| 3 | 
            +
              
         | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # Module containing C function and struct binding for the WinMM 
         | 
| 6 | 
            +
              # driver interface library
         | 
| 7 | 
            +
              #
         | 
| 8 | 
            +
              module Map
         | 
| 9 | 
            +
                
         | 
| 10 | 
            +
                extend FFI::Library
         | 
| 11 | 
            +
                ffi_lib 'Winmm'
         | 
| 12 | 
            +
                ffi_convention :stdcall
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                HeaderFlags = {
         | 
| 15 | 
            +
                  0x00000001 => :done,
         | 
| 16 | 
            +
                  0x00000002 => :prepared,
         | 
| 17 | 
            +
                  0x00000004 => :inqueue,
         | 
| 18 | 
            +
                  0x00000008 => :isstream
         | 
| 19 | 
            +
                }
         | 
| 20 | 
            +
                
         | 
| 21 | 
            +
                CallbackMessageTypes = {
         | 
| 22 | 
            +
                  0x3C1 => :input_open,
         | 
| 23 | 
            +
                  0x3C2 => :input_close,
         | 
| 24 | 
            +
                  0x3C3 => :input_data,        
         | 
| 25 | 
            +
                  0x3C4 => :input_long_data,
         | 
| 26 | 
            +
                  0x3C5 => :input_error,
         | 
| 27 | 
            +
                  0x3C6 => :input_long_error,
         | 
| 28 | 
            +
                  0x3C7 => :output_open, 
         | 
| 29 | 
            +
                  0x3C8 => :output_close,
         | 
| 30 | 
            +
                  0x3C9 => :output_data,
         | 
| 31 | 
            +
                  0x3CC => :output_more_data
         | 
| 32 | 
            +
                }
         | 
| 33 | 
            +
                
         | 
| 34 | 
            +
                Errors = {
         | 
| 35 | 
            +
                  1 => "Unspecified",
         | 
| 36 | 
            +
                  2 => "Bad Device ID",
         | 
| 37 | 
            +
                  3 => "Not Enabled", 
         | 
| 38 | 
            +
                  4 => "Allocation",
         | 
| 39 | 
            +
                  5 => "Invalid Handle",
         | 
| 40 | 
            +
                  6 => "No Driver",
         | 
| 41 | 
            +
                  7 => "No Memory",
         | 
| 42 | 
            +
                  8 => "Not Supported",
         | 
| 43 | 
            +
                  9 => "Bad Error Number",
         | 
| 44 | 
            +
                  10 => "Invalid Flag",
         | 
| 45 | 
            +
                  11 => "Invalid Parameter",
         | 
| 46 | 
            +
                  12 => "Handle Busy",
         | 
| 47 | 
            +
                  13 => "Invalid Alias"
         | 
| 48 | 
            +
                }
         | 
| 49 | 
            +
                
         | 
| 50 | 
            +
                # Byte (8 bits). Declared as unsigned char.
         | 
| 51 | 
            +
                typedef :uchar, :BYTE
         | 
| 52 | 
            +
                # 32-bit unsigned integer. The range is 0 through 4,294,967,295 decimal.
         | 
| 53 | 
            +
                typedef :uint32, :DWORD
         | 
| 54 | 
            +
                # Unsigned long type for pointer precision. Use when casting a pointer to a long type.
         | 
| 55 | 
            +
                typedef :ulong, :DWORD_PTR
         | 
| 56 | 
            +
                # (L) Handle to an object. WinNT.h: #typedef PVOID HANDLE;
         | 
| 57 | 
            +
                typedef :ulong, :HANDLE
         | 
| 58 | 
            +
                # Handle for a MIDI input device.
         | 
| 59 | 
            +
                typedef :HANDLE, :HMIDIIN
         | 
| 60 | 
            +
                # Handle for a MIDI output device.
         | 
| 61 | 
            +
                typedef :HANDLE, :HMIDIOUT
         | 
| 62 | 
            +
                # system function result codes
         | 
| 63 | 
            +
                typedef :uint, :MMRESULT
         | 
| 64 | 
            +
                # Unsigned INT_PTR.
         | 
| 65 | 
            +
                typedef :uint, :UINT_PTR
         | 
| 66 | 
            +
                # 16-bit unsigned integer. The range is 0 through 65535 decimal.
         | 
| 67 | 
            +
                typedef :ushort, :WORD
         | 
| 68 | 
            +
                
         | 
| 69 | 
            +
                class MIDIEvent < FFI::Struct
         | 
| 70 | 
            +
                  layout :dwDeltaTime, :ulong,
         | 
| 71 | 
            +
                    :dwStreamID, :ulong,
         | 
| 72 | 
            +
                    :dwEvent, :ulong,
         | 
| 73 | 
            +
                    :dwParms, [:ulong, 8]
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
                
         | 
| 76 | 
            +
                class MIDIHdr < FFI::Struct
         | 
| 77 | 
            +
                  layout :lpData, :pointer,
         | 
| 78 | 
            +
                    :dwBufferLength, :DWORD,
         | 
| 79 | 
            +
                    :dwBytesRecorded, :DWORD,
         | 
| 80 | 
            +
                    :dwUser, :DWORD_PTR,
         | 
| 81 | 
            +
                    :dwFlags, :DWORD,
         | 
| 82 | 
            +
                    :lpNext, :pointer,
         | 
| 83 | 
            +
                    :reserved, :DWORD_PTR,
         | 
| 84 | 
            +
                    :dwOffset, :DWORD,
         | 
| 85 | 
            +
                    :dwReserved, :DWORD_PTR  
         | 
| 86 | 
            +
                    
         | 
| 87 | 
            +
                    def write_data(size, string = '')
         | 
| 88 | 
            +
                      ptr = FFI::MemoryPointer.new(:char, size)
         | 
| 89 | 
            +
                      blank = " " * (size-string.length-1)
         | 
| 90 | 
            +
                      ptr.put_string(0, string + blank)
         | 
| 91 | 
            +
                      self[:lpData] = ptr
         | 
| 92 | 
            +
                      self[:dwBufferLength] = string.length
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
                
         | 
| 96 | 
            +
                class MIDIInputInfo < FFI::Struct
         | 
| 97 | 
            +
                  layout :wMid, :WORD,
         | 
| 98 | 
            +
                   :wPid, :WORD,
         | 
| 99 | 
            +
                   :vDriverVersion, :ulong,
         | 
| 100 | 
            +
                   :szPname, [:char, 32],
         | 
| 101 | 
            +
                   :dwSupport, :DWORD
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
                
         | 
| 104 | 
            +
                class MIDIOutputInfo < FFI::Struct
         | 
| 105 | 
            +
                   layout :wMid, :WORD,
         | 
| 106 | 
            +
                     :wPid, :WORD,
         | 
| 107 | 
            +
                     :vDriverVersion, :ulong,
         | 
| 108 | 
            +
                     :szPname, [:char, 32],
         | 
| 109 | 
            +
                     :wTechnology, :WORD,
         | 
| 110 | 
            +
                     :wVoices, :WORD,
         | 
| 111 | 
            +
                     :wNotes, :WORD,
         | 
| 112 | 
            +
                     :wChannelMask, :WORD,
         | 
| 113 | 
            +
                     :dwSupport, :DWORD
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
                
         | 
| 116 | 
            +
                DeviceInfo = {
         | 
| 117 | 
            +
                
         | 
| 118 | 
            +
                :input => MIDIInputInfo,
         | 
| 119 | 
            +
                :output => MIDIOutputInfo
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                }
         | 
| 122 | 
            +
                
         | 
| 123 | 
            +
                # void CALLBACK MidiInProc(HMIDIIN hMidiIn,UINT wMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
         | 
| 124 | 
            +
                callback :input_callback, [:pointer, :uint, :DWORD_PTR, :DWORD_PTR, :DWORD_PTR], :void
         | 
| 125 | 
            +
                
         | 
| 126 | 
            +
                # void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
         | 
| 127 | 
            +
                callback :output_callback, [:pointer, :uint, :DWORD_PTR, :DWORD_PTR, :DWORD_PTR], :void
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                #
         | 
| 130 | 
            +
                # initialize/close devices
         | 
| 131 | 
            +
                #
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                # MMRESULT midiInOpen(LPHMIDIIN lphMidiIn, UINT_PTR uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwCallbackInstance, DWORD dwFlags)
         | 
| 134 | 
            +
                # LPHMIDIIN = *HMIDIIN
         | 
| 135 | 
            +
                attach_function :midiInOpen, [:pointer, :UINT_PTR, :input_callback, :DWORD_PTR, :DWORD], :MMRESULT
         | 
| 136 | 
            +
                
         | 
| 137 | 
            +
                # MMRESULT midiOutOpen(LPHMIDIOUT lphmo, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwCallbackInstance, DWORD dwFlags)
         | 
| 138 | 
            +
                # LPHMIDIOUT = *HMIDIOUT
         | 
| 139 | 
            +
                # :output_callback
         | 
| 140 | 
            +
                attach_function :midiOutOpen, [:pointer, :uint, :output_callback, :DWORD_PTR, :DWORD], :MMRESULT
         | 
| 141 | 
            +
                
         | 
| 142 | 
            +
                attach_function :midiInClose, [:ulong], :ulong
         | 
| 143 | 
            +
                attach_function :midiOutClose, [:ulong], :ulong
         | 
| 144 | 
            +
                attach_function :midiInReset, [:pointer], :ulong
         | 
| 145 | 
            +
                
         | 
| 146 | 
            +
                # MMRESULT midiOutReset(HMIDIOUT hmo)
         | 
| 147 | 
            +
                attach_function :midiOutReset, [:HMIDIOUT], :MMRESULT
         | 
| 148 | 
            +
                
         | 
| 149 | 
            +
                #
         | 
| 150 | 
            +
                # for message output
         | 
| 151 | 
            +
                #
         | 
| 152 | 
            +
                
         | 
| 153 | 
            +
                # MMRESULT midiOutShortMsg(HMIDIOUT hmo, DWORD dwMsg)
         | 
| 154 | 
            +
                attach_function :midiOutShortMsg, [:HMIDIOUT, :DWORD], :MMRESULT
         | 
| 155 | 
            +
                
         | 
| 156 | 
            +
                # MMRESULT midiOutLongMsg(HMIDIOUT hmo, LPMIDIHDR lpMidiOutHdr,UINT cbMidiOutHdr)
         | 
| 157 | 
            +
                # LPMIDIHDR = *MIDIHDR
         | 
| 158 | 
            +
                attach_function :midiOutLongMsg, [:HMIDIOUT, :pointer, :uint], :MMRESULT
         | 
| 159 | 
            +
                
         | 
| 160 | 
            +
                # MMRESULT midiStreamOpen(LPHMIDISTRM lphStream,LPUINT puDeviceID, DWORD cMidi, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
         | 
| 161 | 
            +
                attach_function :midiStreamOpen, [:pointer, :pointer, :DWORD, :DWORD_PTR, :DWORD_PTR, :DWORD], :MMRESULT    
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                # MMRESULT midiOutPrepareHeader(HMIDIOUT hmo, LPMIDIHDR lpMidiOutHdr, UINT cbMidiOutHdr)
         | 
| 164 | 
            +
                attach_function :midiOutPrepareHeader, [:HMIDIOUT, :pointer, :uint], :MMRESULT
         | 
| 165 | 
            +
                
         | 
| 166 | 
            +
                #MMRESULT midiOutUnprepareHeader(HMIDIOUT hmo, LPMIDIHDR lpMidiOutHdr, UINT cbMidiOutHdr)
         | 
| 167 | 
            +
                attach_function :midiOutUnprepareHeader, [:HMIDIOUT, :ulong, :uint], :MMRESULT
         | 
| 168 | 
            +
                
         | 
| 169 | 
            +
                #MMRESULT midiOutGetVolume(HMIDIOUT hmo, LPDWORD lpdwVolume)
         | 
| 170 | 
            +
                attach_function :midiOutGetVolume, [:HMIDIOUT, :pointer], :MMRESULT
         | 
| 171 | 
            +
                
         | 
| 172 | 
            +
                # MMRESULT midiOutSetVolume(HMIDIOUT hmo, DWORD dwVolume)
         | 
| 173 | 
            +
                attach_function :midiOutSetVolume, [:HMIDIOUT, :DWORD], :MMRESULT
         | 
| 174 | 
            +
                
         | 
| 175 | 
            +
                #
         | 
| 176 | 
            +
                # input
         | 
| 177 | 
            +
                #
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                # MMRESULT midiInPrepareHeader(HMIDIIN hMidiIn, LPMIDIHDR lpMidiInHdr, UINT cbMidiInHdr)
         | 
| 180 | 
            +
                attach_function :midiInPrepareHeader, [:HMIDIIN, :pointer, :uint], :MMRESULT
         | 
| 181 | 
            +
                
         | 
| 182 | 
            +
                attach_function :midiInUnprepareHeader, [:HMIDIIN, :pointer, :uint], :MMRESULT
         | 
| 183 | 
            +
                attach_function :midiInAddBuffer, [:HMIDIIN, :pointer, :uint], :MMRESULT
         | 
| 184 | 
            +
                
         | 
| 185 | 
            +
                # MMRESULT midiInStart(HMIDIIN hMidiIn)
         | 
| 186 | 
            +
                attach_function :midiInStart, [:HMIDIIN], :MMRESULT
         | 
| 187 | 
            +
                
         | 
| 188 | 
            +
                # MMRESULT midiInStop(HMIDIIN hMidiIn)
         | 
| 189 | 
            +
                attach_function :midiInStop, [:HMIDIIN], :MMRESULT
         | 
| 190 | 
            +
                
         | 
| 191 | 
            +
                #
         | 
| 192 | 
            +
                # enumerate devices
         | 
| 193 | 
            +
                #
         | 
| 194 | 
            +
                
         | 
| 195 | 
            +
                # UINT midiInGetNumDevs(void)
         | 
| 196 | 
            +
                attach_function :midiInGetNumDevs, [], :uint
         | 
| 197 | 
            +
                
         | 
| 198 | 
            +
                # UINT midiOutGetNumDevs(void)
         | 
| 199 | 
            +
                attach_function :midiOutGetNumDevs, [], :uint
         | 
| 200 | 
            +
                
         | 
| 201 | 
            +
                # MMRESULT midiInGetDevCaps(UINT_PTR uDeviceID, LPMIDIINCAPS lpMidiInCaps, UINT cbMidiInCaps);
         | 
| 202 | 
            +
                attach_function :midiInGetDevCapsA, [:UINT_PTR, :pointer, :uint], :MMRESULT
         | 
| 203 | 
            +
                
         | 
| 204 | 
            +
                attach_function :midiOutGetDevCapsA, [:UINT_PTR, :pointer, :uint], :MMRESULT
         | 
| 205 | 
            +
               
         | 
| 206 | 
            +
                # shortcut for calling winmm midi functions
         | 
| 207 | 
            +
                def self.cfunc(type, funcname, *args)
         | 
| 208 | 
            +
                  t = type.to_s.gsub(/put/,'') # convert output to out, input to in
         | 
| 209 | 
            +
                  name = funcname.to_s
         | 
| 210 | 
            +
                  name[0] = name[0,1].upcase # capitalize
         | 
| 211 | 
            +
                  t[0] = t[0,1].upcase # capitalize
         | 
| 212 | 
            +
                  self.send("midi#{t}#{name}", *args)
         | 
| 213 | 
            +
                end
         | 
| 214 | 
            +
                
         | 
| 215 | 
            +
                def self.winmm_func(name, *args)
         | 
| 216 | 
            +
                  status = self.send(name, *args)
         | 
| 217 | 
            +
                  raise "#{name.to_s}: #{error(status)}" if error?(status)
         | 
| 218 | 
            +
                end
         | 
| 219 | 
            +
                
         | 
| 220 | 
            +
                def self.error?(num)
         | 
| 221 | 
            +
                  !Map::Errors[num].nil?
         | 
| 222 | 
            +
                end
         | 
| 223 | 
            +
                  
         | 
| 224 | 
            +
                def self.error(num)
         | 
| 225 | 
            +
                  Map::Errors[num]
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
                
         | 
| 228 | 
            +
              end
         | 
| 229 | 
            +
            end
         | 
| @@ -0,0 +1,139 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            module MIDIWinMM
         | 
| 3 | 
            +
              
         | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # Output device class for the WinMM driver interface 
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              class Output
         | 
| 8 | 
            +
                
         | 
| 9 | 
            +
                include Device
         | 
| 10 | 
            +
                
         | 
| 11 | 
            +
                BufferSize = 2048
         | 
| 12 | 
            +
                
         | 
| 13 | 
            +
                attr_reader :buffer
         | 
| 14 | 
            +
               
         | 
| 15 | 
            +
                # initialize this device
         | 
| 16 | 
            +
                def enable(options = {}, &block)
         | 
| 17 | 
            +
                  init_output_buffer
         | 
| 18 | 
            +
                  Map.winmm_func(:midiOutOpen, Output::HandlePointer, @id, Output::EventCallback, 0, Device::WinmmCallbackFlag)
         | 
| 19 | 
            +
                  @handle = HandlePointer.read_int
         | 
| 20 | 
            +
                  @enabled = true
         | 
| 21 | 
            +
                  unless block.nil?
         | 
| 22 | 
            +
                    begin
         | 
| 23 | 
            +
                      yield(self)
         | 
| 24 | 
            +
                    ensure
         | 
| 25 | 
            +
                      close
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                  else
         | 
| 28 | 
            +
                    self
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
                alias_method :start, :enable
         | 
| 32 | 
            +
                alias_method :open, :enable
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                # close this device
         | 
| 35 | 
            +
                def close
         | 
| 36 | 
            +
                  Map.winmm_func(:midiOutClose, @handle)
         | 
| 37 | 
            +
                  @enabled = false
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
                
         | 
| 40 | 
            +
                # returns a hash of fixnum values { :left => n, :right => n2 } 
         | 
| 41 | 
            +
                def volume
         | 
| 42 | 
            +
                  volume = FFI::MemoryPointer.new(FFI.type_size(:ulong))
         | 
| 43 | 
            +
                  
         | 
| 44 | 
            +
                  Map.winmm_func(:midiOutGetVolume, @handle, volume)
         | 
| 45 | 
            +
                  
         | 
| 46 | 
            +
                  str = dwmsg_to_array_of_bytes(volume.read_ulong)
         | 
| 47 | 
            +
                  left = str.slice!(0,4)
         | 
| 48 | 
            +
                  
         | 
| 49 | 
            +
                  { :left => left.hex, :right => str.hex }
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
                
         | 
| 52 | 
            +
                # accepts either a hash of fixnums { :left => n, :right => n2 } or 
         | 
| 53 | 
            +
                # a single fixnum that will be applied to both channels
         | 
| 54 | 
            +
                def volume=(val)
         | 
| 55 | 
            +
                  vol = val.kind_of?(Hash) ? (val[:left] + (val[:right] << 16)) : (val + (val << 16))
         | 
| 56 | 
            +
                  Map.winmm_func(:midiOutSetVolume, @handle, vol)        
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
                
         | 
| 59 | 
            +
                def reset
         | 
| 60 | 
            +
                  Map.winmm_func(:midiOutReset, @handle)
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
                
         | 
| 63 | 
            +
                # send a message of an indeterminate type
         | 
| 64 | 
            +
                def puts(*a)
         | 
| 65 | 
            +
                  case a.first
         | 
| 66 | 
            +
                    when Array    then puts_bytes(*a.first)
         | 
| 67 | 
            +
                    when Numeric  then puts_bytes(*a) 
         | 
| 68 | 
            +
                    when String   then puts_s(*a)
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
                
         | 
| 72 | 
            +
                # send a message consisting of Numeric bytes
         | 
| 73 | 
            +
                def puts_bytes(*message_bytes)
         | 
| 74 | 
            +
                  format = "C" * message_bytes.size
         | 
| 75 | 
            +
                  
         | 
| 76 | 
            +
                  packed = message_bytes.pack(format)
         | 
| 77 | 
            +
                  data_pointer = FFI::MemoryPointer.new(message_bytes.size).put_bytes(0, packed)
         | 
| 78 | 
            +
                  
         | 
| 79 | 
            +
                  @header[:dwBufferLength] = message_bytes.size
         | 
| 80 | 
            +
                  @header[:dwBytesRecorded] = message_bytes.size
         | 
| 81 | 
            +
                  @header[:lpData] = data_pointer
         | 
| 82 | 
            +
                  
         | 
| 83 | 
            +
                  Map.winmm_func(:midiOutPrepareHeader, @handle, @header.pointer, @header.size)
         | 
| 84 | 
            +
                  
         | 
| 85 | 
            +
                  Map.winmm_func(:midiOutLongMsg, @handle, @header.pointer, @header.size)
         | 
| 86 | 
            +
                  
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
                
         | 
| 89 | 
            +
                # send a message consisisting of a String of hex digits 
         | 
| 90 | 
            +
                def puts_s(data)
         | 
| 91 | 
            +
                  data = data.dup
         | 
| 92 | 
            +
            	  output = []
         | 
| 93 | 
            +
                  until (str = data.slice!(0,2)).eql?("")
         | 
| 94 | 
            +
                  	output << str.hex
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                  puts_bytes(*output)
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
                alias_method :puts_bytestr, :puts_s
         | 
| 99 | 
            +
                alias_method :puts_hex, :puts_s
         | 
| 100 | 
            +
                
         | 
| 101 | 
            +
                def self.first
         | 
| 102 | 
            +
                  Device::first(:output)
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                def self.last
         | 
| 106 | 
            +
                  Device::last(:output)
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
                
         | 
| 109 | 
            +
                def self.all
         | 
| 110 | 
            +
                  Device.all_by_type[:output]
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
                
         | 
| 113 | 
            +
                private
         | 
| 114 | 
            +
                
         | 
| 115 | 
            +
                EventCallback = Proc.new do |hmo, wMsg, dwInstance, dwParam1, dwParam2|
         | 
| 116 | 
            +
                  msg_type = Map::CallbackMessageTypes[wMsg] || ''
         | 
| 117 | 
            +
                  if msg_type.eql?(:output_data)
         | 
| 118 | 
            +
                    header = dwParam1
         | 
| 119 | 
            +
                    handle = HandlePointer.read_int
         | 
| 120 | 
            +
                    Map.winmm_func(:midiOutUnprepareHeader, handle, header, Map::MIDIHdr.size)
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
                
         | 
| 124 | 
            +
                HandlePointer = FFI::MemoryPointer.new(FFI.type_size(:int))
         | 
| 125 | 
            +
                
         | 
| 126 | 
            +
                def init_output_buffer
         | 
| 127 | 
            +
                  @header = Map::MIDIHdr.new
         | 
| 128 | 
            +
                  d = FFI::MemoryPointer.new(:char, Output::BufferSize)
         | 
| 129 | 
            +
                  d.put_string(0, " " * (Output::BufferSize - 1)) # initialize with a string of spaces
         | 
| 130 | 
            +
                  @header[:lpData] = d.address
         | 
| 131 | 
            +
                  @header[:dwBufferLength] = Output::BufferSize
         | 
| 132 | 
            +
                  @header[:dwBytesRecorded] = 0
         | 
| 133 | 
            +
                  @header[:dwFlags] = 0
         | 
| 134 | 
            +
                  @header[:dwUser] = 0
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
                   
         | 
| 137 | 
            +
              end
         | 
| 138 | 
            +
              
         | 
| 139 | 
            +
            end
         | 
    
        data/test/config.rb
    ADDED
    
    | @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            module TestHelper::Config
         | 
| 2 | 
            +
              
         | 
| 3 | 
            +
              include MIDIWinMM
         | 
| 4 | 
            +
              
         | 
| 5 | 
            +
              # adjust these constants to suit your hardware configuration 
         | 
| 6 | 
            +
              # before running tests
         | 
| 7 | 
            +
              
         | 
| 8 | 
            +
              NumDevices = 4 # this is the total number of MIDI devices that are connected to your system
         | 
| 9 | 
            +
              TestInput = Input.first # this is the device you wish to use to test input
         | 
| 10 | 
            +
              TestOutput = Output.all[1] # likewise for output
         | 
| 11 | 
            +
              
         | 
| 12 | 
            +
            end
         | 
    
        data/test/helper.rb
    ADDED
    
    | @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            dir = File.dirname(File.expand_path(__FILE__))
         | 
| 2 | 
            +
            $LOAD_PATH.unshift dir + '/../lib'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'test/unit'
         | 
| 5 | 
            +
            require 'midi-winmm'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module TestHelper
         | 
| 8 | 
            +
              
         | 
| 9 | 
            +
              def bytestrs_to_ints(arr)
         | 
| 10 | 
            +
                data = arr.map { |m| m[:data] }.join
         | 
| 11 | 
            +
                output = []
         | 
| 12 | 
            +
                until (bytestr = data.slice!(0,2)).eql?("")
         | 
| 13 | 
            +
                  output << bytestr.hex
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
                output      
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
              
         | 
| 18 | 
            +
              # some MIDI messages
         | 
| 19 | 
            +
              VariousMIDIMessages = [
         | 
| 20 | 
            +
                [0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7], # SysEx
         | 
| 21 | 
            +
                [0x90, 100, 100], # note on
         | 
| 22 | 
            +
                [0x90, 43, 100], # note on
         | 
| 23 | 
            +
                [0x90, 76, 100], # note on
         | 
| 24 | 
            +
                [0x90, 60, 100], # note on
         | 
| 25 | 
            +
                [0x80, 100, 100] # note off
         | 
| 26 | 
            +
              ]
         | 
| 27 | 
            +
              
         | 
| 28 | 
            +
              # some MIDI messages
         | 
| 29 | 
            +
              VariousMIDIByteStrMessages = [
         | 
| 30 | 
            +
                "F04110421240007F0041F7", # SysEx
         | 
| 31 | 
            +
                "906440", # note on
         | 
| 32 | 
            +
                "804340" # note off
         | 
| 33 | 
            +
              ]
         | 
| 34 | 
            +
              
         | 
| 35 | 
            +
            end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            require File.dirname(__FILE__) + '/config'
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'helper'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class InputBufferTest < Test::Unit::TestCase
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              include MIDIWinMM
         | 
| 8 | 
            +
              include TestHelper
         | 
| 9 | 
            +
              include TestHelper::Config # before running these tests, adjust the constants in config.rb to suit your hardware setup
         | 
| 10 | 
            +
              # ** this test assumes that TestOutput is connected to TestInput
         | 
| 11 | 
            +
              
         | 
| 12 | 
            +
              def test_input_buffer
         | 
| 13 | 
            +
                sleep(1)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                messages = VariousMIDIMessages
         | 
| 16 | 
            +
                bytes = []
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                TestOutput.open do |output|
         | 
| 19 | 
            +
                  TestInput.open do |input|
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    messages.each do |msg|
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      $>.puts "sending: " + msg.inspect
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      output.puts(msg)
         | 
| 26 | 
            +
                      
         | 
| 27 | 
            +
                      bytes += msg 
         | 
| 28 | 
            +
                      
         | 
| 29 | 
            +
                      sleep(0.5)
         | 
| 30 | 
            +
                      
         | 
| 31 | 
            +
                      buffer = input.buffer.map { |m| m[:data] }.flatten
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                      $>.puts "received: " + buffer.to_s
         | 
| 34 | 
            +
                      
         | 
| 35 | 
            +
                      assert_equal(bytes, buffer)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                    
         | 
| 39 | 
            +
                    assert_equal(bytes.length, input.buffer.map { |m| m[:data] }.flatten.length)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            end
         | 
    
        data/test/test_io.rb
    ADDED
    
    | @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'helper'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class IoTest < Test::Unit::TestCase
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              include MIDIWinMM
         | 
| 8 | 
            +
              include TestHelper
         | 
| 9 | 
            +
              include TestHelper::Config # before running these tests, adjust the constants in config.rb to suit your hardware setup
         | 
| 10 | 
            +
              # ** this test assumes that TestOutput is connected to TestInput
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              def test_full_io
         | 
| 13 | 
            +
                sleep(1)
         | 
| 14 | 
            +
                messages = VariousMIDIMessages
         | 
| 15 | 
            +
                messages_arr = messages.inject { |a,b| a+b }.flatten
         | 
| 16 | 
            +
                received_arr = []
         | 
| 17 | 
            +
                pointer = 0
         | 
| 18 | 
            +
                TestOutput.open do |output|
         | 
| 19 | 
            +
                  TestInput.open do |input|
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    messages.each do |msg|
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      $>.puts "sending: " + msg.inspect
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      output.puts(msg)
         | 
| 26 | 
            +
                      sleep(1)
         | 
| 27 | 
            +
                      received = input.gets.map { |m| m[:data] }.flatten
         | 
| 28 | 
            +
                      
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                      $>.puts "received: " + received.inspect
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      assert_equal(messages_arr.slice(pointer, received.length), received)
         | 
| 33 | 
            +
                      
         | 
| 34 | 
            +
                      pointer += received.length
         | 
| 35 | 
            +
                      
         | 
| 36 | 
            +
                      received_arr += received
         | 
| 37 | 
            +
                      
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                    
         | 
| 40 | 
            +
                    assert_equal(messages_arr.length, received_arr.length)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              # ** this test assumes that TestOutput is connected to TestInput
         | 
| 47 | 
            +
              def test_full_io_bytestr
         | 
| 48 | 
            +
                sleep(1) # pause between tests
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                messages = VariousMIDIByteStrMessages
         | 
| 51 | 
            +
                messages_str = messages.join
         | 
| 52 | 
            +
                received_str = ""
         | 
| 53 | 
            +
                pointer = 0
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                TestOutput.open do |output|
         | 
| 56 | 
            +
                  TestInput.open do |input|
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    messages.each do |msg|
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                      $>.puts "sending: " + msg.inspect
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                      output.puts(msg)
         | 
| 63 | 
            +
                      sleep(1)
         | 
| 64 | 
            +
                      received = input.gets_bytestr.map { |m| m[:data] }.flatten.join
         | 
| 65 | 
            +
                      $>.puts "received: " + received.inspect
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      assert_equal(messages_str.slice(pointer, received.length), received)
         | 
| 68 | 
            +
                      
         | 
| 69 | 
            +
                      pointer += received.length
         | 
| 70 | 
            +
                      
         | 
| 71 | 
            +
                      received_str += received
         | 
| 72 | 
            +
                      
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
               
         | 
| 75 | 
            +
                    assert_equal(messages_str, received_str)
         | 
| 76 | 
            +
                    
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: midi-winmm
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.10
         | 
| 5 | 
            +
              prerelease: 
         | 
| 6 | 
            +
            platform: ruby
         | 
| 7 | 
            +
            authors:
         | 
| 8 | 
            +
            - Ari Russo
         | 
| 9 | 
            +
            autorequire: 
         | 
| 10 | 
            +
            bindir: bin
         | 
| 11 | 
            +
            cert_chain: []
         | 
| 12 | 
            +
            date: 2011-09-11 00:00:00.000000000Z
         | 
| 13 | 
            +
            dependencies:
         | 
| 14 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 15 | 
            +
              name: ffi
         | 
| 16 | 
            +
              requirement: &70311577275740 !ruby/object:Gem::Requirement
         | 
| 17 | 
            +
                none: false
         | 
| 18 | 
            +
                requirements:
         | 
| 19 | 
            +
                - - ! '>='
         | 
| 20 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 21 | 
            +
                    version: '1.0'
         | 
| 22 | 
            +
              type: :runtime
         | 
| 23 | 
            +
              prerelease: false
         | 
| 24 | 
            +
              version_requirements: *70311577275740
         | 
| 25 | 
            +
            description: Realtime MIDI IO with Ruby in Windows/Cygwin using the WinMM system API
         | 
| 26 | 
            +
            email:
         | 
| 27 | 
            +
            - ari.russo@gmail.com
         | 
| 28 | 
            +
            executables: []
         | 
| 29 | 
            +
            extensions: []
         | 
| 30 | 
            +
            extra_rdoc_files: []
         | 
| 31 | 
            +
            files:
         | 
| 32 | 
            +
            - lib/midi-winmm/device.rb
         | 
| 33 | 
            +
            - lib/midi-winmm/input.rb
         | 
| 34 | 
            +
            - lib/midi-winmm/map.rb
         | 
| 35 | 
            +
            - lib/midi-winmm/output.rb
         | 
| 36 | 
            +
            - lib/midi-winmm.rb
         | 
| 37 | 
            +
            - test/config.rb
         | 
| 38 | 
            +
            - test/helper.rb
         | 
| 39 | 
            +
            - test/test_input_buffer.rb
         | 
| 40 | 
            +
            - test/test_io.rb
         | 
| 41 | 
            +
            - LICENSE
         | 
| 42 | 
            +
            - README.rdoc
         | 
| 43 | 
            +
            homepage: http://github.com/arirusso/midi-winmm
         | 
| 44 | 
            +
            licenses: []
         | 
| 45 | 
            +
            post_install_message: 
         | 
| 46 | 
            +
            rdoc_options: []
         | 
| 47 | 
            +
            require_paths:
         | 
| 48 | 
            +
            - lib
         | 
| 49 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 50 | 
            +
              none: false
         | 
| 51 | 
            +
              requirements:
         | 
| 52 | 
            +
              - - ! '>='
         | 
| 53 | 
            +
                - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                  version: '0'
         | 
| 55 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 56 | 
            +
              none: false
         | 
| 57 | 
            +
              requirements:
         | 
| 58 | 
            +
              - - ! '>='
         | 
| 59 | 
            +
                - !ruby/object:Gem::Version
         | 
| 60 | 
            +
                  version: 1.3.6
         | 
| 61 | 
            +
            requirements: []
         | 
| 62 | 
            +
            rubyforge_project: midi-winmm
         | 
| 63 | 
            +
            rubygems_version: 1.8.6
         | 
| 64 | 
            +
            signing_key: 
         | 
| 65 | 
            +
            specification_version: 3
         | 
| 66 | 
            +
            summary: Realtime MIDI IO with Ruby in Windows/Cygwin
         | 
| 67 | 
            +
            test_files: []
         |