2

Expected behavior: middle C played on one midi instrunment, then another. Actual behavior: a DL deprecation warning and no sound. Running Windows 7.

The code:

require "dl/import"

class LiveMIDI
    ON = 0x90
    OFF =0x80
    PC = 0xc0

    def initialize
        open
    end

    def note_on(channel, note, velocity=64)
        message(ON | channel, note, velocity)
    end

    def note_off(channel, note, velocity=64)
        message(OFF | channel, note, velocity)
    end

    def program_change(channel, preset) 
        message(PC | channel, preset)
    end

    module C
        extend DL::Importer
        dlload "winmm"

        extern "int midiOutOpen(HMIDIOUT*, int, int, int, int)"
        extern "int midiOutClose(int)"
        extern "int midiOutShortMsg(int, int)"
    end

    def open
        @device = DL.malloc(DL::Importer.sizeof("int"))
        C.midiOutOpen(@device, -1, 0, 0, 0)
    end

    def close
        C.midiOutClose(@device.ptr.to_i)
    end

    def message(one, two=0, three=0)
        message = one + (two << 8) + (three << 16)
        C.midiOutShortMsg(DL::CPtr.to_ptr(@device).to_i, message)
    end
end

midi = LiveMIDI.new
midi.note_on(0, 60, 100)
sleep(1)
midi.note_off(0, 60)
midi.program_change(1, 40)
midi.note_on(1, 60, 100)
sleep(1)
midi.note_off(1, 60)

Taken from the book Practical Ruby Projects. By the numbers on the page, 11-15, in chapter 2. The code is slightly modified to handle changes to the Ruby DL in Ruby 1.9.

Fluffy Ribbit
  • 316
  • 1
  • 4
  • 15
  • I think it should be `DL::CPtr.malloc(DL::Importer.sizeof("int")` and `@device.ptr.to_i` instead of `DL::CPtr.to_ptr(@device).to_i`. Also some of the types are wrong and the code likely doesn't work on 64-bit Windows. I can't test it unless a possible answer using Fiddle (DL's successor) is okay for you. – cremno Sep 17 '15 at 05:20
  • Sweet! I made the changes and it worked! If you want to make those changes and post it as an answer, I'll accept it. If you show me the fiddle code, I'd also appreciate it. Thank you so much! – Fluffy Ribbit Sep 17 '15 at 06:11

1 Answers1

2

You have to write

DL::CPtr.malloc(DL::Importer.sizeof("int")

instead of

DL.malloc(DL::Importer.sizeof("int"))

to create a pointer object (DL::CPtr) and not just get an address as integer.

Also

DL::CPtr.to_ptr(@device).to_i

has to be

@device.ptr.to_i

or maybe even

@device.ptr

Here's an fixed version of your code using DL's replacement Fiddle:

require 'fiddle/import'
require 'fiddle/types'

class LiveMIDI
  ON = 0x90
  OFF = 0x80
  PC = 0xc0

  def initialize
    open
  end

  def note_on(channel, note, velocity = 64)
    message(ON | channel, note, velocity)
  end

  def note_off(channel, note, velocity = 64)
    message(OFF | channel, note, velocity)
  end

  def program_change(channel, preset)
    message(PC | channel, preset)
  end

  module C
    extend Fiddle::Importer
    dlload 'winmm'
    # defines a few Windows-specific types such as DWORD or UINT
    include Fiddle::Win32Types
    # some other types not defined by the previous line
    typealias 'HMIDIOUT', 'void*'
    typealias 'LPHMIDIOUT', 'HMIDIOUT*'
    typealias 'DWORD_PTR', 'uintptr_t'
    typealias 'MMRESULT', 'UINT'

    extern 'MMRESULT midiOutOpen(LPHMIDIOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD)'
    extern 'MMRESULT midiOutClose(HMIDIOUT)'
    extern 'MMRESULT midiOutShortMsg(HMIDIOUT, DWORD)'
  end

  def open
    @device = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
    C.midiOutOpen(@device, -1, 0, 0, 0)
  end

  def close
    C.midiOutClose(@device.ptr)
  end

  def message(one, two = 0, three = 0)
    message = one + (two << 8) + (three << 16)
    C.midiOutShortMsg(@device.ptr, message)
  end
end

It's more or less the same except for the type stuff which I've added or corrected according to the documentation on MSDN. Wrong types may lead to non-obvious issues.

cremno
  • 4,672
  • 1
  • 16
  • 27