shithub: choc

ref: 73eac9f95f513260a106f10cf9f8501ccc7eb36c
dir: /src/i_midisocket.c/

View raw version
//
// Copyright(C) 2013 James Haley et al.
// Copyright(C) 2017 Alex Mayfield
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//     Client Interface to RPC Midi Server
//

#if _WIN32

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <SDL_net.h>

#include "i_midisocket.h"

#include "m_misc.h"
#include "net_packet.h"

#if defined(_DEBUG)
#define DEBUGOUT(s) puts(s)
#else
#define DEBUGOUT(s)
#endif

typedef enum {
	NET_MIDISOCKET_PACKET_TYPE_PREPARE_NEW_SONG,
    NET_MIDISOCKET_PACKET_TYPE_REGISTER_SONG,
	NET_MIDISOCKET_PACKET_TYPE_PLAY_SONG,
	NET_MIDISOCKET_PACKET_TYPE_STOP_SONG,
	NET_MIDISOCKET_PACKET_TYPE_CHANGE_VOLUME,
	NET_MIDISOCKET_PACKET_TYPE_PAUSE_SONG,
	NET_MIDISOCKET_PACKET_TYPE_RESUME_SONG,
	NET_MIDISOCKET_PACKET_TYPE_STOP_SERVER
} net_midisocket_packet_type_t;

//=============================================================================
//
// Data
//

static TCPsocket midi_socket;       // Client socket
static boolean server_init = false; // if true, server was started
static boolean client_init = false; // if true, client was bound

// server process information
static STARTUPINFO         si;
static PROCESS_INFORMATION pi;

//=============================================================================
//
// RPC Wrappers
//

//
// CHECK_RPC_STATUS
//
// If either server or client initialization failed, we don't try to make any
// RPC calls.
//
#define CHECK_RPC_STATUS() \
    if(!server_init || !client_init) \
        return false

#define MIDIRPC_MAXTRIES 50 // This number * 10 is the amount of time you can try to wait for.

static boolean I_MidiSocketWaitForServer()
{
    int tries = 0;
    while(false) // TODO: Is there some way to tell if the server is listening?
    {
        I_Sleep(10);
        if (++tries >= MIDIRPC_MAXTRIES)
        {
            return false;
        }
    }
    return true;
}

//
// I_MidiSocketRegisterSong
//
// Prepare the RPC MIDI engine to receive new song data, and transmit the song
// filename to the server process.
//
boolean I_MidiSocketRegisterSong(const char *filename)
{
    CHECK_RPC_STATUS();

    net_packet_t *packet;
    packet = NET_NewPacket(2);
    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_PREPARE_NEW_SONG);
    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
    if (len < packet->len)
    {
        goto fail;
    }
    NET_FreePacket(packet);

    packet = NET_NewPacket(64);
    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_REGISTER_SONG);
    NET_WriteString(packet, filename);
    len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
    if (len < packet->len)
    {
        goto fail;
    }
    NET_FreePacket(packet);

    DEBUGOUT("I_MidiSocketRegisterSong succeeded");
    return true;

fail:
    NET_FreePacket(packet);

    DEBUGOUT("I_MidiSocketRegisterSong failed");
    return false;
}

//
// I_MidiSocketPlaySong
//
// Tell the RPC server to start playing a song.
//
boolean I_MidiSocketPlaySong(boolean looping)
{
    CHECK_RPC_STATUS();

    net_packet_t *packet;
    packet = NET_NewPacket(3);
    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_PLAY_SONG);
    NET_WriteInt8(packet, looping);
    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
    NET_FreePacket(packet);
    if (len < packet->len)
    {
        DEBUGOUT("I_MidiSocketPlaySong failed");
        return false;
    }

    DEBUGOUT("I_MidiSocketPlaySong succeeded");
    return true;
}

// 
// I_MidiSocketStopSong
//
// Tell the RPC server to stop any currently playing song.
//
boolean I_MidiSocketStopSong()
{
    CHECK_RPC_STATUS();

    net_packet_t *packet;
    packet = NET_NewPacket(2);
    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_STOP_SONG);
    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
    NET_FreePacket(packet);
    if (len < packet->len)
    {
        DEBUGOUT("I_MidiSocketStopSong failed");
        return false;
    }

    DEBUGOUT("I_MidiSocketStopSong succeeded");
    return true;
}

//
// I_MidiSocketSetVolume
//
// Change the volume level of music played by the RPC midi server.
//
boolean I_MidiSocketSetVolume(int volume)
{
    CHECK_RPC_STATUS();

    net_packet_t *packet;
    packet = NET_NewPacket(6);
    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_CHANGE_VOLUME);
    NET_WriteInt32(packet, volume);
    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
    NET_FreePacket(packet);
    if (len < packet->len)
    {
        DEBUGOUT("I_MidiSocketSetVolume failed");
        return false;
    }

    DEBUGOUT("I_MidiSocketSetVolume succeeded");
    return true;
}

//
// I_MidiSocketPauseSong
//
// Pause the music being played by the server. In actuality, due to SDL_mixer
// limitations, this just temporarily sets the volume to zero.
//
boolean I_MidiSocketPauseSong()
{
    CHECK_RPC_STATUS();

    net_packet_t *packet;
    packet = NET_NewPacket(2);
    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_PAUSE_SONG);
    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
    NET_FreePacket(packet);
    if (len < packet->len)
    {
        DEBUGOUT("I_MidiSocketPauseSong failed");
        return false;
    }

    DEBUGOUT("I_MidiSocketPauseSong succeeded");
    return true;
}

//
// I_MidiSocketResumeSong
//
// Resume a song after having paused it.
//
boolean I_MidiSocketResumeSong()
{
    CHECK_RPC_STATUS();

    net_packet_t *packet;
    packet = NET_NewPacket(2);
    NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_RESUME_SONG);
    int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
    NET_FreePacket(packet);
    if (len < packet->len)
    {
        DEBUGOUT("I_MidiSocketResumeSong failed");
        return false;
    }

    DEBUGOUT("I_MidiSocketResumeSong succeeded");
   return true;
}

//=============================================================================
//
// Public Interface
//

//
// I_MidiSocketInitServer
//
// Start up the RPC MIDI server.
//
boolean I_MidiSocketInitServer()
{
    struct stat sbuf;
    char filename[MAX_PATH+1];

    memset(filename, 0, sizeof(filename));
    size_t filename_len = GetModuleFileName(NULL, filename, MAX_PATH);

    // Remove filespec
    // TODO: Move this to m_misc
    char *fp = &filename[filename_len];
    while (filename <= fp && *fp != DIR_SEPARATOR)
    {
        fp--;
    }
    *(fp + 1) = '\0';
    char* module = M_StringJoin(filename, "midiproc.exe", NULL);
    DEBUGOUT(module);

    // Look for executable file
    if(stat(module, &sbuf))
    {
        DEBUGOUT("Could not find midiproc");
        return false;
    }

    si.cb = sizeof(si);

    boolean result = CreateProcess(module, NULL, NULL, NULL, FALSE,
                                   0, NULL, NULL, &si, &pi);

    if (result)
    {
        DEBUGOUT("RPC server started");
        server_init = true;
    }
    else
    {
        DEBUGOUT("CreateProcess failed to start midiproc");
    }

    return result;
}

//
// I_MidiSocketInitClient
//
// Initialize client RPC bindings and bind to the server.
//
boolean I_MidiSocketInitClient()
{
    IPaddress ipaddress;

    // If server didn't start, client cannot be bound.
    if (!server_init)
    {
        goto fail;
    }

    // Resolve localhost to an IP address.
    if (SDLNet_ResolveHost(&ipaddress, "localhost", 2993) == -1)
    {
        goto fail;
    }

    // Connect to the server.
    midi_socket = SDLNet_TCP_Open(&ipaddress);
    if (midi_socket == NULL)
    {
        goto fail;
    }

    DEBUGOUT("RPC client initialized");
    client_init = true;

    return I_MidiSocketWaitForServer();

fail:
    DEBUGOUT("RPC client binding failed");
    return false;
}

//
// I_MidiSocketClientShutDown
//
// Shutdown the RPC Client
//
void I_MidiSocketClientShutDown()
{
    // stop the server
    if(server_init)
    {
        net_packet_t *packet;
        packet = NET_NewPacket(2);
        NET_WriteInt16(packet, NET_MIDISOCKET_PACKET_TYPE_STOP_SERVER);
        int len = SDLNet_TCP_Send(midi_socket, packet->data, packet->len);
        NET_FreePacket(packet);
        if (len < packet->len)
        {
            DEBUGOUT("Problem encountered when stopping RPC server");
        }

        server_init = false;
    }

    if (midi_socket)
    {
        SDLNet_TCP_Close(midi_socket);
        midi_socket = NULL;
    }

    client_init = false;
}

//
// I_MidiSocketReady
//
// Returns true if both server and client initialized successfully.
//
boolean I_MidiSocketReady()
{
    CHECK_RPC_STATUS();

    return true;
}

#endif