Need help with MIDI programming in VB.net

Started by Phil75,

Phil75

Hello,

I've programmed a small piece of software with BASS that allows you to trigger audio samples from a MIDI device.
I use several Midi devices, and all of them trigger the same Sub "PlayNote()".
Everything works fine.
But I couldn't find any other way than to number the devices to open.
Without going into too much detail, I open and start the Midi device like this:

DeviceMidiIn1 = New MidiInputDevice(1)
DeviceMidiIn1.Open()
DeviceMidiIn1.Start()
AddHandler DeviceMidiIn1.MessageReceived, AddressOf DeviceMidiIn1_MessageReceived

DeviceMidiIn2 = New MidiInputDevice(2)
DeviceMidiIn2.Open()
DeviceMidiIn2.Start()
AddHandler DeviceMidiIn2.MessageReceived, AddressOf DeviceMidiIn2_MessageReceived
...

And so, I accumulate identical Subs for the received MIDI messages:

    Private Sub DeviceMidiIn1_MessageReceived(sender As Object, e As MidiMessageEventArgs)

        Dim m As MidiShortMessage

        If e.IsSysExMessage = False Then
            If e.ShortMessage.MessageType <> MIDIMessageType.SystemRealtime Then
                If e.IsShortMessage = True Then
                    m = CType(e.ShortMessage, MidiShortMessage)
                    Call PlayNote(m.Note, m.Velocity)
                End If
            End If
        End If

    End Sub

    Private Sub DeviceMidiIn2_MessageReceived(sender As Object, e As MidiMessageEventArgs)

        Dim m As MidiShortMessage

        If e.IsSysExMessage = False Then
            If e.ShortMessage.MessageType <> MIDIMessageType.SystemRealtime Then
                If e.IsShortMessage = True Then
                    m = CType(e.ShortMessage, MidiShortMessage)
                    Call PlayNote(m.Note, m.Velocity)
                End If
            End If
        End If

    End Sub

etc...

Is there a way to avoid this accumulation of Sub "DeviceMidiInX_MessageReceived()"?

Phil75

I'm trying to create a small class to handle MIDI input devices events.

Public Class Form1

    Private a As Integer

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load

        Dim Class1obj As New clsMIDI("loopMIDI1")

        a = 4

    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Call SubTest()

    End Sub

    Public Sub SubTest()

        a = a

    End Sub

End Class

Public Class clsMIDI

    Public Sub New(DeviceName As String)

        Dim DeviceMidiIn As MidiInputDevice = Nothing
        Dim inCaps As New MIDI_INCAPS()

        Dim d As Integer
        Dim i As Integer

        inCaps = New MIDI_INCAPS
        d = Midi.MIDI_InGetNumDevs

        For i = 0 To d - 1
            Midi.MIDI_InGetDevCaps(i, inCaps)
            If inCaps.name = DeviceName Then
                DeviceMidiIn = New MidiInputDevice(i)
                DeviceMidiIn.MessageFilter = MIDIMessageType.SystemRealtime Or MIDIMessageType.SystemExclusive
                AddHandler DeviceMidiIn.MessageReceived, AddressOf DeviceMidiIn_MessageReceived
                If DeviceMidiIn.Open() Then DeviceMidiIn.Start()
            End If
        Next i

    End Sub

    Private Sub DeviceMidiIn_MessageReceived(sender As Object, e As MidiMessageEventArgs)

        If e.EventType = MidiMessageEventType.ShortMessage Then Form1.SubTest()

    End Sub

End Class

But I'm already facing a problem.
This isn't a problem with "BASS", but probably because I'm a bad programmer.

If I click on "Button1", a = 4 in the Sub "SubTest".

If I trigger a note on the MIDI device, my Class triggers the Sub "SubTest", but a = 0 !!

How is this possible ?!

Writing this does not solve the problem :

Public a As Integer

jpf

I'm not a vb.net user myself but as a vb6 user I can try to run your code in my mind.

In Form_Load You create an instance of clsMidi and run it with an argument "loopMIDI1". This I guess initializes a MIDI driver and tells it to execute the code located in the address of DeviceMidiIn_MessageReceived whenever a msg is received.

Then when Form_Load finishes executing the instance of clsMIDI you created ceases to exist  because it's local to Form_Load and the memory allocated to it is probably reclaimed by the garbage colector or overwritten.

So when the driver executes the bytes in that address the result is unpredictable.

To avoid this You could make the instance of classMIDI persistent by assigning it to a variable with class Form1 scope.

But don't take me too seriously; I'm just guessing.

Phil75

@jpf Thank you for your help  :)

If I modify the code like this :

Public Class Form1

    Dim a As Integer
    Dim Class1obj As clsMIDI

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load

        Class1obj = New clsMIDI("loopMIDI1")

        a = 4

    End Sub

...

"a" is equal to zero in the Sub "SubTest" if I trigger a MIDI note.

If I create a Module and put "Class1obj" in it :

Module Module1

    Public Class1obj As clsMIDI

End Module

"a" is equal to zero in the Sub "SubTest" if I trigger a MIDI note.

But if I put "a' in the Module :

Module Module1

    Public a As Integer

End Module

a = 4 in the Sub "SubTest" if I trigger a MIDI note.


For the problem of Subs receiving MIDI events, I tried using the same Sub for each MIDI device and it seems to work.

jpf

I'm glad that You sorted this out by yourself!
What you say makes sense. In vb6 you can't pass the address of a function what's defined inside a class either. It must be defined inside a module. I guess this happens because in vb6 You don't have the operator "this", so it's impossible to know what instance of a class the address belongs to. I didn't know vb.net has this limitation too. Thanks for the info!
It's annoying that vb6 don't display a warning when I do this kind of mistake.