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