/**************************************************************************
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
*
* Copyright 1993 - 1998 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/
/*
* display.c - Functions to manage the display buffer and convert a
* MIDI event to a text string for display.
*
* The display buffer is filled by the application's WndProc()
* function when it receives an MM_MIDIINPUT message. This message
* is sent by the low-level callback function upon reception of a
* MIDI event. When the display buffer becomes full, newly added
* events overwrite the oldest events in the buffer.
*/
#include <windows.h>
#include <stdio.h>
#include "midimon.h"
#include "circbuf.h"
#include "display.h"
/* MIDI event-name strings
*/
char szEventNames[8][24] =
{
"Note Off",
"Note On",
"Key Aftertouch",
"Control Change",
"Program Change",
"Channel Aftertouch",
"Pitch Bend",
"System Message"
};
char szSysMsgNames[16][24] =
{
"System Exclusive",
"MTC Quarter Frame",
"Song Position Pointer",
"Song Select",
"Undefined",
"Undefined",
"Tune Request",
"System Exclusive End",
"Timing Clock",
"Undefined",
"Start",
"Continue",
"Stop",
"Undefined",
"Active Sensing",
"System Reset"
};
/* GetDisplayText - Takes a MIDI event and creates a text string for display.
*
* Params: npText - Points to a string that the function fills.
* lpEvent - Points to a MIDI event.
*
* Return: The number of characters in the text string pointed to by npText.
*/
int GetDisplayText(NPSTR npText, LPEVENT lpEvent)
{
BYTE bStatus, bStatusRaw, bChannel, bData1, bData2;
char chErr;
DWORD dwTimestamp;
bStatusRaw = LOBYTE(LOWORD(lpEvent->data));
bStatus = (BYTE) (bStatusRaw & (BYTE) 0xf0);
bChannel = (BYTE) (bStatusRaw & (BYTE) 0x0f);
bData1 = HIBYTE(LOWORD(lpEvent->data));
bData2 = LOBYTE(HIWORD(lpEvent->data));
dwTimestamp = lpEvent->timestamp;
chErr = (lpEvent->fdwEvent & EVNT_F_ERROR) ? '*' : ' ';
switch(bStatus)
{
/* Three byte events
*/
case NOTEOFF:
case NOTEON:
case KEYAFTERTOUCH:
case CONTROLCHANGE:
case PITCHBEND:
/* A note on with a velocity of 0 is a note off
*/
if((bStatus == NOTEON) && (bData2 == 0))
bStatus = NOTEOFF;
return(sprintf(npText, FORMAT3, dwTimestamp, bStatusRaw, bData1,
bData2, bChannel, chErr, &szEventNames[(bStatus-0x80) >> 4][0]));
break;
/* Two byte events
*/
case PROGRAMCHANGE:
case CHANAFTERTOUCH:
return(sprintf(npText, FORMAT2, dwTimestamp, bStatusRaw, bData1,
bChannel, chErr, &szEventNames[(bStatus-0x80) >> 4][0]));
break;
/* MIDI system events (0xf0 - 0xff)
*/
case SYSTEMMESSAGE:
switch(bStatusRaw)
{
/* Two byte system events
*/
case MTCQUARTERFRAME:
case SONGSELECT:
return(sprintf(npText, FORMAT2X, dwTimestamp, bStatusRaw,
bData1,
chErr,
&szSysMsgNames[(bStatusRaw & 0x0f)][0]));
break;
/* Three byte system events
*/
case SONGPOSPTR:
return(sprintf(npText, FORMAT3X, dwTimestamp, bStatusRaw,
bData1, bData2, chErr,
&szSysMsgNames[(bStatusRaw & 0x0f)][0]));
break;
/* One byte system events
*/
default:
return(sprintf(npText, FORMAT1X, dwTimestamp, bStatusRaw,
chErr, &szSysMsgNames[(bStatusRaw & 0x0f)][0]));
break;
}
break;
default:
return(sprintf(npText, FORMAT3X, dwTimestamp, bStatusRaw, bData1,
bData2, chErr, GetStringRes(IDS_UNKNOWN_EVENT)));
break;
}
}
/* AddDisplayEvent - Puts a MIDI event in the display buffer. The display
* buffer is a circular buffer. Once it is full, newly added events
* overwrite the oldest events in the buffer.
*
* Params: lpBuf - Points to the display buffer.
* lpEvent - Points to a MIDI event.
*
* Return: void
*/
void AddDisplayEvent(LPDISPLAYBUFFER lpBuf, LPEVENT lpEvent)
{
/* Put the event in the buffer, bump the head pointer and byte count.
*/
*lpBuf->lpHead = *lpEvent;
++lpBuf->lpHead;
++lpBuf->dwCount;
/* Wrap pointer, if necessary.
*/
if(lpBuf->lpHead == lpBuf->lpEnd)
lpBuf->lpHead = lpBuf->lpStart;
/* A full buffer is a full buffer, no more.
*/
lpBuf->dwCount = min(lpBuf->dwCount, lpBuf->dwSize);
}
/* GetDisplayEvent - Retrieves a MIDI event from the display buffer.
* Unlike the input buffer, the event is not removed from the buffer.
*
* Params: lpBuf - Points to the display buffer.
* lpEvent - Points to an EVENT structure that is filled with
* the retrieved MIDI event.
* wNum - Specifies which event to retrieve.
*
* Return: void
*/
void GetDisplayEvent(LPDISPLAYBUFFER lpBuf, LPEVENT lpEvent, DWORD wNum)
{
LPEVENT lpFirstEvent, lpThisEvent;
/* Get pointer to the first (oldest) event in buffer.
*/
if(lpBuf->dwCount < lpBuf->dwSize) // buffer is not yet full
lpFirstEvent = lpBuf->lpStart;
else // buffer is full
lpFirstEvent = lpBuf->lpHead;
/* Offset pointer to point to requested event; wrap pointer.
*/
lpThisEvent = lpFirstEvent + wNum;
if(lpThisEvent >= lpBuf->lpEnd)
lpThisEvent = lpBuf->lpStart + (lpThisEvent - lpBuf->lpEnd);
/* Get the requested event.
*/
*(lpEvent) = *lpThisEvent;
}
/* AllocDisplayBuffer - Allocates memory for a DISPLAYBUFFER structure
* and a buffer of the specified size. Each memory block is allocated
* with GlobalAlloc() using GMEM_SHARE and GMEM_MOVEABLE flags and
* locked with GlobalLock(). Since this buffer is only accessed by the
* application, and not the low-level callback function, it does not
* have to be page locked.
*
* Params: dwSize - The size of the buffer, in events.
*
* Return: A pointer to a DISPLAYBUFFER structure identifying the
* allocated display buffer. NULL if the buffer could not be allocated.
*/
LPDISPLAYBUFFER AllocDisplayBuffer(DWORD dwSize)
{
HANDLE hMem;
LPDISPLAYBUFFER lpBuf;
LPEVENT lpMem;
/* Allocate and lock a DISPLAYBUFFER structure.
*/
hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
(DWORD)sizeof(DISPLAYBUFFER));
if(hMem == NULL)
return NULL;
lpBuf = (LPDISPLAYBUFFER)GlobalLock(hMem);
if(lpBuf == NULL)
{
GlobalFree(hMem);
return NULL;
}
/* Save the handle.
*/
lpBuf->hSelf = hMem;
/* Allocate and lock memory for the actual buffer.
*/
hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, dwSize * sizeof(EVENT));
if(hMem == NULL)
{
GlobalUnlock(lpBuf->hSelf);
GlobalFree(lpBuf->hSelf);
return NULL;
}
lpMem = (LPEVENT)GlobalLock(hMem);
if(lpMem == NULL)
{
GlobalFree(hMem);
GlobalUnlock(lpBuf->hSelf);
GlobalFree(lpBuf->hSelf);
return NULL;
}
/* Set up the DISPLAYBUFFER structure.
*/
lpBuf->hBuffer = hMem;
lpBuf->wError = 0;
lpBuf->dwSize = dwSize;
lpBuf->dwCount = 0L;
lpBuf->lpStart = lpMem;
lpBuf->lpEnd = lpMem + dwSize;
lpBuf->lpTail = lpMem;
lpBuf->lpHead = lpMem;
return lpBuf;
}
/* FreeDisplayBuffer - Frees the memory for a display buffer.
*
* Params: lpBuf - Points to the DISPLAYBUFFER to be freed.
*
* Return: void
*/
void FreeDisplayBuffer(LPDISPLAYBUFFER lpBuf)
{
HANDLE hMem;
/* Unlock and free the buffer itself.
*/
GlobalUnlock(lpBuf->hBuffer);
GlobalFree(lpBuf->hBuffer);
/* Unlock and free the DISPLAYBUFFER structure.
*/
hMem = lpBuf->hSelf;
GlobalUnlock(hMem);
GlobalFree(hMem);
}
/* ResetDisplayBuffer - Empties a display buffer.
*
* Params: lpBuf - Points to a display buffer.
*
* Return: void
*/
void ResetDisplayBuffer(LPDISPLAYBUFFER lpBuf)
{
/* Reset the pointers and event count.
*/
lpBuf->lpHead = lpBuf->lpStart;
lpBuf->lpTail = lpBuf->lpStart;
lpBuf->dwCount = 0L;
lpBuf->wError = 0;
}