123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- unit MIDI;
- {$ifdef fpc}
- {$mode objfpc}{$H+}
- {$endif}
- interface
- uses
- {$ifdef FPC}
- Interfaces,
- JWAwindows,
- {$else}
- Windows,
- {$endif}
- Forms, Classes, Messages, SysUtils, Math, Contnrs, MMSystem, Dialogs;
- const
-
- WM_MIDISYSTEM_MESSAGE = WM_USER +1;
- WM_MIDIDATA_ARRIVED = WM_USER +2;
- WM_MIM_ERROR = WM_USER +3;
- WM_MIM_LONGERROR = WM_USER +4;
- const
-
- cMySysExBufferSize = 2048;
- type
- TMIDIChannel = 1..16;
- TMIDIDataByte = 0..$7F;
- TMIDIDataWord = 0..$3FFF;
- TMIDIStatusByte = $80..$FF;
- TMIDIVelocity = TMIDIDataByte;
- TMIDIKey = TMIDIDataByte;
- TMIDINote = TMIDIKey;
- type
-
- TOnMidiInData = procedure (const aDeviceIndex: integer; const aStatus, aData1, aData2: byte) of object;
-
- TOnSysExData = procedure (const aDeviceIndex: integer; const aStream: TMemoryStream) of object;
- EMidiDevices = Exception;
-
- TMidiDevices = class
- private
- fDevices : TStringList;
- fMidiResult : MMResult;
- procedure SetMidiResult(const Value: MMResult);
- protected
- property MidiResult: MMResult read fMidiResult write SetMidiResult;
- function GetHandle(const aDeviceIndex: integer): THandle;
- public
-
- constructor Create; virtual;
-
- destructor Destroy; override;
-
- procedure Open(const aDeviceIndex: integer); virtual; abstract;
-
- procedure Close(const aDeviceIndex: integer); virtual; abstract;
-
- procedure CloseAll;
-
- function IsOpen(ADeviceIndex: Integer): Boolean;
- property Devices: TStringList read fDevices;
- end;
-
- TMidiInput = class(TMidiDevices)
- private
- fOnMidiData : TOnMidiInData;
- fOnSysExData : TOnSysExData;
- fSysExData : TObjectList;
- protected
- procedure DoSysExData( const aDeviceIndex: integer);
- public
-
- constructor Create; override;
-
- destructor Destroy; override;
-
- procedure Open( const aDeviceIndex: integer); override;
-
- procedure Close( const aDeviceIndex: integer); override;
-
- property OnMidiData: TOnMidiInData read fOnMidiData write fOnMidiData;
-
- property OnSysExData: TOnSysExData read fOnSysExData write fOnSysExData;
- end;
-
- TMidiOutput = class(TMidiDevices)
- constructor Create; override;
-
- procedure Open(const aDeviceIndex: integer); override;
-
- procedure Close(const aDeviceIndex: integer); override;
-
- procedure Send(const aDeviceIndex: integer; const aStatus, aData1, aData2: byte);
- procedure SendSystemReset( const aDeviceIndex: integer);
- procedure SendAllSoundOff( const aDeviceIndex: integer; const channel: byte);
-
- procedure SendSysEx( const aDeviceIndex: integer; const aStream: TMemoryStream); overload;
- procedure SendSysEx( const aDeviceIndex: integer; const aString: AnsiString); overload;
- end;
-
- function SysExStreamToStr( const aStream: TMemoryStream): AnsiString;
-
- procedure StrToSysExStream( const aString: AnsiString; const aStream: TMemoryStream);
-
- function MidiInput: TMidiInput;
-
- function MidiOutput: TMidiOutput;
- type
- TSysExBuffer = array[0..cMySysExBufferSize -1] of AnsiChar;
- TSysExData = class
- private
- fSysExStream: TMemoryStream;
- public
- SysExHeader: {$ifdef FPC} _midihdr {$else} TMidiHdr {$endif};
- SysExData: TSysExBuffer;
- constructor Create;
- destructor Destroy; override;
- property SysExStream: TMemoryStream read fSysExStream;
- end;
- implementation
- constructor TMidiDevices.Create;
- begin
- FDevices:= TStringList.Create;
- end;
- destructor TMidiDevices.Destroy;
- begin
- FreeAndNil( FDevices);
- inherited;
- end;
- var
- gMidiInput: TMidiInput;
- gMidiOutput: TMidiOutput;
- function MidiInput: TMidiInput;
- begin
- if not Assigned( gMidiInput)
- then gMidiInput := TMidiInput.Create;
- Result := gMidiInput;
- end;
- function MidiOutput: TMidiOutput;
- begin
- if not Assigned( gMidiOutput)
- then gMidiOutput := TMidiOutput.Create;
- Result := gMidiOutput;
- end;
- {$IFDEF FPC}
- {$ENDIF}
- procedure MidiInCallback( aMidiInHandle: PHMIDIIN; aMsg: Integer; aInstance,
- aMidiData, aTimeStamp: integer); stdcall;
- begin
- case aMsg of
- MIM_DATA:
- begin
- if Assigned(MidiInput.OnMidiData) then
- begin
- MidiInput.OnMidiData( aInstance,
- aMidiData and $000000FF,
- ( aMidiData and $0000FF00) shr 8,
- ( aMidiData and $00FF0000) shr 16);
- PostMessage( Application.MainForm.Handle, WM_MIDIDATA_ARRIVED, aInstance, aMidiData);
- end;
- end;
- MIM_LONGDATA:
- MidiInput.DoSysExData( aInstance);
- MIM_ERROR: PostMessage( Application.MainForm.Handle, WM_MIM_ERROR, aInstance, aMidiData);
- MIM_LONGERROR:
- {$ifdef MIDI_UseExceptions}
- raise Exception.Create( 'Midi In Error!');
- {$else}
- PostMessage( Application.MainForm.Handle, WM_MIM_LONGERROR, aInstance, aMidiData);
- {$endif}
- end;
- end;
- constructor TMidiInput.Create;
- var
- AvailableMIDIinputs : integer;
- lInCaps : TMidiInCaps;
- i : integer;
- begin
- inherited;
- fSysExData := TObjectList.Create(true);
- TRY
- AvailableMIDIinputs:= MidiInGetNumDevs;
- EXCEPT
- AvailableMIDIinputs:= 0;
- end;
- if AvailableMIDIinputs > 0 then
- for i:= 0 to AvailableMIDIinputs - 1 do
- begin
- MidiResult := midiInGetDevCaps(i, @lInCaps, SizeOf(TMidiInCaps));
- if MidiResult = MMSYSERR_NOERROR then
- begin
- fDevices.Add(StrPas(lInCaps.szPname));
- fSysExData.Add(TSysExData.Create);
- end;
- end;
- end;
- destructor TMidiInput.Destroy;
- begin
- FreeAndNil( fSysExData);
- inherited;
- end;
- procedure TMidiInput.Close( const aDeviceIndex: integer);
- begin
- if GetHandle( aDeviceIndex) <> 0 then
- begin
- MidiResult := midiInStop(GetHandle(aDeviceIndex));
- MidiResult := midiInReset(GetHandle(aDeviceIndex));
- MidiResult := midiInUnprepareHeader(GetHandle(aDeviceIndex),
- @TSysExData(fSysExData[aDeviceIndex]).SysExHeader, SizeOf(TMidiHdr));
- MidiResult := midiInClose(GetHandle(aDeviceIndex));
- FDevices.Objects[aDeviceIndex] := nil;
- end;
- end;
-
- procedure TMidiDevices.CloseAll;
- var
- i : integer;
- begin
- for i:= 0 to FDevices.Count - 1 do Close(i);
- end;
- procedure TMidiInput.Open( const aDeviceIndex: integer);
- var
- lSysExData : TSysExData;
- lHandle : THandle;
- begin
- if GetHandle(aDeviceIndex) <> 0 then Exit;
- MidiResult := midiInOpen( @lHandle, aDeviceIndex, cardinal(@midiInCallback),
- aDeviceIndex, CALLBACK_FUNCTION);
- fDevices.Objects[ aDeviceIndex ] := TObject(lHandle);
- lSysExData := TSysExData(fSysExData[aDeviceIndex]);
- lSysExData.SysExHeader.dwFlags := 0;
-
- MidiResult := midiInPrepareHeader(lHandle, @lSysExData.SysExHeader, SizeOf(TMidiHdr));
- MidiResult := midiInAddBuffer( lHandle, @lSysExData.SysExHeader, SizeOf(TMidiHdr));
- MidiResult := midiInStart( lHandle);
- end;
- procedure TMidiInput.DoSysExData( const aDeviceIndex: integer);
- var
- lSysExData : TSysExData;
- begin
- lSysExData := TSysExData(fSysExData[aDeviceIndex]);
- if lSysExData.SysExHeader.dwBytesRecorded = 0 then Exit;
- lSysExData.SysExStream.Write( lSysExData.SysExData,
- lSysExData.SysExHeader.dwBytesRecorded);
- if lSysExData.SysExHeader.dwFlags and MHDR_DONE = MHDR_DONE then
- begin
- lSysExData.SysExStream.Position := 0;
- if assigned(fOnSysExData)
- then fOnSysExData(aDeviceIndex, lSysExData.SysExStream);
- lSysExData.SysExStream.Clear;
- end;
- lSysExData.SysExHeader.dwBytesRecorded := 0;
-
- MidiResult := MidiInPrepareHeader( GetHandle(aDeviceIndex),
- @lSysExData.SysExHeader, SizeOf(TMidiHdr));
- MidiResult := MidiInAddBuffer( GetHandle(aDeviceIndex), @lSysExData.SysExHeader,
- SizeOf( TMidiHdr));
- end;
- constructor TMidiOutput.Create;
- var
- AvailableMIDIoutputs : integer;
- lOutCaps : TMidiOutCaps;
- i : integer;
- begin
- inherited;
- TRY
- AvailableMIDIoutputs := MidiOutGetNumDevs;
- EXCEPT
- AvailableMIDIoutputs := 0;
- end;
-
- for i:= 0 to AvailableMIDIoutputs - 1 do
- begin
- MidiResult := MidiOutGetDevCaps( i, @lOutCaps, SizeOf(TMidiOutCaps));
- fDevices.Add( lOutCaps.szPname);
- end;
- end;
- procedure TMidiOutput.Open( const aDeviceIndex: integer);
- var
- lHandle: THandle;
- begin
- {$ifndef FPC}
- inherited;
- {$endif}
-
- if GetHandle(aDeviceIndex) <> 0 then exit;
- MidiResult := midiOutOpen( @lHandle, aDeviceIndex, 0, 0, CALLBACK_NULL);
- fDevices.Objects[ aDeviceIndex ]:= TObject( lHandle);
- end;
- procedure TMidiOutput.Close( const aDeviceIndex: integer);
- begin
- {$ifndef FPC}
- inherited;
- {$endif}
- if GetHandle(aDeviceIndex) <> 0 then
- begin
- MidiResult := midiOutClose(GetHandle(aDeviceIndex));
- fDevices.Objects[ aDeviceIndex ] := nil;
- end;
- end;
- procedure TMidiOutput.Send( const aDeviceIndex: integer; const aStatus,
- aData1, aData2: byte);
- var
- lMsg: cardinal;
- begin
-
- if not Assigned( fDevices.Objects[ aDeviceIndex ])
- then exit;
-
- lMsg:= aStatus or (aData1 shl 8) or (aData2 shl 16);
- MidiResult := MidiOutShortMsg( GetHandle( aDeviceIndex), lMSG);
- end;
- procedure TMidiOutput.SendSystemReset( const aDeviceIndex: integer);
- begin
- Self.Send( aDeviceIndex, $FF, $0, $0);
- end;
- procedure TMidiOutput.SendAllSoundOff(const aDeviceIndex: integer; const channel: byte);
- begin
- Self.Send( aDeviceIndex, $b0 +channel, $78, $0);
- end;
- procedure TMidiDevices.SetMidiResult( const Value: MMResult);
- var
- lError: array[0..MAXERRORLENGTH] of AnsiChar;
- begin
- fMidiResult := Value;
- if fMidiResult <> MMSYSERR_NOERROR then
- if MidiInGetErrorText( fMidiResult, @lError, MAXERRORLENGTH) = MMSYSERR_NOERROR
- {$ifdef MIDI_UseExceptions}
- then raise EMidiDevices.Create(StrPas(lError));
- {$else}
-
-
-
- then PostMessage( Application.MainForm.Handle, WM_MIDISYSTEM_MESSAGE, fMidiResult, 0);
- {$endif}
- end;
- function TMidiDevices.GetHandle( const aDeviceIndex: integer): THandle;
- begin
- try
- if not InRange(aDeviceIndex, 0, fDevices.Count - 1) then
- raise EMidiDevices.CreateFmt( '%s: Device index out of bounds! (%d)',
- [ClassName,aDeviceIndex]);
- Result:= THandle(fDevices.Objects[ aDeviceIndex ]);
- except
- Result:= 0;
- end;
- end;
- function TMidiDevices.IsOpen(ADeviceIndex: Integer): boolean;
- begin
- Result := GetHandle(ADeviceIndex) <> 0;
- end;
- procedure TMidiOutput.SendSysEx( const aDeviceIndex: integer;
- const aString: AnsiString);
- var
- lStream: TMemoryStream;
- begin
- lStream := TMemoryStream.Create;
- try
- StrToSysExStream( aString, lStream);
- SendSysEx( aDeviceIndex, lStream);
- finally
- FreeAndNil( lStream);
- end;
- end;
- procedure TMidiOutput.SendSysEx( const aDeviceIndex: integer;
- const aStream: TMemoryStream);
- var
- lSysExHeader: TMidiHdr;
- begin
-
- if not assigned(fDevices.Objects[ aDeviceIndex ])
- then exit;
- aStream.Position := 0;
- lSysExHeader.dwBufferLength := aStream.Size;
- lSysExHeader.lpData := aStream.Memory;
- lSysExHeader.dwFlags := 0;
-
- {$ifdef DebugSysEx} ShowMessage( '0 - ' +IntToStr(MidiResult)); {$endif}
- MidiResult := midiOutPrepareHeader(GetHandle(aDeviceIndex), @lSysExHeader, SizeOf(TMidiHdr));
- {$ifdef DebugSysEx} ShowMessage( '1 - ' +IntToStr(MidiResult)); {$endif}
- MidiResult := midiOutLongMsg( GetHandle(aDeviceIndex), @lSysExHeader, SizeOf(TMidiHdr));
- {$ifdef DebugSysEx} ShowMessage( '2 - ' +IntToStr(MidiResult)); {$endif}
- MidiResult := midiOutUnprepareHeader(GetHandle(aDeviceIndex), @lSysExHeader, SizeOf(TMidiHdr));
- {$ifdef DebugSysEx} ShowMessage( '3 - ' +IntToStr(MidiResult)); {$endif}
- {$ifdef DebugSysEx} ShowMessage( '4 - ' +aStream.ReadAnsiString); {$endif}
- end;
- constructor TSysExData.Create;
- begin
- SysExHeader.dwBufferLength := cMySysExBufferSize;
- SysExHeader.lpData := SysExData;
- fSysExStream := TMemoryStream.Create;
- end;
- destructor TSysExData.Destroy;
- begin
- FreeAndNil( fSysExStream);
- end;
- function SysExStreamToStr(const aStream: TMemoryStream): AnsiString;
- var
- i : integer;
- begin
- Result := '';
- aStream.Position:= 0;
- for i:= 0 to aStream.Size - 1
- do Result := Result + Format( '%.2x ', [ byte(pAnsiChar(aStream.Memory)[i]) ]);
- end;
- procedure StrToSysExStream(const aString: AnsiString; const aStream: TMemoryStream);
- const
- cHex : AnsiString = '123456789ABCDEF';
- var
- lStr : AnsiString;
- i : integer;
- L : integer;
- begin
-
- L := length( aString);
- if not (L mod 2 = 0)
- then raise EMidiDevices.Create( 'SysEx string corrupted')
- else if l < 10
- then raise EMidiDevices.Create( 'SysEx string too short');
- lStr := StringReplace( AnsiUpperCase( aString), ' ', '', [rfReplaceAll]);
- aStream.Size := Length( lStr) div 2;
- aStream.Position := 0;
- for i:= 1 to aStream.Size do
- pAnsiChar( aStream.Memory)[i-1] :=
- AnsiChar( AnsiPos( lStr[ i*2 - 1], cHex) shl 4 + AnsiPos( lStr[i*2], cHex));
- end;
- initialization
- gMidiInput := nil;
- gMidiOutput := nil;
- finalization
- FreeAndNil( gMidiInput);
- FreeAndNil( gMidiOutput);
- end.
|