|
|
1.1 root 1: /*
2: * display.c - Functions to manage the display buffer and convert a
3: * MIDI event to a text string for display.
4: *
5: * The display buffer is filled by the application's WndProc()
6: * function when it receives an MM_MIDIINPUT message. This message
7: * is sent by the low-level callback function upon reception of a
8: * MIDI event. When the display buffer becomes full, newly added
9: * events overwrite the oldest events in the buffer.
10: */
11:
12: #include <windows.h>
13: #include <stdio.h>
14: #include "midimon.h"
15: #include "circbuf.h"
16: #include "display.h"
17:
18: /* MIDI event-name strings
19: */
20: char szEventNames[8][24] =
21: {
22: "Note Off",
23: "Note On",
24: "Key Aftertouch",
25: "Control Change",
26: "Program Change",
27: "Channel Aftertouch",
28: "Pitch Bend",
29: "System Message"
30: };
31:
32: char szSysMsgNames[16][24] =
33: {
34: "System Exclusive",
35: "MTC Quarter Frame",
36: "Song Position Pointer",
37: "Song Select",
38: "Undefined",
39: "Undefined",
40: "Tune Request",
41: "System Exclusive End",
42: "Timing Clock",
43: "Undefined",
44: "Start",
45: "Continue",
46: "Stop",
47: "Undefined",
48: "Active Sensing",
49: "System Reset"
50: };
51:
52: /* GetDisplayText - Takes a MIDI event and creates a text string for display.
53: *
54: * Params: npText - Points to a string that the function fills.
55: * lpEvent - Points to a MIDI event.
56: *
57: * Return: The number of characters in the text string pointed to by npText.
58: */
59: int GetDisplayText(NPSTR npText, LPEVENT lpEvent)
60: {
61: BYTE bStatus, bStatusRaw, bChannel, bData1, bData2;
62: DWORD dwTimestamp;
63:
64: bStatusRaw = LOBYTE(LOWORD(lpEvent->data));
65: bStatus = bStatusRaw & (BYTE) 0xf0;
66: bChannel = bStatusRaw & (BYTE) 0x0f;
67: bData1 = HIBYTE(LOWORD(lpEvent->data));
68: bData2 = LOBYTE(HIWORD(lpEvent->data));
69: dwTimestamp = lpEvent->timestamp;
70:
71: switch(bStatus)
72: {
73: /* Three byte events
74: */
75: case NOTEOFF:
76: case NOTEON:
77: case KEYAFTERTOUCH:
78: case CONTROLCHANGE:
79: case PITCHBEND:
80: /* A note on with a velocity of 0 is a note off
81: */
82: if((bStatus == NOTEON) && (bData2 == 0))
83: bStatus = NOTEOFF;
84:
85: return(sprintf(npText, FORMAT3, dwTimestamp, bStatusRaw, bData1,
86: bData2, bChannel, &szEventNames[(bStatus-0x80) >> 4][0]));
87: break;
88:
89: /* Two byte events
90: */
91: case PROGRAMCHANGE:
92: case CHANAFTERTOUCH:
93: return(sprintf(npText, FORMAT2, dwTimestamp, bStatusRaw, bData1,
94: bChannel, &szEventNames[(bStatus-0x80) >> 4][0]));
95: break;
96:
97: /* MIDI system events (0xf0 - 0xff)
98: */
99: case SYSTEMMESSAGE:
100: switch(bStatusRaw)
101: {
102: /* Two byte system events
103: */
104: case MTCQUARTERFRAME:
105: case SONGSELECT:
106: return(sprintf(npText, FORMAT2X, dwTimestamp, bStatusRaw,
107: bData1,
108: &szSysMsgNames[(bStatusRaw & 0x0f)][0]));
109: break;
110:
111: /* Three byte system events
112: */
113: case SONGPOSPTR:
114: return(sprintf(npText, FORMAT3X, dwTimestamp, bStatusRaw,
115: bData1, bData2,
116: &szSysMsgNames[(bStatusRaw & 0x0f)][0]));
117: break;
118:
119: /* One byte system events
120: */
121: default:
122: return(sprintf(npText, FORMAT1X, dwTimestamp, bStatusRaw,
123: &szSysMsgNames[(bStatusRaw & 0x0f)][0]));
124: break;
125: }
126: break;
127:
128: default:
129: return(sprintf(npText, FORMAT3X, dwTimestamp, bStatusRaw, bData1,
130: bData2, "Unknown Event"));
131: break;
132: }
133: }
134:
135: /* AddDisplayEvent - Puts a MIDI event in the display buffer. The display
136: * buffer is a circular buffer. Once it is full, newly added events
137: * overwrite the oldest events in the buffer.
138: *
139: * Params: lpBuf - Points to the display buffer.
140: * lpEvent - Points to a MIDI event.
141: *
142: * Return: void
143: */
144: void AddDisplayEvent(LPDISPLAYBUFFER lpBuf, LPEVENT lpEvent)
145: {
146: /* Put the event in the buffer, bump the head pointer and byte count.
147: */
148: *lpBuf->lpHead = *lpEvent;
149: ++lpBuf->lpHead;
150: ++lpBuf->dwCount;
151:
152: /* Wrap pointer, if necessary.
153: */
154: if(lpBuf->lpHead == lpBuf->lpEnd)
155: lpBuf->lpHead = lpBuf->lpStart;
156:
157: /* A full buffer is a full buffer, no more.
158: */
159: lpBuf->dwCount = min(lpBuf->dwCount, lpBuf->dwSize);
160: }
161:
162:
163: /* GetDisplayEvent - Retrieves a MIDI event from the display buffer.
164: * Unlike the input buffer, the event is not removed from the buffer.
165: *
166: * Params: lpBuf - Points to the display buffer.
167: * lpEvent - Points to an EVENT structure that is filled with
168: * the retrieved MIDI event.
169: * wNum - Specifies which event to retrieve.
170: *
171: * Return: void
172: */
173: void GetDisplayEvent(LPDISPLAYBUFFER lpBuf, LPEVENT lpEvent, DWORD wNum)
174: {
175: LPEVENT lpFirstEvent, lpThisEvent;
176:
177: /* Get pointer to the first (oldest) event in buffer.
178: */
179: if(lpBuf->dwCount < lpBuf->dwSize) // buffer is not yet full
180: lpFirstEvent = lpBuf->lpStart;
181:
182: else // buffer is full
183: lpFirstEvent = lpBuf->lpHead;
184:
185: /* Offset pointer to point to requested event; wrap pointer.
186: */
187: lpThisEvent = lpFirstEvent + wNum;
188: if(lpThisEvent >= lpBuf->lpEnd)
189: lpThisEvent = lpBuf->lpStart + (lpThisEvent - lpBuf->lpEnd);
190:
191: /* Get the requested event.
192: */
193: *(lpEvent) = *lpThisEvent;
194: }
195:
196: /* AllocDisplayBuffer - Allocates memory for a DISPLAYBUFFER structure
197: * and a buffer of the specified size. Each memory block is allocated
198: * with GlobalAlloc() using GMEM_SHARE and GMEM_MOVEABLE flags and
199: * locked with GlobalLock(). Since this buffer is only accessed by the
200: * application, and not the low-level callback function, it does not
201: * have to be page locked.
202: *
203: * Params: dwSize - The size of the buffer, in events.
204: *
205: * Return: A pointer to a DISPLAYBUFFER structure identifying the
206: * allocated display buffer. NULL if the buffer could not be allocated.
207: */
208: LPDISPLAYBUFFER AllocDisplayBuffer(DWORD dwSize)
209: {
210: HANDLE hMem;
211: LPDISPLAYBUFFER lpBuf;
212: LPEVENT lpMem;
213:
214: /* Allocate and lock a DISPLAYBUFFER structure.
215: */
216: hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
217: (DWORD)sizeof(DISPLAYBUFFER));
218: if(hMem == NULL)
219: return NULL;
220:
221: lpBuf = (LPDISPLAYBUFFER)GlobalLock(hMem);
222: if(lpBuf == NULL)
223: {
224: GlobalFree(hMem);
225: return NULL;
226: }
227:
228: /* Save the handle.
229: */
230: lpBuf->hSelf = hMem;
231:
232: /* Allocate and lock memory for the actual buffer.
233: */
234: hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, dwSize * sizeof(EVENT));
235: if(hMem == NULL)
236: {
237: GlobalUnlock(lpBuf->hSelf);
238: GlobalFree(lpBuf->hSelf);
239: return NULL;
240: }
241: lpMem = (LPEVENT)GlobalLock(hMem);
242: if(lpMem == NULL)
243: {
244: GlobalFree(hMem);
245: GlobalUnlock(lpBuf->hSelf);
246: GlobalFree(lpBuf->hSelf);
247: return NULL;
248: }
249:
250: /* Set up the DISPLAYBUFFER structure.
251: */
252: lpBuf->hBuffer = hMem;
253: lpBuf->wError = 0;
254: lpBuf->dwSize = dwSize;
255: lpBuf->dwCount = 0L;
256: lpBuf->lpStart = lpMem;
257: lpBuf->lpEnd = lpMem + dwSize;
258: lpBuf->lpTail = lpMem;
259: lpBuf->lpHead = lpMem;
260:
261: return lpBuf;
262: }
263:
264: /* FreeDisplayBuffer - Frees the memory for a display buffer.
265: *
266: * Params: lpBuf - Points to the DISPLAYBUFFER to be freed.
267: *
268: * Return: void
269: */
270: void FreeDisplayBuffer(LPDISPLAYBUFFER lpBuf)
271: {
272: HANDLE hMem;
273:
274: /* Unlock and free the buffer itself.
275: */
276: GlobalUnlock(lpBuf->hBuffer);
277: GlobalFree(lpBuf->hBuffer);
278:
279: /* Unlock and free the DISPLAYBUFFER structure.
280: */
281: hMem = lpBuf->hSelf;
282: GlobalUnlock(hMem);
283: GlobalFree(hMem);
284: }
285:
286: /* ResetDisplayBuffer - Empties a display buffer.
287: *
288: * Params: lpBuf - Points to a display buffer.
289: *
290: * Return: void
291: */
292: void ResetDisplayBuffer(LPDISPLAYBUFFER lpBuf)
293: {
294: /* Reset the pointers and event count.
295: */
296: lpBuf->lpHead = lpBuf->lpStart;
297: lpBuf->lpTail = lpBuf->lpStart;
298: lpBuf->dwCount = 0L;
299: lpBuf->wError = 0;
300: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.