1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
{ $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 }
{$C PRELOAD FIXED PERMANENT}
interface
uses WinProcs, WinTypes, MMsystem, Circbuf, MidiDefs, MidiCons;
{$IFDEF WIN32}
procedure midiHandler(
hMidiIn: HMidiIn;
wMsg: UINT;
dwInstance: DWORD;
dwParam1: DWORD;
dwParam2: DWORD); stdcall export;
function CircbufPutEvent(PBuffer: PCircularBuffer; PTheEvent: PMidiBufferItem): Boolean; stdcall; export;
{$ELSE}
procedure midiHandler(
hMidiIn: HMidiIn;
wMsg: Word;
dwInstance: DWORD;
dwParam1: DWORD;
dwParam2: DWORD); export;
function CircbufPutEvent(PBuffer: PCircularBuffer; PTheEvent: PMidiBufferItem): Boolean; export;
{$ENDIF}
implementation
{ Add an event to the circular input buffer. }
function CircbufPutEvent(PBuffer: PCircularBuffer; PTheEvent: PMidiBufferItem): Boolean;
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);
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.
|