Hier hab ich ein Midi-In-Beispiel:
es werden 2 Buffer verwendet und die Daten via RegisterMsg empfangen (CALLBACK_WINDOW)
So lassen sich AutoIt Scripte relativ einfach über einen MIDI-Controller steuern.
Spoiler anzeigen
#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <MIDI.au3>
Opt("MustDeclareVars", 1)
Opt("GUIOnEventMode", 1)
Global $hGui = GUICreate("Midi In Example", 1000, 420)
GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
Global $cCombo = GUICtrlCreateCombo("", 10, 10, 400, 20)
Global $cButton_Start = GUICtrlCreateButton("Start", 420, 10, 80, 20)
GUICtrlSetOnEvent(-1, "_Midi_Start")
Global $cButton_Stop = GUICtrlCreateButton("Stop", 510, 10, 80, 20)
GUICtrlSetOnEvent(-1, "_Midi_Stop")
GUICtrlCreateLabel("MIM_DATA", 10, 40, 100, 20)
Global $cEdit_Data = GUICtrlCreateEdit("", 10, 60, 485, 350)
GUICtrlCreateLabel("MIM_LONGDATA", 505, 40, 100, 20)
Global $cEdit_LongData = GUICtrlCreateEdit("", 505, 60, 485, 350)
GUISetState()
_Midi_Startup()
Global $tBuffer1 = _CreateBuffer()
Global $tBuffer2 = _CreateBuffer()
_GetDevices()
Global $hMidi = 0
Global $bRelease = False
Global $iDeviceCurrent = -1
GUIRegisterMsg($MM_MIM_CLOSE, "_MidiWinProc")
GUIRegisterMsg($MM_MIM_DATA, "_MidiWinProc")
GUIRegisterMsg($MM_MIM_ERROR, "_MidiWinProc")
GUIRegisterMsg($MM_MIM_LONGDATA, "_MidiWinProc")
GUIRegisterMsg($MM_MIM_LONGERROR, "_MidiWinProc")
GUIRegisterMsg($MM_MIM_MOREDATA, "_MidiWinProc")
GUIRegisterMsg($MM_MIM_OPEN, "_MidiWinProc")
While Sleep(100)
WEnd
Func _ProcessLongData($pData, $iBytes)
Local $tGetData = DllStructCreate("byte[" & $iBytes & "];", $pData)
GUICtrlSetData($cEdit_LongData, DllStructGetData($tGetData, 1) & @CRLF, 1)
EndFunc ;==>_ProcessLongData
Func _ProcessData($iMidiMessage)
Local $iLoWord = _WinAPI_LoWord($iMidiMessage)
Local $iHiWord = _WinAPI_HiWord($iMidiMessage)
Local $bStatus = BitAND($iLoWord, 0xFF) ;LoByte
Local $bData1 = BitShift($iLoWord,
;HiByte
Local $bData2 = BitAND($iHiWord, 0xFF) ;LoByte
Local $Chan = BitAND($bStatus, 0xF)
Local $MidiMsg = BitShift($bStatus, 4)
GUICtrlSetData($cEdit_Data, "StatusByte: " & $bStatus & @TAB & "DataByte1: " & $bData1 & @TAB & "DataByte2: " & $bData2 & @CRLF, 1)
[/autoit] [autoit][/autoit] [autoit]Switch $MidiMsg
[/autoit] [autoit][/autoit] [autoit]Case 0x8 ; Note Off
GUICtrlSetData($cEdit_Data, @TAB & "NoteOff: MidiChannel: " & $Chan & @TAB & "NoteNumber: " & $bData1 & @TAB & "Velocity: " & $bData2 & @CRLF, 1)
Case 0x9 ; Note On
GUICtrlSetData($cEdit_Data, @TAB & "NoteOn: MidiChannel: " & $Chan & @TAB & "NoteNumber: " & $bData1 & @TAB & "Velocity: " & $bData2 & @CRLF, 1)
Case 0xA ; Polyphonic aftertouch
GUICtrlSetData($cEdit_Data, @TAB & "Polyphonic aftertouch: MidiChannel: " & $Chan & @TAB & "NoteNumber: " & $bData1 & @TAB & "Poly Pressure: " & $bData2 & @CRLF, 1)
Case 0xB ; Control Change
GUICtrlSetData($cEdit_Data, @TAB & "Control Change: MidiChannel: " & $Chan & @TAB & "Controller: " & $bData1 & @TAB & "Value: " & $bData2 & @CRLF, 1)
Case 0xC ; Program Change
GUICtrlSetData($cEdit_Data, @TAB & "Program Change: MidiChannel: " & $Chan & @TAB & "Programm: " & $bData1 & @CRLF, 1)
Case 0xD ; Channel aftertouch
GUICtrlSetData($cEdit_Data, @TAB & "Channel aftertouch: MidiChannel: " & $Chan & @TAB & "Aftertouch : " & $bData1 & @CRLF, 1)
Case 0xE ; Pitch Bend
GUICtrlSetData($cEdit_Data, @TAB & "Pitch Bend: MidiChannel: " & $Chan & @TAB & "LSB: " & $bData1 & @TAB & "MSB: " & $bData2 & @CRLF, 1)
EndSwitch
[/autoit] [autoit][/autoit] [autoit]EndFunc ;==>_ProcessData
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]Func _MidiWinProc($hWnd, $iMsg, $wParam, $lParam)
Switch $iMsg
Case $MIM_OPEN
ConsoleWrite("> MIM_OPEN" & @CRLF)
Case $MIM_CLOSE ;hMidi Handle ab jetzt ungültig
ConsoleWrite("> MIM_CLOSE" & @CRLF)
$hMidi = 0
Case $MIM_DATA ; Normale Midi MSG
ConsoleWrite("> MIM_DATA" & @CRLF)
_ProcessData($lParam)
Case $MIM_MOREDATA
ConsoleWrite("> MIM_MOREDATA" & @CRLF)
Case $MIM_LONGDATA
ConsoleWrite("> MIM_LONGDATA" & @CRLF)
Switch $bRelease
Case False ; SysEx Daten kommen rein
Local $tMidiHDR = DllStructCreate($MIDIHDR, $lParam)
Local $iBytesRecorded = DllStructGetData($tMidiHDR, "dwBytesRecorded")
ConsoleWrite("+ LONGDATA IN: " & $iBytesRecorded & " bytes" & @CRLF)
_ProcessLongData(DllStructGetData($tMidiHDR, "lpData"), $iBytesRecorded)
[/autoit] [autoit][/autoit] [autoit]_MidiIn_AddBuffer($hMidi, $lParam, 48)
[/autoit] [autoit][/autoit] [autoit]Case Else ; Druch _MidiIn_Reset wird hier der Buffer zurückgegeben und muss hier "unprepared" werden
ConsoleWrite("! Unprepare Buffer" & @CRLF)
_MidiIn_UnprepareHeader($hMidi, $lParam, 48)
EndSwitch
[/autoit] [autoit][/autoit] [autoit]Case $MIM_ERROR
ConsoleWrite("> MIM_ERROR" & @CRLF)
Case $MIM_LONGERROR
ConsoleWrite("> MIM_LONGERROR" & @CRLF)
_MidiIn_AddBuffer($hMidi, $lParam, 48)
EndSwitch
EndFunc ;==>_MidiWinProc
Func _Midi_Start()
ConsoleWrite(@CRLF & "!=============================" & @CRLF)
ConsoleWrite("! starting..." & @CRLF & @CRLF)
Local $aRegExp = StringRegExp(GUICtrlRead($cCombo), "<(\d+)>", 3)
If @error Or Not IsArray($aRegExp) Then Return
Local $iDevice = $aRegExp[0]
If $iDevice = $iDeviceCurrent Then
ConsoleWrite("- already running" & @CRLF)
Return
EndIf
$bRelease = False
[/autoit] [autoit][/autoit] [autoit]Local $iRet
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]If $hMidi <> 0 Then _Midi_Stop()
$hMidi = _MidiIn_Open($iDevice, $hGui, 0, BitOR($CALLBACK_WINDOW, $MIDI_IO_STATUS))
ConsoleWrite("! MidiIn Open: " & $hMidi & " error: " & @error & @CRLF)
$iDeviceCurrent = $iDevice
$iRet = _MidiIn_PrepareHeader($hMidi, DllStructGetPtr($tBuffer1, "lpData"), 48)
ConsoleWrite("! Prepare Header: " & $iRet & " error: " & @error & @CRLF)
$iRet = _MidiIn_PrepareHeader($hMidi, DllStructGetPtr($tBuffer2, "lpData"), 48)
ConsoleWrite("! Prepare Header: " & $iRet & " error: " & @error & @CRLF)
$iRet = _MidiIn_AddBuffer($hMidi, DllStructGetPtr($tBuffer2, "lpData"), 48)
ConsoleWrite("! Add Buffer: " & $iRet & " error: " & @error & @CRLF)
$iRet = _MidiIn_AddBuffer($hMidi, DllStructGetPtr($tBuffer1, "lpData"), 48)
ConsoleWrite("! Add Buffer: " & $iRet & " error: " & @error & @CRLF)
$iRet = _MidiIn_Start($hMidi)
ConsoleWrite("! MidiIn Start: " & $iRet & " error: " & @error & @CRLF)
EndFunc ;==>_Midi_Start
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]Func _Midi_Stop()
ConsoleWrite(@CRLF & "!=============================" & @CRLF)
ConsoleWrite("! stopping..." & @CRLF & @CRLF)
$bRelease = True
[/autoit] [autoit][/autoit] [autoit]Local $iRet
$iRet = _MidiIn_Reset($hMidi)
ConsoleWrite("! MidiIn Reset: " & $iRet & " error: " & @error & @CRLF)
$iRet = _MidiIn_Stop($hMidi)
ConsoleWrite("! MidiIn Stop: " & $iRet & " error: " & @error & @CRLF)
$iRet = _MidiIn_Close($hMidi)
ConsoleWrite("! MidiIn Close: " & $iRet & " error: " & @error & @CRLF)
$iDeviceCurrent = -1
EndFunc ;==>_Midi_Stop
Func _GetDevices()
Local $tMidiInCaps = DllStructCreate($MIDIINCAPS)
Local $pMidiInCaps = DllStructGetPtr($tMidiInCaps)
Local $iMidiInCaps = DllStructGetSize($tMidiInCaps)
Local $iDeviceCnt = _MidiIn_GetNumDevs()
Local $sDevice = ""
Local $sDefault = ""
For $i = 0 To ($iDeviceCnt - 1)
_MidiIn_GetDevCaps($i, $pMidiInCaps, $iMidiInCaps)
$sDevice &= "<" & $i & "> " & DllStructGetData($tMidiInCaps, "szPname") & "|"
If $i = 0 Then $sDefault = "<" & $i & "> " & DllStructGetData($tMidiInCaps, "szPname")
Next
GUICtrlSetData($cCombo, $sDevice, $sDefault)
EndFunc ;==>_GetDevices
Func _CreateBuffer($iSize = 256, $iID = 0)
Local $tBuffer = DllStructCreate("uint BufferSize; byte BufferData[" & $iSize & "];" & $MIDIHDR)
DllStructSetData($tBuffer, "BufferSize", $iSize)
DllStructSetData($tBuffer, "lpData", DllStructGetPtr($tBuffer, "BufferData"))
DllStructSetData($tBuffer, "dwBufferLength", $iSize)
DllStructSetData($tBuffer, "dwUser", $iID)
Return $tBuffer
EndFunc ;==>_CreateBuffer
Func _Exit()
Exit
EndFunc ;==>_Exit
Ich hab auch die MIDI.au3 etwas angepasst:
und zwar die DllCall-Return Variable: Local $aResult
und eine errorabfrage
Ich würde auch immer $aResult[0] als Rückgabewert verwenden:
[autoit]Switch $aResult[0]
Case 0 ;$MMSYSERR_NOERROR
Return $aResult[0]
Case Else
Return SetError(1, 0, $aResult[0])
EndSwitch
Falls du dein System beibehalten willst, dann verwende aber trotzdem Switch statt If:
[autoit]Switch $aResult[0]
Case $MMSYSERR_NOERROR
Return 1
Case $MMSYSERR_INVALHANDLE
Return SetError(1,1,0)
Case $MMSYSERR_INVALPARAM
Return SetError(2,2,0)
Case $MMSYSERR_NOMEM
Return SetError(3,3,0)
Case Else
Return SetError(4,4,0)
EndSwitch
E