shithub: choc

Download patch

ref: af06c1fef012afcb4e3c5d8b889e92ab10b3705d
parent: 056cdff7cef7dd545fe045532188c77533c6b5f9
author: Alex Mayfield <alexmax2742@gmail.com>
date: Fri Feb 17 19:40:02 EST 2017

Second attempt, this time with pipes

Unfortunately, as it turns out, SDL_net does not allow you to create a
TCP listening socket that only listens on localhost.  This has the
unfortunate side-effect of showing a firewall message, which is not
desirable.

Instead, I use pipes to communicate with the subprocess over stdin.
This approach actually works - the current state of the code is if you
launch Chocolate Doom, it will communicate with the subprocess.

This is only the beginning.  Although communication is occurring, I
don't appear to actually be able to hear anything, and Chocolate Doom
crashes as soon as there is an attempt to switch songs.

--- a/midiproc/main.c
+++ b/midiproc/main.c
@@ -1,5 +1,6 @@
 //
 // Copyright(C) 2012 James Haley
+// Copyright(C) 2017 Alex Mayfield
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -23,73 +24,56 @@
 // Seriously, how did they screw up something so fundamental?
 //
 
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <stdlib.h>
+
 #include "SDL.h"
 #include "SDL_mixer.h"
-#include "midiproc.h"
 
+#include "buffer.h"
+
+#include "config.h"
+#include "doomtype.h"
+#include "net_defs.h"
+
+static HANDLE    midi_process_in;  // Standard In.
+static HANDLE    midi_process_out; // Standard Out.
+static buffer_t *midi_buffer;      // Data from client.
+
 // Currently playing music track
 static Mix_Music *music = NULL;
-static SDL_RWops *rw    = NULL;
+static char *filename = NULL;
 
 static void UnregisterSong();
 
 //=============================================================================
 //
-// 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);
-}
-
-//=============================================================================
-//
 // SDL_mixer Interface
 //
 
 //
-// InitSDL
-//
-// Start up SDL and SDL_mixer.
-//
-static bool InitSDL()
-{
-   if(SDL_Init(SDL_INIT_AUDIO) == -1)
-      return false;
-
-   if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
-      return false;
-
-   return true;
-}
-
-//
 // RegisterSong
 //
-static void RegisterSong(void *data, size_t size)
+static void RegisterSong(char* filename)
 {
-   if(music)
-      UnregisterSong();
+    if (music)
+    {
+        UnregisterSong();
+    }
 
-   rw    = SDL_RWFromMem(data, size);
-   music = Mix_LoadMUS_RW(rw);
+    music = Mix_LoadMUS(filename);
 }
 
 //
 // StartSong
 //
-static void StartSong(bool loop)
+static void StartSong(boolean loop)
 {
-   if(music)
-      Mix_PlayMusic(music, loop ? -1 : 0);
+    if (music)
+    {
+        Mix_PlayMusic(music, loop ? -1 : 0);
+    }
 }
 
 //
@@ -97,7 +81,7 @@
 //
 static void SetVolume(int volume)
 {
-   Mix_VolumeMusic((volume * 128) / 15);
+    Mix_VolumeMusic((volume * 128) / 15);
 }
 
 static int paused_midi_volume;
@@ -107,8 +91,8 @@
 //
 static void PauseSong()
 {
-   paused_midi_volume = Mix_VolumeMusic(-1);
-   Mix_VolumeMusic(0);
+    paused_midi_volume = Mix_VolumeMusic(-1);
+    Mix_VolumeMusic(0);
 }
 
 //
@@ -116,7 +100,7 @@
 //
 static void ResumeSong()
 {
-   Mix_VolumeMusic(paused_midi_volume);
+    Mix_VolumeMusic(paused_midi_volume);
 }
 
 //
@@ -124,8 +108,10 @@
 //
 static void StopSong()
 {
-   if(music)
-      Mix_HaltMusic();
+    if (music)
+    {
+        Mix_HaltMusic();
+    }
 }
 
 //
@@ -133,13 +119,17 @@
 //
 static void UnregisterSong()
 {
-   if(!music)
-      return;
+    if (!music)
+    {
+        return;
+    }
 
-   StopSong();
-   Mix_FreeMusic(music);
-   rw    = NULL;
-   music = NULL;
+    StopSong();
+    Mix_FreeMusic(music);
+    free(filename);
+
+    filename = NULL;
+    music = NULL;
 }
 
 //
@@ -147,208 +137,277 @@
 //
 static void ShutdownSDL()
 {
-   UnregisterSong();
-   Mix_CloseAudio();
-   SDL_Quit();
+    UnregisterSong();
+    Mix_CloseAudio();
+    SDL_Quit();
 }
 
 //=============================================================================
 //
-// Song Buffer
-//
-// The MIDI program will be transmitted by the client across RPC in fixed-size
-// chunks until all data has been transmitted.
-//
-
-typedef unsigned char midibyte;
-
-class SongBuffer
-{
-protected:
-   midibyte *buffer;    // accumulated input
-   size_t    size;      // size of input
-   size_t    allocated; // amount of memory allocated (>= size)
-
-   static const int defaultSize = 128*1024; // 128 KB
-
-public:
-   // Constructor
-   // Start out with an empty 128 KB buffer.
-   SongBuffer()
-   {
-      buffer = static_cast<midibyte *>(calloc(1, defaultSize));
-      size = 0;
-      allocated = defaultSize;
-   }
-
-   // Destructor.
-   // Release the buffer.
-   ~SongBuffer()
-   {
-      if(buffer)
-      {
-         free(buffer);
-         buffer = NULL;
-         size = allocated = 0;
-      }
-   }
-
-   //
-   // addChunk
-   //
-   // Add a chunk of MIDI data to the buffer.
-   //
-   void addChunk(midibyte *data, size_t newsize)
-   {
-      if(size + newsize > allocated)
-      {
-         allocated += newsize * 2;
-         buffer = static_cast<midibyte *>(realloc(buffer, allocated));
-      }
-
-      memcpy(buffer + size, data, newsize);
-      size += newsize;
-   }
-
-   // Accessors
-
-   midibyte *getBuffer() const { return buffer; }
-   size_t    getSize()   const { return size;   }
-};
-
-static SongBuffer *song;
-
-//=============================================================================
-//
 // RPC Server Interface
 //
 
 //
-// MidiRPC_PrepareNewSong
+// MidiPipe_PrepareNewSong
 //
 // Prepare the engine to receive new song data from the RPC client.
 //
-void MidiRPC_PrepareNewSong()
+boolean MidiPipe_PrepareNewSong()
 {
-   // Stop anything currently playing and free it.
-   UnregisterSong();
+    // Stop anything currently playing and free it.
+    UnregisterSong();
 
-   // free any previous song buffer
-   delete song;
-
-   // prep new song buffer
-   song = new SongBuffer();
+    fprintf(stderr, "%s\n", __FUNCTION__);
+    return true;
 }
 
 //
-// MidiRPC_AddChunk
+// MidiPipe_AddChunk
 //
-// Add a chunk of data to the song.
+// Set the filename of the song.
 //
-void MidiRPC_AddChunk(unsigned int count, byte *pBuf)
+boolean MidiPipe_SetFilename(buffer_reader_t *reader)
 {
-   song->addChunk(pBuf, static_cast<size_t>(count));
+    free(filename);
+    filename = NULL;
+
+    char* file = Reader_ReadString(reader);
+    if (file == NULL)
+    {
+        return false;
+    }
+
+    int size = Reader_BytesRead(reader) - 2;
+    if (size <= 0)
+    {
+        return false;
+    }
+
+    filename = malloc(size);
+    if (filename == NULL)
+    {
+        return false;
+    }
+
+    memcpy(filename, file, size);
+
+    fprintf(stderr, "%s\n", __FUNCTION__);
+    return true;
 }
 
 //
-// MidiRPC_PlaySong
+// MidiPipe_PlaySong
 //
 // Start playing the song.
 //
-void MidiRPC_PlaySong(boolean looping)
+boolean MidiPipe_PlaySong(buffer_reader_t *reader)
 {
-   RegisterSong(song->getBuffer(), song->getSize());
-   StartSong(!!looping);
+    uint8_t looping;
+
+    if (!Reader_ReadInt8(reader, &looping))
+    {
+        return false;
+    }
+
+    RegisterSong(filename);
+    StartSong((boolean)looping);
+
+    fprintf(stderr, "%s\n", __FUNCTION__);
+    return true;
 }
 
 //
-// MidiRPC_StopSong
+// MidiPipe_StopSong
 //
 // Stop the song.
 //
-void MidiRPC_StopSong()
+boolean MidiPipe_StopSong()
 {
-   StopSong();
+    StopSong();
+
+    fprintf(stderr, "%s\n", __FUNCTION__);
+    return true;
 }
 
 //
-// MidiRPC_ChangeVolume
+// MidiPipe_ChangeVolume
 //
 // Set playback volume level.
 //
-void MidiRPC_ChangeVolume(int volume)
+boolean MidiPipe_ChangeVolume(buffer_reader_t *reader)
 {
-   SetVolume(volume);
+    int volume;
+
+    if (!Reader_ReadInt32(reader, &volume))
+    {
+        return false;
+    }
+
+    SetVolume(volume);
+
+    fprintf(stderr, "%s\n", __FUNCTION__);
+    return true;
 }
 
 //
-// MidiRPC_PauseSong
+// MidiPipe_PauseSong
 //
 // Pause the song.
 //
-void MidiRPC_PauseSong()
+boolean MidiPipe_PauseSong()
 {
-   PauseSong();
+    PauseSong();
+
+    fprintf(stderr, "%s\n", __FUNCTION__);
+    return true;
 }
 
 //
-// MidiRPC_ResumeSong
+// MidiPipe_ResumeSong
 //
 // Resume after pausing.
 //
-void MidiRPC_ResumeSong()
+boolean MidiPipe_ResumeSong()
 {
-   ResumeSong();
+    ResumeSong();
+
+    fprintf(stderr, "%s\n", __FUNCTION__);
+    return true;
 }
 
 //
-// MidiRPC_StopServer
+// MidiPipe_StopServer
 //
 // Stops the RPC server so the program can shutdown.
 //
-void MidiRPC_StopServer()
+boolean MidiPipe_StopServer()
 {
-   // Local shutdown tasks
-   ShutdownSDL();
-   delete song;
-   song = NULL;
+    // Local shutdown tasks
+    ShutdownSDL();
+    free(filename);
+    filename = NULL;
 
-   // Stop RPC server
-   RpcMgmtStopServerListening(NULL);
+    fprintf(stderr, "%s\n", __FUNCTION__);
+    return true;
 }
 
+//=============================================================================
 //
-// RPC Server Init
+// Server Implementation
 //
-static bool MidiRPC_InitServer()
+
+boolean ParseCommand(buffer_reader_t *reader, uint16_t command)
 {
-   RPC_STATUS status;
+    switch (command)
+    {
+    case NET_MIDISOCKET_PACKET_TYPE_PREPARE_NEW_SONG:
+        return MidiPipe_PrepareNewSong();
+    case NET_MIDISOCKET_PACKET_TYPE_SET_FILENAME:
+        return MidiPipe_SetFilename(reader);
+    case NET_MIDISOCKET_PACKET_TYPE_PLAY_SONG:
+        return MidiPipe_PlaySong(reader);
+    case NET_MIDISOCKET_PACKET_TYPE_STOP_SONG:
+        return MidiPipe_StopSong();
+    case NET_MIDISOCKET_PACKET_TYPE_CHANGE_VOLUME:
+        return MidiPipe_ChangeVolume(reader);
+    case NET_MIDISOCKET_PACKET_TYPE_PAUSE_SONG:
+        return MidiPipe_PauseSong();
+    case NET_MIDISOCKET_PACKET_TYPE_RESUME_SONG:
+        return MidiPipe_ResumeSong();
+    case NET_MIDISOCKET_PACKET_TYPE_STOP_SERVER:
+        return MidiPipe_StopServer();
+    default:
+        return false;
+    }
+}
 
-   // Initialize RPC protocol
-   status = 
-      RpcServerUseProtseqEp
-      (
-         (RPC_CSTR)("ncalrpc"),
-         RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
-         (RPC_CSTR)("2d4dc2f9-ce90-4080-8a00-1cb819086970"),
-         NULL
-      );
+//
+// Server packet parser
+//
+boolean ParseMessage(buffer_t *buf)
+{
+    uint16_t command;
+    buffer_reader_t *reader = NewReader(buf);
 
-   if(status)
-      return false;
+    // Attempt to read a command out of the buffer.
+    if (!Reader_ReadInt16(reader, &command))
+    {
+        goto fail;
+    }
 
-   // Register server
-   status = RpcServerRegisterIf(MidiRPC_v1_0_s_ifspec, NULL, NULL);
+    // Attempt to parse a complete message.
+    if (!ParseCommand(reader, command))
+    {
+        goto fail;
+    }
 
-   if(status)
-      return false;
+    // We parsed a complete message!  We can now safely shift
+    // the prior message off the front of the buffer.
+    int bytes_read = Reader_BytesRead(reader);
+    DeleteReader(reader);
+    Buffer_Shift(buf, bytes_read);
 
-   // Start listening
-   status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, FALSE);
+    return true;
 
-   return !status;
+fail:
+    // We did not read a complete packet.  Delete our reader and try again
+    // with more data.
+    DeleteReader(reader);
+    return false;
 }
 
+boolean ListenForever()
+{
+    BOOL wok = FALSE;
+    CHAR pipe_buffer[8192];
+    DWORD pipe_buffer_read = 0;
+
+    boolean ok = false;
+    buffer_t *buffer = NewBuffer();
+
+    fprintf(stderr, "%s\n", "In theory we should be reading...");
+    for (;;)
+    {
+        // Wait until we see some data on the pipe.
+        wok = PeekNamedPipe(midi_process_in, NULL, 0, NULL,
+            &pipe_buffer_read, NULL);
+        if (!wok)
+        {
+            return false;
+        }
+        else if (pipe_buffer_read == 0)
+        {
+            SDL_Delay(1);
+            continue;
+        }
+
+        // Read data off the pipe and add it to the buffer.
+        fprintf(stderr, "%s\n", "ReadFile");
+        wok = ReadFile(midi_process_in, pipe_buffer, sizeof(pipe_buffer),
+            &pipe_buffer_read, NULL);
+        if (!wok)
+        {
+            return false;
+        }
+
+        fprintf(stderr, "%s\n", "Buffer_Push");
+        ok = Buffer_Push(buffer, pipe_buffer, pipe_buffer_read);
+        if (!ok)
+        {
+            return false;
+        }
+
+        fprintf(stderr, "%s\n", "ParseMessage");
+        do
+        {
+            // Read messages off the buffer until we can't anymore.
+            ok = ParseMessage(buffer);
+        } while (ok);
+    }
+
+    return false;
+}
+
 //=============================================================================
 //
 // Main Program
@@ -355,23 +414,92 @@
 //
 
 //
-// WinMain
+// InitSDL
 //
-// Application entry point.
+// Start up SDL and SDL_mixer.
 //
-int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
-                     LPSTR lpCmdLine, int nCmdShow)
+boolean InitSDL()
 {
-   // Initialize SDL
-   if(!InitSDL())
-      return -1;
+    if (SDL_Init(SDL_INIT_AUDIO) == -1)
+    {
+        return false;
+    }
 
-   // Initialize RPC Server
-   if(!MidiRPC_InitServer())
-      return -1;
+    if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
+    {
+        return false;
+    }
 
-   return 0;
+    return true;
 }
 
-// EOF
+//
+// InitPipes
+//
+// Ensure that we can communicate.
+//
+boolean InitPipes()
+{
+    midi_process_in = GetStdHandle(STD_INPUT_HANDLE);
+    if (midi_process_in == INVALID_HANDLE_VALUE)
+    {
+        return false;
+    }
+
+    midi_process_out = GetStdHandle(STD_OUTPUT_HANDLE);
+    if (midi_process_out == INVALID_HANDLE_VALUE)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+//
+// main
+//
+// Application entry point.
+//
+int main(int argc, char *argv[])
+{
+    // Make sure we're not launching this process by itself.
+    if (argc < 2)
+    {
+        MessageBox(NULL, TEXT("This program is tasked with playing Native ")
+            TEXT("MIDI music, and is intended to be launched by ")
+            TEXT(PACKAGE_NAME) TEXT("."),
+            TEXT(PACKAGE_STRING), MB_OK | MB_ICONASTERISK);
+
+        return EXIT_FAILURE;
+    }
+
+    // Make sure our Choccolate Doom and midiproc version are lined up.
+    if (strcmp(PACKAGE_STRING, argv[1]) != 0)
+    {
+        MessageBox(NULL, TEXT("It appears that the version of ")
+            TEXT(PACKAGE_NAME) TEXT(" and ") TEXT(PROGRAM_PREFIX)
+            TEXT("midiproc are out of sync.  Please reinstall ")
+            TEXT(PACKAGE_NAME) TEXT("."),
+            TEXT(PACKAGE_STRING), MB_OK | MB_ICONASTERISK);
+
+        return EXIT_FAILURE;
+    }
+
+    if (!InitPipes())
+    {
+        return EXIT_FAILURE;
+    }
+
+    if (!InitSDL())
+    {
+        return EXIT_FAILURE;
+    }
+
+    if (!ListenForever())
+    {
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
 
--- /dev/null
+++ b/src/i_midipipe.c
@@ -1,0 +1,420 @@
+//
+// Copyright(C) 2013 James Haley et al.
+// Copyright(C) 2017 Alex Mayfield
+//
+// 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
+//
+
+#if _WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <SDL_net.h>
+
+#include "i_midipipe.h"
+
+#include "config.h"
+#include "m_misc.h"
+#include "net_packet.h"
+
+#if defined(_DEBUG)
+#define DEBUGOUT(s) puts(s)
+#else
+#define DEBUGOUT(s)
+#endif
+
+//=============================================================================
+//
+// Data
+//
+
+static HANDLE  midi_process_in_reader;  // Input stream for midi process.
+static HANDLE  midi_process_in_writer;
+static HANDLE  midi_process_out_reader; // Output stream for midi process.
+static HANDLE  midi_process_out_writer;
+
+static boolean server_init = false; // if true, server was started
+static boolean client_init = false; // if true, client was bound
+
+//=============================================================================
+//
+// 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(!server_init) \
+        return false
+
+#define MIDIRPC_MAXTRIES 50 // This number * 10 is the amount of time you can try to wait for.
+
+static boolean I_MidiPipeWrite(void *data, int len)
+{
+    DWORD written;
+    if (WriteFile(midi_process_in_writer, data, len, &written, NULL))
+    {
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+static boolean I_MidiPipeWaitForServer()
+{
+    int tries = 0;
+    while(false) // TODO: Is there some way to tell if the server is listening?
+    {
+        I_Sleep(10);
+        if (++tries >= MIDIRPC_MAXTRIES)
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+//
+// I_MidiPipeRegisterSong
+//
+// Prepare the RPC MIDI engine to receive new song data, and transmit the song
+// filename to the server process.
+//
+boolean I_MidiPipeRegisterSong(const char *filename)
+{
+    BOOL wok;
+    net_packet_t *packet;
+
+    CHECK_RPC_STATUS();
+
+    packet = NET_NewPacket(64);
+    NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_PREPARE_NEW_SONG);
+    NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_SET_FILENAME);
+    NET_WriteString(packet, filename);
+    wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
+        NULL, NULL);
+    NET_FreePacket(packet);
+
+    if (!wok)
+    {
+        DEBUGOUT("I_MidiPipeRegisterSong failed");
+        return false;
+    }
+
+    DEBUGOUT("I_MidiPipeRegisterSong succeeded");
+    return true;
+}
+
+//
+// I_MidiPipePlaySong
+//
+// Tell the RPC server to start playing a song.
+//
+boolean I_MidiPipePlaySong(boolean looping)
+{
+    BOOL wok;
+    net_packet_t *packet;
+
+    CHECK_RPC_STATUS();
+
+    packet = NET_NewPacket(3);
+    NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_PLAY_SONG);
+    NET_WriteInt8(packet, looping);
+    wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
+        NULL, NULL);
+    NET_FreePacket(packet);
+
+    if (!wok)
+    {
+        DEBUGOUT("I_MidiPipePlaySong failed");
+        return false;
+    }
+
+    DEBUGOUT("I_MidiPipePlaySong succeeded");
+    return true;
+}
+
+// 
+// I_MidiPipeStopSong
+//
+// Tell the RPC server to stop any currently playing song.
+//
+boolean I_MidiPipeStopSong()
+{
+    BOOL wok;
+    net_packet_t *packet;
+
+    CHECK_RPC_STATUS();
+
+    packet = NET_NewPacket(2);
+    NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_STOP_SONG);
+    wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
+        NULL, NULL);
+    NET_FreePacket(packet);
+
+    if (!wok)
+    {
+        DEBUGOUT("I_MidiPipeStopSong failed");
+        return false;
+    }
+
+    DEBUGOUT("I_MidiPipeStopSong succeeded");
+    return true;
+}
+
+//
+// I_MidiPipeSetVolume
+//
+// Change the volume level of music played by the RPC midi server.
+//
+boolean I_MidiPipeSetVolume(int volume)
+{
+    BOOL wok;
+    net_packet_t *packet;
+
+    CHECK_RPC_STATUS();
+
+    packet = NET_NewPacket(6);
+    NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_CHANGE_VOLUME);
+    NET_WriteInt32(packet, volume);
+    wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
+        NULL, NULL);
+    NET_FreePacket(packet);
+
+    if (!wok)
+    {
+        DEBUGOUT("I_MidiPipeSetVolume failed");
+        return false;
+    }
+
+    DEBUGOUT("I_MidiPipeSetVolume succeeded");
+    return true;
+}
+
+//
+// I_MidiPipePauseSong
+//
+// Pause the music being played by the server. In actuality, due to SDL_mixer
+// limitations, this just temporarily sets the volume to zero.
+//
+boolean I_MidiPipePauseSong()
+{
+    BOOL wok;
+    net_packet_t *packet;
+
+    CHECK_RPC_STATUS();
+
+    packet = NET_NewPacket(2);
+    NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_PAUSE_SONG);
+    wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
+        NULL, NULL);
+    NET_FreePacket(packet);
+
+    if (!wok)
+    {
+        DEBUGOUT("I_MidiPipePauseSong failed");
+        return false;
+    }
+
+    DEBUGOUT("I_MidiPipePauseSong succeeded");
+    return true;
+}
+
+//
+// I_MidiPipeResumeSong
+//
+// Resume a song after having paused it.
+//
+boolean I_MidiPipeResumeSong()
+{
+    BOOL wok;
+    net_packet_t *packet;
+
+    CHECK_RPC_STATUS();
+
+    packet = NET_NewPacket(2);
+    NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_RESUME_SONG);
+    wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
+        NULL, NULL);
+    NET_FreePacket(packet);
+
+    if (!wok)
+    {
+        DEBUGOUT("I_MidiPipeResumeSong failed");
+        return false;
+    }
+
+    DEBUGOUT("I_MidiPipeResumeSong succeeded");
+   return true;
+}
+
+//=============================================================================
+//
+// Public Interface
+//
+
+//
+// I_MidiPipeInitServer
+//
+// Start up the MIDI server.
+//
+boolean I_MidiPipeInitServer()
+{
+    struct stat sbuf;
+    char filename[MAX_PATH+1];
+
+    memset(filename, 0, sizeof(filename));
+    size_t filename_len = GetModuleFileName(NULL, filename, MAX_PATH);
+
+    // Remove filespec
+    // TODO: Move this to m_misc
+    char *fp = &filename[filename_len];
+    while (filename <= fp && *fp != DIR_SEPARATOR)
+    {
+        fp--;
+    }
+    *(fp + 1) = '\0';
+    char* module = M_StringJoin(filename, PROGRAM_PREFIX "midiproc.exe", NULL);
+    char* cmdline = M_StringJoin(module, " \"" PACKAGE_STRING "\"", NULL);
+    DEBUGOUT(module);
+    DEBUGOUT(cmdline);
+
+    // Look for executable file
+    if(stat(module, &sbuf))
+    {
+        DEBUGOUT("Could not find midiproc");
+        return false;
+    }
+
+    // Set up pipes
+    SECURITY_ATTRIBUTES sec_attrs;
+    memset(&sec_attrs, 0, sizeof(SECURITY_ATTRIBUTES));
+    sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sec_attrs.bInheritHandle = TRUE;
+    sec_attrs.lpSecurityDescriptor = NULL;
+
+    if (!CreatePipe(&midi_process_in_reader, &midi_process_in_writer, &sec_attrs, 0))
+    {
+        DEBUGOUT("Could not initialize midiproc stdin");
+        return false;
+    }
+
+    if (!SetHandleInformation(midi_process_in_writer, HANDLE_FLAG_INHERIT, 0))
+    {
+        DEBUGOUT("Could not disinherit midiproc stdin");
+        return false;
+    }
+
+    if (!CreatePipe(&midi_process_out_reader, &midi_process_out_writer, &sec_attrs, 0))
+    {
+        DEBUGOUT("Could not initialize midiproc stdout/stderr");
+        return false;
+    }
+
+    if (!SetHandleInformation(midi_process_out_reader, HANDLE_FLAG_INHERIT, 0))
+    {
+        DEBUGOUT("Could not disinherit midiproc stdin");
+        return false;
+    }
+
+    // Launch the subprocess
+    PROCESS_INFORMATION proc_info;
+    memset(&proc_info, 0, sizeof(proc_info));
+
+    STARTUPINFO startup_info;
+    memset(&startup_info, 0, sizeof(startup_info));
+    startup_info.cb = sizeof(startup_info);
+    startup_info.hStdInput = midi_process_in_reader;
+    startup_info.hStdOutput = midi_process_out_writer;
+    startup_info.dwFlags = STARTF_USESTDHANDLES;
+
+    boolean ok = CreateProcess(TEXT(module), TEXT(cmdline), NULL, NULL, TRUE,
+        CREATE_NEW_CONSOLE, NULL, NULL, &startup_info, &proc_info);
+
+    if (ok)
+    {
+        DEBUGOUT("midiproc started");
+        server_init = true;
+    }
+    else
+    {
+        DEBUGOUT("failed to start midiproc");
+    }
+
+    return ok;
+}
+
+//
+// I_MidiPipeInitClient
+//
+// Ensure that we can actually communicate with the subprocess.
+//
+boolean I_MidiPipeInitClient()
+{
+    client_init = true;
+    return true;
+}
+
+//
+// I_MidiPipeClientShutDown
+//
+// Shutdown the RPC Client
+//
+/* void I_MidiPipeClientShutDown()
+{
+    // stop the server
+    if(server_init)
+    {
+        net_packet_t *packet;
+        packet = NET_NewPacket(2);
+        NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_STOP_SERVER);
+        int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
+        NET_FreePacket(packet);
+        if (len < packet->len)
+        {
+            DEBUGOUT("Problem encountered when stopping RPC server");
+        }
+
+        server_init = false;
+    }
+
+    if (midi_socket)
+    {
+        SDLNet_TCP_Close(midi_socket);
+        midi_socket = NULL;
+    }
+
+    client_init = false;
+} */
+
+//
+// I_MidiPipeReady
+//
+// Returns true if both server and client initialized successfully.
+//
+boolean I_MidiPipeReady()
+{
+    CHECK_RPC_STATUS();
+
+    return true;
+}
+
+#endif
+
--- /dev/null
+++ b/src/i_midipipe.h
@@ -1,0 +1,41 @@
+//
+// Copyright(C) 2013 James Haley et al.
+// Copyright(C) 2017 Alex Mayfield
+//
+// 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
+//
+
+#ifndef __I_MIDISOCKET__
+#define __I_MIDISOCKET__
+
+#if _WIN32
+
+#include "doomtype.h"
+
+boolean I_MidiSocketInitServer();
+boolean I_MidiSocketInitClient();
+void I_MidiSocketClientShutDown();
+boolean I_MidiSocketReady();
+
+boolean I_MidiSocketRegisterSong(const char *filename);
+boolean I_MidiSocketPlaySong(boolean looping);
+boolean I_MidiSocketStopSong();
+boolean I_MidiSocketSetVolume(int volume);
+boolean I_MidiSocketPauseSong();
+boolean I_MidiSocketResumeSong();
+
+#endif
+
+#endif
+
--- a/src/i_midisocket.c
+++ /dev/null
@@ -1,391 +1,0 @@
-//
-// Copyright(C) 2013 James Haley et al.
-// Copyright(C) 2017 Alex Mayfield
-//
-// 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
-//
-
-#if _WIN32
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <SDL_net.h>
-
-#include "i_midisocket.h"
-
-#include "m_misc.h"
-#include "net_packet.h"
-
-#if defined(_DEBUG)
-#define DEBUGOUT(s) puts(s)
-#else
-#define DEBUGOUT(s)
-#endif
-
-typedef enum {
-	NET_MIDISOCKET_PACKET_TYPE_PREPARE_NEW_SONG,
-    NET_MIDISOCKET_PACKET_TYPE_REGISTER_SONG,
-	NET_MIDISOCKET_PACKET_TYPE_PLAY_SONG,
-	NET_MIDISOCKET_PACKET_TYPE_STOP_SONG,
-	NET_MIDISOCKET_PACKET_TYPE_CHANGE_VOLUME,
-	NET_MIDISOCKET_PACKET_TYPE_PAUSE_SONG,
-	NET_MIDISOCKET_PACKET_TYPE_RESUME_SONG,
-	NET_MIDISOCKET_PACKET_TYPE_STOP_SERVER
-} net_midisocket_packet_type_t;
-
-//=============================================================================
-//
-// Data
-//
-
-static TCPsocket midi_socket;       // Client socket
-static boolean server_init = false; // if true, server was started
-static boolean client_init = false; // if true, client was bound
-
-// server process information
-static STARTUPINFO         si;
-static PROCESS_INFORMATION pi;
-
-//=============================================================================
-//
-// 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(!server_init || !client_init) \
-        return false
-
-#define MIDIRPC_MAXTRIES 50 // This number * 10 is the amount of time you can try to wait for.
-
-static boolean I_MidiSocketWaitForServer()
-{
-    int tries = 0;
-    while(false) // TODO: Is there some way to tell if the server is listening?
-    {
-        I_Sleep(10);
-        if (++tries >= MIDIRPC_MAXTRIES)
-        {
-            return false;
-        }
-    }
-    return true;
-}
-
-//
-// I_MidiSocketRegisterSong
-//
-// Prepare the RPC MIDI engine to receive new song data, and transmit the song
-// filename to the server process.
-//
-boolean I_MidiSocketRegisterSong(const char *filename)
-{
-    CHECK_RPC_STATUS();
-
-    net_packet_t *packet;
-    packet = NET_NewPacket(2);
-    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_PREPARE_NEW_SONG);
-    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
-    if (len < packet->len)
-    {
-        goto fail;
-    }
-    NET_FreePacket(packet);
-
-    packet = NET_NewPacket(64);
-    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_REGISTER_SONG);
-    NET_WriteString(packet, filename);
-    len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
-    if (len < packet->len)
-    {
-        goto fail;
-    }
-    NET_FreePacket(packet);
-
-    DEBUGOUT("I_MidiSocketRegisterSong succeeded");
-    return true;
-
-fail:
-    NET_FreePacket(packet);
-
-    DEBUGOUT("I_MidiSocketRegisterSong failed");
-    return false;
-}
-
-//
-// I_MidiSocketPlaySong
-//
-// Tell the RPC server to start playing a song.
-//
-boolean I_MidiSocketPlaySong(boolean looping)
-{
-    CHECK_RPC_STATUS();
-
-    net_packet_t *packet;
-    packet = NET_NewPacket(3);
-    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_PLAY_SONG);
-    NET_WriteInt8(packet, looping);
-    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
-    NET_FreePacket(packet);
-    if (len < packet->len)
-    {
-        DEBUGOUT("I_MidiSocketPlaySong failed");
-        return false;
-    }
-
-    DEBUGOUT("I_MidiSocketPlaySong succeeded");
-    return true;
-}
-
-// 
-// I_MidiSocketStopSong
-//
-// Tell the RPC server to stop any currently playing song.
-//
-boolean I_MidiSocketStopSong()
-{
-    CHECK_RPC_STATUS();
-
-    net_packet_t *packet;
-    packet = NET_NewPacket(2);
-    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_STOP_SONG);
-    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
-    NET_FreePacket(packet);
-    if (len < packet->len)
-    {
-        DEBUGOUT("I_MidiSocketStopSong failed");
-        return false;
-    }
-
-    DEBUGOUT("I_MidiSocketStopSong succeeded");
-    return true;
-}
-
-//
-// I_MidiSocketSetVolume
-//
-// Change the volume level of music played by the RPC midi server.
-//
-boolean I_MidiSocketSetVolume(int volume)
-{
-    CHECK_RPC_STATUS();
-
-    net_packet_t *packet;
-    packet = NET_NewPacket(6);
-    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_CHANGE_VOLUME);
-    NET_WriteInt32(packet, volume);
-    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
-    NET_FreePacket(packet);
-    if (len < packet->len)
-    {
-        DEBUGOUT("I_MidiSocketSetVolume failed");
-        return false;
-    }
-
-    DEBUGOUT("I_MidiSocketSetVolume succeeded");
-    return true;
-}
-
-//
-// I_MidiSocketPauseSong
-//
-// Pause the music being played by the server. In actuality, due to SDL_mixer
-// limitations, this just temporarily sets the volume to zero.
-//
-boolean I_MidiSocketPauseSong()
-{
-    CHECK_RPC_STATUS();
-
-    net_packet_t *packet;
-    packet = NET_NewPacket(2);
-    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_PAUSE_SONG);
-    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
-    NET_FreePacket(packet);
-    if (len < packet->len)
-    {
-        DEBUGOUT("I_MidiSocketPauseSong failed");
-        return false;
-    }
-
-    DEBUGOUT("I_MidiSocketPauseSong succeeded");
-    return true;
-}
-
-//
-// I_MidiSocketResumeSong
-//
-// Resume a song after having paused it.
-//
-boolean I_MidiSocketResumeSong()
-{
-    CHECK_RPC_STATUS();
-
-    net_packet_t *packet;
-    packet = NET_NewPacket(2);
-    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_RESUME_SONG);
-    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
-    NET_FreePacket(packet);
-    if (len < packet->len)
-    {
-        DEBUGOUT("I_MidiSocketResumeSong failed");
-        return false;
-    }
-
-    DEBUGOUT("I_MidiSocketResumeSong succeeded");
-   return true;
-}
-
-//=============================================================================
-//
-// Public Interface
-//
-
-//
-// I_MidiSocketInitServer
-//
-// Start up the RPC MIDI server.
-//
-boolean I_MidiSocketInitServer()
-{
-    struct stat sbuf;
-    char filename[MAX_PATH+1];
-
-    memset(filename, 0, sizeof(filename));
-    size_t filename_len = GetModuleFileName(NULL, filename, MAX_PATH);
-
-    // Remove filespec
-    // TODO: Move this to m_misc
-    char *fp = &filename[filename_len];
-    while (filename <= fp && *fp != DIR_SEPARATOR)
-    {
-        fp--;
-    }
-    *(fp + 1) = '\0';
-    char* module = M_StringJoin(filename, "midiproc.exe", NULL);
-    DEBUGOUT(module);
-
-    // Look for executable file
-    if(stat(module, &sbuf))
-    {
-        DEBUGOUT("Could not find midiproc");
-        return false;
-    }
-
-    si.cb = sizeof(si);
-
-    boolean result = CreateProcess(module, NULL, NULL, NULL, FALSE,
-                                   0, NULL, NULL, &si, &pi);
-
-    if (result)
-    {
-        DEBUGOUT("RPC server started");
-        server_init = true;
-    }
-    else
-    {
-        DEBUGOUT("CreateProcess failed to start midiproc");
-    }
-
-    return result;
-}
-
-//
-// I_MidiSocketInitClient
-//
-// Initialize client RPC bindings and bind to the server.
-//
-boolean I_MidiSocketInitClient()
-{
-    IPaddress ipaddress;
-
-    // If server didn't start, client cannot be bound.
-    if (!server_init)
-    {
-        goto fail;
-    }
-
-    // Resolve localhost to an IP address.
-    if (SDLNet_ResolveHost(&ipaddress, "localhost", 2993) == -1)
-    {
-        goto fail;
-    }
-
-    // Connect to the server.
-    midi_socket = SDLNet_TCP_Open(&ipaddress);
-    if (midi_socket == NULL)
-    {
-        goto fail;
-    }
-
-    DEBUGOUT("RPC client initialized");
-    client_init = true;
-
-    return I_MidiSocketWaitForServer();
-
-fail:
-    DEBUGOUT("RPC client binding failed");
-    return false;
-}
-
-//
-// I_MidiSocketClientShutDown
-//
-// Shutdown the RPC Client
-//
-void I_MidiSocketClientShutDown()
-{
-    // stop the server
-    if(server_init)
-    {
-        net_packet_t *packet;
-        packet = NET_NewPacket(2);
-        NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_STOP_SERVER);
-        int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
-        NET_FreePacket(packet);
-        if (len < packet->len)
-        {
-            DEBUGOUT("Problem encountered when stopping RPC server");
-        }
-
-        server_init = false;
-    }
-
-    if (midi_socket)
-    {
-        SDLNet_TCP_Close(midi_socket);
-        midi_socket = NULL;
-    }
-
-    client_init = false;
-}
-
-//
-// I_MidiSocketReady
-//
-// Returns true if both server and client initialized successfully.
-//
-boolean I_MidiSocketReady()
-{
-    CHECK_RPC_STATUS();
-
-    return true;
-}
-
-#endif
-
--- a/src/i_midisocket.h
+++ /dev/null
@@ -1,41 +1,0 @@
-//
-// Copyright(C) 2013 James Haley et al.
-// Copyright(C) 2017 Alex Mayfield
-//
-// 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
-//
-
-#ifndef __I_MIDISOCKET__
-#define __I_MIDISOCKET__
-
-#if _WIN32
-
-#include "doomtype.h"
-
-boolean I_MidiSocketInitServer();
-boolean I_MidiSocketInitClient();
-void I_MidiSocketClientShutDown();
-boolean I_MidiSocketReady();
-
-boolean I_MidiSocketRegisterSong(const char *filename);
-boolean I_MidiSocketPlaySong(boolean looping);
-boolean I_MidiSocketStopSong();
-boolean I_MidiSocketSetVolume(int volume);
-boolean I_MidiSocketPauseSong();
-boolean I_MidiSocketResumeSong();
-
-#endif
-
-#endif
-
--- a/src/i_sdlmusic.c
+++ b/src/i_sdlmusic.c
@@ -25,7 +25,7 @@
 #include "SDL.h"
 #include "SDL_mixer.h"
 
-#include "i_midisocket.h"
+#include "i_midipipe.h"
 
 #include "config.h"
 #include "doomtype.h"
@@ -975,7 +975,7 @@
     }
 
 #if WIN32
-    I_MidiSocketInitServer();
+    I_MidiPipeInitServer();
 #endif
 
     return music_initialized;
@@ -1000,7 +1000,7 @@
     }
 
 #if WIN32
-    I_MidiSocketSetVolume(vol);
+    I_MidiPipeSetVolume(vol);
 #else
     Mix_VolumeMusic(vol);
 #endif
@@ -1055,7 +1055,7 @@
     }
 
 #if _WIN32
-    I_MidiSocketPlaySong(loops);
+    I_MidiPipePlaySong(loops);
 #else
     Mix_PlayMusic(current_track_music, loops);
 #endif
@@ -1093,7 +1093,7 @@
     }
 
 #if _WIN32
-    I_MidiSocketStopSong();
+    I_MidiPipeStopSong();
 #else
     Mix_HaltMusic();
 #endif
@@ -1209,7 +1209,7 @@
     // we have to generate a temporary file.
 
 #ifdef _WIN32
-    music = (Mix_Music*)I_MidiSocketRegisterSong(filename);
+    music = (Mix_Music*)I_MidiPipeRegisterSong(filename);
 #else
     music = Mix_LoadMUS(filename);
 #endif
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -142,6 +142,17 @@
     NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE,
 } net_master_packet_type_t;
 
+typedef enum {
+    NET_MIDIPIPE_PACKET_TYPE_PREPARE_NEW_SONG,
+    NET_MIDIPIPE_PACKET_TYPE_SET_FILENAME,
+    NET_MIDIPIPE_PACKET_TYPE_PLAY_SONG,
+    NET_MIDIPIPE_PACKET_TYPE_STOP_SONG,
+    NET_MIDIPIPE_PACKET_TYPE_CHANGE_VOLUME,
+    NET_MIDIPIPE_PACKET_TYPE_PAUSE_SONG,
+    NET_MIDIPIPE_PACKET_TYPE_RESUME_SONG,
+    NET_MIDIPIPE_PACKET_TYPE_STOP_SERVER
+} net_midipipe_packet_type_t;
+
 // Settings specified when the client connects to the server.
 
 typedef struct