From 70fb72e89b2ee068ee3a8585b21dd4e32100d23e Mon Sep 17 00:00:00 2001 From: k-m_schindler Date: Sat, 30 Nov 2013 21:59:39 +0000 Subject: rename filenames and unify naming. No code change. git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@3009 b956fd51-792f-4845-bead-9b4dfca2ff2c --- src/lib/midi/CIRCBUF.PAS | 183 ------------ src/lib/midi/CircBuf.pas | 183 ++++++++++++ src/lib/midi/DELPHMCB.PAS | 140 --------- src/lib/midi/DelphiMcb.pas | 140 +++++++++ src/lib/midi/MIDIDEFS.PAS | 55 ---- src/lib/midi/MIDITYPE.PAS | 90 ------ src/lib/midi/MidiComp.cfg | 35 +++ src/lib/midi/MidiComp.dpk | 45 +++ src/lib/midi/MidiComp.res | Bin 0 -> 876 bytes src/lib/midi/MidiCons.pas | 47 +++ src/lib/midi/MidiDefs.pas | 55 ++++ src/lib/midi/MidiIn.pas | 720 +++++++++++++++++++++++++++++++++++++++++++++ src/lib/midi/MidiOut.pas | 612 ++++++++++++++++++++++++++++++++++++++ src/lib/midi/MidiType.pas | 90 ++++++ src/lib/midi/Midicons.pas | 47 --- src/lib/midi/Midiin.pas | 720 --------------------------------------------- src/lib/midi/Midiout.pas | 612 -------------------------------------- src/lib/midi/midiComp.cfg | 35 --- src/lib/midi/midiComp.dpk | 45 --- src/lib/midi/midiComp.res | Bin 876 -> 0 bytes 20 files changed, 1927 insertions(+), 1927 deletions(-) delete mode 100644 src/lib/midi/CIRCBUF.PAS create mode 100644 src/lib/midi/CircBuf.pas delete mode 100644 src/lib/midi/DELPHMCB.PAS create mode 100644 src/lib/midi/DelphiMcb.pas delete mode 100644 src/lib/midi/MIDIDEFS.PAS delete mode 100644 src/lib/midi/MIDITYPE.PAS create mode 100644 src/lib/midi/MidiComp.cfg create mode 100644 src/lib/midi/MidiComp.dpk create mode 100644 src/lib/midi/MidiComp.res create mode 100644 src/lib/midi/MidiCons.pas create mode 100644 src/lib/midi/MidiDefs.pas create mode 100644 src/lib/midi/MidiIn.pas create mode 100644 src/lib/midi/MidiOut.pas create mode 100644 src/lib/midi/MidiType.pas delete mode 100644 src/lib/midi/Midicons.pas delete mode 100644 src/lib/midi/Midiin.pas delete mode 100644 src/lib/midi/Midiout.pas delete mode 100644 src/lib/midi/midiComp.cfg delete mode 100644 src/lib/midi/midiComp.dpk delete mode 100644 src/lib/midi/midiComp.res (limited to 'src') diff --git a/src/lib/midi/CIRCBUF.PAS b/src/lib/midi/CIRCBUF.PAS deleted file mode 100644 index 3ceb4c6e..00000000 --- a/src/lib/midi/CIRCBUF.PAS +++ /dev/null @@ -1,183 +0,0 @@ -{ $Header: /MidiComp/CIRCBUF.PAS 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - - -{ A First-In First-Out circular buffer. - Port of circbuf.c from Microsoft's Windows MIDI monitor example. - I did do a version of this as an object (see Rev 1.1) but it was getting too - complicated and I couldn't see any real benefits to it so I dumped it - for an ordinary memory buffer with pointers. - - This unit is a bit C-like, everything is done with pointers and extensive - use is made of the undocumented feature of the Inc() function that - increments pointers by the size of the object pointed to. - All of this could probably be done using Pascal array notation with - range-checking turned off, but I'm not sure it's worth it. -} - -Unit Circbuf; - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -Uses - Windows, - MMSystem; - -type - { MIDI input event } - TMidiBufferItem = record - timestamp: DWORD; { Timestamp in milliseconds after midiInStart } - data: DWORD; { MIDI message received } - sysex: PMidiHdr; { Pointer to sysex MIDIHDR, nil if not sysex } - end; - PMidiBufferItem = ^TMidiBufferItem; - - { MIDI input buffer } - TCircularBuffer = record - RecordHandle: HGLOBAL; { Windows memory handle for this record } - BufferHandle: HGLOBAL; { Windows memory handle for the buffer } - pStart: PMidiBufferItem; { ptr to start of buffer } - pEnd: PMidiBufferItem; { ptr to end of buffer } - pNextPut: PMidiBufferItem; { next location to fill } - pNextGet: PMidiBufferItem; { next location to empty } - Error: Word; { error code from MMSYSTEM functions } - Capacity: Word; { buffer size (in TMidiBufferItems) } - EventCount: Word; { Number of events in buffer } - end; - - PCircularBuffer = ^TCircularBuffer; - -function GlobalSharedLockedAlloc( Capacity: Word; var hMem: HGLOBAL ): Pointer; -procedure GlobalSharedLockedFree( hMem: HGLOBAL; ptr: Pointer ); - -function CircbufAlloc( Capacity: Word ): PCircularBuffer; -procedure CircbufFree( PBuffer: PCircularBuffer ); -function CircbufRemoveEvent( PBuffer: PCircularBuffer ): Boolean; -function CircbufReadEvent( PBuffer: PCircularBuffer; PEvent: PMidiBufferItem ): Boolean; -{ Note: The PutEvent function is in the DLL } - -implementation - -{ Allocates in global shared memory, returns pointer and handle } -function GlobalSharedLockedAlloc( Capacity: Word; var hMem: HGLOBAL ): Pointer; -var - ptr: Pointer; -begin - { Allocate the buffer memory } - hMem := GlobalAlloc(GMEM_SHARE Or GMEM_MOVEABLE Or GMEM_ZEROINIT, Capacity ); - - if (hMem = 0) then - ptr := Nil - else - begin - ptr := GlobalLock(hMem); - if (ptr = Nil) then - GlobalFree(hMem); - end; - - GlobalSharedLockedAlloc := Ptr; -end; - -procedure GlobalSharedLockedFree( hMem: HGLOBAL; ptr: Pointer ); -begin - if (hMem <> 0) then - begin - GlobalUnlock(hMem); - GlobalFree(hMem); - end; -end; - -function CircbufAlloc( Capacity: Word ): PCircularBuffer; -var - NewCircularBuffer: PCircularBuffer; - NewMIDIBuffer: PMidiBufferItem; - hMem: HGLOBAL; -begin - { TODO: Validate circbuf size, <64K } - NewCircularBuffer := - GlobalSharedLockedAlloc( Sizeof(TCircularBuffer), hMem ); - if (NewCircularBuffer <> Nil) then - begin - NewCircularBuffer^.RecordHandle := hMem; - NewMIDIBuffer := - GlobalSharedLockedAlloc( Capacity * Sizeof(TMidiBufferItem), hMem ); - if (NewMIDIBuffer = Nil) then - begin - { TODO: Exception here? } - GlobalSharedLockedFree( NewCircularBuffer^.RecordHandle, - NewCircularBuffer ); - NewCircularBuffer := Nil; - end - else - begin - NewCircularBuffer^.pStart := NewMidiBuffer; - { Point to item at end of buffer } - NewCircularBuffer^.pEnd := NewMidiBuffer; - Inc(NewCircularBuffer^.pEnd, Capacity); - { Start off the get and put pointers in the same position. These - will get out of sync as the interrupts start rolling in } - NewCircularBuffer^.pNextPut := NewMidiBuffer; - NewCircularBuffer^.pNextGet := NewMidiBuffer; - NewCircularBuffer^.Error := 0; - NewCircularBuffer^.Capacity := Capacity; - NewCircularBuffer^.EventCount := 0; - end; - end; - CircbufAlloc := NewCircularBuffer; -end; - -procedure CircbufFree( pBuffer: PCircularBuffer ); -begin - if (pBuffer <> Nil) then - begin - GlobalSharedLockedFree(pBuffer^.BufferHandle, pBuffer^.pStart); - GlobalSharedLockedFree(pBuffer^.RecordHandle, pBuffer); - end; -end; - -{ Reads first event in queue without removing it. - Returns true if successful, False if no events in queue } -function CircbufReadEvent( PBuffer: PCircularBuffer; PEvent: PMidiBufferItem ): Boolean; -var - PCurrentEvent: PMidiBufferItem; -begin - if (PBuffer^.EventCount <= 0) then - CircbufReadEvent := False - else - begin - PCurrentEvent := PBuffer^.PNextget; - - { Copy the object from the "tail" of the buffer to the caller's object } - PEvent^.Timestamp := PCurrentEvent^.Timestamp; - PEvent^.Data := PCurrentEvent^.Data; - PEvent^.Sysex := PCurrentEvent^.Sysex; - CircbufReadEvent := True; - end; -end; - -{ Remove current event from the queue } -function CircbufRemoveEvent(PBuffer: PCircularBuffer): Boolean; -begin - if (PBuffer^.EventCount > 0) then - begin - Dec( Pbuffer^.EventCount); - - { Advance the buffer pointer, with wrap } - Inc( Pbuffer^.PNextGet ); - If (PBuffer^.PNextGet = PBuffer^.PEnd) then - PBuffer^.PNextGet := PBuffer^.PStart; - - CircbufRemoveEvent := True; - end - else - CircbufRemoveEvent := False; -end; - -end. diff --git a/src/lib/midi/CircBuf.pas b/src/lib/midi/CircBuf.pas new file mode 100644 index 00000000..571d1ee4 --- /dev/null +++ b/src/lib/midi/CircBuf.pas @@ -0,0 +1,183 @@ +{ $Header: /MidiComp/CircBuf.pas 2 10/06/97 7:33 Davec $ } + +{ Written by David Churcher , + released to the public domain. } + + +{ A First-In First-Out circular buffer. + Port of circbuf.c from Microsoft's Windows MIDI monitor example. + I did do a version of this as an object (see Rev 1.1) but it was getting too + complicated and I couldn't see any real benefits to it so I dumped it + for an ordinary memory buffer with pointers. + + This unit is a bit C-like, everything is done with pointers and extensive + use is made of the undocumented feature of the Inc() function that + increments pointers by the size of the object pointed to. + All of this could probably be done using Pascal array notation with + range-checking turned off, but I'm not sure it's worth it. +} + +Unit CircBuf; + +interface + +{$IFDEF FPC} + {$MODE Delphi} + {$H+} // use long strings +{$ENDIF} + +Uses + Windows, + MMSystem; + +type + { MIDI input event } + TMidiBufferItem = record + timestamp: DWORD; { Timestamp in milliseconds after midiInStart } + data: DWORD; { MIDI message received } + sysex: PMidiHdr; { Pointer to sysex MIDIHDR, nil if not sysex } + end; + PMidiBufferItem = ^TMidiBufferItem; + + { MIDI input buffer } + TCircularBuffer = record + RecordHandle: HGLOBAL; { Windows memory handle for this record } + BufferHandle: HGLOBAL; { Windows memory handle for the buffer } + pStart: PMidiBufferItem; { ptr to start of buffer } + pEnd: PMidiBufferItem; { ptr to end of buffer } + pNextPut: PMidiBufferItem; { next location to fill } + pNextGet: PMidiBufferItem; { next location to empty } + Error: Word; { error code from MMSYSTEM functions } + Capacity: Word; { buffer size (in TMidiBufferItems) } + EventCount: Word; { Number of events in buffer } + end; + + PCircularBuffer = ^TCircularBuffer; + +function GlobalSharedLockedAlloc( Capacity: Word; var hMem: HGLOBAL ): Pointer; +procedure GlobalSharedLockedFree( hMem: HGLOBAL; ptr: Pointer ); + +function CircbufAlloc( Capacity: Word ): PCircularBuffer; +procedure CircbufFree( PBuffer: PCircularBuffer ); +function CircbufRemoveEvent( PBuffer: PCircularBuffer ): Boolean; +function CircbufReadEvent( PBuffer: PCircularBuffer; PEvent: PMidiBufferItem ): Boolean; +{ Note: The PutEvent function is in the DLL } + +implementation + +{ Allocates in global shared memory, returns pointer and handle } +function GlobalSharedLockedAlloc( Capacity: Word; var hMem: HGLOBAL ): Pointer; +var + ptr: Pointer; +begin + { Allocate the buffer memory } + hMem := GlobalAlloc(GMEM_SHARE Or GMEM_MOVEABLE Or GMEM_ZEROINIT, Capacity ); + + if (hMem = 0) then + ptr := Nil + else + begin + ptr := GlobalLock(hMem); + if (ptr = Nil) then + GlobalFree(hMem); + end; + + GlobalSharedLockedAlloc := Ptr; +end; + +procedure GlobalSharedLockedFree( hMem: HGLOBAL; ptr: Pointer ); +begin + if (hMem <> 0) then + begin + GlobalUnlock(hMem); + GlobalFree(hMem); + end; +end; + +function CircbufAlloc( Capacity: Word ): PCircularBuffer; +var + NewCircularBuffer: PCircularBuffer; + NewMIDIBuffer: PMidiBufferItem; + hMem: HGLOBAL; +begin + { TODO: Validate circbuf size, <64K } + NewCircularBuffer := + GlobalSharedLockedAlloc( Sizeof(TCircularBuffer), hMem ); + if (NewCircularBuffer <> Nil) then + begin + NewCircularBuffer^.RecordHandle := hMem; + NewMIDIBuffer := + GlobalSharedLockedAlloc( Capacity * Sizeof(TMidiBufferItem), hMem ); + if (NewMIDIBuffer = Nil) then + begin + { TODO: Exception here? } + GlobalSharedLockedFree( NewCircularBuffer^.RecordHandle, + NewCircularBuffer ); + NewCircularBuffer := Nil; + end + else + begin + NewCircularBuffer^.pStart := NewMidiBuffer; + { Point to item at end of buffer } + NewCircularBuffer^.pEnd := NewMidiBuffer; + Inc(NewCircularBuffer^.pEnd, Capacity); + { Start off the get and put pointers in the same position. These + will get out of sync as the interrupts start rolling in } + NewCircularBuffer^.pNextPut := NewMidiBuffer; + NewCircularBuffer^.pNextGet := NewMidiBuffer; + NewCircularBuffer^.Error := 0; + NewCircularBuffer^.Capacity := Capacity; + NewCircularBuffer^.EventCount := 0; + end; + end; + CircbufAlloc := NewCircularBuffer; +end; + +procedure CircbufFree( pBuffer: PCircularBuffer ); +begin + if (pBuffer <> Nil) then + begin + GlobalSharedLockedFree(pBuffer^.BufferHandle, pBuffer^.pStart); + GlobalSharedLockedFree(pBuffer^.RecordHandle, pBuffer); + end; +end; + +{ Reads first event in queue without removing it. + Returns true if successful, False if no events in queue } +function CircbufReadEvent( PBuffer: PCircularBuffer; PEvent: PMidiBufferItem ): Boolean; +var + PCurrentEvent: PMidiBufferItem; +begin + if (PBuffer^.EventCount <= 0) then + CircbufReadEvent := False + else + begin + PCurrentEvent := PBuffer^.PNextget; + + { Copy the object from the "tail" of the buffer to the caller's object } + PEvent^.Timestamp := PCurrentEvent^.Timestamp; + PEvent^.Data := PCurrentEvent^.Data; + PEvent^.Sysex := PCurrentEvent^.Sysex; + CircbufReadEvent := True; + end; +end; + +{ Remove current event from the queue } +function CircbufRemoveEvent(PBuffer: PCircularBuffer): Boolean; +begin + if (PBuffer^.EventCount > 0) then + begin + Dec( Pbuffer^.EventCount); + + { Advance the buffer pointer, with wrap } + Inc( Pbuffer^.PNextGet ); + If (PBuffer^.PNextGet = PBuffer^.PEnd) then + PBuffer^.PNextGet := PBuffer^.PStart; + + CircbufRemoveEvent := True; + end + else + CircbufRemoveEvent := False; +end; + +end. diff --git a/src/lib/midi/DELPHMCB.PAS b/src/lib/midi/DELPHMCB.PAS deleted file mode 100644 index ef0d5451..00000000 --- a/src/lib/midi/DELPHMCB.PAS +++ /dev/null @@ -1,140 +0,0 @@ -{ $Header: /MidiComp/DELPHMCB.PAS 2 10/06/97 7:33 Davec $ } - -{MIDI callback for Delphi, was DLL for Delphi 1} - -unit Delphmcb; - -{ These segment options required for the MIDI callback functions } -{$IFNDEF FPC} -{$C PRELOAD FIXED PERMANENT} -{$ENDIF} - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - Windows, - MMsystem, - Circbuf, - MidiDefs, - MidiCons; - -procedure midiHandler( - hMidiIn: HMidiIn; - wMsg: UINT; - dwInstance: DWORD; - dwParam1: DWORD; - dwParam2: DWORD); stdcall; export; -function CircbufPutEvent(PBuffer: PCircularBuffer; PTheEvent: PMidiBufferItem): Boolean; stdcall; export; - -implementation - -{ Add an event to the circular input buffer. } -function CircbufPutEvent(PBuffer: PCircularBuffer; PTheEvent: PMidiBufferItem): Boolean; stdcall; -begin - If (PBuffer^.EventCount < PBuffer^.Capacity) Then - begin - Inc(Pbuffer^.EventCount); - - { Todo: better way of copying this record } - with PBuffer^.PNextput^ do - begin - Timestamp := PTheEvent^.Timestamp; - Data := PTheEvent^.Data; - Sysex := PTheEvent^.Sysex; - end; - - { Move to next put location, with wrap } - Inc(Pbuffer^.PNextPut); - If (PBuffer^.PNextPut = PBuffer^.PEnd) then - PBuffer^.PNextPut := PBuffer^.PStart; - - CircbufPutEvent := True; - end - else - CircbufPutEvent := False; -end; - -{ This is the callback function specified when the MIDI device was opened - by midiInOpen. It's called at interrupt time when MIDI input is seen - by the MIDI device driver(s). See the docs for midiInOpen for restrictions - on the Windows functions that can be called in this interrupt. } -procedure midiHandler( - hMidiIn: HMidiIn; - wMsg: UINT; - dwInstance: DWORD; - dwParam1: DWORD; - dwParam2: DWORD); stdcall; -var - thisEvent: TMidiBufferItem; - thisCtlInfo: PMidiCtlInfo; - thisBuffer: PCircularBuffer; -Begin - case wMsg of - - mim_Open: {nothing}; - - mim_Error: {TODO: handle (message to trigger exception?) }; - - mim_Data, mim_Longdata, mim_Longerror: - { Note: mim_Longerror included because there's a bug in the Maui - input driver that sends MIM_LONGERROR for subsequent buffers when - the input buffer is smaller than the sysex block being received } - - begin - { TODO: Make filtered messages customisable, I'm sure someone wants to - do something with MTC! } - if (dwParam1 <> MIDI_ACTIVESENSING) and - (dwParam1 <> MIDI_TIMINGCLOCK) then - begin - - { The device driver passes us the instance data pointer we - specified for midiInOpen. Use this to get the buffer address - and window handle for the MIDI control } - thisCtlInfo := PMidiCtlInfo(dwInstance); - thisBuffer := thisCtlInfo^.PBuffer; - - { Screen out short messages if we've been asked to } - if ((wMsg <> mim_Data) or (thisCtlInfo^.SysexOnly = False)) - and (thisCtlInfo <> Nil) and (thisBuffer <> Nil) then - begin - with thisEvent do - begin - timestamp := dwParam2; - if (wMsg = mim_Longdata) or - (wMsg = mim_Longerror) then - begin - data := 0; - sysex := PMidiHdr(dwParam1); - end - else - begin - data := dwParam1; - sysex := Nil; - end; - end; - if CircbufPutEvent( thisBuffer, @thisEvent ) then - { Send a message to the control to say input's arrived } - PostMessage(thisCtlInfo^.hWindow, mim_Data, 0, 0) - else - { Buffer overflow } - PostMessage(thisCtlInfo^.hWindow, mim_Overflow, 0, 0); - end; - end; - end; - - mom_Done: { Sysex output complete, dwParam1 is pointer to MIDIHDR } - begin - { Notify the control that its sysex output is finished. - The control should call midiOutUnprepareHeader before freeing the buffer } - PostMessage(PMidiCtlInfo(dwInstance)^.hWindow, mom_Done, 0, dwParam1); - end; - - end; { Case } -end; - -end. diff --git a/src/lib/midi/DelphiMcb.pas b/src/lib/midi/DelphiMcb.pas new file mode 100644 index 00000000..39b1c61f --- /dev/null +++ b/src/lib/midi/DelphiMcb.pas @@ -0,0 +1,140 @@ +{ $Header: /MidiComp/DelphiMcb.pas 2 10/06/97 7:33 Davec $ } + +{MIDI callback for Delphi, was DLL for Delphi 1} + +unit DelphiMcb; + +{ These segment options required for the MIDI callback functions } +{$IFNDEF FPC} +{$C PRELOAD FIXED PERMANENT} +{$ENDIF} + +interface + +{$IFDEF FPC} + {$MODE Delphi} + {$H+} // use long strings +{$ENDIF} + +uses + Windows, + MMsystem, + CircBuf, + MidiDefs, + MidiCons; + +procedure midiHandler( + hMidiIn: HMidiIn; + wMsg: UINT; + dwInstance: DWORD; + dwParam1: DWORD; + dwParam2: DWORD); stdcall; export; +function CircbufPutEvent(PBuffer: PCircularBuffer; PTheEvent: PMidiBufferItem): Boolean; stdcall; export; + +implementation + +{ Add an event to the circular input buffer. } +function CircbufPutEvent(PBuffer: PCircularBuffer; PTheEvent: PMidiBufferItem): Boolean; stdcall; +begin + If (PBuffer^.EventCount < PBuffer^.Capacity) Then + begin + Inc(Pbuffer^.EventCount); + + { Todo: better way of copying this record } + with PBuffer^.PNextput^ do + begin + Timestamp := PTheEvent^.Timestamp; + Data := PTheEvent^.Data; + Sysex := PTheEvent^.Sysex; + end; + + { Move to next put location, with wrap } + Inc(Pbuffer^.PNextPut); + If (PBuffer^.PNextPut = PBuffer^.PEnd) then + PBuffer^.PNextPut := PBuffer^.PStart; + + CircbufPutEvent := True; + end + else + CircbufPutEvent := False; +end; + +{ This is the callback function specified when the MIDI device was opened + by midiInOpen. It's called at interrupt time when MIDI input is seen + by the MIDI device driver(s). See the docs for midiInOpen for restrictions + on the Windows functions that can be called in this interrupt. } +procedure midiHandler( + hMidiIn: HMidiIn; + wMsg: UINT; + dwInstance: DWORD; + dwParam1: DWORD; + dwParam2: DWORD); stdcall; +var + thisEvent: TMidiBufferItem; + thisCtlInfo: PMidiCtlInfo; + thisBuffer: PCircularBuffer; +Begin + case wMsg of + + mim_Open: {nothing}; + + mim_Error: {TODO: handle (message to trigger exception?) }; + + mim_Data, mim_Longdata, mim_Longerror: + { Note: mim_Longerror included because there's a bug in the Maui + input driver that sends MIM_LONGERROR for subsequent buffers when + the input buffer is smaller than the sysex block being received } + + begin + { TODO: Make filtered messages customisable, I'm sure someone wants to + do something with MTC! } + if (dwParam1 <> MIDI_ACTIVESENSING) and + (dwParam1 <> MIDI_TIMINGCLOCK) then + begin + + { The device driver passes us the instance data pointer we + specified for midiInOpen. Use this to get the buffer address + and window handle for the MIDI control } + thisCtlInfo := PMidiCtlInfo(dwInstance); + thisBuffer := thisCtlInfo^.PBuffer; + + { Screen out short messages if we've been asked to } + if ((wMsg <> mim_Data) or (thisCtlInfo^.SysexOnly = False)) + and (thisCtlInfo <> Nil) and (thisBuffer <> Nil) then + begin + with thisEvent do + begin + timestamp := dwParam2; + if (wMsg = mim_Longdata) or + (wMsg = mim_Longerror) then + begin + data := 0; + sysex := PMidiHdr(dwParam1); + end + else + begin + data := dwParam1; + sysex := Nil; + end; + end; + if CircbufPutEvent( thisBuffer, @thisEvent ) then + { Send a message to the control to say input's arrived } + PostMessage(thisCtlInfo^.hWindow, mim_Data, 0, 0) + else + { Buffer overflow } + PostMessage(thisCtlInfo^.hWindow, mim_Overflow, 0, 0); + end; + end; + end; + + mom_Done: { Sysex output complete, dwParam1 is pointer to MIDIHDR } + begin + { Notify the control that its sysex output is finished. + The control should call midiOutUnprepareHeader before freeing the buffer } + PostMessage(PMidiCtlInfo(dwInstance)^.hWindow, mom_Done, 0, dwParam1); + end; + + end; { Case } +end; + +end. diff --git a/src/lib/midi/MIDIDEFS.PAS b/src/lib/midi/MIDIDEFS.PAS deleted file mode 100644 index 4afe56ef..00000000 --- a/src/lib/midi/MIDIDEFS.PAS +++ /dev/null @@ -1,55 +0,0 @@ -{ $Header: /MidiComp/MIDIDEFS.PAS 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - - -{ Common definitions used by DELPHMID.DPR and the MIDI components. - This must be a separate unit to prevent large chunks of the VCL being - linked into the DLL. } -unit Mididefs; - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - Windows, - MMsystem, - Circbuf; - -type - - {-------------------------------------------------------------------} - { This is the information about the control that must be accessed by - the MIDI input callback function in the DLL at interrupt time } - PMidiCtlInfo = ^TMidiCtlInfo; - TMidiCtlInfo = record - hMem: THandle; { Memory handle for this record } - PBuffer: PCircularBuffer; { Pointer to the MIDI input data buffer } - hWindow: HWnd; { Control's window handle } - SysexOnly: Boolean; { Only process System Exclusive input } - end; - - { Information for the output timer callback function, also required at - interrupt time. } - PMidiOutTimerInfo = ^TMidiOutTimerInfo; - TMidiOutTimerInfo = record - hMem: THandle; { Memory handle for this record } - PBuffer: PCircularBuffer; { Pointer to MIDI output data buffer } - hWindow: HWnd; { Control's window handle } - TimeToNextEvent: DWORD; { Delay to next event after timer set } - MIDIHandle: HMidiOut; { MIDI handle to send output to - (copy of component's FMidiHandle property) } - PeriodMin: Word; { Multimedia timer minimum period supported } - PeriodMax: Word; { Multimedia timer maximum period supported } - TimerId: Word; { Multimedia timer ID of current event } - end; - -implementation - - -end. diff --git a/src/lib/midi/MIDITYPE.PAS b/src/lib/midi/MIDITYPE.PAS deleted file mode 100644 index 45b50820..00000000 --- a/src/lib/midi/MIDITYPE.PAS +++ /dev/null @@ -1,90 +0,0 @@ -{ $Header: /MidiComp/MIDITYPE.PAS 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - - -unit Miditype; - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - Classes, - Windows, - Messages, - MMSystem, - MidiDefs, - Circbuf; - -type - {-------------------------------------------------------------------} - { A MIDI input/output event } - TMyMidiEvent = class(TPersistent) - public - MidiMessage: Byte; { MIDI message status byte } - Data1: Byte; { MIDI message data 1 byte } - Data2: Byte; { MIDI message data 2 byte } - Time: DWORD; { Time in ms since midiInOpen } - SysexLength: Word; { Length of sysex data (0 if none) } - Sysex: PChar; { Pointer to sysex data buffer } - destructor Destroy; override; { Frees sysex data buffer if nec. } - end; - PMyMidiEvent = ^TMyMidiEvent; - - {-------------------------------------------------------------------} - { Encapsulates the MIDIHDR with its memory handle and sysex buffer } - PMyMidiHdr = ^TMyMidiHdr; - TMyMidiHdr = class(TObject) - public - hdrHandle: THandle; - hdrPointer: PMIDIHDR; - sysexHandle: THandle; - sysexPointer: Pointer; - constructor Create(BufferSize: Word); - destructor Destroy; override; - end; - -implementation - -{-------------------------------------------------------------------} -{ Free any sysex buffer associated with the event } -destructor TMyMidiEvent.Destroy; -begin - if (Sysex <> Nil) then - Freemem(Sysex, SysexLength); - - inherited Destroy; -end; - -{-------------------------------------------------------------------} -{ Allocate memory for the sysex header and buffer } -constructor TMyMidiHdr.Create(BufferSize:Word); -begin - inherited Create; - - if BufferSize > 0 then - begin - hdrPointer := GlobalSharedLockedAlloc(sizeof(TMIDIHDR), hdrHandle); - sysexPointer := GlobalSharedLockedAlloc(BufferSize, sysexHandle); - - hdrPointer^.lpData := sysexPointer; - hdrPointer^.dwBufferLength := BufferSize; - end; -end; - -{-------------------------------------------------------------------} -destructor TMyMidiHdr.Destroy; -begin - GlobalSharedLockedFree( hdrHandle, hdrPointer ); - GlobalSharedLockedFree( sysexHandle, sysexPointer ); - inherited Destroy; -end; - - - -end. diff --git a/src/lib/midi/MidiComp.cfg b/src/lib/midi/MidiComp.cfg new file mode 100644 index 00000000..8b774c81 --- /dev/null +++ b/src/lib/midi/MidiComp.cfg @@ -0,0 +1,35 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$Y- +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +-LE"d:\program files\borland\delphi5\Projects\Bpl" +-LN"d:\program files\borland\delphi5\Projects\Bpl" diff --git a/src/lib/midi/MidiComp.dpk b/src/lib/midi/MidiComp.dpk new file mode 100644 index 00000000..7c403eae --- /dev/null +++ b/src/lib/midi/MidiComp.dpk @@ -0,0 +1,45 @@ +package midiComp; + +{$R *.RES} +{$R 'MidiFile.dcr'} +{$R 'Midiin.dcr'} +{$R 'Midiout.dcr'} +{$R 'MidiScope.dcr'} +{$ALIGN ON} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST ON} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Midi related components'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + vcl50; + +contains + Miditype in 'Miditype.pas', + Mididefs in 'Mididefs.pas', + MidiFile in 'MidiFile.pas', + Midiin in 'Midiin.pas', + Midiout in 'Midiout.pas', + MidiScope in 'MidiScope.pas', + Midicons in 'Midicons.pas'; + +end. diff --git a/src/lib/midi/MidiComp.res b/src/lib/midi/MidiComp.res new file mode 100644 index 00000000..91fb756e Binary files /dev/null and b/src/lib/midi/MidiComp.res differ diff --git a/src/lib/midi/MidiCons.pas b/src/lib/midi/MidiCons.pas new file mode 100644 index 00000000..dbd3c5b7 --- /dev/null +++ b/src/lib/midi/MidiCons.pas @@ -0,0 +1,47 @@ +{ $Header: /MidiComp/MidiCons.pas 2 10/06/97 7:33 Davec $ } + +{ Written by David Churcher , + released to the public domain. } + + +{ MIDI Constants } +unit MidiCons; + +interface + +{$IFDEF FPC} + {$MODE Delphi} + {$H+} // use long strings +{$ENDIF} + +uses Messages; + +const + MIDI_ALLNOTESOFF = $7B; + MIDI_NOTEON = $90; + MIDI_NOTEOFF = $80; + MIDI_KEYAFTERTOUCH = $a0; + MIDI_CONTROLCHANGE = $b0; + MIDI_PROGRAMCHANGE = $c0; + MIDI_CHANAFTERTOUCH = $d0; + MIDI_PITCHBEND = $e0; + MIDI_SYSTEMMESSAGE = $f0; + MIDI_BEGINSYSEX = $f0; + MIDI_MTCQUARTERFRAME = $f1; + MIDI_SONGPOSPTR = $f2; + MIDI_SONGSELECT = $f3; + MIDI_ENDSYSEX = $F7; + MIDI_TIMINGCLOCK = $F8; + MIDI_START = $FA; + MIDI_CONTINUE = $FB; + MIDI_STOP = $FC; + MIDI_ACTIVESENSING = $FE; + MIDI_SYSTEMRESET = $FF; + + MIM_OVERFLOW = WM_USER; { Input buffer overflow } + MOM_PLAYBACK_DONE = WM_USER+1; { Timed playback complete } + + +implementation + +end. diff --git a/src/lib/midi/MidiDefs.pas b/src/lib/midi/MidiDefs.pas new file mode 100644 index 00000000..f14b5e77 --- /dev/null +++ b/src/lib/midi/MidiDefs.pas @@ -0,0 +1,55 @@ +{ $Header: /MidiComp/MidiDefs.pas 2 10/06/97 7:33 Davec $ } + +{ Written by David Churcher , + released to the public domain. } + + +{ Common definitions used by DELPHMID.DPR and the MIDI components. + This must be a separate unit to prevent large chunks of the VCL being + linked into the DLL. } +unit Mididefs; + +interface + +{$IFDEF FPC} + {$MODE Delphi} + {$H+} // use long strings +{$ENDIF} + +uses + Windows, + MMsystem, + CircBuf; + +type + + {-------------------------------------------------------------------} + { This is the information about the control that must be accessed by + the MIDI input callback function in the DLL at interrupt time } + PMidiCtlInfo = ^TMidiCtlInfo; + TMidiCtlInfo = record + hMem: THandle; { Memory handle for this record } + PBuffer: PCircularBuffer; { Pointer to the MIDI input data buffer } + hWindow: HWnd; { Control's window handle } + SysexOnly: Boolean; { Only process System Exclusive input } + end; + + { Information for the output timer callback function, also required at + interrupt time. } + PMidiOutTimerInfo = ^TMidiOutTimerInfo; + TMidiOutTimerInfo = record + hMem: THandle; { Memory handle for this record } + PBuffer: PCircularBuffer; { Pointer to MIDI output data buffer } + hWindow: HWnd; { Control's window handle } + TimeToNextEvent: DWORD; { Delay to next event after timer set } + MIDIHandle: HMidiOut; { MIDI handle to send output to + (copy of component's FMidiHandle property) } + PeriodMin: Word; { Multimedia timer minimum period supported } + PeriodMax: Word; { Multimedia timer maximum period supported } + TimerId: Word; { Multimedia timer ID of current event } + end; + +implementation + + +end. diff --git a/src/lib/midi/MidiIn.pas b/src/lib/midi/MidiIn.pas new file mode 100644 index 00000000..5ff17ae8 --- /dev/null +++ b/src/lib/midi/MidiIn.pas @@ -0,0 +1,720 @@ +{ $Header: /MidiComp/MidiIn.pas 2 10/06/97 7:33 Davec $ } + +{ Written by David Churcher , + released to the public domain. } + +unit MidiIn; + +{ + Properties: + DeviceID: Windows numeric device ID for the MIDI input device. + Between 0 and NumDevs-1. + Read-only while device is open, exception when changed while open + + MIDIHandle: The input handle to the MIDI device. + 0 when device is not open + Read-only, runtime-only + + MessageCount: Number of input messages waiting in input buffer + + Capacity: Number of messages input buffer can hold + Defaults to 1024 + Limited to (64K/event size) + Read-only when device is open (exception when changed while open) + + SysexBufferSize: Size in bytes of each sysex buffer + Defaults to 10K + Minimum 0K (no buffers), Maximum 64K-1 + + SysexBufferCount: Number of sysex buffers + Defaults to 16 + Minimum 0 (no buffers), Maximum (avail mem/SysexBufferSize) + Check where these buffers are allocated? + + SysexOnly: True to ignore all non-sysex input events. May be changed while + device is open. Handy for patch editors where you have lots of short MIDI + events on the wire which you are always going to ignore anyway. + + DriverVersion: Version number of MIDI device driver. High-order byte is + major version, low-order byte is minor version. + + ProductName: Name of product (e.g. 'MPU 401 In') + + MID and PID: Manufacturer ID and Product ID, see + "Manufacturer and Product IDs" in MMSYSTEM.HLP for list of possible values. + + Methods: + GetMidiEvent: Read Midi event at the head of the FIFO input buffer. + Returns a TMyMidiEvent object containing MIDI message data, timestamp, + and sysex data if applicable. + This method automatically removes the event from the input buffer. + It makes a copy of the received sysex buffer and puts the buffer back + on the input device. + The TMyMidiEvent object must be freed by calling MyMidiEvent.Free. + + Open: Opens device. Note no input will appear until you call the Start + method. + + Close: Closes device. Any pending system exclusive output will be cancelled. + + Start: Starts receiving MIDI input. + + Stop: Stops receiving MIDI input. + + Events: + OnMidiInput: Called when MIDI input data arrives. Use the GetMidiEvent to + get the MIDI input data. + + OnOverflow: Called if the MIDI input buffer overflows. The caller must + clear the buffer before any more MIDI input can be received. + + Notes: + Buffering: Uses a circular buffer, separate pointers for next location + to fill and next location to empty because a MIDI input interrupt may + be adding data to the buffer while the buffer is being read. Buffer + pointers wrap around from end to start of buffer automatically. If + buffer overflows then the OnBufferOverflow event is triggered and no + further input will be received until the buffer is emptied by calls + to GetMidiEvent. + + Sysex buffers: There are (SysexBufferCount) buffers on the input device. + When sysex events arrive these buffers are removed from the input device and + added to the circular buffer by the interrupt handler in the DLL. When the sysex events + are removed from the circular buffer by the GetMidiEvent method the buffers are + put back on the input. If all the buffers are used up there will be no + more sysex input until at least one sysex event is removed from the input buffer. + In other words if you're expecting lots of sysex input you need to set the + SysexBufferCount property high enough so that you won't run out of + input buffers before you get a chance to read them with GetMidiEvent. + + If the synth sends a block of sysex that's longer than SysexBufferSize it + will be received as separate events. + TODO: Component derived from this one that handles >64K sysex blocks cleanly + and can stream them to disk. + + Midi Time Code (MTC) and Active Sensing: The DLL is currently hardcoded + to filter these short events out, so that we don't spend all our time + processing them. + TODO: implement a filter property to select the events that will be filtered + out. +} + +interface + +{$IFDEF FPC} + {$MODE Delphi} + {$H+} // use long strings +{$ENDIF} + +uses + Classes, + SysUtils, + Messages, + Windows, + MMSystem, + {$IFDEF FPC} + WinAllocation, + {$ENDIF} + MidiDefs, + MidiType, + MidiCons, + CircBuf, + DelphiMcb; + +type + MidiInputState = (misOpen, misClosed, misCreating, misDestroying); + EMidiInputError = class(Exception); + + {-------------------------------------------------------------------} + TMidiInput = class(TComponent) + private + Handle: THandle; { Window handle used for callback notification } + FDeviceID: Word; { MIDI device ID } + FMIDIHandle: HMIDIIn; { Handle to input device } + FState: MidiInputState; { Current device state } + + FError: Word; + FSysexOnly: Boolean; + + { Stuff from MIDIINCAPS } + FDriverVersion: MMVERSION; + FProductName: string; + FMID: Word; { Manufacturer ID } + FPID: Word; { Product ID } + + { Queue } + FCapacity: Word; { Buffer capacity } + PBuffer: PCircularBuffer; { Low-level MIDI input buffer created by Open method } + FNumdevs: Word; { Number of input devices on system } + + { Events } + FOnMIDIInput: TNotifyEvent; { MIDI Input arrived } + FOnOverflow: TNotifyEvent; { Input buffer overflow } + { TODO: Some sort of error handling event for MIM_ERROR } + + { Sysex } + FSysexBufferSize: Word; + FSysexBufferCount: Word; + MidiHdrs: Tlist; + + PCtlInfo: PMidiCtlInfo; { Pointer to control info for DLL } + + protected + procedure Prepareheaders; + procedure UnprepareHeaders; + procedure AddBuffers; + procedure SetDeviceID(DeviceID: Word); + procedure SetProductName(NewProductName: string); + function GetEventCount: Word; + procedure SetSysexBufferSize(BufferSize: Word); + procedure SetSysexBufferCount(BufferCount: Word); + procedure SetSysexOnly(bSysexOnly: Boolean); + function MidiInErrorString(WError: Word): string; + + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + + property MIDIHandle: HMIDIIn read FMIDIHandle; + + property DriverVersion: MMVERSION read FDriverVersion; + property MID: Word read FMID; { Manufacturer ID } + property PID: Word read FPID; { Product ID } + + property Numdevs: Word read FNumdevs; + + property MessageCount: Word read GetEventCount; + { TODO: property to select which incoming messages get filtered out } + + procedure Open; + procedure Close; + procedure Start; + procedure Stop; + { Get first message in input queue } + function GetMidiEvent: TMyMidiEvent; + procedure MidiInput(var Message: TMessage); + + { Some functions to decode and classify incoming messages would be good } + + published + + { TODO: Property editor with dropdown list of product names } + property ProductName: string read FProductName write SetProductName; + + property DeviceID: Word read FDeviceID write SetDeviceID default 0; + property Capacity: Word read FCapacity write FCapacity default 1024; + property Error: Word read FError; + property SysexBufferSize: Word + read FSysexBufferSize + write SetSysexBufferSize + default 10000; + property SysexBufferCount: Word + read FSysexBufferCount + write SetSysexBufferCount + default 16; + property SysexOnly: Boolean + read FSysexOnly + write SetSysexOnly + default False; + + { Events } + property OnMidiInput: TNotifyEvent read FOnMidiInput write FOnMidiInput; + property OnOverflow: TNotifyEvent read FOnOverflow write FOnOverflow; + + end; + +procedure Register; + +{====================================================================} +implementation + +uses Controls, + Graphics; + +(* Not used in Delphi 3 +{ This is the callback procedure in the external DLL. + It's used when midiInOpen is called by the Open method. + There are special requirements and restrictions for this callback + procedure (see midiInOpen in MMSYSTEM.HLP) so it's impractical to + make it an object method } +{$IFDEF MSWINDOWS} +function midiHandler( + hMidiIn: HMidiIn; + wMsg: UINT; + dwInstance: DWORD; + dwParam1: DWORD; + dwParam2: DWORD): Boolean; stdcall; external 'DELMID32.DLL'; +{$ENDIF} +*) +{-------------------------------------------------------------------} + +constructor TMidiInput.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FState := misCreating; + + FSysexOnly := False; + FNumDevs := midiInGetNumDevs; + MidiHdrs := nil; + + { Set defaults } + if (FNumDevs > 0) then + SetDeviceID(0); + FCapacity := 1024; + FSysexBufferSize := 4096; + FSysexBufferCount := 16; + + { Create the window for callback notification } + if not (csDesigning in ComponentState) then + begin + Handle := AllocateHwnd(MidiInput); + end; + + FState := misClosed; + +end; + +{-------------------------------------------------------------------} +{ Close the device if it's open } + +destructor TMidiInput.Destroy; +begin + if (FMidiHandle <> 0) then + begin + Close; + FMidiHandle := 0; + end; + + if (PCtlInfo <> nil) then + GlobalSharedLockedFree(PCtlinfo^.hMem, PCtlInfo); + + DeallocateHwnd(Handle); + inherited Destroy; +end; + +{-------------------------------------------------------------------} +{ Convert the numeric return code from an MMSYSTEM function to a string + using midiInGetErrorText. TODO: These errors aren't very helpful + (e.g. "an invalid parameter was passed to a system function") so + sort out some proper error strings. } + +function TMidiInput.MidiInErrorString(WError: Word): string; +var + errorDesc: PChar; +begin + errorDesc := nil; + try + errorDesc := StrAlloc(MAXERRORLENGTH); + if midiInGetErrorText(WError, errorDesc, MAXERRORLENGTH) = 0 then + result := StrPas(errorDesc) + else + result := 'Specified error number is out of range'; + finally + if errorDesc <> nil then StrDispose(errorDesc); + end; +end; + +{-------------------------------------------------------------------} +{ Set the sysex buffer size, fail if device is already open } + +procedure TMidiInput.SetSysexBufferSize(BufferSize: Word); +begin + if FState = misOpen then + raise EMidiInputError.Create('Change to SysexBufferSize while device was open') + else + { TODO: Validate the sysex buffer size. Is this necessary for WIN32? } + FSysexBufferSize := BufferSize; +end; + +{-------------------------------------------------------------------} +{ Set the sysex buffer count, fail if device is already open } + +procedure TMidiInput.SetSysexBuffercount(Buffercount: Word); +begin + if FState = misOpen then + raise EMidiInputError.Create('Change to SysexBuffercount while device was open') + else + { TODO: Validate the sysex buffer count } + FSysexBuffercount := Buffercount; +end; + +{-------------------------------------------------------------------} +{ Set the Sysex Only flag to eliminate unwanted short MIDI input messages } + +procedure TMidiInput.SetSysexOnly(bSysexOnly: Boolean); +begin + FSysexOnly := bSysexOnly; + { Update the interrupt handler's copy of this property } + if PCtlInfo <> nil then + PCtlInfo^.SysexOnly := bSysexOnly; +end; + +{-------------------------------------------------------------------} +{ Set the Device ID to select a new MIDI input device + Note: If no MIDI devices are installed, throws an 'Invalid Device ID' exception } + +procedure TMidiInput.SetDeviceID(DeviceID: Word); +var + MidiInCaps: TMidiInCaps; +begin + if FState = misOpen then + raise EMidiInputError.Create('Change to DeviceID while device was open') + else + if (DeviceID >= midiInGetNumDevs) then + raise EMidiInputError.Create('Invalid device ID') + else + begin + FDeviceID := DeviceID; + + { Set the name and other MIDIINCAPS properties to match the ID } + FError := + midiInGetDevCaps(DeviceID, @MidiInCaps, sizeof(TMidiInCaps)); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + + FProductName := StrPas(MidiInCaps.szPname); + FDriverVersion := MidiInCaps.vDriverVersion; + FMID := MidiInCaps.wMID; + FPID := MidiInCaps.wPID; + + end; +end; + +{-------------------------------------------------------------------} +{ Set the product name and put the matching input device number in FDeviceID. + This is handy if you want to save a configured input/output device + by device name instead of device number, because device numbers may + change if users add or remove MIDI devices. + Exception if input device with matching name not found, + or if input device is open } + +procedure TMidiInput.SetProductName(NewProductName: string); +var + MidiInCaps: TMidiInCaps; + testDeviceID: Word; + testProductName: string; +begin + if FState = misOpen then + raise EMidiInputError.Create('Change to ProductName while device was open') + else + { Don't set the name if the component is reading properties because + the saved Productname will be from the machine the application was compiled + on, which may not be the same for the corresponding DeviceID on the user's + machine. The FProductname property will still be set by SetDeviceID } + if not (csLoading in ComponentState) then + begin + begin + for testDeviceID := 0 to (midiInGetNumDevs - 1) do + begin + FError := + midiInGetDevCaps(testDeviceID, @MidiInCaps, sizeof(TMidiInCaps)); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + testProductName := StrPas(MidiInCaps.szPname); + if testProductName = NewProductName then + begin + FProductName := NewProductName; + Break; + end; + end; + if FProductName <> NewProductName then + raise EMidiInputError.Create('MIDI Input Device ' + + NewProductName + ' not installed ') + else + SetDeviceID(testDeviceID); + end; + end; +end; + + +{-------------------------------------------------------------------} +{ Get the sysex buffers ready } + +procedure TMidiInput.PrepareHeaders; +var + ctr: Word; + MyMidiHdr: TMyMidiHdr; +begin + if (FSysexBufferCount > 0) and (FSysexBufferSize > 0) + and (FMidiHandle <> 0) then + begin + Midihdrs := TList.Create; + for ctr := 1 to FSysexBufferCount do + begin + { Initialize the header and allocate buffer memory } + MyMidiHdr := TMyMidiHdr.Create(FSysexBufferSize); + + { Store the address of the MyMidiHdr object in the contained MIDIHDR + structure so we can get back to the object when a pointer to the + MIDIHDR is received. + E.g. see TMidiOutput.Output method } + MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr); + + { Get MMSYSTEM's blessing for this header } + FError := midiInPrepareHeader(FMidiHandle, MyMidiHdr.hdrPointer, + sizeof(TMIDIHDR)); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + + { Save it in our list } + MidiHdrs.Add(MyMidiHdr); + end; + end; + +end; + +{-------------------------------------------------------------------} +{ Clean up from PrepareHeaders } + +procedure TMidiInput.UnprepareHeaders; +var + ctr: Word; +begin + if (MidiHdrs <> nil) then { will be Nil if 0 sysex buffers } + begin + for ctr := 0 to MidiHdrs.Count - 1 do + begin + FError := midiInUnprepareHeader(FMidiHandle, + TMyMidiHdr(MidiHdrs.Items[ctr]).hdrPointer, + sizeof(TMIDIHDR)); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + TMyMidiHdr(MidiHdrs.Items[ctr]).Free; + end; + MidiHdrs.Free; + MidiHdrs := nil; + end; +end; + +{-------------------------------------------------------------------} +{ Add sysex buffers, if required, to input device } + +procedure TMidiInput.AddBuffers; +var + ctr: Word; +begin + if MidiHdrs <> nil then { will be Nil if 0 sysex buffers } + begin + if MidiHdrs.Count > 0 then + begin + for ctr := 0 to MidiHdrs.Count - 1 do + begin + FError := midiInAddBuffer(FMidiHandle, + TMyMidiHdr(MidiHdrs.Items[ctr]).hdrPointer, + sizeof(TMIDIHDR)); + if FError <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + end; + end; + end; +end; + +{-------------------------------------------------------------------} + +procedure TMidiInput.Open; +var + hMem: THandle; +begin + try + { Create the buffer for the MIDI input messages } + if (PBuffer = nil) then + PBuffer := CircBufAlloc(FCapacity); + + { Create the control info for the DLL } + if (PCtlInfo = nil) then + begin + PCtlInfo := GlobalSharedLockedAlloc(Sizeof(TMidiCtlInfo), hMem); + PctlInfo^.hMem := hMem; + end; + PctlInfo^.pBuffer := PBuffer; + Pctlinfo^.hWindow := Handle; { Control's window handle } + PCtlInfo^.SysexOnly := FSysexOnly; + FError := midiInOpen(@FMidiHandle, FDeviceId, + DWORD(@midiHandler), + DWORD(PCtlInfo), + CALLBACK_FUNCTION); + + if (FError <> MMSYSERR_NOERROR) then + { TODO: use CreateFmtHelp to add MIDI device name/ID to message } + raise EMidiInputError.Create(MidiInErrorString(FError)); + + { Get sysex buffers ready } + PrepareHeaders; + + { Add them to the input } + AddBuffers; + + FState := misOpen; + + except + if PBuffer <> nil then + begin + CircBufFree(PBuffer); + PBuffer := nil; + end; + + if PCtlInfo <> nil then + begin + GlobalSharedLockedFree(PCtlInfo^.hMem, PCtlInfo); + PCtlInfo := nil; + end; + + end; + +end; + +{-------------------------------------------------------------------} + +function TMidiInput.GetMidiEvent: TMyMidiEvent; +var + thisItem: TMidiBufferItem; +begin + if (FState = misOpen) and + CircBufReadEvent(PBuffer, @thisItem) then + begin + Result := TMyMidiEvent.Create; + with thisItem do + begin + Result.Time := Timestamp; + if (Sysex = nil) then + begin + { Short message } + Result.MidiMessage := LoByte(LoWord(Data)); + Result.Data1 := HiByte(LoWord(Data)); + Result.Data2 := LoByte(HiWord(Data)); + Result.Sysex := nil; + Result.SysexLength := 0; + end + else + { Long Sysex message } + begin + Result.MidiMessage := MIDI_BEGINSYSEX; + Result.Data1 := 0; + Result.Data2 := 0; + Result.SysexLength := Sysex^.dwBytesRecorded; + if Sysex^.dwBytesRecorded <> 0 then + begin + { Put a copy of the sysex buffer in the object } + GetMem(Result.Sysex, Sysex^.dwBytesRecorded); + StrMove(Result.Sysex, Sysex^.lpData, Sysex^.dwBytesRecorded); + end; + + { Put the header back on the input buffer } + FError := midiInPrepareHeader(FMidiHandle, Sysex, + sizeof(TMIDIHDR)); + if Ferror = 0 then + FError := midiInAddBuffer(FMidiHandle, + Sysex, sizeof(TMIDIHDR)); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + end; + end; + CircbufRemoveEvent(PBuffer); + end + else + { Device isn't open, return a nil event } + Result := nil; +end; + +{-------------------------------------------------------------------} + +function TMidiInput.GetEventCount: Word; +begin + if FState = misOpen then + Result := PBuffer^.EventCount + else + Result := 0; +end; + +{-------------------------------------------------------------------} + +procedure TMidiInput.Close; +begin + if FState = misOpen then + begin + FState := misClosed; + + { MidiInReset cancels any pending output. + Note that midiInReset causes an MIM_LONGDATA callback for each sysex + buffer on the input, so the callback function and Midi input buffer + should still be viable at this stage. + All the resulting MIM_LONGDATA callbacks will be completed by the time + MidiInReset returns, though. } + FError := MidiInReset(FMidiHandle); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + + { Remove sysex buffers from input device and free them } + UnPrepareHeaders; + + { Close the device (finally!) } + FError := MidiInClose(FMidiHandle); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + + FMidiHandle := 0; + + if (PBuffer <> nil) then + begin + CircBufFree(PBuffer); + PBuffer := nil; + end; + end; +end; + +{-------------------------------------------------------------------} + +procedure TMidiInput.Start; +begin + if FState = misOpen then + begin + FError := MidiInStart(FMidiHandle); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + end; +end; + +{-------------------------------------------------------------------} + +procedure TMidiInput.Stop; +begin + if FState = misOpen then + begin + FError := MidiInStop(FMidiHandle); + if Ferror <> MMSYSERR_NOERROR then + raise EMidiInputError.Create(MidiInErrorString(FError)); + end; +end; + +{-------------------------------------------------------------------} + +procedure TMidiInput.MidiInput(var Message: TMessage); +{ Triggered by incoming message from DLL. + Note DLL has already put the message in the queue } +begin + case Message.Msg of + mim_data: + { Trigger the user's MIDI input event, if they've specified one and + we're not in the process of closing the device. The check for + GetEventCount > 0 prevents unnecessary event calls where the user has + already cleared all the events from the input buffer using a GetMidiEvent + loop in the OnMidiInput event handler } + if Assigned(FOnMIDIInput) and (FState = misOpen) + and (GetEventCount > 0) then + FOnMIDIInput(Self); + + mim_Overflow: { input circular buffer overflow } + if Assigned(FOnOverflow) and (FState = misOpen) then + FOnOverflow(Self); + end; +end; + +{-------------------------------------------------------------------} + +procedure Register; +begin + RegisterComponents('Synth', [TMIDIInput]); +end; + +end. + diff --git a/src/lib/midi/MidiOut.pas b/src/lib/midi/MidiOut.pas new file mode 100644 index 00000000..60538a08 --- /dev/null +++ b/src/lib/midi/MidiOut.pas @@ -0,0 +1,612 @@ +{ $Header: /MidiComp/MidiOut.pas 2 10/06/97 7:33 Davec $ } + +{ Written by David Churcher , + released to the public domain. } + +{ Thanks very much to Fred Kohler for the Technology code. } + +unit MidiOut; + +{ + MIDI Output component. + + Properties: + DeviceID: Windows numeric device ID for the MIDI output device. + Between 0 and (midioutGetNumDevs-1), or MIDI_MAPPER (-1). + Special value MIDI_MAPPER specifies output to the Windows MIDI mapper + Read-only while device is open, exception if changed while open + + MIDIHandle: The output handle to the MIDI device. + 0 when device is not open + Read-only, runtime-only + + ProductName: Name of the output device product that corresponds to the + DeviceID property (e.g. 'MPU 401 out'). + You can write to this while the device is closed to select a particular + output device by name (the DeviceID property will change to match). + Exception if this property is changed while the device is open. + + Numdevs: Number of MIDI output devices installed on the system. This + is the value returned by midiOutGetNumDevs. It's included for + completeness. + + Technology: Type of technology used by the MIDI device. You can set this + property to one of the values listed for OutportTech (below) and the component + will find an appropriate MIDI device. For example: + MidiOutput.Technology := opt_FMSynth; + will set MidiInput.DeviceID to the MIDI device ID of the FM synth, if one + is installed. If no such device is available an exception is raised, + see MidiOutput.SetTechnology. + + See the MIDIOUTCAPS entry in MMSYSTEM.HLP for descriptions of the + following properties: + DriverVersion + Voices + Notes + ChannelMask + Support + + Error: The error code for the last MMSYSTEM error. See the MMSYSERR_ + entries in MMSYSTEM.INT for possible values. + + Methods: + Open: Open MIDI device specified by DeviceID property for output + + Close: Close device + + PutMidiEvent(Event:TMyMidiEvent): Output a note or sysex message to the + device. This method takes a TMyMidiEvent object and transmits it. + Notes: + 1. If the object contains a sysex event the OnMidiOutput event will + be triggered when the sysex transmission is complete. + 2. You can queue up multiple blocks of system exclusive data for + transmission by chucking them at this method; they will be + transmitted as quickly as the device can manage. + 3. This method will not free the TMyMidiEvent object, the caller + must do that. Any sysex data in the TMyMidiEvent is copied before + transmission so you can free the TMyMidiEvent immediately after + calling PutMidiEvent, even if output has not yet finished. + + PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte): Output a short + MIDI message. Handy when you can't be bothered to build a TMyMidiEvent. + If the message you're sending doesn't use Data1 or Data2, set them to 0. + + PutLong(TheSysex: Pointer; msgLength: Word): Output sysex data. + SysexPointer: Pointer to sysex data to send + msgLength: Length of sysex data. + This is handy when you don't have a TMyMidiEvent. + + SetVolume(Left: Word, Right: Word): Set the volume of the + left and right channels on the output device (only on internal devices?). + 0xFFFF is maximum volume. If the device doesn't support separate + left/right volume control, the value of the Left parameter will be used. + Check the Support property to see whether the device supports volume + control. See also other notes on volume control under midiOutSetVolume() + in MMSYSTEM.HLP. + + Events: + OnMidiOutput: Procedure called when output of a system exclusive block + is completed. + + Notes: + I haven't implemented any methods for midiOutCachePatches and + midiOutCacheDrumpatches, mainly 'cause I don't have any way of testing + them. Does anyone really use these? +} + +interface + +{$IFDEF FPC} + {$MODE Delphi} + {$H+} // use long strings +{$ENDIF} + +uses + SysUtils, + Windows, + Messages, + Classes, + MMSystem, + {$IFDEF FPC} + WinAllocation, + {$ENDIF} + CircBuf, + MidiType, + MidiDefs, + DelphiMcb; + +{$IFDEF FPC} +type TmidioutCaps = MIDIOUTCAPS; +{$ENDIF} + +type + midioutputState = (mosOpen, mosClosed); + EmidioutputError = class(Exception); + + { These are the equivalent of constants prefixed with mod_ + as defined in MMSystem. See SetTechnology } + OutPortTech = ( + opt_None, { none } + opt_MidiPort, { output port } + opt_Synth, { generic internal synth } + opt_SQSynth, { square wave internal synth } + opt_FMSynth, { FM internal synth } + opt_Mapper); { MIDI mapper } + TechNameMap = array[OutPortTech] of string[18]; + + +const + TechName: TechNameMap = ( + 'None', 'MIDI Port', 'Generic Synth', 'Square Wave Synth', + 'FM Synth', 'MIDI Mapper'); + +{-------------------------------------------------------------------} +type + TMidiOutput = class(TComponent) + protected + Handle: THandle; { Window handle used for callback notification } + FDeviceID: Cardinal; { MIDI device ID } + FMIDIHandle: Hmidiout; { Handle to output device } + FState: midioutputState; { Current device state } + PCtlInfo: PMidiCtlInfo; { Pointer to control info for DLL } + + PBuffer: PCircularBuffer; { Output queue for PutTimedEvent, set by Open } + + FError: Word; { Last MMSYSTEM error } + + { Stuff from midioutCAPS } + FDriverVersion: MMVERSION; { Driver version from midioutGetDevCaps } + FProductName: string; { product name } + FTechnology: OutPortTech; { Type of MIDI output device } + FVoices: Word; { Number of voices (internal synth) } + FNotes: Word; { Number of notes (internal synth) } + FChannelMask: Word; { Bit set for each MIDI channels that the + device responds to (internal synth) } + FSupport: DWORD; { Technology supported (volume control, + patch caching etc. } + FNumdevs: Word; { Number of MIDI output devices on system } + + + FOnMIDIOutput: TNotifyEvent; { Sysex output finished } + + procedure MidiOutput(var Message: TMessage); + procedure SetDeviceID(DeviceID: Cardinal); + procedure SetProductName(NewProductName: string); + procedure SetTechnology(NewTechnology: OutPortTech); + function midioutErrorString(WError: Word): string; + + public + { Properties } + property MIDIHandle: Hmidiout read FMIDIHandle; + property DriverVersion: MMVERSION { Driver version from midioutGetDevCaps } + read FDriverVersion; + property Technology: OutPortTech { Type of MIDI output device } + read FTechnology + write SetTechnology + default opt_Synth; + property Voices: Word { Number of voices (internal synth) } + read FVoices; + property Notes: Word { Number of notes (internal synth) } + read FNotes; + property ChannelMask: Word { Bit set for each MIDI channels that the } + read FChannelMask; { device responds to (internal synth) } + property Support: DWORD { Technology supported (volume control, } + read FSupport; { patch caching etc. } + property Error: Word read FError; + property Numdevs: Word read FNumdevs; + + { Methods } + function Open: Boolean; virtual; + function Close: Boolean; virtual; + procedure PutMidiEvent(theEvent: TMyMidiEvent); virtual; + procedure PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte); virtual; + procedure PutLong(TheSysex: Pointer; msgLength: Word); virtual; + procedure SetVolume(Left: Word; Right: Word); + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + + { Some functions to decode and classify incoming messages would be nice } + + published + { TODO: Property editor with dropdown list of product names } + property ProductName: string read FProductName write SetProductName; + + property DeviceID: Cardinal read FDeviceID write SetDeviceID default 0; + { TODO: midiOutGetVolume? Or two properties for Left and Right volume? + Is it worth it?? + midiOutMessage?? Does anyone use this? } + + { Events } + property Onmidioutput: TNotifyEvent + read FOnmidioutput + write FOnmidioutput; + end; + +procedure Register; + +{-------------------------------------------------------------------} +implementation + +(* Not used in Delphi 3 + +{ This is the callback procedure in the external DLL. + It's used when midioutOpen is called by the Open method. + There are special requirements and restrictions for this callback + procedure (see midioutOpen in MMSYSTEM.HLP) so it's impractical to + make it an object method } +{$IFDEF MSWINDOWS} +function midiHandler( + hMidiIn: HMidiIn; + wMsg: UINT; + dwInstance: DWORD; + dwParam1: DWORD; + dwParam2: DWORD): Boolean; stdcall; external 'DELMID32.DLL'; +{$ENDIF} +*) + +{-------------------------------------------------------------------} + +constructor Tmidioutput.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FState := mosClosed; + FNumdevs := midiOutGetNumDevs; + + { Create the window for callback notification } + if not (csDesigning in ComponentState) then + begin + Handle := AllocateHwnd(MidiOutput); + end; + +end; + +{-------------------------------------------------------------------} + +destructor Tmidioutput.Destroy; +begin + if FState = mosOpen then + Close; + if (PCtlInfo <> nil) then + GlobalSharedLockedFree(PCtlinfo^.hMem, PCtlInfo); + DeallocateHwnd(Handle); + inherited Destroy; +end; + +{-------------------------------------------------------------------} +{ Convert the numeric return code from an MMSYSTEM function to a string + using midioutGetErrorText. TODO: These errors aren't very helpful + (e.g. "an invalid parameter was passed to a system function") so + some proper error strings would be nice. } + + +function Tmidioutput.midioutErrorString(WError: Word): string; +var + errorDesc: PChar; +begin + errorDesc := nil; + try + errorDesc := StrAlloc(MAXERRORLENGTH); + if midioutGetErrorText(WError, errorDesc, MAXERRORLENGTH) = 0 then + result := StrPas(errorDesc) + else + result := 'Specified error number is out of range'; + finally + if errorDesc <> nil then StrDispose(errorDesc); + end; +end; + +{-------------------------------------------------------------------} +{ Set the output device ID and change the other properties to match } + +procedure Tmidioutput.SetDeviceID(DeviceID: Cardinal); +var + midioutCaps: TmidioutCaps; +begin + if FState = mosOpen then + raise EmidioutputError.Create('Change to DeviceID while device was open') + else + if (DeviceID >= midioutGetNumDevs) and (DeviceID <> MIDI_MAPPER) then + raise EmidioutputError.Create('Invalid device ID') + else + begin + FDeviceID := DeviceID; + + { Set the name and other midioutCAPS properties to match the ID } + FError := + midioutGetDevCaps(DeviceID, @midioutCaps, sizeof(TmidioutCaps)); + if Ferror > 0 then + raise EmidioutputError.Create(midioutErrorString(FError)); + + with midiOutCaps do + begin + FProductName := StrPas(szPname); + FDriverVersion := vDriverVersion; + FTechnology := OutPortTech(wTechnology); + FVoices := wVoices; + FNotes := wNotes; + FChannelMask := wChannelMask; + FSupport := dwSupport; + end; + + end; +end; + +{-------------------------------------------------------------------} +{ Set the product name property and put the matching output device number + in FDeviceID. + This is handy if you want to save a configured output/output device + by device name instead of device number, because device numbers may + change if users install or remove MIDI devices. + Exception if output device with matching name not found, + or if output device is open } + +procedure Tmidioutput.SetProductName(NewProductName: string); +var + midioutCaps: TmidioutCaps; + testDeviceID: Integer; + testProductName: string; +begin + if FState = mosOpen then + raise EmidioutputError.Create('Change to ProductName while device was open') + else + { Don't set the name if the component is reading properties because + the saved Productname will be from the machine the application was compiled + on, which may not be the same for the corresponding DeviceID on the user's + machine. The FProductname property will still be set by SetDeviceID } + if not (csLoading in ComponentState) then + begin + { Loop uses -1 to test for MIDI_MAPPER as well } + for testDeviceID := -1 to (midioutGetNumDevs - 1) do + begin + FError := + midioutGetDevCaps(testDeviceID, @midioutCaps, sizeof(TmidioutCaps)); + if Ferror > 0 then + raise EmidioutputError.Create(midioutErrorString(FError)); + testProductName := StrPas(midioutCaps.szPname); + if testProductName = NewProductName then + begin + FProductName := NewProductName; + Break; + end; + end; + if FProductName <> NewProductName then + raise EmidioutputError.Create('MIDI output Device ' + + NewProductName + ' not installed') + else + SetDeviceID(testDeviceID); + end; +end; + +{-------------------------------------------------------------------} +{ Set the output technology property and put the matching output device + number in FDeviceID. + This is handy, for example, if you want to be able to switch between a + sound card and a MIDI port } + +procedure TMidiOutput.SetTechnology(NewTechnology: OutPortTech); +var + midiOutCaps: TMidiOutCaps; + testDeviceID: Integer; + testTechnology: OutPortTech; +begin + if FState = mosOpen then + raise EMidiOutputError.Create( + 'Change to Product Technology while device was open') + else + begin + { Loop uses -1 to test for MIDI_MAPPER as well } + for testDeviceID := -1 to (midiOutGetNumDevs - 1) do + begin + FError := + midiOutGetDevCaps(testDeviceID, + @midiOutCaps, sizeof(TMidiOutCaps)); + if Ferror > 0 then + raise EMidiOutputError.Create(MidiOutErrorString(FError)); + testTechnology := OutPortTech(midiOutCaps.wTechnology); + if testTechnology = NewTechnology then + begin + FTechnology := NewTechnology; + Break; + end; + end; + if FTechnology <> NewTechnology then + raise EMidiOutputError.Create('MIDI output technology ' + + TechName[NewTechnology] + ' not installed') + else + SetDeviceID(testDeviceID); + end; +end; + +{-------------------------------------------------------------------} + +function Tmidioutput.Open: Boolean; +var + hMem: THandle; +begin + Result := False; + try + { Create the control info for the DLL } + if (PCtlInfo = nil) then + begin + PCtlInfo := GlobalSharedLockedAlloc(Sizeof(TMidiCtlInfo), hMem); + PctlInfo^.hMem := hMem; + end; + + Pctlinfo^.hWindow := Handle; { Control's window handle } + + FError := midioutOpen(@FMidiHandle, FDeviceId, + DWORD(@midiHandler), + DWORD(PCtlInfo), + CALLBACK_FUNCTION); +{ FError := midioutOpen(@FMidiHandle, FDeviceId, + Handle, + DWORD(PCtlInfo), + CALLBACK_WINDOW); } + if (FError <> 0) then + { TODO: use CreateFmtHelp to add MIDI device name/ID to message } + raise EmidioutputError.Create(midioutErrorString(FError)) + else + begin + Result := True; + FState := mosOpen; + end; + + except + if PCtlInfo <> nil then + begin + GlobalSharedLockedFree(PCtlInfo^.hMem, PCtlInfo); + PCtlInfo := nil; + end; + end; + +end; + +{-------------------------------------------------------------------} + +procedure TMidiOutput.PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte); +var + thisMsg: DWORD; +begin + thisMsg := DWORD(MidiMessage) or + (DWORD(Data1) shl 8) or + (DWORD(Data2) shl 16); + + FError := midiOutShortMsg(FMidiHandle, thisMsg); + if Ferror > 0 then + raise EmidioutputError.Create(midioutErrorString(FError)); +end; + +{-------------------------------------------------------------------} + +procedure TMidiOutput.PutLong(TheSysex: Pointer; msgLength: Word); +{ Notes: This works asynchronously; you send your sysex output by +calling this function, which returns immediately. When the MIDI device +driver has finished sending the data the MidiOutPut function in this +component is called, which will in turn call the OnMidiOutput method +if the component user has defined one. } +{ TODO: Combine common functions with PutTimedLong into subroutine } + +var + MyMidiHdr: TMyMidiHdr; +begin + { Initialize the header and allocate buffer memory } + MyMidiHdr := TMyMidiHdr.Create(msgLength); + + { Copy the data over to the MidiHdr buffer + We can't just use the caller's PChar because the buffer memory + has to be global, shareable, and locked. } + StrMove(MyMidiHdr.SysexPointer, TheSysex, msgLength); + + { Store the MyMidiHdr address in the header so we can find it again quickly + (see the MidiOutput proc) } + MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr); + + { Get MMSYSTEM's blessing for this header } + FError := midiOutPrepareHeader(FMidiHandle, MyMidiHdr.hdrPointer, + sizeof(TMIDIHDR)); + if Ferror > 0 then + raise EMidiOutputError.Create(MidiOutErrorString(FError)); + + { Send it } + FError := midiOutLongMsg(FMidiHandle, MyMidiHdr.hdrPointer, + sizeof(TMIDIHDR)); + if Ferror > 0 then + raise EMidiOutputError.Create(MidiOutErrorString(FError)); + +end; + +{-------------------------------------------------------------------} + +procedure Tmidioutput.PutMidiEvent(theEvent: TMyMidiEvent); +begin + if FState <> mosOpen then + raise EMidiOutputError.Create('MIDI Output device not open'); + + with theEvent do + begin + if Sysex = nil then + begin + PutShort(MidiMessage, Data1, Data2) + end + else + PutLong(Sysex, SysexLength); + end; +end; + +{-------------------------------------------------------------------} + +function Tmidioutput.Close: Boolean; +begin + Result := False; + if FState = mosOpen then + begin + + { Note this sends a lot of fast control change messages which some synths can't handle. + TODO: Make this optional. } +{ FError := midioutReset(FMidiHandle); + if Ferror <> 0 then + raise EMidiOutputError.Create(MidiOutErrorString(FError)); } + + FError := midioutClose(FMidiHandle); + if Ferror <> 0 then + raise EMidiOutputError.Create(MidiOutErrorString(FError)) + else + Result := True; + end; + + FMidiHandle := 0; + FState := mosClosed; + +end; + +{-------------------------------------------------------------------} + +procedure TMidiOutput.SetVolume(Left: Word; Right: Word); +var + dwVolume: DWORD; +begin + dwVolume := (DWORD(Left) shl 16) or Right; + FError := midiOutSetVolume(DeviceID, dwVolume); + if Ferror <> 0 then + raise EMidiOutputError.Create(MidiOutErrorString(FError)); +end; + +{-------------------------------------------------------------------} + +procedure Tmidioutput.midioutput(var Message: TMessage); +{ Triggered when sysex output from PutLong is complete } +var + MyMidiHdr: TMyMidiHdr; + thisHdr: PMidiHdr; +begin + if Message.Msg = Mom_Done then + begin + { Find the MIDIHDR we used for the output. Message.lParam is its address } + thisHdr := PMidiHdr(Message.lParam); + + { Remove it from the output device } + midiOutUnprepareHeader(FMidiHandle, thisHdr, sizeof(TMIDIHDR)); + + { Get the address of the MyMidiHdr object containing this MIDIHDR structure. + We stored this address in the PutLong procedure } + MyMidiHdr := TMyMidiHdr(thisHdr^.dwUser); + + { Header and copy of sysex data no longer required since output is complete } + MyMidiHdr.Free; + + { Call the user's event handler if any } + if Assigned(FOnmidioutput) then + FOnmidioutput(Self); + end; + { TODO: Case for MOM_PLAYBACK_DONE } +end; + +{-------------------------------------------------------------------} + +procedure Register; +begin + RegisterComponents('Synth', [Tmidioutput]); +end; + +end. + diff --git a/src/lib/midi/MidiType.pas b/src/lib/midi/MidiType.pas new file mode 100644 index 00000000..14157dbe --- /dev/null +++ b/src/lib/midi/MidiType.pas @@ -0,0 +1,90 @@ +{ $Header: /MidiComp/MidiType.pas 2 10/06/97 7:33 Davec $ } + +{ Written by David Churcher , + released to the public domain. } + + +unit MidiType; + +interface + +{$IFDEF FPC} + {$MODE Delphi} + {$H+} // use long strings +{$ENDIF} + +uses + Classes, + Windows, + Messages, + MMSystem, + MidiDefs, + CircBuf; + +type + {-------------------------------------------------------------------} + { A MIDI input/output event } + TMyMidiEvent = class(TPersistent) + public + MidiMessage: Byte; { MIDI message status byte } + Data1: Byte; { MIDI message data 1 byte } + Data2: Byte; { MIDI message data 2 byte } + Time: DWORD; { Time in ms since midiInOpen } + SysexLength: Word; { Length of sysex data (0 if none) } + Sysex: PChar; { Pointer to sysex data buffer } + destructor Destroy; override; { Frees sysex data buffer if nec. } + end; + PMyMidiEvent = ^TMyMidiEvent; + + {-------------------------------------------------------------------} + { Encapsulates the MIDIHDR with its memory handle and sysex buffer } + PMyMidiHdr = ^TMyMidiHdr; + TMyMidiHdr = class(TObject) + public + hdrHandle: THandle; + hdrPointer: PMIDIHDR; + sysexHandle: THandle; + sysexPointer: Pointer; + constructor Create(BufferSize: Word); + destructor Destroy; override; + end; + +implementation + +{-------------------------------------------------------------------} +{ Free any sysex buffer associated with the event } +destructor TMyMidiEvent.Destroy; +begin + if (Sysex <> Nil) then + Freemem(Sysex, SysexLength); + + inherited Destroy; +end; + +{-------------------------------------------------------------------} +{ Allocate memory for the sysex header and buffer } +constructor TMyMidiHdr.Create(BufferSize:Word); +begin + inherited Create; + + if BufferSize > 0 then + begin + hdrPointer := GlobalSharedLockedAlloc(sizeof(TMIDIHDR), hdrHandle); + sysexPointer := GlobalSharedLockedAlloc(BufferSize, sysexHandle); + + hdrPointer^.lpData := sysexPointer; + hdrPointer^.dwBufferLength := BufferSize; + end; +end; + +{-------------------------------------------------------------------} +destructor TMyMidiHdr.Destroy; +begin + GlobalSharedLockedFree( hdrHandle, hdrPointer ); + GlobalSharedLockedFree( sysexHandle, sysexPointer ); + inherited Destroy; +end; + + + +end. diff --git a/src/lib/midi/Midicons.pas b/src/lib/midi/Midicons.pas deleted file mode 100644 index 72259beb..00000000 --- a/src/lib/midi/Midicons.pas +++ /dev/null @@ -1,47 +0,0 @@ -{ $Header: /MidiComp/MIDICONS.PAS 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - - -{ MIDI Constants } -unit Midicons; - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses Messages; - -const - MIDI_ALLNOTESOFF = $7B; - MIDI_NOTEON = $90; - MIDI_NOTEOFF = $80; - MIDI_KEYAFTERTOUCH = $a0; - MIDI_CONTROLCHANGE = $b0; - MIDI_PROGRAMCHANGE = $c0; - MIDI_CHANAFTERTOUCH = $d0; - MIDI_PITCHBEND = $e0; - MIDI_SYSTEMMESSAGE = $f0; - MIDI_BEGINSYSEX = $f0; - MIDI_MTCQUARTERFRAME = $f1; - MIDI_SONGPOSPTR = $f2; - MIDI_SONGSELECT = $f3; - MIDI_ENDSYSEX = $F7; - MIDI_TIMINGCLOCK = $F8; - MIDI_START = $FA; - MIDI_CONTINUE = $FB; - MIDI_STOP = $FC; - MIDI_ACTIVESENSING = $FE; - MIDI_SYSTEMRESET = $FF; - - MIM_OVERFLOW = WM_USER; { Input buffer overflow } - MOM_PLAYBACK_DONE = WM_USER+1; { Timed playback complete } - - -implementation - -end. diff --git a/src/lib/midi/Midiin.pas b/src/lib/midi/Midiin.pas deleted file mode 100644 index a055669a..00000000 --- a/src/lib/midi/Midiin.pas +++ /dev/null @@ -1,720 +0,0 @@ -{ $Header: /MidiComp/Midiin.pas 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - -unit MidiIn; - -{ - Properties: - DeviceID: Windows numeric device ID for the MIDI input device. - Between 0 and NumDevs-1. - Read-only while device is open, exception when changed while open - - MIDIHandle: The input handle to the MIDI device. - 0 when device is not open - Read-only, runtime-only - - MessageCount: Number of input messages waiting in input buffer - - Capacity: Number of messages input buffer can hold - Defaults to 1024 - Limited to (64K/event size) - Read-only when device is open (exception when changed while open) - - SysexBufferSize: Size in bytes of each sysex buffer - Defaults to 10K - Minimum 0K (no buffers), Maximum 64K-1 - - SysexBufferCount: Number of sysex buffers - Defaults to 16 - Minimum 0 (no buffers), Maximum (avail mem/SysexBufferSize) - Check where these buffers are allocated? - - SysexOnly: True to ignore all non-sysex input events. May be changed while - device is open. Handy for patch editors where you have lots of short MIDI - events on the wire which you are always going to ignore anyway. - - DriverVersion: Version number of MIDI device driver. High-order byte is - major version, low-order byte is minor version. - - ProductName: Name of product (e.g. 'MPU 401 In') - - MID and PID: Manufacturer ID and Product ID, see - "Manufacturer and Product IDs" in MMSYSTEM.HLP for list of possible values. - - Methods: - GetMidiEvent: Read Midi event at the head of the FIFO input buffer. - Returns a TMyMidiEvent object containing MIDI message data, timestamp, - and sysex data if applicable. - This method automatically removes the event from the input buffer. - It makes a copy of the received sysex buffer and puts the buffer back - on the input device. - The TMyMidiEvent object must be freed by calling MyMidiEvent.Free. - - Open: Opens device. Note no input will appear until you call the Start - method. - - Close: Closes device. Any pending system exclusive output will be cancelled. - - Start: Starts receiving MIDI input. - - Stop: Stops receiving MIDI input. - - Events: - OnMidiInput: Called when MIDI input data arrives. Use the GetMidiEvent to - get the MIDI input data. - - OnOverflow: Called if the MIDI input buffer overflows. The caller must - clear the buffer before any more MIDI input can be received. - - Notes: - Buffering: Uses a circular buffer, separate pointers for next location - to fill and next location to empty because a MIDI input interrupt may - be adding data to the buffer while the buffer is being read. Buffer - pointers wrap around from end to start of buffer automatically. If - buffer overflows then the OnBufferOverflow event is triggered and no - further input will be received until the buffer is emptied by calls - to GetMidiEvent. - - Sysex buffers: There are (SysexBufferCount) buffers on the input device. - When sysex events arrive these buffers are removed from the input device and - added to the circular buffer by the interrupt handler in the DLL. When the sysex events - are removed from the circular buffer by the GetMidiEvent method the buffers are - put back on the input. If all the buffers are used up there will be no - more sysex input until at least one sysex event is removed from the input buffer. - In other words if you're expecting lots of sysex input you need to set the - SysexBufferCount property high enough so that you won't run out of - input buffers before you get a chance to read them with GetMidiEvent. - - If the synth sends a block of sysex that's longer than SysexBufferSize it - will be received as separate events. - TODO: Component derived from this one that handles >64K sysex blocks cleanly - and can stream them to disk. - - Midi Time Code (MTC) and Active Sensing: The DLL is currently hardcoded - to filter these short events out, so that we don't spend all our time - processing them. - TODO: implement a filter property to select the events that will be filtered - out. -} - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - Classes, - SysUtils, - Messages, - Windows, - MMSystem, - {$IFDEF FPC} - WinAllocation, - {$ENDIF} - MidiDefs, - MidiType, - MidiCons, - Circbuf, - Delphmcb; - -type - MidiInputState = (misOpen, misClosed, misCreating, misDestroying); - EMidiInputError = class(Exception); - - {-------------------------------------------------------------------} - TMidiInput = class(TComponent) - private - Handle: THandle; { Window handle used for callback notification } - FDeviceID: Word; { MIDI device ID } - FMIDIHandle: HMIDIIn; { Handle to input device } - FState: MidiInputState; { Current device state } - - FError: Word; - FSysexOnly: Boolean; - - { Stuff from MIDIINCAPS } - FDriverVersion: MMVERSION; - FProductName: string; - FMID: Word; { Manufacturer ID } - FPID: Word; { Product ID } - - { Queue } - FCapacity: Word; { Buffer capacity } - PBuffer: PCircularBuffer; { Low-level MIDI input buffer created by Open method } - FNumdevs: Word; { Number of input devices on system } - - { Events } - FOnMIDIInput: TNotifyEvent; { MIDI Input arrived } - FOnOverflow: TNotifyEvent; { Input buffer overflow } - { TODO: Some sort of error handling event for MIM_ERROR } - - { Sysex } - FSysexBufferSize: Word; - FSysexBufferCount: Word; - MidiHdrs: Tlist; - - PCtlInfo: PMidiCtlInfo; { Pointer to control info for DLL } - - protected - procedure Prepareheaders; - procedure UnprepareHeaders; - procedure AddBuffers; - procedure SetDeviceID(DeviceID: Word); - procedure SetProductName(NewProductName: string); - function GetEventCount: Word; - procedure SetSysexBufferSize(BufferSize: Word); - procedure SetSysexBufferCount(BufferCount: Word); - procedure SetSysexOnly(bSysexOnly: Boolean); - function MidiInErrorString(WError: Word): string; - - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - - property MIDIHandle: HMIDIIn read FMIDIHandle; - - property DriverVersion: MMVERSION read FDriverVersion; - property MID: Word read FMID; { Manufacturer ID } - property PID: Word read FPID; { Product ID } - - property Numdevs: Word read FNumdevs; - - property MessageCount: Word read GetEventCount; - { TODO: property to select which incoming messages get filtered out } - - procedure Open; - procedure Close; - procedure Start; - procedure Stop; - { Get first message in input queue } - function GetMidiEvent: TMyMidiEvent; - procedure MidiInput(var Message: TMessage); - - { Some functions to decode and classify incoming messages would be good } - - published - - { TODO: Property editor with dropdown list of product names } - property ProductName: string read FProductName write SetProductName; - - property DeviceID: Word read FDeviceID write SetDeviceID default 0; - property Capacity: Word read FCapacity write FCapacity default 1024; - property Error: Word read FError; - property SysexBufferSize: Word - read FSysexBufferSize - write SetSysexBufferSize - default 10000; - property SysexBufferCount: Word - read FSysexBufferCount - write SetSysexBufferCount - default 16; - property SysexOnly: Boolean - read FSysexOnly - write SetSysexOnly - default False; - - { Events } - property OnMidiInput: TNotifyEvent read FOnMidiInput write FOnMidiInput; - property OnOverflow: TNotifyEvent read FOnOverflow write FOnOverflow; - - end; - -procedure Register; - -{====================================================================} -implementation - -uses Controls, - Graphics; - -(* Not used in Delphi 3 -{ This is the callback procedure in the external DLL. - It's used when midiInOpen is called by the Open method. - There are special requirements and restrictions for this callback - procedure (see midiInOpen in MMSYSTEM.HLP) so it's impractical to - make it an object method } -{$IFDEF MSWINDOWS} -function midiHandler( - hMidiIn: HMidiIn; - wMsg: UINT; - dwInstance: DWORD; - dwParam1: DWORD; - dwParam2: DWORD): Boolean; stdcall; external 'DELMID32.DLL'; -{$ENDIF} -*) -{-------------------------------------------------------------------} - -constructor TMidiInput.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - FState := misCreating; - - FSysexOnly := False; - FNumDevs := midiInGetNumDevs; - MidiHdrs := nil; - - { Set defaults } - if (FNumDevs > 0) then - SetDeviceID(0); - FCapacity := 1024; - FSysexBufferSize := 4096; - FSysexBufferCount := 16; - - { Create the window for callback notification } - if not (csDesigning in ComponentState) then - begin - Handle := AllocateHwnd(MidiInput); - end; - - FState := misClosed; - -end; - -{-------------------------------------------------------------------} -{ Close the device if it's open } - -destructor TMidiInput.Destroy; -begin - if (FMidiHandle <> 0) then - begin - Close; - FMidiHandle := 0; - end; - - if (PCtlInfo <> nil) then - GlobalSharedLockedFree(PCtlinfo^.hMem, PCtlInfo); - - DeallocateHwnd(Handle); - inherited Destroy; -end; - -{-------------------------------------------------------------------} -{ Convert the numeric return code from an MMSYSTEM function to a string - using midiInGetErrorText. TODO: These errors aren't very helpful - (e.g. "an invalid parameter was passed to a system function") so - sort out some proper error strings. } - -function TMidiInput.MidiInErrorString(WError: Word): string; -var - errorDesc: PChar; -begin - errorDesc := nil; - try - errorDesc := StrAlloc(MAXERRORLENGTH); - if midiInGetErrorText(WError, errorDesc, MAXERRORLENGTH) = 0 then - result := StrPas(errorDesc) - else - result := 'Specified error number is out of range'; - finally - if errorDesc <> nil then StrDispose(errorDesc); - end; -end; - -{-------------------------------------------------------------------} -{ Set the sysex buffer size, fail if device is already open } - -procedure TMidiInput.SetSysexBufferSize(BufferSize: Word); -begin - if FState = misOpen then - raise EMidiInputError.Create('Change to SysexBufferSize while device was open') - else - { TODO: Validate the sysex buffer size. Is this necessary for WIN32? } - FSysexBufferSize := BufferSize; -end; - -{-------------------------------------------------------------------} -{ Set the sysex buffer count, fail if device is already open } - -procedure TMidiInput.SetSysexBuffercount(Buffercount: Word); -begin - if FState = misOpen then - raise EMidiInputError.Create('Change to SysexBuffercount while device was open') - else - { TODO: Validate the sysex buffer count } - FSysexBuffercount := Buffercount; -end; - -{-------------------------------------------------------------------} -{ Set the Sysex Only flag to eliminate unwanted short MIDI input messages } - -procedure TMidiInput.SetSysexOnly(bSysexOnly: Boolean); -begin - FSysexOnly := bSysexOnly; - { Update the interrupt handler's copy of this property } - if PCtlInfo <> nil then - PCtlInfo^.SysexOnly := bSysexOnly; -end; - -{-------------------------------------------------------------------} -{ Set the Device ID to select a new MIDI input device - Note: If no MIDI devices are installed, throws an 'Invalid Device ID' exception } - -procedure TMidiInput.SetDeviceID(DeviceID: Word); -var - MidiInCaps: TMidiInCaps; -begin - if FState = misOpen then - raise EMidiInputError.Create('Change to DeviceID while device was open') - else - if (DeviceID >= midiInGetNumDevs) then - raise EMidiInputError.Create('Invalid device ID') - else - begin - FDeviceID := DeviceID; - - { Set the name and other MIDIINCAPS properties to match the ID } - FError := - midiInGetDevCaps(DeviceID, @MidiInCaps, sizeof(TMidiInCaps)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - - FProductName := StrPas(MidiInCaps.szPname); - FDriverVersion := MidiInCaps.vDriverVersion; - FMID := MidiInCaps.wMID; - FPID := MidiInCaps.wPID; - - end; -end; - -{-------------------------------------------------------------------} -{ Set the product name and put the matching input device number in FDeviceID. - This is handy if you want to save a configured input/output device - by device name instead of device number, because device numbers may - change if users add or remove MIDI devices. - Exception if input device with matching name not found, - or if input device is open } - -procedure TMidiInput.SetProductName(NewProductName: string); -var - MidiInCaps: TMidiInCaps; - testDeviceID: Word; - testProductName: string; -begin - if FState = misOpen then - raise EMidiInputError.Create('Change to ProductName while device was open') - else - { Don't set the name if the component is reading properties because - the saved Productname will be from the machine the application was compiled - on, which may not be the same for the corresponding DeviceID on the user's - machine. The FProductname property will still be set by SetDeviceID } - if not (csLoading in ComponentState) then - begin - begin - for testDeviceID := 0 to (midiInGetNumDevs - 1) do - begin - FError := - midiInGetDevCaps(testDeviceID, @MidiInCaps, sizeof(TMidiInCaps)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - testProductName := StrPas(MidiInCaps.szPname); - if testProductName = NewProductName then - begin - FProductName := NewProductName; - Break; - end; - end; - if FProductName <> NewProductName then - raise EMidiInputError.Create('MIDI Input Device ' + - NewProductName + ' not installed ') - else - SetDeviceID(testDeviceID); - end; - end; -end; - - -{-------------------------------------------------------------------} -{ Get the sysex buffers ready } - -procedure TMidiInput.PrepareHeaders; -var - ctr: Word; - MyMidiHdr: TMyMidiHdr; -begin - if (FSysexBufferCount > 0) and (FSysexBufferSize > 0) - and (FMidiHandle <> 0) then - begin - Midihdrs := TList.Create; - for ctr := 1 to FSysexBufferCount do - begin - { Initialize the header and allocate buffer memory } - MyMidiHdr := TMyMidiHdr.Create(FSysexBufferSize); - - { Store the address of the MyMidiHdr object in the contained MIDIHDR - structure so we can get back to the object when a pointer to the - MIDIHDR is received. - E.g. see TMidiOutput.Output method } - MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr); - - { Get MMSYSTEM's blessing for this header } - FError := midiInPrepareHeader(FMidiHandle, MyMidiHdr.hdrPointer, - sizeof(TMIDIHDR)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - - { Save it in our list } - MidiHdrs.Add(MyMidiHdr); - end; - end; - -end; - -{-------------------------------------------------------------------} -{ Clean up from PrepareHeaders } - -procedure TMidiInput.UnprepareHeaders; -var - ctr: Word; -begin - if (MidiHdrs <> nil) then { will be Nil if 0 sysex buffers } - begin - for ctr := 0 to MidiHdrs.Count - 1 do - begin - FError := midiInUnprepareHeader(FMidiHandle, - TMyMidiHdr(MidiHdrs.Items[ctr]).hdrPointer, - sizeof(TMIDIHDR)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - TMyMidiHdr(MidiHdrs.Items[ctr]).Free; - end; - MidiHdrs.Free; - MidiHdrs := nil; - end; -end; - -{-------------------------------------------------------------------} -{ Add sysex buffers, if required, to input device } - -procedure TMidiInput.AddBuffers; -var - ctr: Word; -begin - if MidiHdrs <> nil then { will be Nil if 0 sysex buffers } - begin - if MidiHdrs.Count > 0 then - begin - for ctr := 0 to MidiHdrs.Count - 1 do - begin - FError := midiInAddBuffer(FMidiHandle, - TMyMidiHdr(MidiHdrs.Items[ctr]).hdrPointer, - sizeof(TMIDIHDR)); - if FError <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - end; - end; - end; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.Open; -var - hMem: THandle; -begin - try - { Create the buffer for the MIDI input messages } - if (PBuffer = nil) then - PBuffer := CircBufAlloc(FCapacity); - - { Create the control info for the DLL } - if (PCtlInfo = nil) then - begin - PCtlInfo := GlobalSharedLockedAlloc(Sizeof(TMidiCtlInfo), hMem); - PctlInfo^.hMem := hMem; - end; - PctlInfo^.pBuffer := PBuffer; - Pctlinfo^.hWindow := Handle; { Control's window handle } - PCtlInfo^.SysexOnly := FSysexOnly; - FError := midiInOpen(@FMidiHandle, FDeviceId, - DWORD(@midiHandler), - DWORD(PCtlInfo), - CALLBACK_FUNCTION); - - if (FError <> MMSYSERR_NOERROR) then - { TODO: use CreateFmtHelp to add MIDI device name/ID to message } - raise EMidiInputError.Create(MidiInErrorString(FError)); - - { Get sysex buffers ready } - PrepareHeaders; - - { Add them to the input } - AddBuffers; - - FState := misOpen; - - except - if PBuffer <> nil then - begin - CircBufFree(PBuffer); - PBuffer := nil; - end; - - if PCtlInfo <> nil then - begin - GlobalSharedLockedFree(PCtlInfo^.hMem, PCtlInfo); - PCtlInfo := nil; - end; - - end; - -end; - -{-------------------------------------------------------------------} - -function TMidiInput.GetMidiEvent: TMyMidiEvent; -var - thisItem: TMidiBufferItem; -begin - if (FState = misOpen) and - CircBufReadEvent(PBuffer, @thisItem) then - begin - Result := TMyMidiEvent.Create; - with thisItem do - begin - Result.Time := Timestamp; - if (Sysex = nil) then - begin - { Short message } - Result.MidiMessage := LoByte(LoWord(Data)); - Result.Data1 := HiByte(LoWord(Data)); - Result.Data2 := LoByte(HiWord(Data)); - Result.Sysex := nil; - Result.SysexLength := 0; - end - else - { Long Sysex message } - begin - Result.MidiMessage := MIDI_BEGINSYSEX; - Result.Data1 := 0; - Result.Data2 := 0; - Result.SysexLength := Sysex^.dwBytesRecorded; - if Sysex^.dwBytesRecorded <> 0 then - begin - { Put a copy of the sysex buffer in the object } - GetMem(Result.Sysex, Sysex^.dwBytesRecorded); - StrMove(Result.Sysex, Sysex^.lpData, Sysex^.dwBytesRecorded); - end; - - { Put the header back on the input buffer } - FError := midiInPrepareHeader(FMidiHandle, Sysex, - sizeof(TMIDIHDR)); - if Ferror = 0 then - FError := midiInAddBuffer(FMidiHandle, - Sysex, sizeof(TMIDIHDR)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - end; - end; - CircbufRemoveEvent(PBuffer); - end - else - { Device isn't open, return a nil event } - Result := nil; -end; - -{-------------------------------------------------------------------} - -function TMidiInput.GetEventCount: Word; -begin - if FState = misOpen then - Result := PBuffer^.EventCount - else - Result := 0; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.Close; -begin - if FState = misOpen then - begin - FState := misClosed; - - { MidiInReset cancels any pending output. - Note that midiInReset causes an MIM_LONGDATA callback for each sysex - buffer on the input, so the callback function and Midi input buffer - should still be viable at this stage. - All the resulting MIM_LONGDATA callbacks will be completed by the time - MidiInReset returns, though. } - FError := MidiInReset(FMidiHandle); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - - { Remove sysex buffers from input device and free them } - UnPrepareHeaders; - - { Close the device (finally!) } - FError := MidiInClose(FMidiHandle); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - - FMidiHandle := 0; - - if (PBuffer <> nil) then - begin - CircBufFree(PBuffer); - PBuffer := nil; - end; - end; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.Start; -begin - if FState = misOpen then - begin - FError := MidiInStart(FMidiHandle); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - end; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.Stop; -begin - if FState = misOpen then - begin - FError := MidiInStop(FMidiHandle); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - end; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.MidiInput(var Message: TMessage); -{ Triggered by incoming message from DLL. - Note DLL has already put the message in the queue } -begin - case Message.Msg of - mim_data: - { Trigger the user's MIDI input event, if they've specified one and - we're not in the process of closing the device. The check for - GetEventCount > 0 prevents unnecessary event calls where the user has - already cleared all the events from the input buffer using a GetMidiEvent - loop in the OnMidiInput event handler } - if Assigned(FOnMIDIInput) and (FState = misOpen) - and (GetEventCount > 0) then - FOnMIDIInput(Self); - - mim_Overflow: { input circular buffer overflow } - if Assigned(FOnOverflow) and (FState = misOpen) then - FOnOverflow(Self); - end; -end; - -{-------------------------------------------------------------------} - -procedure Register; -begin - RegisterComponents('Synth', [TMIDIInput]); -end; - -end. - diff --git a/src/lib/midi/Midiout.pas b/src/lib/midi/Midiout.pas deleted file mode 100644 index 7ce385eb..00000000 --- a/src/lib/midi/Midiout.pas +++ /dev/null @@ -1,612 +0,0 @@ -{ $Header: /MidiComp/MidiOut.pas 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - -{ Thanks very much to Fred Kohler for the Technology code. } - -unit MidiOut; - -{ - MIDI Output component. - - Properties: - DeviceID: Windows numeric device ID for the MIDI output device. - Between 0 and (midioutGetNumDevs-1), or MIDI_MAPPER (-1). - Special value MIDI_MAPPER specifies output to the Windows MIDI mapper - Read-only while device is open, exception if changed while open - - MIDIHandle: The output handle to the MIDI device. - 0 when device is not open - Read-only, runtime-only - - ProductName: Name of the output device product that corresponds to the - DeviceID property (e.g. 'MPU 401 out'). - You can write to this while the device is closed to select a particular - output device by name (the DeviceID property will change to match). - Exception if this property is changed while the device is open. - - Numdevs: Number of MIDI output devices installed on the system. This - is the value returned by midiOutGetNumDevs. It's included for - completeness. - - Technology: Type of technology used by the MIDI device. You can set this - property to one of the values listed for OutportTech (below) and the component - will find an appropriate MIDI device. For example: - MidiOutput.Technology := opt_FMSynth; - will set MidiInput.DeviceID to the MIDI device ID of the FM synth, if one - is installed. If no such device is available an exception is raised, - see MidiOutput.SetTechnology. - - See the MIDIOUTCAPS entry in MMSYSTEM.HLP for descriptions of the - following properties: - DriverVersion - Voices - Notes - ChannelMask - Support - - Error: The error code for the last MMSYSTEM error. See the MMSYSERR_ - entries in MMSYSTEM.INT for possible values. - - Methods: - Open: Open MIDI device specified by DeviceID property for output - - Close: Close device - - PutMidiEvent(Event:TMyMidiEvent): Output a note or sysex message to the - device. This method takes a TMyMidiEvent object and transmits it. - Notes: - 1. If the object contains a sysex event the OnMidiOutput event will - be triggered when the sysex transmission is complete. - 2. You can queue up multiple blocks of system exclusive data for - transmission by chucking them at this method; they will be - transmitted as quickly as the device can manage. - 3. This method will not free the TMyMidiEvent object, the caller - must do that. Any sysex data in the TMyMidiEvent is copied before - transmission so you can free the TMyMidiEvent immediately after - calling PutMidiEvent, even if output has not yet finished. - - PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte): Output a short - MIDI message. Handy when you can't be bothered to build a TMyMidiEvent. - If the message you're sending doesn't use Data1 or Data2, set them to 0. - - PutLong(TheSysex: Pointer; msgLength: Word): Output sysex data. - SysexPointer: Pointer to sysex data to send - msgLength: Length of sysex data. - This is handy when you don't have a TMyMidiEvent. - - SetVolume(Left: Word, Right: Word): Set the volume of the - left and right channels on the output device (only on internal devices?). - 0xFFFF is maximum volume. If the device doesn't support separate - left/right volume control, the value of the Left parameter will be used. - Check the Support property to see whether the device supports volume - control. See also other notes on volume control under midiOutSetVolume() - in MMSYSTEM.HLP. - - Events: - OnMidiOutput: Procedure called when output of a system exclusive block - is completed. - - Notes: - I haven't implemented any methods for midiOutCachePatches and - midiOutCacheDrumpatches, mainly 'cause I don't have any way of testing - them. Does anyone really use these? -} - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - SysUtils, - Windows, - Messages, - Classes, - MMSystem, - {$IFDEF FPC} - WinAllocation, - {$ENDIF} - Circbuf, - MidiType, - MidiDefs, - Delphmcb; - -{$IFDEF FPC} -type TmidioutCaps = MIDIOUTCAPS; -{$ENDIF} - -type - midioutputState = (mosOpen, mosClosed); - EmidioutputError = class(Exception); - - { These are the equivalent of constants prefixed with mod_ - as defined in MMSystem. See SetTechnology } - OutPortTech = ( - opt_None, { none } - opt_MidiPort, { output port } - opt_Synth, { generic internal synth } - opt_SQSynth, { square wave internal synth } - opt_FMSynth, { FM internal synth } - opt_Mapper); { MIDI mapper } - TechNameMap = array[OutPortTech] of string[18]; - - -const - TechName: TechNameMap = ( - 'None', 'MIDI Port', 'Generic Synth', 'Square Wave Synth', - 'FM Synth', 'MIDI Mapper'); - -{-------------------------------------------------------------------} -type - TMidiOutput = class(TComponent) - protected - Handle: THandle; { Window handle used for callback notification } - FDeviceID: Cardinal; { MIDI device ID } - FMIDIHandle: Hmidiout; { Handle to output device } - FState: midioutputState; { Current device state } - PCtlInfo: PMidiCtlInfo; { Pointer to control info for DLL } - - PBuffer: PCircularBuffer; { Output queue for PutTimedEvent, set by Open } - - FError: Word; { Last MMSYSTEM error } - - { Stuff from midioutCAPS } - FDriverVersion: MMVERSION; { Driver version from midioutGetDevCaps } - FProductName: string; { product name } - FTechnology: OutPortTech; { Type of MIDI output device } - FVoices: Word; { Number of voices (internal synth) } - FNotes: Word; { Number of notes (internal synth) } - FChannelMask: Word; { Bit set for each MIDI channels that the - device responds to (internal synth) } - FSupport: DWORD; { Technology supported (volume control, - patch caching etc. } - FNumdevs: Word; { Number of MIDI output devices on system } - - - FOnMIDIOutput: TNotifyEvent; { Sysex output finished } - - procedure MidiOutput(var Message: TMessage); - procedure SetDeviceID(DeviceID: Cardinal); - procedure SetProductName(NewProductName: string); - procedure SetTechnology(NewTechnology: OutPortTech); - function midioutErrorString(WError: Word): string; - - public - { Properties } - property MIDIHandle: Hmidiout read FMIDIHandle; - property DriverVersion: MMVERSION { Driver version from midioutGetDevCaps } - read FDriverVersion; - property Technology: OutPortTech { Type of MIDI output device } - read FTechnology - write SetTechnology - default opt_Synth; - property Voices: Word { Number of voices (internal synth) } - read FVoices; - property Notes: Word { Number of notes (internal synth) } - read FNotes; - property ChannelMask: Word { Bit set for each MIDI channels that the } - read FChannelMask; { device responds to (internal synth) } - property Support: DWORD { Technology supported (volume control, } - read FSupport; { patch caching etc. } - property Error: Word read FError; - property Numdevs: Word read FNumdevs; - - { Methods } - function Open: Boolean; virtual; - function Close: Boolean; virtual; - procedure PutMidiEvent(theEvent: TMyMidiEvent); virtual; - procedure PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte); virtual; - procedure PutLong(TheSysex: Pointer; msgLength: Word); virtual; - procedure SetVolume(Left: Word; Right: Word); - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - - { Some functions to decode and classify incoming messages would be nice } - - published - { TODO: Property editor with dropdown list of product names } - property ProductName: string read FProductName write SetProductName; - - property DeviceID: Cardinal read FDeviceID write SetDeviceID default 0; - { TODO: midiOutGetVolume? Or two properties for Left and Right volume? - Is it worth it?? - midiOutMessage?? Does anyone use this? } - - { Events } - property Onmidioutput: TNotifyEvent - read FOnmidioutput - write FOnmidioutput; - end; - -procedure Register; - -{-------------------------------------------------------------------} -implementation - -(* Not used in Delphi 3 - -{ This is the callback procedure in the external DLL. - It's used when midioutOpen is called by the Open method. - There are special requirements and restrictions for this callback - procedure (see midioutOpen in MMSYSTEM.HLP) so it's impractical to - make it an object method } -{$IFDEF MSWINDOWS} -function midiHandler( - hMidiIn: HMidiIn; - wMsg: UINT; - dwInstance: DWORD; - dwParam1: DWORD; - dwParam2: DWORD): Boolean; stdcall; external 'DELMID32.DLL'; -{$ENDIF} -*) - -{-------------------------------------------------------------------} - -constructor Tmidioutput.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - FState := mosClosed; - FNumdevs := midiOutGetNumDevs; - - { Create the window for callback notification } - if not (csDesigning in ComponentState) then - begin - Handle := AllocateHwnd(MidiOutput); - end; - -end; - -{-------------------------------------------------------------------} - -destructor Tmidioutput.Destroy; -begin - if FState = mosOpen then - Close; - if (PCtlInfo <> nil) then - GlobalSharedLockedFree(PCtlinfo^.hMem, PCtlInfo); - DeallocateHwnd(Handle); - inherited Destroy; -end; - -{-------------------------------------------------------------------} -{ Convert the numeric return code from an MMSYSTEM function to a string - using midioutGetErrorText. TODO: These errors aren't very helpful - (e.g. "an invalid parameter was passed to a system function") so - some proper error strings would be nice. } - - -function Tmidioutput.midioutErrorString(WError: Word): string; -var - errorDesc: PChar; -begin - errorDesc := nil; - try - errorDesc := StrAlloc(MAXERRORLENGTH); - if midioutGetErrorText(WError, errorDesc, MAXERRORLENGTH) = 0 then - result := StrPas(errorDesc) - else - result := 'Specified error number is out of range'; - finally - if errorDesc <> nil then StrDispose(errorDesc); - end; -end; - -{-------------------------------------------------------------------} -{ Set the output device ID and change the other properties to match } - -procedure Tmidioutput.SetDeviceID(DeviceID: Cardinal); -var - midioutCaps: TmidioutCaps; -begin - if FState = mosOpen then - raise EmidioutputError.Create('Change to DeviceID while device was open') - else - if (DeviceID >= midioutGetNumDevs) and (DeviceID <> MIDI_MAPPER) then - raise EmidioutputError.Create('Invalid device ID') - else - begin - FDeviceID := DeviceID; - - { Set the name and other midioutCAPS properties to match the ID } - FError := - midioutGetDevCaps(DeviceID, @midioutCaps, sizeof(TmidioutCaps)); - if Ferror > 0 then - raise EmidioutputError.Create(midioutErrorString(FError)); - - with midiOutCaps do - begin - FProductName := StrPas(szPname); - FDriverVersion := vDriverVersion; - FTechnology := OutPortTech(wTechnology); - FVoices := wVoices; - FNotes := wNotes; - FChannelMask := wChannelMask; - FSupport := dwSupport; - end; - - end; -end; - -{-------------------------------------------------------------------} -{ Set the product name property and put the matching output device number - in FDeviceID. - This is handy if you want to save a configured output/output device - by device name instead of device number, because device numbers may - change if users install or remove MIDI devices. - Exception if output device with matching name not found, - or if output device is open } - -procedure Tmidioutput.SetProductName(NewProductName: string); -var - midioutCaps: TmidioutCaps; - testDeviceID: Integer; - testProductName: string; -begin - if FState = mosOpen then - raise EmidioutputError.Create('Change to ProductName while device was open') - else - { Don't set the name if the component is reading properties because - the saved Productname will be from the machine the application was compiled - on, which may not be the same for the corresponding DeviceID on the user's - machine. The FProductname property will still be set by SetDeviceID } - if not (csLoading in ComponentState) then - begin - { Loop uses -1 to test for MIDI_MAPPER as well } - for testDeviceID := -1 to (midioutGetNumDevs - 1) do - begin - FError := - midioutGetDevCaps(testDeviceID, @midioutCaps, sizeof(TmidioutCaps)); - if Ferror > 0 then - raise EmidioutputError.Create(midioutErrorString(FError)); - testProductName := StrPas(midioutCaps.szPname); - if testProductName = NewProductName then - begin - FProductName := NewProductName; - Break; - end; - end; - if FProductName <> NewProductName then - raise EmidioutputError.Create('MIDI output Device ' + - NewProductName + ' not installed') - else - SetDeviceID(testDeviceID); - end; -end; - -{-------------------------------------------------------------------} -{ Set the output technology property and put the matching output device - number in FDeviceID. - This is handy, for example, if you want to be able to switch between a - sound card and a MIDI port } - -procedure TMidiOutput.SetTechnology(NewTechnology: OutPortTech); -var - midiOutCaps: TMidiOutCaps; - testDeviceID: Integer; - testTechnology: OutPortTech; -begin - if FState = mosOpen then - raise EMidiOutputError.Create( - 'Change to Product Technology while device was open') - else - begin - { Loop uses -1 to test for MIDI_MAPPER as well } - for testDeviceID := -1 to (midiOutGetNumDevs - 1) do - begin - FError := - midiOutGetDevCaps(testDeviceID, - @midiOutCaps, sizeof(TMidiOutCaps)); - if Ferror > 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); - testTechnology := OutPortTech(midiOutCaps.wTechnology); - if testTechnology = NewTechnology then - begin - FTechnology := NewTechnology; - Break; - end; - end; - if FTechnology <> NewTechnology then - raise EMidiOutputError.Create('MIDI output technology ' + - TechName[NewTechnology] + ' not installed') - else - SetDeviceID(testDeviceID); - end; -end; - -{-------------------------------------------------------------------} - -function Tmidioutput.Open: Boolean; -var - hMem: THandle; -begin - Result := False; - try - { Create the control info for the DLL } - if (PCtlInfo = nil) then - begin - PCtlInfo := GlobalSharedLockedAlloc(Sizeof(TMidiCtlInfo), hMem); - PctlInfo^.hMem := hMem; - end; - - Pctlinfo^.hWindow := Handle; { Control's window handle } - - FError := midioutOpen(@FMidiHandle, FDeviceId, - DWORD(@midiHandler), - DWORD(PCtlInfo), - CALLBACK_FUNCTION); -{ FError := midioutOpen(@FMidiHandle, FDeviceId, - Handle, - DWORD(PCtlInfo), - CALLBACK_WINDOW); } - if (FError <> 0) then - { TODO: use CreateFmtHelp to add MIDI device name/ID to message } - raise EmidioutputError.Create(midioutErrorString(FError)) - else - begin - Result := True; - FState := mosOpen; - end; - - except - if PCtlInfo <> nil then - begin - GlobalSharedLockedFree(PCtlInfo^.hMem, PCtlInfo); - PCtlInfo := nil; - end; - end; - -end; - -{-------------------------------------------------------------------} - -procedure TMidiOutput.PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte); -var - thisMsg: DWORD; -begin - thisMsg := DWORD(MidiMessage) or - (DWORD(Data1) shl 8) or - (DWORD(Data2) shl 16); - - FError := midiOutShortMsg(FMidiHandle, thisMsg); - if Ferror > 0 then - raise EmidioutputError.Create(midioutErrorString(FError)); -end; - -{-------------------------------------------------------------------} - -procedure TMidiOutput.PutLong(TheSysex: Pointer; msgLength: Word); -{ Notes: This works asynchronously; you send your sysex output by -calling this function, which returns immediately. When the MIDI device -driver has finished sending the data the MidiOutPut function in this -component is called, which will in turn call the OnMidiOutput method -if the component user has defined one. } -{ TODO: Combine common functions with PutTimedLong into subroutine } - -var - MyMidiHdr: TMyMidiHdr; -begin - { Initialize the header and allocate buffer memory } - MyMidiHdr := TMyMidiHdr.Create(msgLength); - - { Copy the data over to the MidiHdr buffer - We can't just use the caller's PChar because the buffer memory - has to be global, shareable, and locked. } - StrMove(MyMidiHdr.SysexPointer, TheSysex, msgLength); - - { Store the MyMidiHdr address in the header so we can find it again quickly - (see the MidiOutput proc) } - MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr); - - { Get MMSYSTEM's blessing for this header } - FError := midiOutPrepareHeader(FMidiHandle, MyMidiHdr.hdrPointer, - sizeof(TMIDIHDR)); - if Ferror > 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); - - { Send it } - FError := midiOutLongMsg(FMidiHandle, MyMidiHdr.hdrPointer, - sizeof(TMIDIHDR)); - if Ferror > 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); - -end; - -{-------------------------------------------------------------------} - -procedure Tmidioutput.PutMidiEvent(theEvent: TMyMidiEvent); -begin - if FState <> mosOpen then - raise EMidiOutputError.Create('MIDI Output device not open'); - - with theEvent do - begin - if Sysex = nil then - begin - PutShort(MidiMessage, Data1, Data2) - end - else - PutLong(Sysex, SysexLength); - end; -end; - -{-------------------------------------------------------------------} - -function Tmidioutput.Close: Boolean; -begin - Result := False; - if FState = mosOpen then - begin - - { Note this sends a lot of fast control change messages which some synths can't handle. - TODO: Make this optional. } -{ FError := midioutReset(FMidiHandle); - if Ferror <> 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); } - - FError := midioutClose(FMidiHandle); - if Ferror <> 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)) - else - Result := True; - end; - - FMidiHandle := 0; - FState := mosClosed; - -end; - -{-------------------------------------------------------------------} - -procedure TMidiOutput.SetVolume(Left: Word; Right: Word); -var - dwVolume: DWORD; -begin - dwVolume := (DWORD(Left) shl 16) or Right; - FError := midiOutSetVolume(DeviceID, dwVolume); - if Ferror <> 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); -end; - -{-------------------------------------------------------------------} - -procedure Tmidioutput.midioutput(var Message: TMessage); -{ Triggered when sysex output from PutLong is complete } -var - MyMidiHdr: TMyMidiHdr; - thisHdr: PMidiHdr; -begin - if Message.Msg = Mom_Done then - begin - { Find the MIDIHDR we used for the output. Message.lParam is its address } - thisHdr := PMidiHdr(Message.lParam); - - { Remove it from the output device } - midiOutUnprepareHeader(FMidiHandle, thisHdr, sizeof(TMIDIHDR)); - - { Get the address of the MyMidiHdr object containing this MIDIHDR structure. - We stored this address in the PutLong procedure } - MyMidiHdr := TMyMidiHdr(thisHdr^.dwUser); - - { Header and copy of sysex data no longer required since output is complete } - MyMidiHdr.Free; - - { Call the user's event handler if any } - if Assigned(FOnmidioutput) then - FOnmidioutput(Self); - end; - { TODO: Case for MOM_PLAYBACK_DONE } -end; - -{-------------------------------------------------------------------} - -procedure Register; -begin - RegisterComponents('Synth', [Tmidioutput]); -end; - -end. - diff --git a/src/lib/midi/midiComp.cfg b/src/lib/midi/midiComp.cfg deleted file mode 100644 index 8b774c81..00000000 --- a/src/lib/midi/midiComp.cfg +++ /dev/null @@ -1,35 +0,0 @@ --$A+ --$B- --$C+ --$D+ --$E- --$F- --$G+ --$H+ --$I+ --$J+ --$K- --$L+ --$M- --$N+ --$O+ --$P+ --$Q- --$R- --$S- --$T- --$U- --$V+ --$W- --$X+ --$Y- --$Z1 --cg --AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; --H+ --W+ --M --$M16384,1048576 --K$00400000 --LE"d:\program files\borland\delphi5\Projects\Bpl" --LN"d:\program files\borland\delphi5\Projects\Bpl" diff --git a/src/lib/midi/midiComp.dpk b/src/lib/midi/midiComp.dpk deleted file mode 100644 index 7c403eae..00000000 --- a/src/lib/midi/midiComp.dpk +++ /dev/null @@ -1,45 +0,0 @@ -package midiComp; - -{$R *.RES} -{$R 'MidiFile.dcr'} -{$R 'Midiin.dcr'} -{$R 'Midiout.dcr'} -{$R 'MidiScope.dcr'} -{$ALIGN ON} -{$ASSERTIONS ON} -{$BOOLEVAL OFF} -{$DEBUGINFO ON} -{$EXTENDEDSYNTAX ON} -{$IMPORTEDDATA ON} -{$IOCHECKS ON} -{$LOCALSYMBOLS ON} -{$LONGSTRINGS ON} -{$OPENSTRINGS ON} -{$OPTIMIZATION ON} -{$OVERFLOWCHECKS OFF} -{$RANGECHECKS OFF} -{$REFERENCEINFO OFF} -{$SAFEDIVIDE OFF} -{$STACKFRAMES OFF} -{$TYPEDADDRESS OFF} -{$VARSTRINGCHECKS ON} -{$WRITEABLECONST ON} -{$MINENUMSIZE 1} -{$IMAGEBASE $400000} -{$DESCRIPTION 'Midi related components'} -{$DESIGNONLY} -{$IMPLICITBUILD ON} - -requires - vcl50; - -contains - Miditype in 'Miditype.pas', - Mididefs in 'Mididefs.pas', - MidiFile in 'MidiFile.pas', - Midiin in 'Midiin.pas', - Midiout in 'Midiout.pas', - MidiScope in 'MidiScope.pas', - Midicons in 'Midicons.pas'; - -end. diff --git a/src/lib/midi/midiComp.res b/src/lib/midi/midiComp.res deleted file mode 100644 index 91fb756e..00000000 Binary files a/src/lib/midi/midiComp.res and /dev/null differ -- cgit v1.2.3