4 purpose: Midi circular buffer based on ms sdk example code
5 Author: KOL translation and > 64 sysex buffering: Thaddy de Koning
6 Original Author: David Churcher
7 Copyright: released to the public domain
8 Remarks: Well known Great components.
9 Do not confuse this with my project JEDI midi translation for KOL
11 { $Header: /MidiComp/CIRCBUF.PAS 2 10/06/97 7:33 Davec $ }
13 { Written by David Churcher <dchurcher@cix.compulink.co.uk>,
14 released to the public domain. }
17 { A First-In First-Out circular buffer.
18 Port of circbuf.c from Microsoft's Windows MIDI monitor example.
19 I did do a version of this as an object (see Rev 1.1) but it was getting too
20 complicated and I couldn't see any real benefits to it so I dumped it
21 for an ordinary memory buffer with pointers.
23 This unit is a bit C-like, everything is done with pointers and extensive
24 use is made of the undocumented feature of the Inc() function that
25 increments pointers by the size of the object pointed to.
26 All of this could probably be done using Pascal array notation with
27 range-checking turned off, but I'm not sure it's worth it.
32 1: Allocation granularity changed from word to dword in globzl* functions.
33 2: Midievents capacity chged to DWORD
34 3: Changed the Tcircular buffer record type accordingly
36 These changes overcome the 64K sysex limit in the original code
43 Uses KOL
, windows
,MMSystem
;
48 TMidiBufferItem
= record
49 timestamp
: DWORD
; { Timestamp in milliseconds after midiInStart }
50 data
: DWORD
; { MIDI message received }
51 sysex
: PMidiHdr
; { Pointer to sysex MIDIHDR, nil if not sysex }
53 PMidiBufferItem
= ^TMidiBufferItem
;
56 TCircularBuffer
= record
57 RecordHandle
: HGLOBAL
; { Windows memory handle for this record }
58 BufferHandle
: HGLOBAL
; { Windows memory handle for the buffer }
59 pStart
: PMidiBufferItem
; { ptr to start of buffer }
60 pEnd
: PMidiBufferItem
; { ptr to end of buffer }
61 pNextPut
: PMidiBufferItem
; { next location to fill }
62 pNextGet
: PMidiBufferItem
; { next location to empty }
63 Error
: Word; { error code from MMSYSTEM functions }
64 Capacity
: dWord
; { buffer size (in TMidiBufferItems) } //Changed from WORD, tdk
65 EventCount
: dWord
; { Number of events in buffer }// Changed from WORD, tdk
68 PCircularBuffer
= ^TCircularBuffer
;
70 function GlobalSharedLockedAlloc( Capacity
: dWord
; var hMem
: HGLOBAL
): Pointer;
71 procedure GlobalSharedLockedFree( hMem
: HGLOBAL
; ptr
: Pointer );
73 function CircbufAlloc( Capacity
: dWord
): PCircularBuffer
;
74 procedure CircbufFree( PBuffer
: PCircularBuffer
);
75 function CircbufRemoveEvent( PBuffer
: PCircularBuffer
): Boolean;
76 function CircbufReadEvent( PBuffer
: PCircularBuffer
; PEvent
: PMidiBufferItem
): Boolean;
77 { Note: The PutEvent function is in the DLL }
81 { Allocates in global shared memory, returns pointer and handle }
82 function GlobalSharedLockedAlloc( Capacity
: dWord
; var hMem
: HGLOBAL
): Pointer;
86 { Allocate the buffer memory }
87 //getmem(pointer(hmem),capacity);
88 //Result:=pointer(hmem);
91 hmem
:= GlobalAlloc(GMEM_SHARE
Or GMEM_MOVEABLE
Or GMEM_ZEROINIT
, Capacity
);
97 ptr
:= GlobalLock(hMem
);
102 GlobalSharedLockedAlloc
:= Ptr
;
107 procedure GlobalSharedLockedFree( hMem
: HGLOBAL
; ptr
: Pointer );
109 //freemem(pointer(hmem));
119 function CircbufAlloc( Capacity
: dWord
): PCircularBuffer
;
121 NewCircularBuffer
: PCircularBuffer
;
122 NewMIDIBuffer
: PMidiBufferItem
;
125 { TODO: Validate circbuf size, <64K } //DONE, or rather skipped:this is pure win32 code:), tdk
127 GlobalSharedLockedAlloc( Sizeof(TCircularBuffer
), hMem
);
128 if (NewCircularBuffer
<> Nil) then
130 NewCircularBuffer
^.RecordHandle
:= hMem
;
132 GlobalSharedLockedAlloc( Capacity
* Sizeof(TMidiBufferItem
), hMem
);
133 if (NewMIDIBuffer
= Nil) then
135 { TODO: Exception here? }
136 GlobalSharedLockedFree( NewCircularBuffer
^.RecordHandle
,
138 NewCircularBuffer
:= Nil;
142 NewCircularBuffer
^.pStart
:= NewMidiBuffer
;
143 { Point to item at end of buffer }
144 NewCircularBuffer
^.pEnd
:= NewMidiBuffer
;
145 Inc(NewCircularBuffer
^.pEnd
, Capacity
);
146 { Start off the get and put pointers in the same position. These
147 will get out of sync as the interrupts start rolling in }
148 NewCircularBuffer
^.pNextPut
:= NewMidiBuffer
;
149 NewCircularBuffer
^.pNextGet
:= NewMidiBuffer
;
150 NewCircularBuffer
^.Error
:= 0;
151 NewCircularBuffer
^.Capacity
:= Capacity
;
152 NewCircularBuffer
^.EventCount
:= 0;
155 CircbufAlloc
:= NewCircularBuffer
;
158 procedure CircbufFree( pBuffer
: PCircularBuffer
);
160 if (pBuffer
<> Nil) then
162 GlobalSharedLockedFree(pBuffer
^.BufferHandle
, pBuffer
^.pStart
);
163 GlobalSharedLockedFree(pBuffer
^.RecordHandle
, pBuffer
);
167 { Reads first event in queue without removing it.
168 Returns true if successful, False if no events in queue }
169 function CircbufReadEvent( PBuffer
: PCircularBuffer
; PEvent
: PMidiBufferItem
): Boolean;
171 PCurrentEvent
: PMidiBufferItem
;
173 if (PBuffer
^.EventCount
<= 0) then
174 CircbufReadEvent
:= False
177 PCurrentEvent
:= PBuffer
^.PNextget
;
179 { Copy the object from the "tail" of the buffer to the caller's object }
180 PEvent
^.Timestamp
:= PCurrentEvent
^.Timestamp
;
181 PEvent
^.Data
:= PCurrentEvent
^.Data
;
182 PEvent
^.Sysex
:= PCurrentEvent
^.Sysex
;
183 CircbufReadEvent
:= True;
187 { Remove current event from the queue }
188 function CircbufRemoveEvent(PBuffer
: PCircularBuffer
): Boolean;
190 if (PBuffer
^.EventCount
> 0) then
192 Dec( Pbuffer
^.EventCount
);
194 { Advance the buffer pointer, with wrap }
195 Inc( Pbuffer
^.PNextGet
);
196 If (PBuffer
^.PNextGet
= PBuffer
^.PEnd
) then
197 PBuffer
^.PNextGet
:= PBuffer
^.PStart
;
199 CircbufRemoveEvent
:= True;
202 CircbufRemoveEvent
:= False;