ref: 090bc3bb61c59cc9d335d840ecc36e9fe3336fa1
dir: /midiproc/main.c/
// // 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 RPC Server // // Uses RPC 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? // #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdlib.h> #include "SDL.h" #include "SDL_mixer.h" #include "buffer.h" #include "config.h" #include "doomtype.h" #include "net_defs.h" static HANDLE midi_process_in; // Standard In. static HANDLE midi_process_out; // Standard Out. static buffer_t *midi_buffer; // Data from client. // Currently playing music track static Mix_Music *music = NULL; static char *filename = NULL; static void UnregisterSong(); //============================================================================= // // SDL_mixer Interface // // // RegisterSong // static void RegisterSong(char* filename) { if (music) { UnregisterSong(); } music = Mix_LoadMUS(filename); } // // StartSong // static void StartSong(boolean loop) { if (music) { Mix_PlayMusic(music, loop ? -1 : 0); } } // // SetVolume // static void SetVolume(int volume) { Mix_VolumeMusic((volume * 128) / 15); } static int paused_midi_volume; // // PauseSong // static void PauseSong() { paused_midi_volume = Mix_VolumeMusic(-1); Mix_VolumeMusic(0); } // // ResumeSong // static void ResumeSong() { Mix_VolumeMusic(paused_midi_volume); } // // StopSong // static void StopSong() { if (music) { Mix_HaltMusic(); } } // // UnregisterSong // static void UnregisterSong() { if (!music) { return; } StopSong(); Mix_FreeMusic(music); free(filename); filename = NULL; music = NULL; } // // ShutdownSDL // static void ShutdownSDL() { UnregisterSong(); Mix_CloseAudio(); SDL_Quit(); } //============================================================================= // // RPC Server Interface // // // MidiPipe_PrepareNewSong // // Prepare the engine to receive new song data from the RPC client. // boolean MidiPipe_PrepareNewSong() { // 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); if (filename == NULL) { return false; } memcpy(filename, file, size); fprintf(stderr, "%s\n", __FUNCTION__); return true; } // // MidiPipe_PlaySong // // Start playing the song. // boolean MidiPipe_PlaySong(buffer_reader_t *reader) { uint8_t looping; if (!Reader_ReadInt8(reader, &looping)) { return false; } RegisterSong(filename); StartSong((boolean)looping); fprintf(stderr, "%s\n", __FUNCTION__); return true; } // // MidiPipe_StopSong // // Stop the song. // boolean MidiPipe_StopSong() { 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; } //============================================================================= // // Server Implementation // 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_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(); default: return false; } } // // Server packet parser // boolean ParseMessage(buffer_t *buf) { 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. int bytes_read = Reader_BytesRead(reader); DeleteReader(reader); Buffer_Shift(buf, bytes_read); return true; fail: // We did not read a complete packet. Delete our reader and try again // with more data. DeleteReader(reader); return false; } boolean ListenForever() { BOOL wok = FALSE; CHAR pipe_buffer[8192]; DWORD pipe_buffer_read = 0; boolean ok = false; buffer_t *buffer = NewBuffer(); fprintf(stderr, "%s\n", "In theory we should be reading..."); for (;;) { // Wait until we see some data on the pipe. wok = PeekNamedPipe(midi_process_in, NULL, 0, NULL, &pipe_buffer_read, NULL); if (!wok) { return false; } else if (pipe_buffer_read == 0) { SDL_Delay(1); continue; } // Read data off the pipe and add it to the buffer. fprintf(stderr, "%s\n", "ReadFile"); wok = ReadFile(midi_process_in, pipe_buffer, sizeof(pipe_buffer), &pipe_buffer_read, NULL); if (!wok) { return false; } fprintf(stderr, "%s\n", "Buffer_Push"); ok = Buffer_Push(buffer, pipe_buffer, pipe_buffer_read); if (!ok) { return false; } fprintf(stderr, "%s\n", "ParseMessage"); do { // Read messages off the buffer until we can't anymore. ok = ParseMessage(buffer); } while (ok); } return false; } //============================================================================= // // Main Program // // // InitSDL // // Start up SDL and SDL_mixer. // boolean InitSDL() { if (SDL_Init(SDL_INIT_AUDIO) == -1) { return false; } if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { return false; } return true; } // // InitPipes // // Ensure that we can communicate. // boolean InitPipes() { midi_process_in = GetStdHandle(STD_INPUT_HANDLE); if (midi_process_in == INVALID_HANDLE_VALUE) { return false; } midi_process_out = GetStdHandle(STD_OUTPUT_HANDLE); if (midi_process_out == INVALID_HANDLE_VALUE) { return false; } return true; } // // main // // Application entry point. // int main(int argc, char *argv[]) { // Make sure we're not launching this process by itself. if (argc < 2) { MessageBox(NULL, TEXT("This program is tasked with playing Native ") TEXT("MIDI music, and is intended to be launched by ") TEXT(PACKAGE_NAME) TEXT("."), TEXT(PACKAGE_STRING), MB_OK | MB_ICONASTERISK); return EXIT_FAILURE; } // Make sure our Choccolate Doom and midiproc version are lined up. if (strcmp(PACKAGE_STRING, argv[1]) != 0) { MessageBox(NULL, TEXT("It appears that the version of ") TEXT(PACKAGE_NAME) TEXT(" and ") TEXT(PROGRAM_PREFIX) TEXT("midiproc are out of sync. Please reinstall ") TEXT(PACKAGE_NAME) TEXT("."), TEXT(PACKAGE_STRING), MB_OK | MB_ICONASTERISK); return EXIT_FAILURE; } if (!InitPipes()) { return EXIT_FAILURE; } if (!InitSDL()) { return EXIT_FAILURE; } if (!ListenForever()) { return EXIT_FAILURE; } return EXIT_SUCCESS; }