Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
sending MIDI notes to/from QB64PE to a real MIDI keyboard plugged into the PC?
#2
Just now I thought I'd ask Google Gemini and actually got an answer!
I haven't had a chance to run it (not at the computer with a MIDI keyboard) 
but here's what it came up with... 

Send MIDI notes + pitch wheel to keyboard
Prompt: Write code in QB64PE 4.0.0 running on Windows 11 to send MIDI notes and control the pitch wheel to a real MIDI keyboard plugged into the PC.  

Code snippet

Code: (Select All)
DECLARE LIBRARY "winmm.dll"
    FUNCTION midiOutOpen& (lphMidiOut&, uDeviceID&, dwCallback&, dwCallbackInstance&, dwFlags&)
    FUNCTION midiOutShortMsg& (hMidiOut&, dwMsg&)
    FUNCTION midiOutClose& (hMidiOut&)
END DECLARE LIBRARY
 
CONST MIDI_MAPPER = -1
CONST MIDI_NOTEON = &H90
CONST MIDI_NOTEOFF = &H80
CONST MIDI_PITCHWHEEL = &HE0
 
DIM midiHandle&
 
SUB OpenMidi
    DIM result&
    result& = midiOutOpen&(midiHandle&, MIDI_MAPPER, 0, 0, 0)
    IF result& <> 0 THEN
        PRINT "Could not open MIDI device."
        END
    END IF
END SUB
 
SUB CloseMidi
    IF midiHandle& <> 0 THEN
        midiOutClose& midiHandle&
        midiHandle& = 0
    END IF
END SUB
 
SUB SendMidiNote (channel%, note%, velocity%)
    DIM msg&
    IF midiHandle& = 0 THEN
        PRINT "MIDI device not open."
        EXIT SUB
    END IF
    ' Note On
    msg& = velocity% * 65536 + note% * 256 + (MIDI_NOTEON OR channel%)
    midiOutShortMsg& midiHandle&, msg&
END SUB
 
SUB SendMidiNoteOff (channel%, note%, velocity%)
    DIM msg&
    IF midiHandle& = 0 THEN
        PRINT "MIDI device not open."
        EXIT SUB
    END IF
    ' Note Off
    msg& = velocity% * 65536 + note% * 256 + (MIDI_NOTEOFF OR channel%)
    midiOutShortMsg& midiHandle&, msg&
END SUB
 
SUB SendPitchWheel (channel%, pitchWheelValue%)
    DIM lsb%, msb%
    DIM msg&
    IF midiHandle& = 0 THEN
        PRINT "MIDI device not open."
        EXIT SUB
    END IF
    lsb% = pitchWheelValue% AND &H7F
    msb% = (pitchWheelValue% \ 128) AND &H7F
    msg& = lsb% + msb% * 256 + (MIDI_PITCHWHEEL OR channel%)
    midiOutShortMsg& midiHandle&, msg&
END SUB
 
SUB PlayNoteWithPitchWheel (channel%, note%, velocity%, durationMS%, pitchWheelStart%, pitchWheelEnd%)
    DIM startTime!, currentTime!, elapsedTime!
    DIM pitchWheelValue%
    DIM currentMS%
 
    OpenMidi
    SendMidiNote channel%, note%, velocity%
    startTime! = TIMER
 
    DO WHILE (TIMER - startTime!) * 1000 < durationMS%
        currentTime! = TIMER
        elapsedTime! = (currentTime! - startTime!) * 1000
        ' Calculate pitch wheel value (linear interpolation)
        pitchWheelValue% = pitchWheelStart% + (pitchWheelEnd% - pitchWheelStart%) * (elapsedTime! / durationMS%)
        SendPitchWheel channel%, pitchWheelValue%
        'Delay to reduce cpu usage.
        currentMS% = (TIMER-startTime!)*1000
        DO WHILE ((TIMER-startTime!)*1000) < currentMS% + 1 : LOOP
    LOOP
 
    SendMidiNoteOff channel%, note%, velocity%
    SendPitchWheel channel%, 8192 ' Reset pitch wheel to center
    CloseMidi
END SUB
 
' Example Usage
OpenMidi
PlayNoteWithPitchWheel 0, 60, 100, 2000, 8192, 0 'Middle C, 2 sec, no bend to full bend down.
CloseMidi
 
END

Explanation and Key Differences (QB64PE):
  1. Library Declarations:
    • QB64PE uses DECLARE LIBRARY "winmm.dll" to import the Windows Multimedia (WinMM) API functions.
    • The function declarations are similar to VBA but use QB64PE's syntax.
  2. Data Types:
    • QB64PE uses % for integers, & for long integers, and ! for single-precision floating-point numbers.
    • Correct data types are very important for the API calls to work.
  3. Constants:
    • Constants are declared using CONST.
  4. Subroutines:
    • Subroutines are defined using SUB and END SUB.
  5. MIDI Message Construction:
    • The MIDI message construction is the same as in VBA.
  6. Pitch Wheel Handling:
    • The pitch wheel calculation and handling are identical.
  7. Timing:
    • QB64PE uses the TIMER function for timing.
    • A small delay loop is added within the PlayNoteWithPitchWheel subroutine to reduce CPU usage. This is essential, because without it, the loop will consume 100% of a CPU core.
  8. Example Usage:
    • The example usage is included directly in the main program.
  9. No DoEvents:
    • QB64PE does not have a direct equivalent of VBA's DoEvents. The delay loop is a basic way of providing time for other processes.
How to Use:
  1. Open QB64PE: Launch QB64PE.
  2. Paste Code: Paste the code into the QB64PE editor.
  3. Run Program: Press F5 to run the program.
  4. MIDI Setup: Ensure your MIDI keyboard is connected and working.
  5. Adjust Parameters: Modify the parameters in the PlayNoteWithPitchWheel call to experiment with different notes, velocities, durations, and pitch bend amounts.
Important Notes:
  • MIDI Drivers: Make sure your MIDI keyboard's drivers are correctly installed.
  • Error Handling: Add more robust error handling as needed.
  • Timing: The timing using TIMER and the delay loop might not be perfectly precise.
  • CPU Usage: The delay loop is important to prevent excessive CPU usage.
  • 64-bit: QB64PE is 64 bit, so the long integers are correct.
 
 
Read MIDI notes + pitch wheel from keyboard
Prompt: Write code in QB64PE 4.0.0 running on Windows 11 to read MIDI notes and the pitch wheel from a real MIDI keyboard plugged into the PC. 
Code snippet


Code: (Select All)
DECLARE LIBRARY "winmm.dll"
    FUNCTION midiInOpen& (lphMidiIn&, uDeviceID&, dwCallback&, dwCallbackInstance&, dwFlags&)
    FUNCTION midiInClose& (hMidiIn&)
    FUNCTION midiInStart& (hMidiIn&)
    FUNCTION midiInStop& (hMidiIn&)
    FUNCTION midiInReset& (hMidiIn&)
END DECLARE LIBRARY
 
CONST MIDI_MAPPER = -1
CONST MIDI_NOTEON = &H90
CONST MIDI_NOTEOFF = &H80
CONST MIDI_PITCHWHEEL = &HE0
CONST MM_MIM_DATA = &H3C1
CONST CALLBACK_FUNCTION = 1
 
DIM midiInHandle&
DIM midiCallbackAddress&
 
TYPE MIDIHDR
    lpData&
    dwBufferLength&
    dwBytesRecorded&
    dwUser&
    dwFlags&
    lpNext&
    dwReserved&
    dwOffset&
    dwReserved2&(3)
END TYPE
 
DIM midiBuffer%(0 TO 0) ' Single byte buffer
DIM midiHeader AS MIDIHDR
 
SUB OpenMidiInput
    DIM result&
 
    midiCallbackAddress& = VARSEG(MidiInProc) * &H10000 + VARPTR(MidiInProc) ' Callback address
 
    result& = midiInOpen&(midiInHandle&, MIDI_MAPPER, midiCallbackAddress&, 0, CALLBACK_FUNCTION)
    IF result& <> 0 THEN
        PRINT "Could not open MIDI input device."
        END
    END IF
 
    result& = midiInStart&(midiInHandle&)
    IF result& <> 0 THEN
        PRINT "Could not start MIDI input."
        midiInClose& midiInHandle&
        midiInHandle& = 0
        END
    END IF
 
    midiHeader.lpData& = VARPTR(midiBuffer%(0))
    midiHeader.dwBufferLength& = 1
    ' Add midiInAddBuffer here if needed.
END SUB
 
SUB CloseMidiInput
    IF midiInHandle& <> 0 THEN
        midiInStop& midiInHandle&
        midiInReset& midiInHandle&
        midiInClose& midiInHandle&
        midiInHandle& = 0
    END IF
END SUB
 
SUB MidiInProc (hMidiIn&, wMsg&, dwInstance&, dwParam1&, dwParam2&)
    DIM status&
    DIM data1%
    DIM data2%
    DIM channel%
    DIM note%
    DIM velocity%
    DIM pitchWheelLSB%
    DIM pitchWheelMSB%
    DIM pitchWheelValue%
 
    IF wMsg& = MM_MIM_DATA THEN
        status& = dwParam1& AND &HF0
        channel% = dwParam1& AND &HF
        data1% = (dwParam1& \ &H100) AND &H7F
        data2% = (dwParam1& \ &H10000) AND &H7F
 
        SELECT CASE status&
            CASE MIDI_NOTEON
                note% = data1%
                velocity% = data2%
                PRINT "Note On: Channel="; channel% + 1; ", Note="; note%; ", Velocity="; velocity%
            CASE MIDI_NOTEOFF
                note% = data1%
                velocity% = data2%
                PRINT "Note Off: Channel="; channel% + 1; ", Note="; note%; ", Velocity="; velocity%
            CASE MIDI_PITCHWHEEL
                pitchWheelLSB% = data1%
                pitchWheelMSB% = data2%
                pitchWheelValue% = pitchWheelLSB% + (pitchWheelMSB% * 128)
                PRINT "Pitch Wheel: Channel="; channel% + 1; ", Value="; pitchWheelValue%
            CASE ELSE
                PRINT "MIDI Message: Status="; HEX$(status&); ", Data1="; data1%; ", Data2="; data2%
        END SELECT
    END IF
END SUB
 
' Example Usage
OpenMidiInput
 
PRINT "Listening for MIDI input. Press any key to stop."
DO
    IF INKEY$ <> "" THEN EXIT DO
LOOP
 
CloseMidiInput
 
END

Key Changes and Explanations for QB64PE:
  1. Library Declarations:
    • DECLARE LIBRARY "winmm.dll" is used for WinMM API functions.
  2. Data Types:
    • Correct data types are crucial (% for integers, & for long integers).
  3. Callback Address:
    • midiCallbackAddress& = VARSEG(MidiInProc) * &H10000 + VARPTR(MidiInProc) is used to calculate the callback address. This is essential in QB64PE.
  4. MIDI Header:
    • The MIDIHDR type and midiBuffer% are used for receiving MIDI data.
  5. Opening and Closing MIDI Input:
    • OpenMidiInput and CloseMidiInput handle device opening and closing.
  6. Callback Function (MidiInProc):
    • This function receives and parses MIDI messages.
    • The SELECT CASE statement handles MIDI_NOTEON, MIDI_NOTEOFF, and MIDI_PITCHWHEEL.
  7. Message Parsing:
    • The code extracts the status byte, channel, and data bytes from dwParam1&.
    • The pitch wheel value is calculated.
  8. Example Usage:
    • The example starts MIDI input and waits for a key press to stop.
    • MIDI messages are printed to the console.
  9. Console Output:
    • PRINT statements display MIDI data in the console.
  10. No DoEvents:
    • In a simple listening loop, a tight loop is acceptable. If you need it to run in the background while the user does other things, you will need to restructure the code.
How to Use:
  1. Open QB64PE: Launch QB64PE.
  2. Paste Code: Paste the code into the editor.
  3. Run Program: Press F5.
  4. Play Keyboard: Play your MIDI keyboard. The MIDI messages will be displayed in the console.
  5. Stop: Press any key to stop the program.
  6. MIDI Setup: Ensure your MIDI keyboard is connected and working.
Reply


Messages In This Thread
RE: sending MIDI notes to/from QB64PE to a real MIDI keyboard plugged into the PC? - by madscijr - Yesterday, 08:05 PM



Users browsing this thread: 3 Guest(s)