| //
      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.batif (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 exampleif (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 instanceg_pMidi = new CMidiExt();
    
      // read the entire MIDI file into a memory bufferiRetCode = 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-Cwhile (!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 bracketreturn 0;
    
      // now iNdxSrc is the index of the '<' character++iNdxSrc;
    
      // copy the file name until the closing '>' or end of string is reachedwhile (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 spaceswhile (cBuffer[iNdxSrc] != '\0' &&
 (cBuffer[iNdxSrc]
      == ' ' || cBuffer[iNdxSrc] == '\t') )
 ++iNdxSrc;
    
      // skip beginning quoteif (cBuffer[iNdxSrc] == '"' || cBuffer[iNdxSrc] ==
      '\'')
 ++iNdxSrc;
    
      // copy shared memory name upto the end quote or end of stringwhile (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");}
 |