mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-10 23:02:03 +00:00
Use SDL audiolib driver on Windows
This shitcans the SDL_mixer requirement but leaves platforms other than Windows without built-in MIDI playback capability until Nuke.YKT's OPL3 emulator is merged. This also reworks sdlmusic.cpp into music_external.cpp, including an untested port of the code to Windows. git-svn-id: https://svn.eduke32.com/eduke32@8214 1a8010ca-5511-0410-912e-c29ae57300e0 # Conflicts: # GNUmakefile # platform/Windows/eduke32.vcxproj # platform/Windows/props/build_common.props # source/audiolib/src/sdlmusic.cpp # Conflicts: # Common.mak # GNUmakefile # platform/Windows/audiolib.vcxproj # platform/Windows/eduke32.vcxproj # platform/Windows/eduke32.vcxproj.filters # platform/Windows/props/build_common.props # source/audiolib/src/driver_sdl.cpp # source/audiolib/src/sdlmusic.cpp # source/build/include/vfs.h # source/build/include/winbits.h # source/build/src/winbits.cpp
This commit is contained in:
parent
c0b7745013
commit
d6e52ed526
7 changed files with 503 additions and 681 deletions
|
@ -37,36 +37,51 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
|
||||||
extern int32_t MUSIC_ErrorCode;
|
extern int32_t MUSIC_ErrorCode;
|
||||||
|
|
||||||
enum MUSIC_ERRORS
|
enum MUSIC_ERRORS
|
||||||
{
|
{
|
||||||
MUSIC_Warning = -2,
|
MUSIC_Error = -1,
|
||||||
MUSIC_Error = -1,
|
MUSIC_Ok = 0,
|
||||||
MUSIC_Ok = 0,
|
MUSIC_FileError,
|
||||||
MUSIC_MidiError,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t tickposition;
|
uint32_t tickposition;
|
||||||
uint32_t milliseconds;
|
uint32_t milliseconds;
|
||||||
uint32_t measure;
|
uint32_t measure;
|
||||||
uint32_t beat;
|
uint32_t beat;
|
||||||
uint32_t tick;
|
uint32_t tick;
|
||||||
} songposition;
|
} songposition;
|
||||||
|
|
||||||
|
|
||||||
#define MUSIC_LoopSong ( 1 == 1 )
|
#define MUSIC_LoopSong ( 1 == 1 )
|
||||||
#define MUSIC_PlayOnce ( !MUSIC_LoopSong )
|
#define MUSIC_PlayOnce ( !MUSIC_LoopSong )
|
||||||
|
|
||||||
const char *MUSIC_ErrorString( int32_t ErrorNumber );
|
#define MUSIC_SetErrorCode(status) MUSIC_ErrorCode = (status);
|
||||||
int32_t MUSIC_Init( int32_t SoundCard, int32_t Address );
|
|
||||||
int32_t MUSIC_Shutdown( void );
|
extern char const *errorMessage;
|
||||||
void MUSIC_SetVolume( int32_t volume );
|
|
||||||
int32_t MUSIC_GetVolume( void );
|
static inline const char *MUSIC_ErrorString(int32_t ErrorNumber)
|
||||||
void MUSIC_SetLoopFlag( int32_t loopflag );
|
{
|
||||||
void MUSIC_Continue( void );
|
switch (ErrorNumber)
|
||||||
void MUSIC_Pause( void );
|
{
|
||||||
int32_t MUSIC_StopSong( void );
|
case MUSIC_Error: return errorMessage;
|
||||||
int32_t MUSIC_PlaySong( char *song, int32_t songsize, int32_t loopflag );
|
case MUSIC_Ok: return "Music ok.";
|
||||||
void MUSIC_Update(void);
|
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
|
#endif
|
||||||
|
|
|
@ -22,13 +22,13 @@
|
||||||
* libSDL output driver for MultiVoc
|
* libSDL output driver for MultiVoc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define NEED_SDL_MIXER
|
|
||||||
#include "driver_sdl.h"
|
#include "driver_sdl.h"
|
||||||
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "multivoc.h"
|
#include "multivoc.h"
|
||||||
#include "mutex.h"
|
#include "mutex.h"
|
||||||
#include "sdl_inc.h"
|
#include "sdl_inc.h"
|
||||||
|
#include "vfs.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SDLErr_Warning = -2,
|
SDLErr_Warning = -2,
|
||||||
|
@ -42,33 +42,26 @@ enum {
|
||||||
static int32_t ErrorCode = SDLErr_Ok;
|
static int32_t ErrorCode = SDLErr_Ok;
|
||||||
static int32_t Initialised;
|
static int32_t Initialised;
|
||||||
static int32_t Playing;
|
static int32_t Playing;
|
||||||
static int32_t StartedSDL = -1;
|
static uint32_t StartedSDL;
|
||||||
|
|
||||||
static char *MixBuffer;
|
static char *MixBuffer;
|
||||||
static int32_t MixBufferSize;
|
static int32_t MixBufferSize;
|
||||||
static int32_t MixBufferCount;
|
static int32_t MixBufferCount;
|
||||||
static int32_t MixBufferCurrent;
|
static int32_t MixBufferCurrent;
|
||||||
static int32_t MixBufferUsed;
|
static int32_t MixBufferUsed;
|
||||||
static void ( *MixCallBack )( void );
|
static void (*MixCallBack)(void);
|
||||||
|
|
||||||
static Mix_Chunk *DummyChunk;
|
#if (SDL_MAJOR_VERSION == 2)
|
||||||
static uint8_t *DummyBuffer;
|
static SDL_AudioDeviceID audio_dev;
|
||||||
static int32_t InterruptsDisabled;
|
#endif
|
||||||
static mutex_t EffectFence;
|
|
||||||
|
|
||||||
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;
|
char *sptr;
|
||||||
|
|
||||||
UNREFERENCED_PARAMETER(chan);
|
|
||||||
UNREFERENCED_PARAMETER(udata);
|
|
||||||
|
|
||||||
if (!MixBuffer || !MixCallBack)
|
|
||||||
return;
|
|
||||||
|
|
||||||
mutex_lock(&EffectFence);
|
|
||||||
|
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
if (MixBufferUsed == MixBufferSize) {
|
if (MixBufferUsed == MixBufferSize) {
|
||||||
MixCallBack();
|
MixCallBack();
|
||||||
|
@ -90,16 +83,13 @@ static void fillData(int chan, void *ptr, int remaining, void *udata)
|
||||||
|
|
||||||
memcpy(ptr, sptr, len);
|
memcpy(ptr, sptr, len);
|
||||||
|
|
||||||
ptr = (void *)((uintptr_t)(ptr) + len);
|
ptr += len;
|
||||||
MixBufferUsed += len;
|
MixBufferUsed += len;
|
||||||
remaining -= len;
|
remaining -= len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&EffectFence);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32_t SDLDrv_GetError(void)
|
int32_t SDLDrv_GetError(void)
|
||||||
{
|
{
|
||||||
return ErrorCode;
|
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)
|
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);
|
UNREFERENCED_PARAMETER(initdata);
|
||||||
|
|
||||||
|
Uint32 inited;
|
||||||
|
int err = 0;
|
||||||
|
SDL_AudioSpec spec, actual;
|
||||||
|
|
||||||
if (Initialised) {
|
if (Initialised) {
|
||||||
SDLDrv_PCM_Shutdown();
|
SDLDrv_PCM_Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
inited = SDL_WasInit(SDL_INIT_EVERYTHING);
|
inited = SDL_WasInit(SDL_INIT_AUDIO);
|
||||||
|
|
||||||
if (inited == 0) {
|
if (!(inited & SDL_INIT_AUDIO)) {
|
||||||
// nothing was initialised
|
|
||||||
err = SDL_Init(SDL_INIT_AUDIO);
|
|
||||||
StartedSDL = 0;
|
|
||||||
} else if (!(inited & SDL_INIT_AUDIO)) {
|
|
||||||
err = SDL_InitSubSystem(SDL_INIT_AUDIO);
|
err = SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||||
StartedSDL = 1;
|
StartedSDL = SDL_WasInit(SDL_INIT_AUDIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
@ -169,51 +153,76 @@ int32_t SDLDrv_PCM_Init(int32_t *mixrate, int32_t *numchannels, void * initdata)
|
||||||
return SDLErr_Error;
|
return SDLErr_Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunksize = 512;
|
int chunksize = 512;
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
chunksize = droidinfo.audio_buffer_size;
|
chunksize = droidinfo.audio_buffer_size;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (*mixrate >= 16000) chunksize *= 2;
|
spec.freq = *mixrate;
|
||||||
if (*mixrate >= 32000) chunksize *= 2;
|
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;
|
ErrorCode = SDLErr_OpenAudio;
|
||||||
return SDLErr_Error;
|
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;
|
for (int i=0;drivername[i] != 0;++i)
|
||||||
int intnumchannels = *numchannels;
|
drivername[i] = toupperlookup[drivername[i]];
|
||||||
if (Mix_QuerySpec(&intmixrate, &fmt, &intnumchannels))
|
|
||||||
{
|
MV_Printf("SDL %s driver on %s\n", drivername ? drivername : "(error)", SDL_GetAudioDeviceName(0, 0));
|
||||||
if (fmt == AUDIO_U8 || fmt == AUDIO_S8)
|
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;
|
ErrorCode = SDLErr_OpenAudio;
|
||||||
return SDLErr_Error;
|
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);
|
if (err)
|
||||||
|
{
|
||||||
// channel 0 and 1 are actual sounds
|
SDL_CloseAudio();
|
||||||
// dummy channel 2 runs our fillData() callback as an effect
|
return SDLErr_Error;
|
||||||
Mix_RegisterEffect(2, fillData, nullptr, nullptr);
|
}
|
||||||
|
|
||||||
DummyBuffer = (uint8_t *) calloc(1, chunksize);
|
|
||||||
|
|
||||||
DummyChunk = Mix_QuickLoad_RAW(DummyBuffer, chunksize);
|
|
||||||
|
|
||||||
Mix_PlayChannel(2, DummyChunk, -1);
|
|
||||||
|
|
||||||
Initialised = 1;
|
Initialised = 1;
|
||||||
|
|
||||||
return SDLErr_Ok;
|
return SDLErr_Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,25 +230,11 @@ void SDLDrv_PCM_Shutdown(void)
|
||||||
{
|
{
|
||||||
if (!Initialised)
|
if (!Initialised)
|
||||||
return;
|
return;
|
||||||
else Mix_HaltChannel(-1);
|
|
||||||
|
|
||||||
if (DummyChunk != nullptr)
|
if (StartedSDL)
|
||||||
{
|
SDL_QuitSubSystem(StartedSDL);
|
||||||
Mix_FreeChunk(DummyChunk);
|
|
||||||
DummyChunk = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
DO_FREE_AND_NULL(DummyBuffer);
|
StartedSDL = 0;
|
||||||
|
|
||||||
Mix_CloseAudio();
|
|
||||||
|
|
||||||
if (StartedSDL > 0) {
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
|
||||||
} else if (StartedSDL == 0) {
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
StartedSDL = -1;
|
|
||||||
Initialised = 0;
|
Initialised = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,8 +260,11 @@ int32_t SDLDrv_PCM_BeginPlayback(char *BufferStart, int32_t BufferSize,
|
||||||
// prime the buffer
|
// prime the buffer
|
||||||
MixCallBack();
|
MixCallBack();
|
||||||
|
|
||||||
Mix_Resume(-1);
|
#if (SDL_MAJOR_VERSION == 2)
|
||||||
|
SDL_PauseAudioDevice(audio_dev, 0);
|
||||||
|
#else
|
||||||
|
SDL_PauseAudio(0);
|
||||||
|
#endif
|
||||||
Playing = 1;
|
Playing = 1;
|
||||||
|
|
||||||
return SDLErr_Ok;
|
return SDLErr_Ok;
|
||||||
|
@ -278,23 +276,29 @@ void SDLDrv_PCM_StopPlayback(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mix_Pause(-1);
|
#if (SDL_MAJOR_VERSION == 2)
|
||||||
|
SDL_PauseAudioDevice(audio_dev, 1);
|
||||||
|
#else
|
||||||
|
SDL_PauseAudio(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
Playing = 0;
|
Playing = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLDrv_PCM_Lock(void)
|
void SDLDrv_PCM_Lock(void)
|
||||||
{
|
{
|
||||||
if (InterruptsDisabled++)
|
#if (SDL_MAJOR_VERSION == 2)
|
||||||
return;
|
SDL_LockAudioDevice(audio_dev);
|
||||||
|
#else
|
||||||
mutex_lock(&EffectFence);
|
SDL_LockAudio();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLDrv_PCM_Unlock(void)
|
void SDLDrv_PCM_Unlock(void)
|
||||||
{
|
{
|
||||||
if (--InterruptsDisabled)
|
#if (SDL_MAJOR_VERSION == 2)
|
||||||
return;
|
SDL_UnlockAudioDevice(audio_dev);
|
||||||
|
#else
|
||||||
mutex_unlock(&EffectFence);
|
SDL_UnlockAudio();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -581,7 +581,9 @@ int32_t MV_GetPosition(int32_t handle, int32_t *position)
|
||||||
#ifdef HAVE_FLAC
|
#ifdef HAVE_FLAC
|
||||||
case FMT_FLAC: *position = MV_GetFLACPosition(voice); break;
|
case FMT_FLAC: *position = MV_GetFLACPosition(voice); break;
|
||||||
#endif
|
#endif
|
||||||
case FMT_XA: *position = MV_GetXAPosition(voice); break;
|
case FMT_XA: *position = MV_GetXAPosition(voice);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
#ifdef HAVE_XMP
|
#ifdef HAVE_XMP
|
||||||
case FMT_XMP: *position = MV_GetXMPPosition(voice); break;
|
case FMT_XMP: *position = MV_GetXMPPosition(voice); break;
|
||||||
#endif
|
#endif
|
||||||
|
@ -607,7 +609,9 @@ int32_t MV_SetPosition(int32_t handle, int32_t position)
|
||||||
#ifdef HAVE_FLAC
|
#ifdef HAVE_FLAC
|
||||||
case FMT_FLAC: MV_SetFLACPosition(voice, position); break;
|
case FMT_FLAC: MV_SetFLACPosition(voice, position); break;
|
||||||
#endif
|
#endif
|
||||||
case FMT_XA: MV_SetXAPosition(voice, position); break;
|
case FMT_XA: MV_SetXAPosition(voice, position);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
#ifdef HAVE_XMP
|
#ifdef HAVE_XMP
|
||||||
case FMT_XMP: MV_SetXMPPosition(voice, position); break;
|
case FMT_XMP: MV_SetXMPPosition(voice, position); break;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -30,29 +30,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
int32_t MUSIC_SoundDevice = -1;
|
int32_t MUSIC_SoundDevice = -1;
|
||||||
int32_t MUSIC_ErrorCode = MUSIC_Ok;
|
int32_t MUSIC_ErrorCode = MUSIC_Ok;
|
||||||
|
char const *errorMessage;
|
||||||
static midifuncs MUSIC_MidiFunctions;
|
static midifuncs MUSIC_MidiFunctions;
|
||||||
|
|
||||||
int32_t MUSIC_InitMidi(int32_t card, midifuncs *Funcs, int32_t Address);
|
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)
|
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(songsize);
|
||||||
|
UNREFERENCED_PARAMETER(fn);
|
||||||
|
|
||||||
MUSIC_SetErrorCode(MUSIC_Ok)
|
MUSIC_SetErrorCode(MUSIC_Ok)
|
||||||
|
|
||||||
if (MIDI_PlaySong(song, loopflag) != MIDI_Ok)
|
if (MIDI_PlaySong(song, loopflag) != MIDI_Ok)
|
||||||
{
|
{
|
||||||
MUSIC_SetErrorCode(MUSIC_MidiError);
|
MUSIC_SetErrorCode(MUSIC_FileError);
|
||||||
return MUSIC_Warning;
|
return MUSIC_Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MUSIC_Ok;
|
return MUSIC_Ok;
|
||||||
|
|
|
@ -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 <signal.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
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<numargs+1; i++)
|
|
||||||
initprintf(" %s\n", external_midi_argv[i]);
|
|
||||||
initprintf("----\n");
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
music_initialized = 1;
|
|
||||||
return MUSIC_Ok;
|
|
||||||
|
|
||||||
fallback:
|
|
||||||
initprintf("Error setting music command, falling back to timidity.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
static const char *s[] = { "/etc/timidity.cfg", "/etc/timidity/timidity.cfg", "/etc/timidity/freepats.cfg" };
|
|
||||||
FILE *fp;
|
|
||||||
int32_t i;
|
|
||||||
|
|
||||||
for (i = ARRAY_SIZE(s)-1; 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)
|
|
||||||
{}
|
|
360
source/duke3d/src/music_external.cpp
Normal file
360
source/duke3d/src/music_external.cpp
Normal file
|
@ -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 <signal.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
}
|
|
@ -239,7 +239,7 @@ static int S_PlayMusic(const char *fn)
|
||||||
|
|
||||||
if (!Bmemcmp(MyMusicPtr, "MThd", 4))
|
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)
|
if (retval != MUSIC_Ok)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue