shithub: choc

ref: 2f8ed99c11279ab932dbda6db0647f70c3280b7a
dir: /src/i_midirpc.c/

View raw version
//
// Copyright(C) 2013 James Haley et al.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//     Client Interface to RPC Midi Server
//

#ifdef EE_FEATURE_MIDIRPC

#include <windows.h>
#include "midiproc.h"

#include "../hal/i_timer.h"
#include "../m_qstr.h"

#if defined(_DEBUG) && defined(EE_RPC_DEBUG)
#define DEBUGOUT(s) puts(s)
#else
#define DEBUGOUT(s)
#endif

//=============================================================================
//
// Data
//

static unsigned char *szStringBinding; // RPC client binding string
static bool serverInit = false;        // if true, server was started
static bool clientInit = false;        // if true, client was bound

// server process information
static STARTUPINFO         si;
static PROCESS_INFORMATION pi;

//=============================================================================
//
// RPC Memory Management
//

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t size)
{
   return malloc(size);
}

void __RPC_USER midl_user_free(void __RPC_FAR *p)
{
   free(p);
}

//=============================================================================
//
// RPC Wrappers
//

//
// CHECK_RPC_STATUS
//
// If either server or client initialization failed, we don't try to make any
// RPC calls.
//
#define CHECK_RPC_STATUS()        \
   if(!serverInit || !clientInit) \
      return false

#define MIDIRPC_MAXTRIES 50 // This number * 10 is the amount of time you can try to wait for.

static bool I_MidiRPCWaitForServer()
{
   int tries = 0;
   while(RpcMgmtIsServerListening(hMidiRPCBinding) != RPC_S_OK)
   {
      i_haltimer.Sleep(10);
      if(++tries >= MIDIRPC_MAXTRIES)
         return false;
   }
   return true;
}

//
// I_MidiRPCRegisterSong
//
// Prepare the RPC MIDI engine to receive new song data, and transmit the song
// data to the server process.
//
bool I_MidiRPCRegisterSong(void *data, int size)
{
   unsigned int rpcSize = static_cast<unsigned int>(size);

   CHECK_RPC_STATUS();

   RpcTryExcept
   {
      MidiRPC_PrepareNewSong();

      // TODO: Try passing it as one chunk; if this ends up not working, 
      // I'll have to stream it in as smaller divisions.
      MidiRPC_AddChunk(rpcSize, static_cast<byte *>(data));
   }
   RpcExcept(1)
   {
      DEBUGOUT("I_MidiRPCRegisterSong failed");
      return false;
   }
   RpcEndExcept

   DEBUGOUT("I_MidiRPCRegisterSong succeeded");
   return true;
}

//
// I_MidiRPCPlaySong
//
// Tell the RPC server to start playing a song.
//
bool I_MidiRPCPlaySong(bool looping)
{
   CHECK_RPC_STATUS();

   RpcTryExcept
   {
      MidiRPC_PlaySong(looping ? TRUE : FALSE);
   }
   RpcExcept(1)
   {
      DEBUGOUT("I_MidiRPCPlaySong failed");
      return false;
   }
   RpcEndExcept

   DEBUGOUT("I_MidiRPCPlaySong succeeded");
   return true;
}

// 
// I_MidiRPCStopSong
//
// Tell the RPC server to stop any currently playing song.
//
bool I_MidiRPCStopSong()
{
   CHECK_RPC_STATUS();

   RpcTryExcept
   {
      MidiRPC_StopSong();
   }
   RpcExcept(1)
   {
      DEBUGOUT("I_MidiRPCStopSong failed");
      return false;
   }
   RpcEndExcept

   DEBUGOUT("I_MidiRPCStopSong succeeded");
   return true;
}

//
// I_MidiRPCSetVolume
//
// Change the volume level of music played by the RPC midi server.
//
bool I_MidiRPCSetVolume(int volume)
{
   CHECK_RPC_STATUS();
   
   RpcTryExcept
   {
      MidiRPC_ChangeVolume(volume);
   }
   RpcExcept(1)
   {
      DEBUGOUT("I_MidiRPCSetVolume failed");
      return false;
   }
   RpcEndExcept

   DEBUGOUT("I_MidiRPCSetVolume succeeded");
   return true;
}

//
// I_MidiRPCPauseSong
//
// Pause the music being played by the server. In actuality, due to SDL_mixer
// limitations, this just temporarily sets the volume to zero.
//
bool I_MidiRPCPauseSong()
{
   CHECK_RPC_STATUS();

   RpcTryExcept
   {
      MidiRPC_PauseSong();
   }
   RpcExcept(1)
   {
      DEBUGOUT("I_MidiRPCPauseSong failed");
      return false;
   }
   RpcEndExcept

   DEBUGOUT("I_MidiRPCPauseSong succeeded");
   return true;
}

//
// I_MidiRPCResumeSong
//
// Resume a song after having paused it.
//
bool I_MidiRPCResumeSong()
{
   CHECK_RPC_STATUS();

   RpcTryExcept
   {
      MidiRPC_ResumeSong();
   }
   RpcExcept(1)
   {
      DEBUGOUT("I_MidiRPCResumeSong failed");
      return false;
   }
   RpcEndExcept

   DEBUGOUT("I_MidiRPCResumeSong succeeded");
   return true;
}

//=============================================================================
//
// Public Interface
//

//
// I_MidiRPCInitServer
//
// Start up the RPC MIDI server.
//
bool I_MidiRPCInitServer()
{
   struct stat sbuf;
   char filename[MAX_PATH+1];

   memset(filename, 0, sizeof(filename));
   GetModuleFileName(NULL, filename, MAX_PATH);

   qstring module;

   module = filename;
   module.removeFileSpec();
   module.pathConcatenate("midiproc.exe");
   DEBUGOUT(module.constPtr());

   // Look for executable file
   if(stat(module.constPtr(), &sbuf))
   {
      DEBUGOUT("Could not find midiproc");
      return false;
   }

   si.cb = sizeof(si);

   BOOL result = CreateProcess(module.constPtr(), NULL, NULL, NULL, FALSE,
                               0, NULL, NULL, &si, &pi);

   if(result)
   {
      DEBUGOUT("RPC server started");
      serverInit = true;
   }
   else
      DEBUGOUT("CreateProcess failed to start midiproc");

   return !!result;
}

//
// I_MidiRPCInitClient
//
// Initialize client RPC bindings and bind to the server.
//
bool I_MidiRPCInitClient()
{
   RPC_STATUS status;

   // If server didn't start, client cannot be bound.
   if(!serverInit)
      return false;

   // Compose binding string
   status =
      RpcStringBindingCompose
      (
         NULL,
         (RPC_CSTR)("ncalrpc"),
         NULL,
         (RPC_CSTR)("2d4dc2f9-ce90-4080-8a00-1cb819086970"),
         NULL,
         &szStringBinding
      );

   if(status)
   {
      DEBUGOUT("RPC binding composition failed");
      return false;
   }

   // Create binding handle
   status = RpcBindingFromStringBinding(szStringBinding, &hMidiRPCBinding);

   if(status)
   {
      DEBUGOUT("RPC client binding failed");
      return false;
   }

   DEBUGOUT("RPC client initialized");
   clientInit = true;

   return I_MidiRPCWaitForServer();
}

//
// I_MidiRPCClientShutDown
//
// Shutdown the RPC Client
//
void I_MidiRPCClientShutDown()
{
   // stop the server
   if(serverInit)
   {
      RpcTryExcept
      {
         MidiRPC_StopServer();
      }
      RpcExcept(1)
      {
         DEBUGOUT("Exception thrown when stopping RPC server");
      }
      RpcEndExcept

      serverInit = false;
   }

   if(szStringBinding)
   {
      RpcStringFree(&szStringBinding);
      szStringBinding = NULL;
   }

   if(hMidiRPCBinding)
   {
      RpcBindingFree(&hMidiRPCBinding);
      hMidiRPCBinding = NULL;
   }

   clientInit = false;
}

//
// I_MidiRPCReady
//
// Returns true if both server and client initialized successfully.
//
bool I_MidiRPCReady()
{
   CHECK_RPC_STATUS();

   return true;
}

#endif

// EOF