First working prototype

Since I had communication working, I then completely reworked the
original interface to cater to Chocolate Doom.  A couple of hours
later, I finally have a working prototype - launching Chocolate Doom
will play music, and you can control the music volume independently of
the sound effects.
This commit is contained in:
Alex Mayfield 2017-02-18 18:59:25 -05:00
parent 4328e3dc10
commit 73ef8a7e9d
5 changed files with 220 additions and 436 deletions

View file

@ -43,93 +43,48 @@ static buffer_t *midi_buffer; // Data from client.
// Currently playing music track
static Mix_Music *music = NULL;
static char *filename = NULL;
static void UnregisterSong();
//=============================================================================
//
// SDL_mixer Interface
//
//
// RegisterSong
//
static void RegisterSong(char* filename)
static boolean RegisterSong(const char *filename)
{
if (music)
{
UnregisterSong();
}
fprintf(stderr, "%s %s\n", __FUNCTION__, filename);
music = Mix_LoadMUS(filename);
}
fprintf(stderr, "<-- %p\n", music);
//
// StartSong
//
static void StartSong(boolean loop)
{
if (music)
if (music == NULL)
{
Mix_PlayMusic(music, loop ? -1 : 0);
fprintf(stderr, "Error loading midi: %s\n", Mix_GetError());
return false;
}
return true;
}
//
// SetVolume
//
static void SetVolume(int volume)
static void SetVolume(int vol)
{
Mix_VolumeMusic((volume * 128) / 15);
fprintf(stderr, "%s %d\n", __FUNCTION__, vol);
Mix_VolumeMusic(vol);
}
static int paused_midi_volume;
//
// PauseSong
//
static void PauseSong()
static void PlaySong(int loops)
{
paused_midi_volume = Mix_VolumeMusic(-1);
Mix_VolumeMusic(0);
fprintf(stderr, "%s %d\n", __FUNCTION__, loops);
Mix_PlayMusic(music, loops);
}
//
// ResumeSong
//
static void ResumeSong()
{
Mix_VolumeMusic(paused_midi_volume);
}
//
// StopSong
//
static void StopSong()
{
if (music)
{
Mix_HaltMusic();
}
}
fprintf(stderr, "%s\n", __FUNCTION__);
//
// UnregisterSong
//
static void UnregisterSong()
{
if (!music)
{
return;
}
StopSong();
Mix_FreeMusic(music);
free(filename);
filename = NULL;
music = NULL;
Mix_HaltMusic();
}
//
@ -144,150 +99,62 @@ static void ShutdownSDL()
//=============================================================================
//
// RPC Server Interface
// Pipe Server Interface
//
//
// MidiPipe_PrepareNewSong
//
// Prepare the engine to receive new song data from the RPC client.
//
boolean MidiPipe_PrepareNewSong()
static boolean MidiPipe_RegisterSong(buffer_reader_t *reader)
{
// Stop anything currently playing and free it.
UnregisterSong();
fprintf(stderr, "%s\n", __FUNCTION__);
return true;
}
//
// MidiPipe_AddChunk
//
// Set the filename of the song.
//
boolean MidiPipe_SetFilename(buffer_reader_t *reader)
{
free(filename);
filename = NULL;
char* file = Reader_ReadString(reader);
if (file == NULL)
{
return false;
}
int size = Reader_BytesRead(reader) - 2;
if (size <= 0)
{
return false;
}
filename = malloc(size);
char *filename = Reader_ReadString(reader);
if (filename == NULL)
{
return false;
}
memcpy(filename, file, size);
RegisterSong(filename);
unsigned int i = NET_MIDIPIPE_PACKET_TYPE_REGISTER_SONG_ACK;
CHAR buffer[2];
buffer[0] = (i >> 8) & 0xff;
buffer[1] = i & 0xff;
BOOL ok = WriteFile(midi_process_out, buffer, sizeof(buffer),
NULL, NULL);
fprintf(stderr, "%s\n", __FUNCTION__);
return true;
}
//
// MidiPipe_PlaySong
//
// Start playing the song.
//
boolean MidiPipe_PlaySong(buffer_reader_t *reader)
boolean MidiPipe_SetVolume(buffer_reader_t *reader)
{
uint8_t looping;
if (!Reader_ReadInt8(reader, &looping))
int vol;
boolean ok = Reader_ReadInt32(reader, &vol);
if (!ok)
{
return false;
}
RegisterSong(filename);
StartSong((boolean)looping);
SetVolume(vol);
fprintf(stderr, "%s\n", __FUNCTION__);
return true;
}
//
// MidiPipe_StopSong
//
// Stop the song.
//
boolean MidiPipe_StopSong()
boolean MidiPipe_PlaySong(buffer_reader_t *reader)
{
int loops;
boolean ok = Reader_ReadInt32(reader, &loops);
if (!ok)
{
return false;
}
PlaySong(loops);
return true;
}
boolean MidiPipe_StopSong(buffer_reader_t *reader)
{
StopSong();
fprintf(stderr, "%s\n", __FUNCTION__);
return true;
}
//
// MidiPipe_ChangeVolume
//
// Set playback volume level.
//
boolean MidiPipe_ChangeVolume(buffer_reader_t *reader)
{
int volume;
if (!Reader_ReadInt32(reader, &volume))
{
return false;
}
SetVolume(volume);
fprintf(stderr, "%s\n", __FUNCTION__);
return true;
}
//
// MidiPipe_PauseSong
//
// Pause the song.
//
boolean MidiPipe_PauseSong()
{
PauseSong();
fprintf(stderr, "%s\n", __FUNCTION__);
return true;
}
//
// MidiPipe_ResumeSong
//
// Resume after pausing.
//
boolean MidiPipe_ResumeSong()
{
ResumeSong();
fprintf(stderr, "%s\n", __FUNCTION__);
return true;
}
//
// MidiPipe_StopServer
//
// Stops the RPC server so the program can shutdown.
//
boolean MidiPipe_StopServer()
{
// Local shutdown tasks
ShutdownSDL();
free(filename);
filename = NULL;
fprintf(stderr, "%s\n", __FUNCTION__);
return true;
}
@ -296,26 +163,21 @@ boolean MidiPipe_StopServer()
// Server Implementation
//
//
// Parses a command and directs to the proper read function.
//
boolean ParseCommand(buffer_reader_t *reader, uint16_t command)
{
switch (command)
{
case NET_MIDIPIPE_PACKET_TYPE_PREPARE_NEW_SONG:
return MidiPipe_PrepareNewSong();
case NET_MIDIPIPE_PACKET_TYPE_SET_FILENAME:
return MidiPipe_SetFilename(reader);
case NET_MIDIPIPE_PACKET_TYPE_REGISTER_SONG:
return MidiPipe_RegisterSong(reader);
case NET_MIDIPIPE_PACKET_TYPE_SET_VOLUME:
return MidiPipe_SetVolume(reader);
case NET_MIDIPIPE_PACKET_TYPE_PLAY_SONG:
return MidiPipe_PlaySong(reader);
case NET_MIDIPIPE_PACKET_TYPE_STOP_SONG:
return MidiPipe_StopSong();
case NET_MIDIPIPE_PACKET_TYPE_CHANGE_VOLUME:
return MidiPipe_ChangeVolume(reader);
case NET_MIDIPIPE_PACKET_TYPE_PAUSE_SONG:
return MidiPipe_PauseSong();
case NET_MIDIPIPE_PACKET_TYPE_RESUME_SONG:
return MidiPipe_ResumeSong();
case NET_MIDIPIPE_PACKET_TYPE_STOP_SERVER:
return MidiPipe_StopServer();
return MidiPipe_StopSong(reader);
default:
return false;
}
@ -365,7 +227,6 @@ boolean ListenForever()
boolean ok = false;
buffer_t *buffer = NewBuffer();
fprintf(stderr, "%s\n", "In theory we should be reading...");
for (;;)
{
// Wait until we see some data on the pipe.
@ -382,7 +243,6 @@ boolean ListenForever()
}
// Read data off the pipe and add it to the buffer.
fprintf(stderr, "%s\n", "ReadFile");
wok = ReadFile(midi_process_in, pipe_buffer, sizeof(pipe_buffer),
&pipe_buffer_read, NULL);
if (!wok)
@ -390,14 +250,12 @@ boolean ListenForever()
return false;
}
fprintf(stderr, "%s\n", "Buffer_Push");
ok = Buffer_Push(buffer, pipe_buffer, pipe_buffer_read);
if (!ok)
{
return false;
}
fprintf(stderr, "%s\n", "ParseMessage");
do
{
// Read messages off the buffer until we can't anymore.

View file

@ -20,11 +20,11 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <SDL_net.h>
#include "i_midipipe.h"
#include "config.h"
#include "i_timer.h"
#include "m_misc.h"
#include "net_packet.h"
@ -39,6 +39,8 @@
// Data
//
#define MIDIPIPE_MAX_WAIT 500 // 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.
@ -49,220 +51,203 @@ static boolean client_init = false; // if true, client was bound
//=============================================================================
//
// RPC Wrappers
// Private functions
//
//
// CHECK_RPC_STATUS
// WritePipe
//
// If either server or client initialization failed, we don't try to make any
// RPC calls.
// Writes packet data to the subprocess' standard in.
//
#define CHECK_RPC_STATUS() \
if(!server_init) \
return false
#define MIDIRPC_MAXTRIES 50 // This number * 10 is the amount of time you can try to wait for.
static boolean I_MidiPipeWrite(void *data, int len)
static boolean WritePipe(net_packet_t *packet)
{
DWORD written;
if (WriteFile(midi_process_in_writer, data, len, &written, NULL))
{
return true;
}
else
BOOL ok = WriteFile(midi_process_in_writer, packet->data, packet->len,
NULL, NULL);
if (!ok)
{
return false;
}
}
static boolean I_MidiPipeWaitForServer()
{
int tries = 0;
while(false) // TODO: Is there some way to tell if the server is listening?
{
I_Sleep(10);
if (++tries >= MIDIRPC_MAXTRIES)
{
return false;
}
}
return true;
}
//
// I_MidiPipeRegisterSong
// ExpectPipe
//
// Prepare the RPC MIDI engine to receive new song data, and transmit the song
// filename to the server process.
// 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.
//
boolean I_MidiPipeRegisterSong(const char *filename)
static boolean ExpectPipe(net_packet_t *packet)
{
BOOL wok;
net_packet_t *packet;
BOOL ok;
CHAR pipe_buffer[8192];
DWORD pipe_buffer_read = 0;
CHECK_RPC_STATUS();
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;
}
int 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)
{
goto fail;
}
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)
{
goto fail;
}
// Compare our data buffer to the packet.
if (memcmp(packet->data, pipe_buffer, packet->len) != 0)
{
goto fail;
}
return true;
// Continue looping as long as we don't exceed our maximum wait time.
} while (start + MIDIPIPE_MAX_WAIT > I_GetTimeMS());
fail:
// TODO: Deal with the wedged process.
return false;
}
//=============================================================================
//
// 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.
//
Mix_Music *I_MidiPipe_RegisterSong(const char *filename)
{
boolean ok;
net_packet_t *packet;
packet = NET_NewPacket(64);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_PREPARE_NEW_SONG);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_SET_FILENAME);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_REGISTER_SONG);
NET_WriteString(packet, filename);
wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
NULL, NULL);
ok = WritePipe(packet);
NET_FreePacket(packet);
if (!wok)
if (!ok)
{
DEBUGOUT("I_MidiPipeRegisterSong failed");
DEBUGOUT("I_MidiPipe_RegisterSong failed");
return false;
}
DEBUGOUT("I_MidiPipeRegisterSong succeeded");
packet = NET_NewPacket(2);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_REGISTER_SONG_ACK);
ok = ExpectPipe(packet);
NET_FreePacket(packet);
if (!ok)
{
DEBUGOUT("I_MidiPipe_RegisterSong ack failed");
return false;
}
DEBUGOUT("I_MidiPipe_RegisterSong succeeded");
return true;
}
//
// I_MidiPipePlaySong
// I_MidiPipe_SetVolume
//
// Tell the RPC server to start playing a song.
// Tells the MIDI subprocess to set a specific volume for the song.
//
boolean I_MidiPipePlaySong(boolean looping)
void I_MidiPipe_SetVolume(int vol)
{
BOOL wok;
boolean ok;
net_packet_t *packet;
CHECK_RPC_STATUS();
packet = NET_NewPacket(6);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_SET_VOLUME);
NET_WriteInt32(packet, vol);
ok = WritePipe(packet);
NET_FreePacket(packet);
packet = NET_NewPacket(3);
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, NET_MIDIPIPE_PACKET_TYPE_PLAY_SONG);
NET_WriteInt8(packet, looping);
wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
NULL, NULL);
NET_WriteInt32(packet, loops);
ok = WritePipe(packet);
NET_FreePacket(packet);
if (!wok)
if (!ok)
{
DEBUGOUT("I_MidiPipePlaySong failed");
return false;
DEBUGOUT("I_MidiPipe_PlaySong failed");
return;
}
DEBUGOUT("I_MidiPipePlaySong succeeded");
return true;
DEBUGOUT("I_MidiPipe_PlaySong succeeded");
}
//
// I_MidiPipeStopSong
//
// Tell the RPC server to stop any currently playing song.
// I_MidiPipe_StopSong
//
boolean I_MidiPipeStopSong()
// Tells the MIDI subprocess to stop playing the currently loaded song.
//
void I_MidiPipe_StopSong()
{
BOOL wok;
boolean ok;
net_packet_t *packet;
CHECK_RPC_STATUS();
packet = NET_NewPacket(2);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_STOP_SONG);
wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
NULL, NULL);
ok = WritePipe(packet);
NET_FreePacket(packet);
if (!wok)
if (!ok)
{
DEBUGOUT("I_MidiPipeStopSong failed");
return false;
DEBUGOUT("I_MidiPipe_StopSong failed");
return;
}
DEBUGOUT("I_MidiPipeStopSong succeeded");
return true;
}
//
// I_MidiPipeSetVolume
//
// Change the volume level of music played by the RPC midi server.
//
boolean I_MidiPipeSetVolume(int volume)
{
BOOL wok;
net_packet_t *packet;
CHECK_RPC_STATUS();
packet = NET_NewPacket(6);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_CHANGE_VOLUME);
NET_WriteInt32(packet, volume);
wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
NULL, NULL);
NET_FreePacket(packet);
if (!wok)
{
DEBUGOUT("I_MidiPipeSetVolume failed");
return false;
}
DEBUGOUT("I_MidiPipeSetVolume succeeded");
return true;
}
//
// I_MidiPipePauseSong
//
// Pause the music being played by the server. In actuality, due to SDL_mixer
// limitations, this just temporarily sets the volume to zero.
//
boolean I_MidiPipePauseSong()
{
BOOL wok;
net_packet_t *packet;
CHECK_RPC_STATUS();
packet = NET_NewPacket(2);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_PAUSE_SONG);
wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
NULL, NULL);
NET_FreePacket(packet);
if (!wok)
{
DEBUGOUT("I_MidiPipePauseSong failed");
return false;
}
DEBUGOUT("I_MidiPipePauseSong succeeded");
return true;
}
//
// I_MidiPipeResumeSong
//
// Resume a song after having paused it.
//
boolean I_MidiPipeResumeSong()
{
BOOL wok;
net_packet_t *packet;
CHECK_RPC_STATUS();
packet = NET_NewPacket(2);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_RESUME_SONG);
wok = WriteFile(midi_process_in_writer, packet->data, packet->len,
NULL, NULL);
NET_FreePacket(packet);
if (!wok)
{
DEBUGOUT("I_MidiPipeResumeSong failed");
return false;
}
DEBUGOUT("I_MidiPipeResumeSong succeeded");
return true;
DEBUGOUT("I_MidiPipe_StopSong succeeded");
}
//=============================================================================
@ -275,7 +260,7 @@ boolean I_MidiPipeResumeSong()
//
// Start up the MIDI server.
//
boolean I_MidiPipeInitServer()
boolean I_MidiPipe_InitServer()
{
struct stat sbuf;
char filename[MAX_PATH+1];
@ -361,60 +346,5 @@ boolean I_MidiPipeInitServer()
return ok;
}
//
// I_MidiPipeInitClient
//
// Ensure that we can actually communicate with the subprocess.
//
boolean I_MidiPipeInitClient()
{
client_init = true;
return true;
}
//
// I_MidiPipeClientShutDown
//
// Shutdown the RPC Client
//
/* void I_MidiPipeClientShutDown()
{
// stop the server
if(server_init)
{
net_packet_t *packet;
packet = NET_NewPacket(2);
NET_WriteInt16(packet, NET_MIDIPIPE_PACKET_TYPE_STOP_SERVER);
int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
NET_FreePacket(packet);
if (len < packet->len)
{
DEBUGOUT("Problem encountered when stopping RPC server");
}
server_init = false;
}
if (midi_socket)
{
SDLNet_TCP_Close(midi_socket);
midi_socket = NULL;
}
client_init = false;
} */
//
// I_MidiPipeReady
//
// Returns true if both server and client initialized successfully.
//
boolean I_MidiPipeReady()
{
CHECK_RPC_STATUS();
return true;
}
#endif

View file

@ -21,19 +21,16 @@
#if _WIN32
#include "SDL_mixer.h"
#include "doomtype.h"
boolean I_MidiPipeInitServer();
boolean I_MidiPipeInitClient();
void I_MidiPipeClientShutDown();
boolean I_MidiPipeReady();
Mix_Music *I_MidiPipe_RegisterSong(const char *filename);
void I_MidiPipe_SetVolume(int vol);
void I_MidiPipe_PlaySong(int loops);
void I_MidiPipe_StopSong();
boolean I_MidiPipeRegisterSong(const char *filename);
boolean I_MidiPipePlaySong(boolean looping);
boolean I_MidiPipeStopSong();
boolean I_MidiPipeSetVolume(int volume);
boolean I_MidiPipePauseSong();
boolean I_MidiPipeResumeSong();
boolean I_MidiPipe_InitServer();
#endif

View file

@ -975,7 +975,7 @@ static boolean I_SDL_InitMusic(void)
}
#if WIN32
I_MidiPipeInitServer();
I_MidiPipe_InitServer();
#endif
return music_initialized;
@ -1000,7 +1000,7 @@ static void UpdateMusicVolume(void)
}
#if WIN32
I_MidiPipeSetVolume(vol);
I_MidiPipe_SetVolume(vol);
#else
Mix_VolumeMusic(vol);
#endif
@ -1055,7 +1055,7 @@ static void I_SDL_PlaySong(void *handle, boolean looping)
}
#if _WIN32
I_MidiPipePlaySong(loops);
I_MidiPipe_PlaySong(loops);
#else
Mix_PlayMusic(current_track_music, loops);
#endif
@ -1093,7 +1093,7 @@ static void I_SDL_StopSong(void)
}
#if _WIN32
I_MidiPipeStopSong();
I_MidiPipe_StopSong();
#else
Mix_HaltMusic();
#endif
@ -1116,7 +1116,9 @@ static void I_SDL_UnRegisterSong(void *handle)
return;
}
#ifndef _WIN32
Mix_FreeMusic(music);
#endif
}
// Determine whether memory block is a .mid file
@ -1209,7 +1211,7 @@ static void *I_SDL_RegisterSong(void *data, int len)
// we have to generate a temporary file.
#ifdef _WIN32
music = (Mix_Music*)I_MidiPipeRegisterSong(filename);
music = I_MidiPipe_RegisterSong(filename);
#else
music = Mix_LoadMUS(filename);
#endif

View file

@ -143,14 +143,11 @@ typedef enum
} net_master_packet_type_t;
typedef enum {
NET_MIDIPIPE_PACKET_TYPE_PREPARE_NEW_SONG,
NET_MIDIPIPE_PACKET_TYPE_SET_FILENAME,
NET_MIDIPIPE_PACKET_TYPE_REGISTER_SONG,
NET_MIDIPIPE_PACKET_TYPE_REGISTER_SONG_ACK,
NET_MIDIPIPE_PACKET_TYPE_SET_VOLUME,
NET_MIDIPIPE_PACKET_TYPE_PLAY_SONG,
NET_MIDIPIPE_PACKET_TYPE_STOP_SONG,
NET_MIDIPIPE_PACKET_TYPE_CHANGE_VOLUME,
NET_MIDIPIPE_PACKET_TYPE_PAUSE_SONG,
NET_MIDIPIPE_PACKET_TYPE_RESUME_SONG,
NET_MIDIPIPE_PACKET_TYPE_STOP_SERVER
} net_midipipe_packet_type_t;
// Settings specified when the client connects to the server.