diff --git a/source/audiolib/include/music.h b/source/audiolib/include/music.h index 678b94a60..b87a11125 100644 --- a/source/audiolib/include/music.h +++ b/source/audiolib/include/music.h @@ -37,36 +37,51 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) extern int32_t MUSIC_ErrorCode; enum MUSIC_ERRORS - { - MUSIC_Warning = -2, - MUSIC_Error = -1, - MUSIC_Ok = 0, - MUSIC_MidiError, - }; +{ + MUSIC_Error = -1, + MUSIC_Ok = 0, + MUSIC_FileError, +}; typedef struct - { - uint32_t tickposition; - uint32_t milliseconds; - uint32_t measure; - uint32_t beat; - uint32_t tick; - } songposition; +{ + uint32_t tickposition; + uint32_t milliseconds; + uint32_t measure; + uint32_t beat; + uint32_t tick; +} songposition; #define MUSIC_LoopSong ( 1 == 1 ) #define MUSIC_PlayOnce ( !MUSIC_LoopSong ) -const char *MUSIC_ErrorString( int32_t ErrorNumber ); -int32_t MUSIC_Init( int32_t SoundCard, int32_t Address ); -int32_t MUSIC_Shutdown( void ); -void MUSIC_SetVolume( int32_t volume ); -int32_t MUSIC_GetVolume( void ); -void MUSIC_SetLoopFlag( int32_t loopflag ); -void MUSIC_Continue( void ); -void MUSIC_Pause( void ); -int32_t MUSIC_StopSong( void ); -int32_t MUSIC_PlaySong( char *song, int32_t songsize, int32_t loopflag ); -void MUSIC_Update(void); +#define MUSIC_SetErrorCode(status) MUSIC_ErrorCode = (status); + +extern char const *errorMessage; + +static inline const char *MUSIC_ErrorString(int32_t ErrorNumber) +{ + switch (ErrorNumber) + { + case MUSIC_Error: return errorMessage; + case MUSIC_Ok: return "Music ok."; + case MUSIC_FileError: return "Error playing file."; + default: return "Unknown error."; + } + + return NULL; +} + +int32_t MUSIC_Init(int32_t SoundCard, int32_t Address); +int32_t MUSIC_Shutdown(void); +void MUSIC_SetVolume(int32_t volume); +int32_t MUSIC_GetVolume(void); +void MUSIC_SetLoopFlag(int32_t loopflag); +void MUSIC_Continue(void); +void MUSIC_Pause(void); +int32_t MUSIC_StopSong(void); +int32_t MUSIC_PlaySong(char *song, int32_t songsize, int32_t loopflag, const char *fn = nullptr); +void MUSIC_Update(void); #endif diff --git a/source/audiolib/src/driver_sdl.cpp b/source/audiolib/src/driver_sdl.cpp index 1c99b084c..8ce653af3 100644 --- a/source/audiolib/src/driver_sdl.cpp +++ b/source/audiolib/src/driver_sdl.cpp @@ -22,13 +22,13 @@ * libSDL output driver for MultiVoc */ -#define NEED_SDL_MIXER #include "driver_sdl.h" #include "compat.h" #include "multivoc.h" #include "mutex.h" #include "sdl_inc.h" +#include "vfs.h" enum { SDLErr_Warning = -2, @@ -42,33 +42,26 @@ enum { static int32_t ErrorCode = SDLErr_Ok; static int32_t Initialised; static int32_t Playing; -static int32_t StartedSDL = -1; +static uint32_t StartedSDL; static char *MixBuffer; static int32_t MixBufferSize; static int32_t MixBufferCount; static int32_t MixBufferCurrent; static int32_t MixBufferUsed; -static void ( *MixCallBack )( void ); +static void (*MixCallBack)(void); -static Mix_Chunk *DummyChunk; -static uint8_t *DummyBuffer; -static int32_t InterruptsDisabled; -static mutex_t EffectFence; +#if (SDL_MAJOR_VERSION == 2) +static SDL_AudioDeviceID audio_dev; +#endif -static void fillData(int chan, void *ptr, int remaining, void *udata) +static void fillData(void * userdata, Uint8 * ptr, int remaining) { - int32_t len; + UNREFERENCED_PARAMETER(userdata); + + int len; char *sptr; - UNREFERENCED_PARAMETER(chan); - UNREFERENCED_PARAMETER(udata); - - if (!MixBuffer || !MixCallBack) - return; - - mutex_lock(&EffectFence); - while (remaining > 0) { if (MixBufferUsed == MixBufferSize) { MixCallBack(); @@ -90,16 +83,13 @@ static void fillData(int chan, void *ptr, int remaining, void *udata) memcpy(ptr, sptr, len); - ptr = (void *)((uintptr_t)(ptr) + len); + ptr += len; MixBufferUsed += len; remaining -= len; } } - - mutex_unlock(&EffectFence); } - int32_t SDLDrv_GetError(void) { return ErrorCode; @@ -141,27 +131,21 @@ const char *SDLDrv_ErrorString( int32_t ErrorNumber ) int32_t SDLDrv_PCM_Init(int32_t *mixrate, int32_t *numchannels, void * initdata) { - uint32_t inited; - int32_t err = 0; - int32_t chunksize; - uint16_t fmt; - - UNREFERENCED_PARAMETER(numchannels); UNREFERENCED_PARAMETER(initdata); + Uint32 inited; + int err = 0; + SDL_AudioSpec spec, actual; + if (Initialised) { SDLDrv_PCM_Shutdown(); } - inited = SDL_WasInit(SDL_INIT_EVERYTHING); + inited = SDL_WasInit(SDL_INIT_AUDIO); - if (inited == 0) { - // nothing was initialised - err = SDL_Init(SDL_INIT_AUDIO); - StartedSDL = 0; - } else if (!(inited & SDL_INIT_AUDIO)) { + if (!(inited & SDL_INIT_AUDIO)) { err = SDL_InitSubSystem(SDL_INIT_AUDIO); - StartedSDL = 1; + StartedSDL = SDL_WasInit(SDL_INIT_AUDIO); } if (err < 0) { @@ -169,51 +153,76 @@ int32_t SDLDrv_PCM_Init(int32_t *mixrate, int32_t *numchannels, void * initdata) return SDLErr_Error; } - chunksize = 512; + int chunksize = 512; #ifdef __ANDROID__ chunksize = droidinfo.audio_buffer_size; #endif - if (*mixrate >= 16000) chunksize *= 2; - if (*mixrate >= 32000) chunksize *= 2; + spec.freq = *mixrate; + spec.format = AUDIO_S16SYS; + spec.channels = *numchannels; + spec.samples = chunksize; + spec.callback = fillData; + spec.userdata = nullptr; - err = Mix_OpenAudio(*mixrate, AUDIO_S16SYS, *numchannels, chunksize); + Bmemset(&actual, 0, sizeof(actual)); - if (err < 0) { +#if (SDL_MAJOR_VERSION == 1) + err = !SDL_OpenAudio(&spec, &actual); +#else + audio_dev = err = SDL_OpenAudioDevice(NULL, 0, &spec, &actual, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); +#endif + + if (err == 0) { ErrorCode = SDLErr_OpenAudio; return SDLErr_Error; } +#if (SDL_MAJOR_VERSION == 1) + char drivername[64] = "(error)"; + SDL_AudioDriverName(drivername, sizeof(drivername)); + MV_Printf("SDL %s driver\n", drivername); +#else + char *drivername = Xstrdup(SDL_GetCurrentAudioDriver()); - int intmixrate = *mixrate; - int intnumchannels = *numchannels; - if (Mix_QuerySpec(&intmixrate, &fmt, &intnumchannels)) - { - if (fmt == AUDIO_U8 || fmt == AUDIO_S8) - { + for (int i=0;drivername[i] != 0;++i) + drivername[i] = toupperlookup[drivername[i]]; + + MV_Printf("SDL %s driver on %s\n", drivername ? drivername : "(error)", SDL_GetAudioDeviceName(0, 0)); + Xfree(drivername); +#endif + +#if (SDL_MAJOR_VERSION == 1) + if (actual.freq == 0 || actual.channels == 0) { + // hack for when SDL said it opened the audio, but clearly didn't + SDL_CloseAudio(); ErrorCode = SDLErr_OpenAudio; return SDLErr_Error; } +#endif + err = 0; + + *mixrate = actual.freq; + if (actual.format == AUDIO_U8 || actual.format == AUDIO_S8) + { + ErrorCode = SDLErr_OpenAudio; + err = 1; } - *mixrate = intmixrate; - *numchannels = intnumchannels; - //Mix_SetPostMix(fillData, nullptr); + *numchannels = actual.channels; + if (actual.channels != 1 && actual.channels != 2) + { + ErrorCode = SDLErr_OpenAudio; + err = 1; + } - mutex_init(&EffectFence); - - // channel 0 and 1 are actual sounds - // dummy channel 2 runs our fillData() callback as an effect - Mix_RegisterEffect(2, fillData, nullptr, nullptr); - - DummyBuffer = (uint8_t *) calloc(1, chunksize); - - DummyChunk = Mix_QuickLoad_RAW(DummyBuffer, chunksize); - - Mix_PlayChannel(2, DummyChunk, -1); + if (err) + { + SDL_CloseAudio(); + return SDLErr_Error; + } Initialised = 1; - return SDLErr_Ok; } @@ -221,25 +230,11 @@ void SDLDrv_PCM_Shutdown(void) { if (!Initialised) return; - else Mix_HaltChannel(-1); - if (DummyChunk != nullptr) - { - Mix_FreeChunk(DummyChunk); - DummyChunk = nullptr; - } + if (StartedSDL) + SDL_QuitSubSystem(StartedSDL); - DO_FREE_AND_NULL(DummyBuffer); - - Mix_CloseAudio(); - - if (StartedSDL > 0) { - SDL_QuitSubSystem(SDL_INIT_AUDIO); - } else if (StartedSDL == 0) { - SDL_Quit(); - } - - StartedSDL = -1; + StartedSDL = 0; Initialised = 0; } @@ -265,8 +260,11 @@ int32_t SDLDrv_PCM_BeginPlayback(char *BufferStart, int32_t BufferSize, // prime the buffer MixCallBack(); - Mix_Resume(-1); - +#if (SDL_MAJOR_VERSION == 2) + SDL_PauseAudioDevice(audio_dev, 0); +#else + SDL_PauseAudio(0); +#endif Playing = 1; return SDLErr_Ok; @@ -278,23 +276,29 @@ void SDLDrv_PCM_StopPlayback(void) return; } - Mix_Pause(-1); +#if (SDL_MAJOR_VERSION == 2) + SDL_PauseAudioDevice(audio_dev, 1); +#else + SDL_PauseAudio(1); +#endif Playing = 0; } void SDLDrv_PCM_Lock(void) { - if (InterruptsDisabled++) - return; - - mutex_lock(&EffectFence); +#if (SDL_MAJOR_VERSION == 2) + SDL_LockAudioDevice(audio_dev); +#else + SDL_LockAudio(); +#endif } void SDLDrv_PCM_Unlock(void) { - if (--InterruptsDisabled) - return; - - mutex_unlock(&EffectFence); +#if (SDL_MAJOR_VERSION == 2) + SDL_UnlockAudioDevice(audio_dev); +#else + SDL_UnlockAudio(); +#endif } diff --git a/source/audiolib/src/multivoc.cpp b/source/audiolib/src/multivoc.cpp index d35658ef1..1ab3c0e8b 100644 --- a/source/audiolib/src/multivoc.cpp +++ b/source/audiolib/src/multivoc.cpp @@ -581,7 +581,9 @@ int32_t MV_GetPosition(int32_t handle, int32_t *position) #ifdef HAVE_FLAC case FMT_FLAC: *position = MV_GetFLACPosition(voice); break; #endif - case FMT_XA: *position = MV_GetXAPosition(voice); break; + case FMT_XA: *position = MV_GetXAPosition(voice); + default: + break; #ifdef HAVE_XMP case FMT_XMP: *position = MV_GetXMPPosition(voice); break; #endif @@ -607,7 +609,9 @@ int32_t MV_SetPosition(int32_t handle, int32_t position) #ifdef HAVE_FLAC case FMT_FLAC: MV_SetFLACPosition(voice, position); break; #endif - case FMT_XA: MV_SetXAPosition(voice, position); break; + case FMT_XA: MV_SetXAPosition(voice, position); + default: + break; #ifdef HAVE_XMP case FMT_XMP: MV_SetXMPPosition(voice, position); break; #endif diff --git a/source/audiolib/src/music.cpp b/source/audiolib/src/music.cpp index 0288a6e52..996bf65b0 100644 --- a/source/audiolib/src/music.cpp +++ b/source/audiolib/src/music.cpp @@ -30,29 +30,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. int32_t MUSIC_SoundDevice = -1; int32_t MUSIC_ErrorCode = MUSIC_Ok; - +char const *errorMessage; static midifuncs MUSIC_MidiFunctions; int32_t MUSIC_InitMidi(int32_t card, midifuncs *Funcs, int32_t Address); -#define MUSIC_SetErrorCode(status) MUSIC_ErrorCode = (status); - -const char *MUSIC_ErrorString(int32_t ErrorNumber) -{ - const char *ErrorString; - - switch (ErrorNumber) - { - case MUSIC_Warning: - case MUSIC_Error: ErrorString = MUSIC_ErrorString(MUSIC_ErrorCode); break; - case MUSIC_Ok: ErrorString = "Music ok."; break; - case MUSIC_MidiError: ErrorString = "Error playing MIDI file."; break; - default: ErrorString = "Unknown Music error code."; break; - } - - return ErrorString; -} - int32_t MUSIC_Init(int32_t SoundCard, int32_t Address) { @@ -90,16 +72,17 @@ int32_t MUSIC_StopSong(void) } -int32_t MUSIC_PlaySong(char *song, int32_t songsize, int32_t loopflag) +int32_t MUSIC_PlaySong(char *song, int32_t songsize, int32_t loopflag, const char *fn /*= nullptr*/) { UNREFERENCED_PARAMETER(songsize); + UNREFERENCED_PARAMETER(fn); MUSIC_SetErrorCode(MUSIC_Ok) if (MIDI_PlaySong(song, loopflag) != MIDI_Ok) { - MUSIC_SetErrorCode(MUSIC_MidiError); - return MUSIC_Warning; + MUSIC_SetErrorCode(MUSIC_FileError); + return MUSIC_Error; } return MUSIC_Ok; diff --git a/source/audiolib/src/sdlmusic.cpp b/source/audiolib/src/sdlmusic.cpp deleted file mode 100644 index 221e4dc92..000000000 --- a/source/audiolib/src/sdlmusic.cpp +++ /dev/null @@ -1,544 +0,0 @@ -//------------------------------------------------------------------------- -/* -Copyright (C) 2010 EDuke32 developers and contributors - -This file is part of EDuke32. - -EDuke32 is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -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. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -//------------------------------------------------------------------------- - -/* - * A reimplementation of Jim Dose's FX_MAN routines, using SDL_mixer 1.2. - * Whee. FX_MAN is also known as the "Apogee Sound System", or "ASS" for - * short. How strangely appropriate that seems. - */ - -// This object is shared by all Build games with MIDI playback! - -#define NEED_SDL_MIXER - -#include "compat.h" - -#include "common_game.h" -#include "cache1d.h" - -#include "sdlayer.h" -#include "music.h" -#include "al_midi.h" -#include "oplmidi.h" - -#if !defined _WIN32 && !defined(GEKKO) -//# define FORK_EXEC_MIDI 1 -#endif - -#if defined FORK_EXEC_MIDI // fork/exec based external midi player -#include -#include -#include -static char **external_midi_argv; -static pid_t external_midi_pid=-1; -static int8_t external_midi_restart=0; -#endif - -#ifdef __ANDROID__ //TODO fix -static char const *external_midi_tempfn = APPBASENAME "-music.mid"; -#else -static char const *external_midi_tempfn = "/tmp/" APPBASENAME "-music.mid"; -#endif - -static int32_t external_midi = 0; - -int32_t MUSIC_SoundDevice = MIDIDEVICE_NONE; -int32_t MUSIC_ErrorCode = MUSIC_Ok; - -static OPLMusic::midifuncs MUSIC_MidiFunctions; -#define MUSIC_SetErrorCode(status) MUSIC_ErrorCode = (status); - -static char warningMessage[80]; -static char errorMessage[80]; - -static int32_t music_initialized = 0; -static int32_t music_context = 0; -static int32_t music_loopflag = MUSIC_PlayOnce; -static Mix_Music *music_musicchunk = NULL; - -static void setErrorMessage(const char *msg) -{ - Bstrncpyz(errorMessage, msg, sizeof(errorMessage)); -} - -// The music functions... - -const char *MUSIC_ErrorString(int32_t ErrorNumber) -{ - switch (ErrorNumber) - { - case MUSIC_Warning: - return warningMessage; - - case MUSIC_Error: - return errorMessage; - - case MUSIC_Ok: - return "OK; no error."; - - case MUSIC_MidiError: - return "MIDI error."; - - default: - return "Unknown error."; - } // switch - - return NULL; -} // MUSIC_ErrorString - -int32_t MUSIC_Init(int32_t SoundCard, int32_t Address) -{ - MUSIC_SoundDevice = SoundCard; - if (SoundCard == MIDIDEVICE_OPL) - { - return OPLMusic::MUSIC_InitMidi(SoundCard, &MUSIC_MidiFunctions, Address); - } -#ifdef __ANDROID__ - music_initialized = 1; - return MUSIC_Ok; -#endif - // Use an external MIDI player if the user has specified to do so - char *command = getenv("EDUKE32_MUSIC_CMD"); - const SDL_version *linked = Mix_Linked_Version(); - - UNREFERENCED_PARAMETER(SoundCard); - UNREFERENCED_PARAMETER(Address); - - if (music_initialized) - { - setErrorMessage("Music system is already initialized."); - return MUSIC_Error; - } // if - - if (SDL_VERSIONNUM(linked->major,linked->minor,linked->patch) < MIX_REQUIREDVERSION) - { - // reject running with SDL_Mixer versions older than what is stated in sdl_inc.h - initprintf("You need at least v%d.%d.%d of SDL_mixer for music\n",SDL_MIXER_MIN_X,SDL_MIXER_MIN_Y,SDL_MIXER_MIN_Z); - return MUSIC_Error; - } - - external_midi = (command != NULL && command[0] != 0); - - if (external_midi) - { -#if defined FORK_EXEC_MIDI - int32_t ws=1, numargs=0, pagesize=sysconf(_SC_PAGE_SIZE); - char *c, *cmd; - size_t sz; -#endif - - initprintf("Setting music command to \"%s\".\n", command); - -#if !defined FORK_EXEC_MIDI - if (Mix_SetMusicCMD(command)==-1) - { - perror("Mix_SetMusicCMD"); - goto fallback; - } -#else - - if (pagesize==-1) - goto fallback; - - for (c=command; *c; c++) - { - if (isspace(*c)) - ws = 1; - else if (ws) - { - ws = 0; - numargs++; - } - } - - if (numargs==0) - goto fallback; - - sz = (numargs+2)*sizeof(char *) + (c-command+1); - sz = ((sz+pagesize-1)/pagesize)*pagesize; -#if defined(__APPLE__) || defined(__ANDROID__) - external_midi_argv = Xcalloc(1,sz+pagesize); - external_midi_argv = (char **)((intptr_t)external_midi_argv + (pagesize-(((intptr_t)external_midi_argv)&(pagesize-1)))); -#else - if (posix_memalign((void **)&external_midi_argv, pagesize, sz)) - goto fallback; -#endif - cmd = (char *)external_midi_argv + (numargs+2)*sizeof(char *); - Bmemcpy(cmd, command, c-command+1); - - ws = 1; - numargs = 0; - for (c=cmd; *c; c++) - { - if (isspace(*c)) - { - ws = 1; - *c = 0; - } - else if (ws) - { - ws = 0; - external_midi_argv[numargs++] = c; - } - } - external_midi_argv[numargs] = external_midi_tempfn; - external_midi_argv[numargs+1] = NULL; - - if (mprotect(external_midi_argv, sz, PROT_READ)==-1) // make argv and command string read-only - { - perror("MUSIC_Init: mprotect"); - goto fallback; - } -# if 0 - { - int i; - initprintf("----Music argv:\n"); - for (i=0; i=0; i--) - { - fp = Bfopen(s[i], "r"); - if (fp == NULL) - { - if (i == 0) - { - initprintf("Error: couldn't open any of the following files:\n"); - for (i = ARRAY_SIZE(s)-1; i>=0; i--) - initprintf("%s\n",s[i]); - return MUSIC_Error; - } - continue; - } - else break; - } - Bfclose(fp); - } - - music_initialized = 1; - return MUSIC_Ok; -} // MUSIC_Init - - -int32_t MUSIC_Shutdown(void) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - OPLMusic::MIDI_StopSong(); - - return MUSIC_Ok; - } - // TODO - make sure this is being called from the menu -- SA -#if !defined FORK_EXEC_MIDI - if (external_midi) - Mix_SetMusicCMD(NULL); -#endif - - MUSIC_StopSong(); - music_context = 0; - music_initialized = 0; - music_loopflag = MUSIC_PlayOnce; - - return MUSIC_Ok; -} // MUSIC_Shutdown - - -void MUSIC_SetMaxFMMidiChannel(int32_t channel) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - OPLMusic::AL_SetMaxMidiChannel(channel); - } - // UNREFERENCED_PARAMETER(channel); -} // MUSIC_SetMaxFMMidiChannel - - -void MUSIC_SetVolume(int32_t volume) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - OPLMusic::MIDI_SetVolume(min(max(0, volume), 255)); - return; - } - volume = max(0, volume); - volume = min(volume, 255); - - Mix_VolumeMusic(volume >> 1); // convert 0-255 to 0-128. -} // MUSIC_SetVolume - - -int32_t MUSIC_GetVolume(void) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - return OPLMusic::MIDI_GetVolume(); - } - return (Mix_VolumeMusic(-1) << 1); // convert 0-128 to 0-255. -} // MUSIC_GetVolume - - -void MUSIC_SetLoopFlag(int32_t loopflag) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - OPLMusic::MIDI_SetLoopFlag(loopflag); - return; - } - music_loopflag = loopflag; -} // MUSIC_SetLoopFlag - - -void MUSIC_Continue(void) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - OPLMusic::MIDI_ContinueSong(); - return; - } - if (Mix_PausedMusic()) - Mix_ResumeMusic(); -} // MUSIC_Continue - - -void MUSIC_Pause(void) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - OPLMusic::MIDI_PauseSong(); - return; - } - Mix_PauseMusic(); -} // MUSIC_Pause - -int32_t MUSIC_StopSong(void) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - OPLMusic::MIDI_StopSong(); - MUSIC_SetErrorCode(MUSIC_Ok); - return MUSIC_Ok; - } -#if defined FORK_EXEC_MIDI - if (external_midi) - { - if (external_midi_pid > 0) - { - int32_t ret; - struct timespec ts; - - external_midi_restart = 0; // make SIGCHLD handler a no-op - - ts.tv_sec = 0; - ts.tv_nsec = 5000000; // sleep 5ms at most - - kill(external_midi_pid, SIGTERM); - nanosleep(&ts, NULL); - ret = waitpid(external_midi_pid, NULL, WNOHANG|WUNTRACED); -// printf("(%d)", ret); - - if (ret != external_midi_pid) - { - if (ret==-1) - perror("waitpid"); - else - { - // we tried to be nice, but no... - kill(external_midi_pid, SIGKILL); - initprintf("%s: wait for SIGTERM timed out.\n", __func__); - if (waitpid(external_midi_pid, NULL, WUNTRACED)==-1) - perror("waitpid (2)"); - } - } - - external_midi_pid = -1; - } - - return MUSIC_Ok; - } -#endif - - //if (!fx_initialized) - if (!Mix_QuerySpec(NULL, NULL, NULL)) - { - setErrorMessage("Need FX system initialized, too. Sorry."); - return MUSIC_Error; - } // if - - if ((Mix_PlayingMusic()) || (Mix_PausedMusic())) - Mix_HaltMusic(); - - if (music_musicchunk) - Mix_FreeMusic(music_musicchunk); - - music_musicchunk = NULL; - - return MUSIC_Ok; -} // MUSIC_StopSong - -#if defined FORK_EXEC_MIDI -static int32_t playmusic() -{ - pid_t pid = vfork(); - - if (pid==-1) // error - { - initprintf("%s: vfork: %s\n", __func__, strerror(errno)); - return MUSIC_Error; - } - else if (pid==0) // child - { - // exec without PATH lookup - if (execv(external_midi_argv[0], external_midi_argv) < 0) - { - perror("execv"); - _exit(EXIT_FAILURE); - } - } - else // parent - { - external_midi_pid = pid; - } - - return MUSIC_Ok; -} - -static void sigchld_handler(int signo) -{ - if (signo==SIGCHLD && external_midi_restart) - { - int status; - - if (external_midi_pid > 0) - { - if (waitpid(external_midi_pid, &status, WUNTRACED)==-1) - perror("waitpid (3)"); - - if (WIFEXITED(status) && WEXITSTATUS(status)==0) - { - // loop ... - playmusic(); - } - } - } -} -#endif - -// Duke3D-specific. --ryan. -// void MUSIC_PlayMusic(char *_filename) -int32_t MUSIC_PlaySong(char *song, int32_t songsize, int32_t loopflag) -{ - if (MUSIC_SoundDevice == MIDIDEVICE_OPL) - { - MUSIC_SetErrorCode(MUSIC_Ok) - - if (OPLMusic::MIDI_PlaySong(song, loopflag) != OPLMusic::MIDI_Ok) - { - MUSIC_SetErrorCode(MUSIC_MidiError); - return MUSIC_Warning; - } - - return MUSIC_Ok; - } - if (external_midi) - { - FILE *fp; - -#if defined FORK_EXEC_MIDI - static int32_t sigchld_handler_set = 0; - - if (!sigchld_handler_set) - { - struct sigaction sa; - sa.sa_handler=sigchld_handler; - sa.sa_flags=0; - sigemptyset(&sa.sa_mask); - - if (sigaction(SIGCHLD, &sa, NULL)==-1) - initprintf("%s: sigaction: %s\n", __func__, strerror(errno)); - - sigchld_handler_set = 1; - } -#endif - - fp = Bfopen(external_midi_tempfn, "wb"); - if (fp) - { - fwrite(song, 1, songsize, fp); - Bfclose(fp); - -#if defined FORK_EXEC_MIDI - external_midi_restart = loopflag; - int32_t retval = playmusic(); - if (retval != MUSIC_Ok) - return retval; -#else - music_musicchunk = Mix_LoadMUS(external_midi_tempfn); - if (!music_musicchunk) - { - initprintf("Mix_LoadMUS: %s\n", Mix_GetError()); - return MUSIC_Error; - } -#endif - } - else - { - initprintf("%s: fopen: %s\n", __func__, strerror(errno)); - return MUSIC_Error; - } - } - else - music_musicchunk = Mix_LoadMUS_RW(SDL_RWFromMem(song, songsize) -#if (SDL_MAJOR_VERSION > 1) - , SDL_FALSE -#endif - ); - - if (music_musicchunk == NULL) - return MUSIC_Error; - - if (Mix_PlayMusic(music_musicchunk, (loopflag == MUSIC_LoopSong)?-1:0) == -1) - { - initprintf("Mix_PlayMusic: %s\n", Mix_GetError()); - return MUSIC_Error; - } - - return MUSIC_Ok; -} - - -void MUSIC_Update(void) -{} diff --git a/source/duke3d/src/music_external.cpp b/source/duke3d/src/music_external.cpp new file mode 100644 index 000000000..d82ca5ec9 --- /dev/null +++ b/source/duke3d/src/music_external.cpp @@ -0,0 +1,360 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010 EDuke32 developers and contributors + +This file is part of EDuke32. + +EDuke32 is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +//------------------------------------------------------------------------- + +// This object is shared by all Build games with MIDI playback! + +#include "cache1d.h" +#include "compat.h" +#include "duke3d.h" +#include "music.h" +#include "vfs.h" +#include "winbits.h" + +// fork/exec based external music player +#ifndef _WIN32 +#include +#include +#include + +#define INVALID_HANDLE_VALUE -1 +typedef pid_t proc_t; +#else +typedef HANDLE proc_t; +#endif + +static char ** g_musicPlayerArgv; +static int32_t g_musicPlayerEnabled; +static proc_t g_musicPlayerHandle = INVALID_HANDLE_VALUE; +static int32_t g_musicPlayerReady; +static int8_t g_musicPlayerRestart; +static char * g_musicPlayerCommandLine; + +static char g_musicFileName[BMAX_PATH]; +static int g_musicFileNameArgvPos; + +char const *errorMessage; + +int32_t MUSIC_Init(int32_t SoundCard, int32_t Address) +{ + // Use an external music player + g_musicPlayerCommandLine = getenv("EDUKE32_MUSIC_CMD"); + + UNREFERENCED_PARAMETER(SoundCard); + UNREFERENCED_PARAMETER(Address); + + if (g_musicPlayerReady) + { + errorMessage = "MUSIC_Init: external player already initialized!"; + return MUSIC_Error; + } // if + + g_musicPlayerEnabled = (g_musicPlayerCommandLine && g_musicPlayerCommandLine[0]); + + if (!g_musicPlayerEnabled) + { + errorMessage = "MUSIC_Init: no external player configured!"; + return MUSIC_Error; + } + + initprintf("Using external music player: \"%s\"\n", g_musicPlayerCommandLine); + +#ifndef _WIN32 + int32_t ws=1, numargs=0, pagesize=Bgetpagesize(); + char *c, *cmd; + size_t sz; + + if (pagesize == -1) + { + errorMessage = "MUSIC_Init: unable to determine system page size"; + return MUSIC_Error; + } + for (c=g_musicPlayerCommandLine; *c; c++) + { + if (isspace(*c)) + ws = 1; + else if (ws) + { + ws = 0; + numargs++; + } + } + + if (numargs == 0) + { + errorMessage = "MUSIC_Init: not enough arguments for external player"; + return MUSIC_Error; + } + + sz = (numargs+2)*sizeof(char *) + (c-g_musicPlayerCommandLine+1); + sz = ((sz+pagesize-1)/pagesize)*pagesize; + g_musicPlayerArgv = (char **)Xaligned_alloc(pagesize, sz); + cmd = (char *)g_musicPlayerArgv + (numargs+2)*sizeof(intptr_t); + Bmemcpy(cmd, g_musicPlayerCommandLine, c-g_musicPlayerCommandLine+1); + + ws = 1; + numargs = 0; + for (c=cmd; *c; c++) + { + if (isspace(*c)) + { + ws = 1; + *c = 0; + } + else if (ws) + { + ws = 0; + g_musicPlayerArgv[numargs++] = c; + } + } + g_musicFileNameArgvPos = numargs; + g_musicPlayerArgv[numargs] = g_musicFileName; + g_musicPlayerArgv[numargs+1] = NULL; + +#if 0 + if (mprotect(g_musicPlayerArgv, sz, PROT_READ)==-1) // make argv and command string read-only + { + initprintf("MUSIC_Init: mprotect(): %s\n"); + errorMessage = "MUSIC_Init: mprotect() failure"; + return MUSIC_Error; + } +#endif +#endif + + g_musicPlayerReady = 1; + return MUSIC_Ok; +} + + +int32_t MUSIC_Shutdown(void) +{ + MUSIC_StopSong(); + g_musicPlayerReady = 0; + + return MUSIC_Ok; +} // MUSIC_Shutdown + + +void MUSIC_SetMaxFMMidiChannel(int32_t channel) +{ + UNREFERENCED_PARAMETER(channel); +} // MUSIC_SetMaxFMMidiChannel + +int MUSIC_Volume; + +void MUSIC_SetVolume(int32_t volume) +{ + volume = max(0, volume); + volume = min(volume, 255); + + MUSIC_Volume = volume; +} // MUSIC_SetVolume + + +int32_t MUSIC_GetVolume(void) +{ + return MUSIC_Volume; +} // MUSIC_GetVolume + + +void MUSIC_SetLoopFlag(int32_t loopflag) +{ + UNREFERENCED_PARAMETER(loopflag); +} // MUSIC_SetLoopFlag + + +void MUSIC_Continue(void) +{ +} // MUSIC_Continue + + +void MUSIC_Pause(void) +{ +} // MUSIC_Pause + +int32_t MUSIC_StopSong(void) +{ + if (!g_musicPlayerEnabled) + return MUSIC_Ok; + + if (g_musicPlayerHandle != INVALID_HANDLE_VALUE) + { + g_musicPlayerRestart = 0; // make SIGCHLD handler a no-op + +#ifndef _WIN32 + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 5000000; // sleep 5ms at most + + kill(g_musicPlayerHandle, SIGTERM); + nanosleep(&ts, NULL); + + if (int ret = waitpid(g_musicPlayerHandle, NULL, WNOHANG|WUNTRACED) != g_musicPlayerHandle) + { + if (ret==-1) + initprintf("%s: waitpid: %s\n", __func__, strerror(errno)); + else + { + // we tried to be nice, but no... + kill(g_musicPlayerHandle, SIGKILL); + + initprintf("%s: SIGTERM timed out--trying SIGKILL\n", __func__); + + if (waitpid(g_musicPlayerHandle, NULL, WUNTRACED)==-1) + initprintf("%s: waitpid: %s\n", __func__, strerror(errno)); + } + } +#else + TerminateProcess(g_musicPlayerHandle, 0); +#endif + g_musicPlayerHandle = INVALID_HANDLE_VALUE; + } + + return MUSIC_Ok; +} // MUSIC_StopSong + +static int32_t MUSIC_PlayExternal() +{ +#ifdef _WIN32 + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si,sizeof(si)); + ZeroMemory(&pi,sizeof(pi)); + si.cb = sizeof(si); + + if (!CreateProcess(NULL,g_musicPlayerCommandLine,NULL,NULL,0,0,NULL,NULL,&si,&pi)) + { + initprintf("%s: CreateProcess: %s\n", __func__, windowsGetErrorMessage(GetLastError())); + return MUSIC_Error; + } + else + g_musicPlayerHandle = pi.hProcess; +#else + proc_t pid = vfork(); + + if (pid==-1) // error + { + initprintf("%s: vfork: %s\n", __func__, strerror(errno)); + return MUSIC_Error; + } + else if (pid==0) // child + { + // exec without PATH lookup + if (execv(g_musicPlayerArgv[0], g_musicPlayerArgv) < 0) + { + initprintf("%s: execv: %s\n", __func__, strerror(errno)); + _exit(EXIT_FAILURE); + } + } + else // parent + { + g_musicPlayerHandle = pid; + } +#endif + return MUSIC_Ok; +} + +#ifndef _WIN32 +static void sigchld_handler(int signo) +{ + if (g_musicPlayerHandle <= 0 || !g_musicPlayerRestart || signo != SIGCHLD) + return; + + int status; + + if (waitpid(g_musicPlayerHandle, &status, WUNTRACED)==-1) + initprintf("%s: waitpid: %s\n", __func__, strerror(errno)); + + if (WIFEXITED(status) && WEXITSTATUS(status)==0) + { + // loop ... + MUSIC_PlayExternal(); + } +} +#endif + +int32_t MUSIC_PlaySong(char *song, int32_t songsize, int32_t loopflag, const char *fn /*= nullptr*/) +{ + if (!g_musicPlayerEnabled) + { + errorMessage = "MUSIC_Init: no external player configured!"; + return MUSIC_Error; + } + +#ifndef _WIN32 + static int32_t sigchld_handler_set; + + if (!sigchld_handler_set) + { + struct sigaction sa; + sa.sa_handler=sigchld_handler; + sa.sa_flags=0; + sigemptyset(&sa.sa_mask); + + if (sigaction(SIGCHLD, &sa, NULL)==-1) + initprintf("%s: sigaction: %s\n", __func__, strerror(errno)); + + sigchld_handler_set = 1; + } +#endif + + auto ext = Xstrdup(fn); + auto const c = Bsnprintf(g_musicFileName, sizeof(g_musicFileName), "%s/external%s", Bgethomedir(), strtok(ext,".")); + g_musicFileName[c] = '\0'; + Xfree(ext); + + if (auto fp = buildvfs_fopen_write(g_musicFileName)) + { + buildvfs_fwrite(song, 1, songsize, fp); + buildvfs_fclose(fp); + + g_musicPlayerRestart = loopflag; + g_musicPlayerArgv[g_musicFileNameArgvPos] = g_musicFileName; + + if (int retval = MUSIC_PlayExternal() != MUSIC_Ok) + return retval; + } + else + { + initprintf("%s: fopen: %s\n", __func__, strerror(errno)); + return MUSIC_Error; + } + + return MUSIC_Ok; +} + + +void MUSIC_Update(void) +{ +#ifdef _WIN32 + if (g_musicPlayerHandle == INVALID_HANDLE_VALUE || !g_musicPlayerRestart) + return; + + DWORD exitCode = -1; + + GetExitCodeProcess(g_musicPlayerHandle, &exitCode); + + if (exitCode != STILL_ACTIVE) + MUSIC_PlayExternal(); +#endif +} diff --git a/source/duke3d/src/sounds.cpp b/source/duke3d/src/sounds.cpp index 95758367a..a27e76aae 100644 --- a/source/duke3d/src/sounds.cpp +++ b/source/duke3d/src/sounds.cpp @@ -239,7 +239,7 @@ static int S_PlayMusic(const char *fn) if (!Bmemcmp(MyMusicPtr, "MThd", 4)) { - int32_t retval = MUSIC_PlaySong(MyMusicPtr, MyMusicSize, MUSIC_LoopSong); + int32_t retval = MUSIC_PlaySong(MyMusicPtr, MyMusicSize, MUSIC_LoopSong, fn); if (retval != MUSIC_Ok) {