Add midiproc from Crispy Doom (#100)

* Add midiproc from Crispy Doom

* Fix gcc build

* Restore original code and cleanup

* Try to fix gcc build

* Fix midiproc not starting when using drag-and-drop

* Few fixes

* Add check midiproc version

* Move snd_samplerate declaration

* Add install rule
This commit is contained in:
Roman Fomin 2021-01-22 01:17:08 +07:00 committed by GitHub
parent cb98e17d72
commit d194546154
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1987 additions and 30 deletions

View File

@ -267,3 +267,4 @@ add_subdirectory(docs)
add_subdirectory(examples)
add_subdirectory(Source)
add_subdirectory(toolsrc)
add_subdirectory(midiproc)

View File

@ -28,6 +28,7 @@ set(WOOF_SOURCES
hu_lib.c hu_lib.h
hu_stuff.c hu_stuff.h
i_main.c
i_midipipe.c i_midipipe.h
i_net.c i_net.h
i_sound.c i_sound.h
i_system.c i_system.h
@ -43,6 +44,8 @@ set(WOOF_SOURCES
m_random.c m_random.h
m_swap.h
mmus2mid.c mmus2mid.h
net_defs.h
net_packet.c net_packet.h
p_ceilng.c
p_doors.c
p_enemy.c p_enemy.h

513
Source/i_midipipe.c Normal file
View File

@ -0,0 +1,513 @@
//
// 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 Midi Server
//
#if _WIN32
#include <stdlib.h>
#include <sys/stat.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "i_midipipe.h"
#include "config.h"
#include "i_sound.h"
#include "i_system.h"
#include "i_video.h" // for I_Sleep
#include "m_misc2.h"
#include "net_packet.h"
#include "../midiproc/proto.h"
#if defined(_DEBUG)
#define DEBUGOUT(s) puts(s)
#else
#define DEBUGOUT(s)
#endif
//=============================================================================
//
// Public Data
//
// True if the midi proces was initialized at least once and has not been
// explicitly shut down. This remains true if the server is momentarily
// unreachable.
boolean midi_server_initialized = false;
// True if the current track is being handled via the MIDI server.
boolean midi_server_registered = false;
//=============================================================================
//
// Data
//
#define MIDIPIPE_MAX_WAIT 1000 // Max amount of ms to wait for expected 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;
//=============================================================================
//
// Private functions
//
//
// FreePipes
//
// Free all pipes in use by this module.
//
static void FreePipes()
{
if (midi_process_in_reader != NULL)
{
CloseHandle(midi_process_in_reader);
midi_process_in_reader = NULL;
}
if (midi_process_in_writer != NULL)
{
CloseHandle(midi_process_in_writer);
midi_process_in_writer = NULL;
}
if (midi_process_out_reader != NULL)
{
CloseHandle(midi_process_out_reader);
midi_process_in_reader = NULL;
}
if (midi_process_out_writer != NULL)
{
CloseHandle(midi_process_out_writer);
midi_process_out_writer = NULL;
}
}
//
// UsingNativeMidi
//
// Enumerate all music decoders and return true if NATIVEMIDI is one of them.
//
// If this is the case, using the MIDI server is probably necessary. If not,
// we're likely using Timidity and thus don't need to start the server.
//
static boolean UsingNativeMidi()
{
int i;
int decoders = Mix_GetNumMusicDecoders();
for (i = 0; i < decoders; i++)
{
if (strcmp(Mix_GetMusicDecoder(i), "NATIVEMIDI") == 0)
{
return true;
}
}
return false;
}
//
// WritePipe
//
// Writes packet data to the subprocess' standard in.
//
static boolean WritePipe(net_packet_t *packet)
{
DWORD bytes_written;
BOOL ok = WriteFile(midi_process_in_writer, packet->data, packet->len,
&bytes_written, NULL);
return ok;
}
//
// ExpectPipe
//
// Expect the contents of a packet off of the subprocess' stdout. If the
// response is unexpected, or doesn't arrive within a specific amuont of time,
// assume the subprocess is in an unknown state.
//
static boolean ExpectPipe(net_packet_t *packet)
{
int start;
BOOL ok;
CHAR pipe_buffer[8192];
DWORD pipe_buffer_read = 0;
if (packet->len > sizeof(pipe_buffer))
{
// The size of the packet we're expecting is larger than our buffer
// size, so bail out now.
return false;
}
start = I_GetTimeMS();
do
{
// Wait until we see exactly the amount of data we expect on the pipe.
ok = PeekNamedPipe(midi_process_out_reader, NULL, 0, NULL,
&pipe_buffer_read, NULL);
if (!ok)
{
break;
}
else if (pipe_buffer_read < packet->len)
{
I_Sleep(1);
continue;
}
// Read precisely the number of bytes we're expecting, and no more.
ok = ReadFile(midi_process_out_reader, pipe_buffer, packet->len,
&pipe_buffer_read, NULL);
if (!ok || pipe_buffer_read != packet->len)
{
break;
}
// Compare our data buffer to the packet.
if (memcmp(packet->data, pipe_buffer, packet->len) != 0)
{
break;
}
return true;
// Continue looping as long as we don't exceed our maximum wait time.
} while (I_GetTimeMS() - start <= MIDIPIPE_MAX_WAIT);
// TODO: Deal with the wedged process?
return false;
}
//
// RemoveFileSpec
//
// A reimplementation of PathRemoveFileSpec that doesn't bring in Shlwapi
//
void RemoveFileSpec(TCHAR *path, size_t size)
{
TCHAR *fp = NULL;
fp = &path[size];
while (path <= fp && *fp != DIR_SEPARATOR)
{
fp--;
}
*(fp + 1) = '\0';
}
static boolean BlockForAck(void)
{
boolean ok;
net_packet_t *packet;
packet = NET_NewPacket(2);
NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_ACK);
ok = ExpectPipe(packet);
NET_FreePacket(packet);
return ok;
}
//=============================================================================
//
// Protocol Commands
//
//
// I_MidiPipe_RegisterSong
//
// Tells the MIDI subprocess to load a specific filename for playing. This
// function blocks until there is an acknowledgement from the server.
//
boolean I_MidiPipe_RegisterSong(char *filename)
{
boolean ok;
net_packet_t *packet;
packet = NET_NewPacket(64);
NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_REGISTER_SONG);
NET_WriteString(packet, filename);
ok = WritePipe(packet);
NET_FreePacket(packet);
midi_server_registered = false;
ok = ok && BlockForAck();
if (!ok)
{
DEBUGOUT("I_MidiPipe_RegisterSong failed");
return false;
}
midi_server_registered = true;
DEBUGOUT("I_MidiPipe_RegisterSong succeeded");
return true;
}
//
// I_MidiPipe_UnregisterSong
//
// Tells the MIDI subprocess to unload the current song.
//
void I_MidiPipe_UnregisterSong(void)
{
boolean ok;
net_packet_t *packet;
packet = NET_NewPacket(64);
NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_UNREGISTER_SONG);
ok = WritePipe(packet);
NET_FreePacket(packet);
ok = ok && BlockForAck();
if (!ok)
{
DEBUGOUT("I_MidiPipe_UnregisterSong failed");
return;
}
midi_server_registered = false;
DEBUGOUT("I_MidiPipe_UnregisterSong succeeded");
}
//
// I_MidiPipe_SetVolume
//
// Tells the MIDI subprocess to set a specific volume for the song.
//
void I_MidiPipe_SetVolume(int vol)
{
boolean ok;
net_packet_t *packet;
packet = NET_NewPacket(6);
NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_SET_VOLUME);
NET_WriteInt32(packet, vol);
ok = WritePipe(packet);
NET_FreePacket(packet);
ok = ok && BlockForAck();
if (!ok)
{
DEBUGOUT("I_MidiPipe_SetVolume failed");
return;
}
DEBUGOUT("I_MidiPipe_SetVolume succeeded");
}
//
// I_MidiPipe_PlaySong
//
// Tells the MIDI subprocess to play the currently loaded song.
//
void I_MidiPipe_PlaySong(int loops)
{
boolean ok;
net_packet_t *packet;
packet = NET_NewPacket(6);
NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_PLAY_SONG);
NET_WriteInt32(packet, loops);
ok = WritePipe(packet);
NET_FreePacket(packet);
ok = ok && BlockForAck();
if (!ok)
{
DEBUGOUT("I_MidiPipe_PlaySong failed");
return;
}
DEBUGOUT("I_MidiPipe_PlaySong succeeded");
}
//
// I_MidiPipe_StopSong
//
// Tells the MIDI subprocess to stop playing the currently loaded song.
//
void I_MidiPipe_StopSong()
{
boolean ok;
net_packet_t *packet;
packet = NET_NewPacket(2);
NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_STOP_SONG);
ok = WritePipe(packet);
NET_FreePacket(packet);
ok = ok && BlockForAck();
if (!ok)
{
DEBUGOUT("I_MidiPipe_StopSong failed");
return;
}
DEBUGOUT("I_MidiPipe_StopSong succeeded");
}
//
// I_MidiPipe_ShutdownServer
//
// Tells the MIDI subprocess to shutdown.
//
void I_MidiPipe_ShutdownServer()
{
boolean ok;
net_packet_t *packet;
packet = NET_NewPacket(2);
NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_SHUTDOWN);
ok = WritePipe(packet);
NET_FreePacket(packet);
ok = ok && BlockForAck();
FreePipes();
midi_server_initialized = false;
if (!ok)
{
DEBUGOUT("I_MidiPipe_ShutdownServer failed");
return;
}
DEBUGOUT("I_MidiPipe_ShutdownServer succeeded");
}
//=============================================================================
//
// Public Interface
//
//
// I_MidiPipeInitServer
//
// Start up the MIDI server.
//
boolean I_MidiPipe_InitServer()
{
TCHAR dirname[MAX_PATH + 1];
DWORD dirname_len;
char *module = NULL;
char *cmdline = NULL;
char *fullpath = NULL;
char params_buf[128];
SECURITY_ATTRIBUTES sec_attrs;
PROCESS_INFORMATION proc_info;
STARTUPINFO startup_info;
BOOL ok;
int snd_samplerate = 44100;
if (!UsingNativeMidi() /*|| strlen(snd_musiccmd) > 0*/)
{
// If we're not using native MIDI, or if we're playing music through
// an exteranl program, we don't need to start the server.
return false;
}
// Get directory name
memset(dirname, 0, sizeof(dirname));
dirname_len = GetModuleFileName(NULL, dirname, MAX_PATH);
if (dirname_len == 0)
{
return false;
}
RemoveFileSpec(dirname, dirname_len);
// Define the module.
module = "woof-midiproc.exe";
// Set up pipes
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;
}
// Define the command line. Version, Sample Rate, and handles follow
// the executable name.
M_snprintf(params_buf, sizeof(params_buf), "%d %Iu %Iu",
snd_samplerate, (size_t) midi_process_in_reader, (size_t) midi_process_out_writer);
cmdline = M_StringJoin(module, " \"" PROJECT_STRING "\"", " ", params_buf, NULL);
// Launch the subprocess
memset(&proc_info, 0, sizeof(proc_info));
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
fullpath = M_StringJoin(dirname, module, NULL);
ok = CreateProcess(TEXT(fullpath), TEXT(cmdline), NULL, NULL, TRUE,
0, NULL, dirname, &startup_info, &proc_info);
if (!ok)
{
FreePipes();
free(cmdline);
free(fullpath);
return false;
}
// Since the server has these handles, we don't need them anymore.
CloseHandle(midi_process_in_reader);
midi_process_in_reader = NULL;
CloseHandle(midi_process_out_writer);
midi_process_out_writer = NULL;
midi_server_initialized = true;
return true;
}
#endif

49
Source/i_midipipe.h Normal file
View File

@ -0,0 +1,49 @@
//
// 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 Midi Server
//
#ifndef __I_MIDIPIPE__
#define __I_MIDIPIPE__
#if _WIN32
#include "SDL_mixer.h"
#include "doomtype.h"
extern boolean midi_server_initialized;
extern boolean midi_server_registered;
boolean I_MidiPipe_RegisterSong(char *filename);
void I_MidiPipe_UnregisterSong(void);
void I_MidiPipe_SetVolume(int vol);
void I_MidiPipe_PlaySong(int loops);
void I_MidiPipe_StopSong();
void I_MidiPipe_ShutdownServer();
boolean I_MidiPipe_InitServer();
#else
#include "doomtype.h"
static const boolean midi_server_registered = false;
#endif
#endif

View File

@ -648,6 +648,8 @@ void I_InitSound(void)
#include "mmus2mid.h"
#include "m_misc.h"
#include "m_misc2.h"
#include "i_midipipe.h"
// Only one track at a time
static Mix_Music *music = NULL;
@ -671,7 +673,16 @@ static void *music_block = NULL;
//
void I_ShutdownMusic(void)
{
I_UnRegisterSong(1);
#if defined(_WIN32)
if (midi_server_initialized)
{
I_MidiPipe_ShutdownServer();
}
else
#endif
{
I_UnRegisterSong(1);
}
}
//
@ -684,6 +695,13 @@ void I_InitMusic(void)
case -1:
printf("I_InitMusic: Using SDL_mixer.\n");
mus_init = true;
// Initialize SDL_Mixer for MIDI music playback
Mix_Init(MIX_INIT_MID | MIX_INIT_FLAC | MIX_INIT_OGG | MIX_INIT_MP3); // [crispy] initialize some more audio formats
#if defined(_WIN32)
// [AM] Start up midiproc to handle playing MIDI music.
I_MidiPipe_InitServer();
#endif
break;
default:
printf("I_InitMusic: Music is disabled.\n");
@ -700,6 +718,13 @@ void I_PlaySong(int handle, int looping)
if(!mus_init)
return;
#if defined(_WIN32)
if (midi_server_registered)
{
I_MidiPipe_PlaySong(looping ? -1 : 1);
}
else
#endif
if(CHECK_MUSIC(handle) && Mix_PlayMusic(music, looping ? -1 : 0) == -1)
{
dprintf("I_PlaySong: Mix_PlayMusic failed\n");
@ -713,19 +738,38 @@ void I_PlaySong(int handle, int looping)
//
// I_SetMusicVolume
//
static int current_midi_volume;
void I_SetMusicVolume(int volume)
{
// haleyjd 09/04/06: adjust to use scale from 0 to 15
Mix_VolumeMusic((volume * 128) / 15);
}
current_midi_volume = (volume * 128) / 15;
static int paused_midi_volume;
#if defined(_WIN32)
if (midi_server_registered)
{
I_MidiPipe_SetVolume(current_midi_volume);
}
else
#endif
{
Mix_VolumeMusic(current_midi_volume);
}
}
//
// I_PauseSong
//
void I_PauseSong(int handle)
{
#if defined(_WIN32)
if (midi_server_registered)
{
I_MidiPipe_SetVolume(0);
}
else
#endif
if(CHECK_MUSIC(handle))
{
// Not for mids
@ -734,7 +778,6 @@ void I_PauseSong(int handle)
else
{
// haleyjd 03/21/06: set MIDI volume to zero on pause
paused_midi_volume = Mix_VolumeMusic(-1);
Mix_VolumeMusic(0);
}
}
@ -745,13 +788,20 @@ void I_PauseSong(int handle)
//
void I_ResumeSong(int handle)
{
#if defined(_WIN32)
if (midi_server_registered)
{
I_MidiPipe_SetVolume(current_midi_volume);
}
else
#endif
if(CHECK_MUSIC(handle))
{
// Not for mids
if(Mix_GetMusicType(music) != MUS_MID)
Mix_ResumeMusic();
else
Mix_VolumeMusic(paused_midi_volume);
Mix_VolumeMusic(current_midi_volume);
}
}
@ -760,6 +810,13 @@ void I_ResumeSong(int handle)
//
void I_StopSong(int handle)
{
#if defined(_WIN32)
if (midi_server_registered)
{
I_MidiPipe_StopSong();
}
else
#endif
if(CHECK_MUSIC(handle))
Mix_HaltMusic();
}
@ -769,6 +826,13 @@ void I_StopSong(int handle)
//
void I_UnRegisterSong(int handle)
{
#if defined(_WIN32)
if (midi_server_registered)
{
I_MidiPipe_UnregisterSong();
}
else
#endif
if(CHECK_MUSIC(handle))
{
// Stop and free song
@ -790,6 +854,22 @@ void I_UnRegisterSong(int handle)
}
}
#if defined(_WIN32)
static void MidiProc_RegisterSong(void *data, int size)
{
char* filename;
filename = M_TempFile("doom"); // [crispy] generic filename
M_WriteFile(filename, data, size);
if (!I_MidiPipe_RegisterSong(filename))
{
fprintf(stderr, "Error loading midi: %s\n",
"Could not communicate with midiproc.");
}
}
#endif
//
// I_RegisterSong
//
@ -797,20 +877,29 @@ int I_RegisterSong(void *data, int size)
{
if(music != NULL)
I_UnRegisterSong(1);
rw = SDL_RWFromMem(data, size);
music = Mix_LoadMUS_RW(rw, false);
// It's not recognized by SDL_mixer, is it a mus?
if(music == NULL)
{
if (size < 4 || memcmp(data, "MUS\x1a", 4)) // [crispy] MUS_HEADER_MAGIC
{
#if defined(_WIN32)
if (size >= 4 && memcmp(data, "MThd", 4) == 0 && midi_server_initialized) // MIDI header magic
{
music = NULL;
MidiProc_RegisterSong(data, size);
return 1;
}
else
#endif
{
rw = SDL_RWFromMem(data, size);
music = Mix_LoadMUS_RW(rw, false);
}
}
else // Assume a MUS file and try to convert
{
int err;
MIDI mididata;
UBYTE *mid;
int midlen;
SDL_FreeRW(rw);
rw = NULL;
memset(&mididata, 0, sizeof(MIDI));
@ -823,21 +912,34 @@ int I_RegisterSong(void *data, int size)
// Hurrah! Let's make it a mid and give it to SDL_mixer
MIDIToMidi(&mididata, &mid, &midlen);
rw = SDL_RWFromMem(mid, midlen);
music = Mix_LoadMUS_RW(rw, false);
#if defined(_WIN32)
if (midi_server_initialized)
{
music = NULL;
MidiProc_RegisterSong(mid, midlen);
free(mid);
return 1;
}
else
#endif
{
rw = SDL_RWFromMem(mid, midlen);
music = Mix_LoadMUS_RW(rw, false);
if(music == NULL)
{
// Conversion failed, free everything
SDL_FreeRW(rw);
rw = NULL;
free(mid);
}
else
{
// Conversion succeeded
// -> save memory block to free when unregistering
music_block = mid;
if(music == NULL)
{
// Conversion failed, free everything
SDL_FreeRW(rw);
rw = NULL;
free(mid);
}
else
{
// Conversion succeeded
// -> save memory block to free when unregistering
music_block = mid;
}
}
}

View File

@ -74,6 +74,34 @@ boolean M_FileExists(const char *filename)
}
}
// Returns the path to a temporary file of the given name, stored
// inside the system temporary directory.
//
// The returned value must be freed with Z_Free after use.
char *M_TempFile(const char *s)
{
const char *tempdir;
#ifdef _WIN32
// Check the TEMP environment variable to find the location.
tempdir = getenv("TEMP");
if (tempdir == NULL)
{
tempdir = ".";
}
#else
// In Unix, just use /tmp.
tempdir = "/tmp";
#endif
return M_StringJoin(tempdir, DIR_SEPARATOR_S, s, NULL);
}
// Check if a file exists by probing for common case variation of its filename.
// Returns a newly allocated string that the caller is responsible for freeing.
@ -382,3 +410,40 @@ char *M_StringJoin(const char *s, ...)
return result;
}
// Safe, portable vsnprintf().
int M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args)
{
int result;
if (buf_len < 1)
{
return 0;
}
// Windows (and other OSes?) has a vsnprintf() that doesn't always
// append a trailing \0. So we must do it, and write into a buffer
// that is one byte shorter; otherwise this function is unsafe.
result = vsnprintf(buf, buf_len, s, args);
// If truncated, change the final char in the buffer to a \0.
// A negative result indicates a truncated buffer on Windows.
if (result < 0 || result >= buf_len)
{
buf[buf_len - 1] = '\0';
result = buf_len - 1;
}
return result;
}
// Safe, portable snprintf().
int M_snprintf(char *buf, size_t buf_len, const char *s, ...)
{
va_list args;
int result;
va_start(args, s);
result = M_vsnprintf(buf, buf_len, s, args);
va_end(args);
return result;
}

View File

@ -23,6 +23,7 @@
void M_MakeDirectory(const char *dir);
boolean M_FileExists(const char *file);
char *M_TempFile(const char *s);
char *M_FileCaseExists(const char *file);
char *M_DirName(const char *path);
const char *M_BaseName(const char *path);
@ -35,5 +36,6 @@ char *M_StringReplace(const char *haystack, const char *needle,
const char *replacement);
char *M_StringJoin(const char *s, ...);
boolean M_StringEndsWith(const char *s, const char *suffix);
int M_snprintf(char *buf, size_t buf_len, const char *s, ...) PRINTF_ATTR(3, 4);
#endif

31
Source/net_defs.h Normal file
View File

@ -0,0 +1,31 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// 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:
// Definitions for use in networking code.
//
#ifndef NET_DEFS_H
#define NET_DEFS_H
typedef struct _net_packet_s net_packet_t;
struct _net_packet_s
{
byte *data;
size_t len;
size_t alloced;
unsigned int pos;
};
#endif /* #ifndef NET_DEFS_H */

329
Source/net_packet.c Normal file
View File

@ -0,0 +1,329 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// 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:
// Network packet manipulation (net_packet_t)
//
#include <ctype.h>
#include <string.h>
#include "m_misc.h"
#include "m_misc2.h"
#include "net_packet.h"
#include "z_zone.h"
static int total_packet_memory = 0;
net_packet_t *NET_NewPacket(int initial_size)
{
net_packet_t *packet;
packet = (net_packet_t *) Z_Malloc(sizeof(net_packet_t), PU_STATIC, 0);
if (initial_size == 0)
initial_size = 256;
packet->alloced = initial_size;
packet->data = Z_Malloc(initial_size, PU_STATIC, 0);
packet->len = 0;
packet->pos = 0;
total_packet_memory += sizeof(net_packet_t) + initial_size;
//printf("total packet memory: %i bytes\n", total_packet_memory);
//printf("%p: allocated\n", packet);
return packet;
}
// duplicates an existing packet
net_packet_t *NET_PacketDup(net_packet_t *packet)
{
net_packet_t *newpacket;
newpacket = NET_NewPacket(packet->len);
memcpy(newpacket->data, packet->data, packet->len);
newpacket->len = packet->len;
return newpacket;
}
void NET_FreePacket(net_packet_t *packet)
{
//printf("%p: destroyed\n", packet);
total_packet_memory -= sizeof(net_packet_t) + packet->alloced;
Z_Free(packet->data);
Z_Free(packet);
}
// Read a byte from the packet, returning true if read
// successfully
boolean NET_ReadInt8(net_packet_t *packet, unsigned int *data)
{
if (packet->pos + 1 > packet->len)
return false;
*data = packet->data[packet->pos];
packet->pos += 1;
return true;
}
// Read a 16-bit integer from the packet, returning true if read
// successfully
boolean NET_ReadInt16(net_packet_t *packet, unsigned int *data)
{
byte *p;
if (packet->pos + 2 > packet->len)
return false;
p = packet->data + packet->pos;
*data = (p[0] << 8) | p[1];
packet->pos += 2;
return true;
}
// Read a 32-bit integer from the packet, returning true if read
// successfully
boolean NET_ReadInt32(net_packet_t *packet, unsigned int *data)
{
byte *p;
if (packet->pos + 4 > packet->len)
return false;
p = packet->data + packet->pos;
*data = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
packet->pos += 4;
return true;
}
// Signed read functions
boolean NET_ReadSInt8(net_packet_t *packet, signed int *data)
{
if (NET_ReadInt8(packet,(unsigned int *) data))
{
if (*data & (1 << 7))
{
*data &= ~(1 << 7);
*data -= (1 << 7);
}
return true;
}
else
{
return false;
}
}
boolean NET_ReadSInt16(net_packet_t *packet, signed int *data)
{
if (NET_ReadInt16(packet, (unsigned int *) data))
{
if (*data & (1 << 15))
{
*data &= ~(1 << 15);
*data -= (1 << 15);
}
return true;
}
else
{
return false;
}
}
boolean NET_ReadSInt32(net_packet_t *packet, signed int *data)
{
if (NET_ReadInt32(packet, (unsigned int *) data))
{
if (*data & (1U << 31))
{
*data &= ~(1U << 31);
*data -= (1U << 31);
}
return true;
}
else
{
return false;
}
}
// Read a string from the packet. Returns NULL if a terminating
// NUL character was not found before the end of the packet.
char *NET_ReadString(net_packet_t *packet)
{
char *start;
start = (char *) packet->data + packet->pos;
// Search forward for a NUL character
while (packet->pos < packet->len && packet->data[packet->pos] != '\0')
{
++packet->pos;
}
if (packet->pos >= packet->len)
{
// Reached the end of the packet
return NULL;
}
// packet->data[packet->pos] == '\0': We have reached a terminating
// NULL. Skip past this NULL and continue reading immediately
// after it.
++packet->pos;
return start;
}
// Read a string from the packet, but (potentially) modify it to strip
// out any unprintable characters which could be malicious control codes.
// Note that this may modify the original packet contents.
char *NET_ReadSafeString(net_packet_t *packet)
{
char *r, *w, *result;
result = NET_ReadString(packet);
if (result == NULL)
{
return NULL;
}
// w is always <= r, so we never produce a longer string than the original.
w = result;
for (r = result; *r != '\0'; ++r)
{
// TODO: This is a very naive way of producing a safe string; only
// ASCII characters are allowed. Probably this should really support
// UTF-8 characters as well.
if (isprint(*r) || *r == '\n')
{
*w = *r;
++w;
}
}
*w = '\0';
return result;
}
// Dynamically increases the size of a packet
static void NET_IncreasePacket(net_packet_t *packet)
{
byte *newdata;
total_packet_memory -= packet->alloced;
packet->alloced *= 2;
newdata = Z_Malloc(packet->alloced, PU_STATIC, 0);
memcpy(newdata, packet->data, packet->len);
Z_Free(packet->data);
packet->data = newdata;
total_packet_memory += packet->alloced;
}
// Write a single byte to the packet
void NET_WriteInt8(net_packet_t *packet, unsigned int i)
{
if (packet->len + 1 > packet->alloced)
NET_IncreasePacket(packet);
packet->data[packet->len] = i;
packet->len += 1;
}
// Write a 16-bit integer to the packet
void NET_WriteInt16(net_packet_t *packet, unsigned int i)
{
byte *p;
if (packet->len + 2 > packet->alloced)
NET_IncreasePacket(packet);
p = packet->data + packet->len;
p[0] = (i >> 8) & 0xff;
p[1] = i & 0xff;
packet->len += 2;
}
// Write a single byte to the packet
void NET_WriteInt32(net_packet_t *packet, unsigned int i)
{
byte *p;
if (packet->len + 4 > packet->alloced)
NET_IncreasePacket(packet);
p = packet->data + packet->len;
p[0] = (i >> 24) & 0xff;
p[1] = (i >> 16) & 0xff;
p[2] = (i >> 8) & 0xff;
p[3] = i & 0xff;
packet->len += 4;
}
void NET_WriteString(net_packet_t *packet, const char *string)
{
byte *p;
size_t string_size;
string_size = strlen(string) + 1;
// Increase the packet size until large enough to hold the string
while (packet->len + string_size > packet->alloced)
{
NET_IncreasePacket(packet);
}
p = packet->data + packet->len;
M_StringCopy((char *) p, string, string_size);
packet->len += string_size;
}

45
Source/net_packet.h Normal file
View File

@ -0,0 +1,45 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// 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:
// Definitions for use in networking code.
//
#ifndef NET_PACKET_H
#define NET_PACKET_H
#include "net_defs.h"
net_packet_t *NET_NewPacket(int initial_size);
net_packet_t *NET_PacketDup(net_packet_t *packet);
void NET_FreePacket(net_packet_t *packet);
boolean NET_ReadInt8(net_packet_t *packet, unsigned int *data);
boolean NET_ReadInt16(net_packet_t *packet, unsigned int *data);
boolean NET_ReadInt32(net_packet_t *packet, unsigned int *data);
boolean NET_ReadSInt8(net_packet_t *packet, signed int *data);
boolean NET_ReadSInt16(net_packet_t *packet, signed int *data);
boolean NET_ReadSInt32(net_packet_t *packet, signed int *data);
char *NET_ReadString(net_packet_t *packet);
char *NET_ReadSafeString(net_packet_t *packet);
void NET_WriteInt8(net_packet_t *packet, unsigned int i);
void NET_WriteInt16(net_packet_t *packet, unsigned int i);
void NET_WriteInt32(net_packet_t *packet, unsigned int i);
void NET_WriteString(net_packet_t *packet, const char *string);
#endif /* #ifndef NET_PACKET_H */

12
midiproc/CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
if(WIN32)
include(WoofSettings)
add_executable(woof-midiproc WIN32 buffer.c buffer.h main.c proto.h)
target_woof_settings(woof-midiproc)
target_include_directories(woof-midiproc
PRIVATE "../Source/" "${CMAKE_CURRENT_BINARY_DIR}/../")
target_link_libraries(woof-midiproc SDL2::SDL2main SDL2::mixer)
install(TARGETS woof-midiproc RUNTIME DESTINATION .)
endif()

255
midiproc/buffer.c Normal file
View File

@ -0,0 +1,255 @@
//
// 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:
// A simple buffer and reader implementation.
//
#ifdef _WIN32
#include "buffer.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
//
// Create a new buffer.
//
buffer_t *NewBuffer()
{
buffer_t *buf = malloc(sizeof(buffer_t));
buf->buffer_end = buf->buffer + BUFFER_SIZE;
Buffer_Clear(buf);
return buf;
}
//
// Free a buffer.
//
void DeleteBuffer(buffer_t *buf)
{
free(buf);
}
//
// Return the data in the buffer.
//
int Buffer_Data(buffer_t *buf, byte **data)
{
*data = buf->data;
return buf->data_len;
}
//
// Push data onto the end of the buffer.
//
boolean Buffer_Push(buffer_t *buf, const void *data, int len)
{
ptrdiff_t space_begin, space_end;
if (len <= 0)
{
// Do nothing, successfully.
return true;
}
space_begin = buf->data - buf->buffer;
space_end = buf->buffer_end - buf->data_end;
if (len > space_end)
{
if (len > space_begin + space_end)
{
// Don't overflow the buffer.
return false;
}
// Move our data to the front of the buffer.
memmove(buf->buffer, buf->data, buf->data_len);
buf->data = buf->buffer;
buf->data_end = buf->buffer + buf->data_len;
}
// Append to the buffer.
memcpy(buf->data_end, data, len);
buf->data_len += len;
buf->data_end = buf->data + buf->data_len;
return true;
}
//
// Shift len bytes off of the front of the buffer.
//
void Buffer_Shift(buffer_t *buf, int len)
{
ptrdiff_t max_shift;
if (len <= 0)
{
// Do nothing.
return;
}
max_shift = buf->data_end - buf->data;
if (len >= max_shift)
{
// If the operation would clear the buffer, just zero everything.
Buffer_Clear(buf);
}
else
{
buf->data += len;
buf->data_len -= len;
}
}
//
// Clear the buffer.
//
void Buffer_Clear(buffer_t *buf)
{
buf->data = buf->buffer;
buf->data_end = buf->buffer;
buf->data_len = 0;
}
//
// Create a new buffer reader.
//
// WARNING: This reader will invalidate if the underlying buffer changes.
// Use it, then delete it before you touch the underlying buffer again.
//
buffer_reader_t *NewReader(buffer_t* buffer)
{
buffer_reader_t *reader = malloc(sizeof(buffer_reader_t));
reader->buffer = buffer;
reader->pos = buffer->data;
return reader;
}
//
// Delete a buffer reader.
//
void DeleteReader(buffer_reader_t *reader)
{
free(reader);
}
//
// Count the number of bytes read thus far.
//
int Reader_BytesRead(buffer_reader_t *reader)
{
return reader->pos - reader->buffer->data;
}
//
// Read an unsigned byte from a buffer.
//
boolean Reader_ReadInt8(buffer_reader_t *reader, uint8_t *out)
{
byte *data, *data_end;
int len = Buffer_Data(reader->buffer, &data);
data_end = data + len;
if (data_end - reader->pos < 1)
{
return false;
}
*out = (uint8_t)*reader->pos;
reader->pos += 1;
return true;
}
//
// Read an unsigned short from a buffer.
//
boolean Reader_ReadInt16(buffer_reader_t *reader, uint16_t *out)
{
byte *data, *data_end, *dp;
int len = Buffer_Data(reader->buffer, &data);
data_end = data + len;
dp = reader->pos;
if (data_end - reader->pos < 2)
{
return false;
}
*out = (uint16_t)((dp[0] << 8) | dp[1]);
reader->pos += 2;
return true;
}
//
// Read an unsigned int from a buffer.
//
boolean Reader_ReadInt32(buffer_reader_t *reader, uint32_t *out)
{
byte *data, *data_end, *dp;
int len = Buffer_Data(reader->buffer, &data);
data_end = data + len;
dp = reader->pos;
if (data_end - reader->pos < 4)
{
return false;
}
*out = (uint32_t)((dp[0] << 24) | (dp[1] << 16) | (dp[2] << 8) | dp[3]);
reader->pos += 4;
return true;
}
//
// Read a string from a buffer.
//
char *Reader_ReadString(buffer_reader_t *reader)
{
byte *data, *data_start, *data_end, *dp;
int len = Buffer_Data(reader->buffer, &data);
data_start = reader->pos;
data_end = data + len;
dp = reader->pos;
while (dp < data_end && *dp != '\0')
{
dp++;
}
if (dp >= data_end)
{
// Didn't see a null terminator, not a complete string.
return NULL;
}
reader->pos = dp + 1;
return (char*)data_start;
}
#endif // #ifdef _WIN32

54
midiproc/buffer.h Normal file
View File

@ -0,0 +1,54 @@
//
// 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:
// A simple buffer and reader implementation.
//
#ifndef __BUFFER__
#define __BUFFER__
#include "../Source/doomtype.h"
#define BUFFER_SIZE 1024
typedef struct {
byte buffer[BUFFER_SIZE]; // Buffer.
byte *buffer_end; // End of Buffer.
byte *data; // Start of actual data.
byte *data_end; // End of actual data.
int data_len; // Length of actual data.
} buffer_t;
typedef struct {
buffer_t *buffer;
byte *pos;
} buffer_reader_t;
buffer_t *NewBuffer();
void DeleteBuffer(buffer_t* buf);
int Buffer_Data(buffer_t *buf, byte **data);
boolean Buffer_Push(buffer_t *buf, const void *data, int len);
void Buffer_Shift(buffer_t *buf, int len);
void Buffer_Clear(buffer_t *buf);
buffer_reader_t *NewReader(buffer_t* buffer);
void DeleteReader(buffer_reader_t *reader);
int Reader_BytesRead(buffer_reader_t *reader);
boolean Reader_ReadInt8(buffer_reader_t *reader, uint8_t *out);
boolean Reader_ReadInt16(buffer_reader_t *reader, uint16_t *out);
boolean Reader_ReadInt32(buffer_reader_t *reader, uint32_t *out);
char *Reader_ReadString(buffer_reader_t *reader);
#endif

463
midiproc/main.c Normal file
View File

@ -0,0 +1,463 @@
//
// 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
// 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:
//
// Win32/SDL_mixer MIDI Server
//
// Uses pipes to communicate with Doom. This allows this separate process to
// have its own independent volume control even under Windows Vista and up's
// broken, stupid, completely useless mixer model that can't assign separate
// volumes to different devices for the same process.
//
// Seriously, how did they screw up something so fundamental?
//
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "SDL.h"
#include "SDL_mixer.h"
#include "buffer.h"
#include "proto.h"
#include "config.h"
#include "doomtype.h"
static HANDLE midi_process_in; // Standard In.
static HANDLE midi_process_out; // Standard Out.
// Sound sample rate to use for digital output (Hz)
static int snd_samplerate = 0;
// Currently playing music track.
static Mix_Music *music = NULL;
//=============================================================================
//
// Private functions
//
//
// Write an unsigned integer into a simple CHAR buffer.
//
static boolean WriteInt16(CHAR *out, size_t osize, unsigned int in)
{
if (osize < 2)
{
return false;
}
out[0] = (in >> 8) & 0xff;
out[1] = in & 0xff;
return true;
}
//
// Cleanly close our in-use pipes.
//
static void FreePipes(void)
{
if (midi_process_in != NULL)
{
CloseHandle(midi_process_in);
midi_process_in = NULL;
}
if (midi_process_out != NULL)
{
CloseHandle(midi_process_out);
midi_process_out = NULL;
}
}
//
// Unregisters the currently playing song. This is never called from the
// protocol, we simply do this before playing a new song.
//
static void UnregisterSong()
{
if (music == NULL)
{
return;
}
Mix_FreeMusic(music);
music = NULL;
}
//
// Cleanly shut down SDL.
//
static void ShutdownSDL(void)
{
UnregisterSong();
Mix_CloseAudio();
SDL_Quit();
}
//=============================================================================
//
// SDL_mixer Interface
//
static boolean RegisterSong(const char *filename)
{
music = Mix_LoadMUS(filename);
// Remove the temporary MIDI file
remove(filename);
if (music == NULL)
{
return false;
}
return true;
}
static void SetVolume(int vol)
{
Mix_VolumeMusic(vol);
}
static void PlaySong(int loops)
{
Mix_PlayMusic(music, loops);
// [AM] BUG: In my testing, setting the volume of a MIDI track while there
// is no song playing appears to be a no-op. This can happen when
// you're mixing midiproc with vanilla SDL_Mixer, such as when you
// are alternating between a digital music pack (in the parent
// process) and MIDI (in this process).
//
// To work around this bug, we set the volume to itself after the MIDI
// has started playing.
Mix_VolumeMusic(Mix_VolumeMusic(-1));
}
static void StopSong()
{
Mix_HaltMusic();
}
//=============================================================================
//
// Pipe Server Interface
//
static boolean MidiPipe_RegisterSong(buffer_reader_t *reader)
{
char *filename = Reader_ReadString(reader);
if (filename == NULL)
{
return false;
}
return RegisterSong(filename);
}
static boolean MidiPipe_UnregisterSong(buffer_reader_t *reader)
{
UnregisterSong();
return true;
}
boolean MidiPipe_SetVolume(buffer_reader_t *reader)
{
int vol;
boolean ok = Reader_ReadInt32(reader, (uint32_t*)&vol);
if (!ok)
{
return false;
}
SetVolume(vol);
return true;
}
boolean MidiPipe_PlaySong(buffer_reader_t *reader)
{
int loops;
boolean ok = Reader_ReadInt32(reader, (uint32_t*)&loops);
if (!ok)
{
return false;
}
PlaySong(loops);
return true;
}
boolean MidiPipe_StopSong()
{
StopSong();
return true;
}
boolean MidiPipe_Shutdown()
{
exit(EXIT_SUCCESS);
}
//=============================================================================
//
// Server Implementation
//
//
// Parses a command and directs to the proper read function.
//
boolean ParseCommand(buffer_reader_t *reader, uint16_t command)
{
switch (command)
{
case MIDIPIPE_PACKET_TYPE_REGISTER_SONG:
return MidiPipe_RegisterSong(reader);
case MIDIPIPE_PACKET_TYPE_UNREGISTER_SONG:
return MidiPipe_UnregisterSong(reader);
case MIDIPIPE_PACKET_TYPE_SET_VOLUME:
return MidiPipe_SetVolume(reader);
case MIDIPIPE_PACKET_TYPE_PLAY_SONG:
return MidiPipe_PlaySong(reader);
case MIDIPIPE_PACKET_TYPE_STOP_SONG:
return MidiPipe_StopSong();
case MIDIPIPE_PACKET_TYPE_SHUTDOWN:
return MidiPipe_Shutdown();
default:
return false;
}
}
//
// Server packet parser
//
boolean ParseMessage(buffer_t *buf)
{
CHAR buffer[2];
DWORD bytes_written;
int bytes_read;
uint16_t command;
buffer_reader_t *reader = NewReader(buf);
// Attempt to read a command out of the buffer.
if (!Reader_ReadInt16(reader, &command))
{
goto fail;
}
// Attempt to parse a complete message.
if (!ParseCommand(reader, command))
{
goto fail;
}
// We parsed a complete message! We can now safely shift
// the prior message off the front of the buffer.
bytes_read = Reader_BytesRead(reader);
DeleteReader(reader);
Buffer_Shift(buf, bytes_read);
// Send acknowledgement back that the command has completed.
if (!WriteInt16(buffer, sizeof(buffer), MIDIPIPE_PACKET_TYPE_ACK))
{
goto fail;
}
WriteFile(midi_process_out, buffer, sizeof(buffer),
&bytes_written, NULL);
return true;
fail:
// We did not read a complete packet. Delete our reader and try again
// with more data.
DeleteReader(reader);
return false;
}
//
// The main pipe "listening" loop
//
boolean ListenForever()
{
BOOL wok = FALSE;
CHAR pipe_buffer[8192];
DWORD pipe_buffer_read = 0;
boolean ok = false;
buffer_t *buffer = NewBuffer();
for (;;)
{
// Wait until we see some data on the pipe.
wok = PeekNamedPipe(midi_process_in, NULL, 0, NULL,
&pipe_buffer_read, NULL);
if (!wok)
{
break;
}
else if (pipe_buffer_read == 0)
{
SDL_Delay(1);
continue;
}
// Read data off the pipe and add it to the buffer.
wok = ReadFile(midi_process_in, pipe_buffer, sizeof(pipe_buffer),
&pipe_buffer_read, NULL);
if (!wok)
{
break;
}
ok = Buffer_Push(buffer, pipe_buffer, pipe_buffer_read);
if (!ok)
{
break;
}
do
{
// Read messages off the buffer until we can't anymore.
ok = ParseMessage(buffer);
} while (ok);
}
return false;
}
//=============================================================================
//
// Main Program
//
//
// InitSDL
//
// Start up SDL and SDL_mixer.
//
boolean InitSDL()
{
if (SDL_Init(SDL_INIT_AUDIO) == -1)
{
return false;
}
if (Mix_OpenAudioDevice(snd_samplerate, MIX_DEFAULT_FORMAT, 2, 2048, NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) < 0)
{
return false;
}
atexit(ShutdownSDL);
return true;
}
//
// InitPipes
//
// Ensure that we can communicate.
//
void InitPipes(HANDLE in, HANDLE out)
{
midi_process_in = in;
midi_process_out = out;
atexit(FreePipes);
}
//
// main
//
// Application entry point.
//
int main(int argc, char *argv[])
{
HANDLE in, out;
// Make sure we're not launching this process by itself.
if (argc < 5)
{
MessageBox(NULL, TEXT("This program is tasked with playing Native ")
TEXT("MIDI music, and is intended to be launched by ")
TEXT(PROJECT_NAME) TEXT("."),
TEXT(PROJECT_STRING), MB_OK | MB_ICONASTERISK);
return EXIT_FAILURE;
}
// Make sure our Choccolate Doom and midiproc version are lined up.
if (strcmp(PROJECT_STRING, argv[1]) != 0)
{
char message[1024];
_snprintf(message, sizeof(message),
"It appears that the version of %s and woof-midiproc are out "
"of sync. Please reinstall %s.\r\n\r\n"
"Server Version: %s\r\nClient Version: %s",
PROJECT_NAME, PROJECT_NAME,
PROJECT_STRING, argv[1]);
message[sizeof(message) - 1] = '\0';
MessageBox(NULL, TEXT(message),
TEXT(PROJECT_STRING), MB_OK | MB_ICONASTERISK);
return EXIT_FAILURE;
}
// Parse out the sample rate - if we can't, default to 44100.
snd_samplerate = strtol(argv[2], NULL, 10);
if (snd_samplerate == LONG_MAX || snd_samplerate == LONG_MIN ||
snd_samplerate == 0)
{
snd_samplerate = 44100;
}
// Parse out our handle ids.
in = (HANDLE) strtol(argv[3], NULL, 10);
if (in == 0)
{
return EXIT_FAILURE;
}
out = (HANDLE) strtol(argv[4], NULL, 10);
if (out == 0)
{
return EXIT_FAILURE;
}
InitPipes(in, out);
if (!InitSDL())
{
return EXIT_FAILURE;
}
if (!ListenForever())
{
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#endif // #ifdef _WIN32

33
midiproc/proto.h Normal file
View File

@ -0,0 +1,33 @@
//
// 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:
// Headers for all types of midipipe messages.
//
#ifndef __PROTO__
#define __PROTO__
typedef enum {
MIDIPIPE_PACKET_TYPE_REGISTER_SONG,
MIDIPIPE_PACKET_TYPE__DEPRECATED_1,
MIDIPIPE_PACKET_TYPE_SET_VOLUME,
MIDIPIPE_PACKET_TYPE_PLAY_SONG,
MIDIPIPE_PACKET_TYPE_STOP_SONG,
MIDIPIPE_PACKET_TYPE_SHUTDOWN,
MIDIPIPE_PACKET_TYPE_UNREGISTER_SONG,
MIDIPIPE_PACKET_TYPE_ACK,
} net_midipipe_packet_type_t;
#endif