mirror of
https://github.com/fabiangreffrath/woof.git
synced 2025-09-25 22:05:20 -04:00
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:
parent
cb98e17d72
commit
d194546154
@ -267,3 +267,4 @@ add_subdirectory(docs)
|
||||
add_subdirectory(examples)
|
||||
add_subdirectory(Source)
|
||||
add_subdirectory(toolsrc)
|
||||
add_subdirectory(midiproc)
|
||||
|
@ -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
513
Source/i_midipipe.c
Normal 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
49
Source/i_midipipe.h
Normal 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
|
||||
|
162
Source/i_sound.c
162
Source/i_sound.c
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
31
Source/net_defs.h
Normal 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
329
Source/net_packet.c
Normal 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
45
Source/net_packet.h
Normal 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
12
midiproc/CMakeLists.txt
Normal 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
255
midiproc/buffer.c
Normal 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
54
midiproc/buffer.h
Normal 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
463
midiproc/main.c
Normal 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
33
midiproc/proto.h
Normal 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user