//
MIDIplay.cpp : demonstration of playing MIDI file to be run in
//
an external process
// Copyright (C) 2002 ProtoDesign, Inc. All Rights Reserved.
//
// This console application demonstrates how to use Win32 shared memory to
// communicate with an in-process simulator running under SansGUI. This
// program is intended to be launched from the Run External Process
Simulator
// button through which SansGUI will write out a Model File for this
program
// to read. Two essential pieces of information in the Model File are:
// 1) The path of the MIDI file
// 2) The name of the shared memory
// Both are entered in a Project Model in the SansGUI Environment.
//
// The channel number and the data field of each MIDI event are recorded
in
//
the shared memory space containing two integers. The In-Process
simulator
//
reads the data with a certain time interval; therefore, achieving
//
asynchronous communication. If your application requires synchronous
//
communication, mutexes, semaphores, or event objects may be employed.
//
// For more information, please consult:
// 1) Chapter 5 Developing External Process Simulators of the SansGUI
//
Developer's Guide
// 2) Chapter 4 SansGUI Simulation Control of the SansGUI Reference Manual
// 3) Chapter 5 SansGUI Model File Format of the SansGUI Reference Manual
//
// This program uses a CMIDI class developed by Jörg König.
// Please see the headers of MIDI.h and MIDI.cpp files for copyright
details.
// Both files have been modified to remove the dependency of Microsoft
//
Foundation Class(MFC) library.
#include
"StdAfx.h"
#include "conio.h"
#include "stdio.h"
#include "MIDI.h"
#define
MAX_STRBUF_LEN 2047
//
===========================================================================
// class CMidiExt - extends CMIDI with overriding routines to handle
events
//
---------------------------------------------------------------------------
class CMidiExt : public CMIDI
{
public:
// overriding routines to handle MIDI out events
// see the end of this source file for implementation
void OnMidiOutDone(MIDIHDR & hdr);
void OnMidiOutClose();
};
//
===========================================================================
// function prototypes
//
---------------------------------------------------------------------------
static int readModelFile(const char*);
static int readMidiFile(const char*, DWORD&, DWORD&);
static int createSharedMemory(void);
static int parseMidiFileName(char[]);
static int parseSharedMemoryName(char[]);
static DWORD getFileSize(FILE*);
static void cleanUp(void);
//
===========================================================================
// global variables, all are prefixed with g_*
//
---------------------------------------------------------------------------
BOOL g_bDone = FALSE;
int* g_pChannel;
int* g_pData;
HANDLE g_hMappedFile = (HANDLE)0;
LPVOID g_pSharedMem = (LPVOID)NULL;
LPVOID g_pMemBuffer = (LPVOID)NULL;
CMidiExt* g_pMidi = (CMidiExt*)NULL;
char
g_cSharedName[MAX_PATH + 1];
char g_cMidiFileName[MAX_PATH + 1];
//
===========================================================================
// MIDIplay Main Program
//
---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
int iRetCode;
DWORD w2Size, w2Read;
// ========== COMMAND LINE ARGUMENTS PROCESSING
// check the command line parameters, passed by command
script
// bin\SGxProc.bat
if (argc < 4)
{
fprintf(stderr, "Usage: %s
<OptionCode> <ModelFile> <FileType>\n",
argv[0] );
return -1;
}
// check the model file type, we use Tabular Data
Blocks format in
// this example
if (atoi(argv[3]) != 0)
{
fprintf(stderr,
"Error: Select Tabular
Data Blocks in the Simulation control object.\n" );
return -2;
}
// ========== READ MODEL FILE TO OBTAIN MIDI FILE NAME AND SHARED MEMORY
NAME
// read and parse the model file created by SansGUI
iRetCode = readModelFile(argv[2]);
if (iRetCode != 0)
{
fprintf(stderr, "Error[%d]:
Cannot open and read Model File [%s]\n",
iRetCode, argv[2] );
return -3;
}
// create the MIDI instance
g_pMidi = new CMidiExt();
// read the entire MIDI file into a memory buffer
iRetCode = readMidiFile(g_cMidiFileName, w2Size,
w2Read);
if (iRetCode != 0)
{
if (g_pMidi != (CMidiExt*)NULL)
delete
g_pMidi;
fprintf(stderr, "Error[%d]:
Cannot open and read MIDI file [%s]\n",
iRetCode, g_cMidiFileName );
return -4;
}
// create shared memory using mapped file
iRetCode = createSharedMemory();
if (iRetCode != 0)
{
if (g_pMidi != (CMidiExt*)NULL)
delete
g_pMidi;
fprintf(stderr,
"Error[%d]: Cannot create shared memory [%s] for
communication.\n",
iRetCode, g_cSharedName );
return -5;
}
// ========== PLAY THE MIDI FILE
// write the MIDI file information
fprintf(stderr,
"MIDI FILE NAME: %s\nLENGTH [Bytes]: %d\n READ [Bytes]: %d\n",
g_cMidiFileName, (int)w2Size, (int)w2Read );
// play the MIDI music
g_pMidi->Play();
// ========== LOOP UNTIL IT ENDS OR WHEN USER HITS Ctrl-C
while (!g_bDone && !_kbhit())
{
if (_getch() == 0x03)
{ // Ctrl-C
termination
g_bDone
= TRUE;
fprintf(stderr,
"\nKeyboard
interrupt [Ctrl-C] detected. Closing MIDI channel...\n" );
}
}
// ended normally, close the MIDI channel and return
cleanUp();
return 0;
}
int
readModelFile(const char* cName)
{
int iMidiBlock = 0; // see the switch.case statement
below for its definition
char cBuffer[MAX_STRBUF_LEN + 1]; // line buffer for
reading
char cMidiClass[] = "Collection.MIDI"; //
class name defined in SansGUI
FILE* pFile = fopen(cName, "r");
if (pFile == (FILE*)NULL)
return -1;
while (!feof(pFile))
{
// fetch data line by line
if (fgets(cBuffer,
MAX_STRBUF_LEN, pFile) == (char*)NULL)
break;
// skip all comment lines
if (cBuffer[0] == '#')
continue;
if (strncmp(cBuffer, cMidiClass,
strlen(cMidiClass)) == 0)
{ // mark inside
MIDI class block
iMidiBlock = 1;
continue;
}
switch (iMidiBlock)
{
case 1: // read the first line
with <ObjectName> <MIDI File Name>
if (parseMidiFileName(cBuffer)
> 0)
iMidiBlock = 2;
else
return -2;
break;
case 2: // read the second line
with <Shared Memory Name>
if (parseSharedMemoryName(cBuffer)
> 0)
iMidiBlock = 3;
else
return -3;
break;
default:
break;
}
if (iMidiBlock == 3) //
completed
break;
}
// strncpy(g_cSharedName, "MIDIplay Piano",
MAX_PATH);
// strncpy(g_cMidiFileName, "SJent.mid",
MAX_PATH);
fclose(pFile);
return 0;
}
int
readMidiFile(const char* cName, DWORD& w2Size, DWORD& w2Read)
{
// read the MIDI file and attach it to the MIDI object
FILE* pFile = fopen(cName, "rb");
if (pFile == (FILE*)NULL)
return -1;
w2Size = getFileSize(pFile);
if (w2Size < 1)
{
fclose(pFile);
return -2;
}
// allocate memory buffer and read all MIDI data into
memory
g_pMemBuffer = (LPVOID)GlobalAlloc(GMEM_FIXED, w2Size);
if (g_pMemBuffer == (LPVOID)NULL)
{
fclose(pFile);
return -3;
}
w2Read = (DWORD)fread(g_pMemBuffer, sizeof(char),
w2Size, pFile);
if (!g_pMidi->Create(g_pMemBuffer, w2Read))
{
fclose(pFile);
return -4;
}
fclose(pFile);
return 0;
}
int
createSharedMemory()
{
// The shared memory contains only two integers for:
// 1. channel number (where g_pChannel will point to)
// 2. MIDI data (where g_pData will point to)
// The sizeof(int)*2 parameter indicates that 2
integers are needed.
g_hMappedFile = CreateFileMapping((HANDLE)0xffffffff,
NULL,
PAGE_READWRITE, 0, sizeof(int) * 2, g_cSharedName );
if (g_hMappedFile == NULL)
return -1;
g_pSharedMem = (LPDWORD)MapViewOfFile(g_hMappedFile, FILE_MAP_ALL_ACCESS,
0, 0, 0 );
if (g_pSharedMem == (LPVOID)NULL)
return -2;
g_pChannel = (int*)g_pSharedMem;
g_pData = g_pChannel + 1; // warning: pointer
arithmetic here
return 0;
}
int
parseMidiFileName(char cBuffer[])
{
// the file name is enclosed by a pair of angle
brackets < >
int iNdxSrc = 0;
int iNdxDst = 0;
while (cBuffer[iNdxSrc] != '\0' && cBuffer[iNdxSrc] != '<')
iNdxSrc++;
if (cBuffer[iNdxSrc] != '<') // cannot find the opening bracket
return 0;
// now iNdxSrc is the index of the '<' character
++iNdxSrc;
// copy the file name until the closing '>' or end of string is reached
while (cBuffer[iNdxSrc] != '\0' &&
cBuffer[iNdxSrc] != '>' &&
iNdxDst < MAX_PATH )
{
g_cMidiFileName[iNdxDst] =
cBuffer[iNdxSrc];
++iNdxSrc;
++iNdxDst;
}
// NULL terminate the string
g_cMidiFileName[iNdxDst] = '\0';
return iNdxDst; // should be the length of the string
}
int
parseSharedMemoryName(char cBuffer[])
{
int iNdxSrc = 0;
int iNdxDst = 0;
// skip leading white spaces
while (cBuffer[iNdxSrc] != '\0' &&
(cBuffer[iNdxSrc]
== ' ' || cBuffer[iNdxSrc] == '\t') )
++iNdxSrc;
// skip beginning quote
if (cBuffer[iNdxSrc] == '"' || cBuffer[iNdxSrc] ==
'\'')
++iNdxSrc;
// copy shared memory name upto the end quote or end of string
while (cBuffer[iNdxSrc] != '\0' &&
cBuffer[iNdxSrc] != '"' &&
cBuffer[iNdxSrc] != '\'' && iNdxDst < MAX_PATH )
{
g_cSharedName[iNdxDst] =
cBuffer[iNdxSrc];
++iNdxSrc;
++iNdxDst;
}
// NULL terminate the string
g_cSharedName[iNdxDst] = '\0';
return iNdxDst; // should be the length of the string
}
DWORD
getFileSize(FILE* pFile)
{
// get the file size in a more system-independent way
DWORD w2Size = 0;
if (fseek(pFile, 0L, SEEK_END) == 0)
{
w2Size = ftell(pFile);
rewind(pFile);
}
return w2Size;
}
void
cleanUp()
{
if (g_pMidi != (CMidiExt*)NULL)
{
// also will stop playing MIDI
delete g_pMidi;
g_pMidi = (CMidiExt*)NULL;
}
// mark termination by setting the channel number to a negative value
*g_pChannel = -1;
if (g_pSharedMem != (LPVOID)NULL)
{
UnmapViewOfFile(g_pSharedMem);
g_pSharedMem = (LPVOID)NULL;
}
if (g_hMappedFile != (HANDLE)0)
{
CloseHandle(g_hMappedFile);
g_hMappedFile = (HANDLE)0;
}
if (g_pMemBuffer != (LPVOID)NULL)
{
GlobalFree(g_pMemBuffer);
g_pMemBuffer = (LPVOID)NULL;
}
}
//
===========================================================================
// Implementation of CMidiExt class functions -- MIDI event handlers
//
---------------------------------------------------------------------------
void CMidiExt::OnMidiOutDone(MIDIHDR& hdr)
{
CMIDI::OnMidiOutDone(hdr);
if (g_bDone)
{
fflush(stdout);
return;
}
static int iCount = 0;
MIDIEVENT* pEvent = (MIDIEVENT*)(hdr.lpData +
hdr.dwOffset);
int iChannel = MIDIEVENT_CHANNEL(pEvent->dwEvent);
int iType = MIDIEVENT_TYPE(pEvent->dwEvent);
int iData = MIDIEVENT_DATA1(pEvent->dwEvent);
int iVolume = MIDIEVENT_VOLUME(pEvent->dwEvent);
if (g_pSharedMem != (LPVOID)NULL)
{ // when shared memory exists, write out
MIDI message data for verification
*g_pChannel = iChannel;
*g_pData = iData;
printf("%5d: [%3d %3d %3d
%3d]\n", ++iCount, iChannel,
iType, iData, iVolume );
}
else
{ // when there is no shared memory, just
print out a dot for
// each MIDI message
// do not print out any dot
after MIDI out has been closed
if (g_hMappedFile != (HANDLE)0)
fprintf(stderr, ".");
}
}
void
CMidiExt::OnMidiOutClose()
{
CMIDI::OnMidiOutClose();
// mark termination by setting the channel number to a negative value
*g_pChannel = -1;
g_bDone = TRUE;
fprintf(stderr, "\nEnd of MIDI file. Press a key to exit...\n");
}
|