SRB2/src/win32/win_snd.c

857 lines
22 KiB
C
Raw Normal View History

2014-03-15 16:59:03 +00:00
/// \file
/// \brief FMOD Ex interface for sound
#include "../doomdef.h"
#include "../sounds.h"
#include "../i_sound.h"
#include "../s_sound.h"
#include "../w_wad.h"
#include "../z_zone.h"
#include "../byteptr.h"
#include <fmod.h> // FMOD Ex
#define errcode _errcode
#include <fmod_errors.h>
#undef errcode
#ifdef HAVE_LIBGME
#include "gme/gme.h"
#define GME_TREBLE 5.0
#define GME_BASS 1.0
#ifdef HAVE_PNG /// TODO: compile with zlib support without libpng
#define HAVE_ZLIB
#ifndef _MSC_VER
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#endif
#ifndef _LFS64_LARGEFILE
#define _LFS64_LARGEFILE
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 0
#endif
#include "zlib.h"
#endif
#endif
static FMOD_SYSTEM *fsys;
static FMOD_SOUND *music_stream;
static FMOD_CHANNEL *music_channel;
static UINT8 music_volume, midi_volume, sfx_volume;
static INT32 current_track;
#ifdef HAVE_LIBGME
static Music_Emu *gme;
#endif
//
// SYSTEM
//
// spew console messages for general errors.
static void FMR_Debug(FMOD_RESULT e, int line)
{
if (e != FMOD_OK)
CONS_Alert(CONS_ERROR, "FMOD:%d - %s\n", line, FMOD_ErrorString(e));
}
#define FMR(x) FMR_Debug(x, __LINE__)
// for operations on music_channel ONLY.
// returns false if music was released.
// (in other words, if !FMR_MUSIC(op) return immediately,
// because music_stream and music_channel are no longer valid.)
static boolean FMR_Music_Debug(FMOD_RESULT e, int line)
{
switch(e)
{
case FMOD_ERR_INVALID_HANDLE: // non-looping song ended
case FMOD_ERR_CHANNEL_STOLEN: // song ended and then sfx was played
FMOD_Sound_Release(music_stream);
music_stream = NULL;
return false;
default:
FMR_Debug(e, line);
break;
}
return true;
}
#define FMR_MUSIC(x) FMR_Music_Debug(x, __LINE__)
void I_StartupSound(void)
{
I_Assert(!sound_started);
sound_started = true;
FMR(FMOD_System_Create(&fsys));
FMR(FMOD_System_SetDSPBufferSize(fsys, 44100 / 30, 2));
FMR(FMOD_System_Init(fsys, 64, FMOD_INIT_VOL0_BECOMES_VIRTUAL, NULL));
music_stream = NULL;
#ifdef HAVE_LIBGME
gme = NULL;
#endif
current_track = -1;
}
void I_ShutdownSound(void)
{
I_Assert(sound_started);
sound_started = false;
#ifdef HAVE_LIBGME
if (gme)
gme_delete(gme);
#endif
FMR(FMOD_System_Release(fsys));
}
void I_UpdateSound(void)
{
FMR(FMOD_System_Update(fsys));
}
#ifdef HAVE_LIBGME
// Callback hook to read streaming GME data.
static FMOD_RESULT F_CALLBACK GMEReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen)
{
Music_Emu *emu;
void *emuvoid = NULL;
// get our emu
FMR(FMOD_Sound_GetUserData(sound, &emuvoid));
emu = emuvoid;
// no emu? no play.
if (!emu)
return FMOD_ERR_FILE_EOF;
if (gme_track_ended(emu))
{
// don't delete the primary music stream
if (emu == gme)
return FMOD_ERR_FILE_EOF;
// do delete sfx streams
FMR(FMOD_Sound_SetUserData(sound, NULL));
gme_delete(emu);
return FMOD_ERR_FILE_EOF;
}
// play beautiful musics theme of ancient land.
if (gme_play(emu, datalen/2, data))
return FMOD_ERR_FILE_BAD;
// O.K
return FMOD_OK;
}
#endif
//
// SFX
//
// convert doom format sound (signed 8bit)
// to an fmod format sound (unsigned 8bit)
// and return the FMOD_SOUND.
static inline FMOD_SOUND *ds2fmod(char *stream)
{
FMOD_SOUND *sound;
UINT16 ver;
UINT16 freq;
UINT32 samples;
FMOD_CREATESOUNDEXINFO fmt;
UINT32 i;
UINT8 *p;
// lump header
ver = READUINT16(stream); // sound version format?
if (ver != 3) // It should be 3 if it's a doomsound...
return NULL; // onos! it's not a doomsound!
freq = READUINT16(stream);
samples = READUINT32(stream);
//CONS_Printf("FMT: v%d f%d, s%d, b%d\n", ver, freq, samples, bps);
memset(&fmt, 0, sizeof(FMOD_CREATESOUNDEXINFO));
fmt.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
// convert to unsigned
fmt.format = FMOD_SOUND_FORMAT_PCM8; // 8bit
fmt.length = samples*1; // 1 bps
fmt.numchannels = 1; // mono
fmt.defaultfrequency = freq;
// Make a duplicate of the stream to change format.
p = Z_Malloc(fmt.length, PU_SOUND, NULL);
for (i = 0; i < fmt.length; i++)
p[i] = (UINT8)(stream[i]+0x80); // convert from signed to unsigned
stream = (char *)p;
// Create FMOD_SOUND.
FMR(FMOD_System_CreateSound(fsys, stream, FMOD_CREATESAMPLE|FMOD_OPENMEMORY|FMOD_OPENRAW|FMOD_SOFTWARE|FMOD_LOWMEM, &fmt, &sound));
Z_Free(stream); // FMOD made its own copy, so we don't need this anymore.
return sound;
}
void *I_GetSfx(sfxinfo_t *sfx)
{
FMOD_SOUND *sound;
char *lump;
FMOD_CREATESOUNDEXINFO fmt;
#ifdef HAVE_LIBGME
Music_Emu *emu;
gme_info_t *info;
#endif
if (sfx->lumpnum == LUMPERROR)
sfx->lumpnum = S_GetSfxLumpNum(sfx);
sfx->length = W_LumpLength(sfx->lumpnum);
lump = W_CacheLumpNum(sfx->lumpnum, PU_SOUND);
sound = ds2fmod(lump);
if (sound)
{
Z_Free(lump);
return sound;
}
// It's not a doom sound.
// Try to read it as an FMOD sound?
memset(&fmt, 0, sizeof(FMOD_CREATESOUNDEXINFO));
fmt.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
fmt.length = sfx->length;
#ifdef HAVE_LIBGME
// VGZ format
if ((UINT8)lump[0] == 0x1F
&& (UINT8)lump[1] == 0x8B)
{
#ifdef HAVE_ZLIB
UINT8 *inflatedData;
size_t inflatedLen;
z_stream stream;
int zErr; // Somewhere to handle any error messages zlib tosses out
memset(&stream, 0x00, sizeof (z_stream)); // Init zlib stream
// Begin the inflation process
inflatedLen = *(UINT32 *)(lump + (sfx->length-4)); // Last 4 bytes are the decompressed size, typically
inflatedData = (UINT8 *)Z_Malloc(inflatedLen, PU_SOUND, NULL); // Make room for the decompressed data
stream.total_in = stream.avail_in = sfx->length;
stream.total_out = stream.avail_out = inflatedLen;
stream.next_in = (UINT8 *)lump;
stream.next_out = inflatedData;
zErr = inflateInit2(&stream, 32 + MAX_WBITS);
if (zErr == Z_OK) // We're good to go
{
zErr = inflate(&stream, Z_FINISH);
if (zErr == Z_STREAM_END) {
// Run GME on new data
if (!gme_open_data(inflatedData, inflatedLen, &emu, 44100))
{
Z_Free(inflatedData); // GME supposedly makes a copy for itself, so we don't need this lying around
Z_Free(lump); // We're done with the uninflated lump now, too.
gme_start_track(emu, 0);
gme_track_info(emu, &info, 0);
fmt.format = FMOD_SOUND_FORMAT_PCM16;
fmt.defaultfrequency = 44100;
fmt.numchannels = 2;
fmt.length = ((UINT32)info->play_length * 441 / 10) * 4;
fmt.decodebuffersize = (44100 * 2) / 35;
fmt.pcmreadcallback = GMEReadCallback;
fmt.userdata = emu;
FMR(FMOD_System_CreateSound(fsys, NULL, FMOD_CREATESAMPLE|FMOD_OPENUSER|FMOD_SOFTWARE|FMOD_LOWMEM, &fmt, &sound));
return sound;
}
}
else
{
const char *errorType;
switch (zErr)
{
case Z_ERRNO:
errorType = "Z_ERRNO"; break;
case Z_STREAM_ERROR:
errorType = "Z_STREAM_ERROR"; break;
case Z_DATA_ERROR:
errorType = "Z_DATA_ERROR"; break;
case Z_MEM_ERROR:
errorType = "Z_MEM_ERROR"; break;
case Z_BUF_ERROR:
errorType = "Z_BUF_ERROR"; break;
case Z_VERSION_ERROR:
errorType = "Z_VERSION_ERROR"; break;
default:
errorType = "unknown error";
}
CONS_Alert(CONS_ERROR,"Encountered %s when running inflate: %s\n", errorType, stream.msg);
}
(void)inflateEnd(&stream);
}
else // Hold up, zlib's got a problem
{
const char *errorType;
switch (zErr)
{
case Z_ERRNO:
errorType = "Z_ERRNO"; break;
case Z_STREAM_ERROR:
errorType = "Z_STREAM_ERROR"; break;
case Z_DATA_ERROR:
errorType = "Z_DATA_ERROR"; break;
case Z_MEM_ERROR:
errorType = "Z_MEM_ERROR"; break;
case Z_BUF_ERROR:
errorType = "Z_BUF_ERROR"; break;
case Z_VERSION_ERROR:
errorType = "Z_VERSION_ERROR"; break;
default:
errorType = "unknown error";
}
CONS_Alert(CONS_ERROR,"Encountered %s when running inflateInit: %s\n", errorType, stream.msg);
}
Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up
#else
//CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n");
#endif
}
// Try to read it as a GME sound
else if (!gme_open_data(lump, sfx->length, &emu, 44100))
{
Z_Free(lump);
gme_start_track(emu, 0);
gme_track_info(emu, &info, 0);
fmt.format = FMOD_SOUND_FORMAT_PCM16;
fmt.defaultfrequency = 44100;
fmt.numchannels = 2;
fmt.length = ((UINT32)info->play_length * 441 / 10) * 4;
fmt.decodebuffersize = (44100 * 2) / 35;
fmt.pcmreadcallback = GMEReadCallback;
fmt.userdata = emu;
gme_free_info(info);
FMR(FMOD_System_CreateSound(fsys, NULL, FMOD_CREATESAMPLE|FMOD_OPENUSER|FMOD_SOFTWARE|FMOD_LOWMEM, &fmt, &sound));
return sound;
}
#endif
// Ogg, Mod, Midi, etc.
FMR(FMOD_System_CreateSound(fsys, lump, FMOD_CREATESAMPLE|FMOD_OPENMEMORY|FMOD_SOFTWARE|FMOD_LOWMEM, &fmt, &sound));
Z_Free(lump); // We're done with the lump now, at least.
return sound;
}
void I_FreeSfx(sfxinfo_t *sfx)
{
if (sfx->data)
FMOD_Sound_Release(sfx->data);
sfx->data = NULL;
}
INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority)
{
FMOD_SOUND *sound;
FMOD_CHANNEL *chan;
INT32 i;
float frequency;
sound = (FMOD_SOUND *)S_sfx[id].data;
I_Assert(sound != NULL);
FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, sound, true, &chan));
if (sep == 0)
sep = 1;
FMR(FMOD_Channel_SetVolume(chan, (vol / 255.0) * (sfx_volume / 31.0)));
FMR(FMOD_Channel_SetPan(chan, (sep - 128) / 127.0));
FMR(FMOD_Sound_GetDefaults(sound, &frequency, NULL, NULL, NULL));
FMR(FMOD_Channel_SetFrequency(chan, (pitch / 128.0) * frequency));
FMR(FMOD_Channel_SetPriority(chan, priority));
//UNREFERENCED_PARAMETER(priority);
//FMR(FMOD_Channel_SetPriority(chan, 1 + ((0xff-vol)>>1))); // automatic priority 1 - 128 based on volume (priority 0 is music)
FMR(FMOD_Channel_GetIndex(chan, &i));
FMR(FMOD_Channel_SetPaused(chan, false));
return i;
}
void I_StopSound(INT32 handle)
{
FMOD_CHANNEL *chan;
FMR(FMOD_System_GetChannel(fsys, handle, &chan));
if (music_stream && chan == music_channel)
return;
FMR(FMOD_Channel_Stop(chan));
}
boolean I_SoundIsPlaying(INT32 handle)
{
FMOD_CHANNEL *chan;
FMOD_BOOL playing;
FMOD_RESULT e;
FMR(FMOD_System_GetChannel(fsys, handle, &chan));
if (music_stream && chan == music_channel)
return false;
e = FMOD_Channel_IsPlaying(chan, &playing);
switch(e)
{
case FMOD_ERR_INVALID_HANDLE: // Sound effect finished.
case FMOD_ERR_CHANNEL_STOLEN: // Sound effect interrupted. -- technically impossible due to GetChannel by handle. :(
return false; // therefore, it's not playing anymore.
default:
FMR(e);
break;
}
return (boolean)playing;
}
// seems to never be called on an invalid channel (calls I_SoundIsPlaying first?)
// so I'm not gonna worry about it.
void I_UpdateSoundParams(INT32 handle, UINT8 vol, UINT8 sep, UINT8 pitch)
{
FMOD_CHANNEL *chan;
FMOD_SOUND *sound;
float frequency;
FMR(FMOD_System_GetChannel(fsys, handle, &chan));
FMR(FMOD_Channel_SetVolume(chan, (vol / 255.0) * (sfx_volume / 31.0)));
FMR(FMOD_Channel_SetPan(chan, (sep - 128) / 127.0));
FMR(FMOD_Channel_GetCurrentSound(chan, &sound));
FMR(FMOD_Sound_GetDefaults(sound, &frequency, NULL, NULL, NULL));
FMR(FMOD_Channel_SetFrequency(chan, (pitch / 128.0) * frequency));
//FMR(FMOD_Channel_SetPriority(chan, 1 + ((0xff-vol)>>1))); // automatic priority 1 - 128 based on volume (priority 0 is music)
}
void I_SetSfxVolume(UINT8 volume)
{
// volume is 0 to 31.
sfx_volume = volume;
}
//
// MUSIC
//
musictype_t I_GetMusicType(void)
{
#ifdef HAVE_LIBGME
if (gme)
return MU_GME;
#endif
if (!music_stream)
return MU_NONE;
FMOD_SOUND_TYPE type;
if (FMOD_Sound_GetFormat(music_stream, &type, NULL, NULL, NULL) == FMOD_OK)
{
switch(type)
{
case FMOD_SOUND_TYPE_WAV:
return MU_WAV;
case FMOD_SOUND_TYPE_MOD:
return MU_MOD;
case FMOD_SOUND_TYPE_MID:
return MU_MID;
case FMOD_SOUND_TYPE_OGGVORBIS:
return MU_OGG;
case FMOD_SOUND_TYPE_MP3:
return MU_MP3;
case FMOD_SOUND_TYPE_FLAC:
return MU_FLAC;
default:
return MU_NONE;
}
}
else
return MU_NONE;
}
2014-03-15 16:59:03 +00:00
void I_InitMusic(void)
{
}
void I_ShutdownMusic(void)
{
I_StopSong();
2014-03-15 16:59:03 +00:00
}
void I_PauseSong(void)
2014-03-15 16:59:03 +00:00
{
UNREFERENCED_PARAMETER(handle);
if (music_stream)
FMR_MUSIC(FMOD_Channel_SetPaused(music_channel, true));
}
void I_ResumeSong(void)
2014-03-15 16:59:03 +00:00
{
UNREFERENCED_PARAMETER(handle);
if (music_stream)
FMR_MUSIC(FMOD_Channel_SetPaused(music_channel, false));
}
boolean I_LoadSong(char *data, size_t len)
2014-03-15 16:59:03 +00:00
{
char *data;
size_t len;
FMOD_CREATESOUNDEXINFO fmt;
lumpnum_t lumpnum = W_CheckNumForName(va("O_%s",musicname));
if (lumpnum == LUMPERROR)
{
lumpnum = W_CheckNumForName(va("D_%s",musicname));
if (lumpnum == LUMPERROR)
return false;
}
data = (char *)W_CacheLumpNum(lumpnum, PU_MUSIC);
len = W_LumpLength(lumpnum);
memset(&fmt, 0, sizeof(FMOD_CREATESOUNDEXINFO));
fmt.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
#ifdef HAVE_LIBGME
if ((UINT8)data[0] == 0x1F
&& (UINT8)data[1] == 0x8B)
{
#ifdef HAVE_ZLIB
UINT8 *inflatedData;
size_t inflatedLen;
z_stream stream;
int zErr; // Somewhere to handle any error messages zlib tosses out
memset(&stream, 0x00, sizeof (z_stream)); // Init zlib stream
// Begin the inflation process
inflatedLen = *(UINT32 *)(data + (len-4)); // Last 4 bytes are the decompressed size, typically
inflatedData = (UINT8 *)Z_Calloc(inflatedLen, PU_MUSIC, NULL); // Make room for the decompressed data
stream.total_in = stream.avail_in = len;
stream.total_out = stream.avail_out = inflatedLen;
stream.next_in = (UINT8 *)data;
stream.next_out = inflatedData;
zErr = inflateInit2(&stream, 32 + MAX_WBITS);
if (zErr == Z_OK) // We're good to go
{
zErr = inflate(&stream, Z_FINISH);
if (zErr == Z_STREAM_END) {
// Run GME on new data
if (!gme_open_data(inflatedData, inflatedLen, &gme, 44100))
{
gme_equalizer_t gmeq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0};
Z_Free(inflatedData); // GME supposedly makes a copy for itself, so we don't need this lying around
Z_Free(data); // We don't need this, either.
gme_start_track(gme, 0);
current_track = 0;
gme_set_equalizer(gme,&gmeq);
fmt.format = FMOD_SOUND_FORMAT_PCM16;
fmt.defaultfrequency = 44100;
fmt.numchannels = 2;
fmt.length = -1;
fmt.decodebuffersize = (44100 * 2) / 35;
fmt.pcmreadcallback = GMEReadCallback;
fmt.userdata = gme;
FMR(FMOD_System_CreateStream(fsys, NULL, FMOD_OPENUSER | (looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream));
FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
FMR(FMOD_Channel_SetPriority(music_channel, 0));
return true;
}
}
else
{
const char *errorType;
switch (zErr)
{
case Z_ERRNO:
errorType = "Z_ERRNO"; break;
case Z_STREAM_ERROR:
errorType = "Z_STREAM_ERROR"; break;
case Z_DATA_ERROR:
errorType = "Z_DATA_ERROR"; break;
case Z_MEM_ERROR:
errorType = "Z_MEM_ERROR"; break;
case Z_BUF_ERROR:
errorType = "Z_BUF_ERROR"; break;
case Z_VERSION_ERROR:
errorType = "Z_VERSION_ERROR"; break;
default:
errorType = "unknown error";
}
CONS_Alert(CONS_ERROR,"Encountered %s when running inflate: %s\n", errorType, stream.msg);
}
(void)inflateEnd(&stream);
}
else // Hold up, zlib's got a problem
{
const char *errorType;
switch (zErr)
{
case Z_ERRNO:
errorType = "Z_ERRNO"; break;
case Z_STREAM_ERROR:
errorType = "Z_STREAM_ERROR"; break;
case Z_DATA_ERROR:
errorType = "Z_DATA_ERROR"; break;
case Z_MEM_ERROR:
errorType = "Z_MEM_ERROR"; break;
case Z_BUF_ERROR:
errorType = "Z_BUF_ERROR"; break;
case Z_VERSION_ERROR:
errorType = "Z_VERSION_ERROR"; break;
default:
errorType = "unknown error";
}
CONS_Alert(CONS_ERROR,"Encountered %s when running inflateInit: %s\n", errorType, stream.msg);
}
Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up
#else
//CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n");
#endif
}
else if (!gme_open_data(data, len, &gme, 44100))
{
gme_equalizer_t gmeq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0};
Z_Free(data); // We don't need this anymore.
gme_set_equalizer(gme,&gmeq);
fmt.format = FMOD_SOUND_FORMAT_PCM16;
fmt.defaultfrequency = 44100;
fmt.numchannels = 2;
fmt.length = -1;
fmt.decodebuffersize = (44100 * 2) / 35;
fmt.pcmreadcallback = GMEReadCallback;
fmt.userdata = gme;
FMR(FMOD_System_CreateStream(fsys, NULL, FMOD_OPENUSER | (looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream));
return true;
}
#endif
fmt.length = len;
FMOD_RESULT e = FMOD_System_CreateStream(fsys, data, FMOD_OPENMEMORY_POINT|(looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream);
if (e != FMOD_OK)
2014-03-15 16:59:03 +00:00
{
if (e == FMOD_ERR_FORMAT)
CONS_Alert(CONS_WARNING, "Failed to play music lump %s due to invalid format.\n", W_CheckNameForNum(lumpnum));
else
FMR(e);
return false;
2014-03-15 16:59:03 +00:00
}
// Try to find a loop point in streaming music formats (ogg, mp3)
FMOD_RESULT e;
FMOD_TAG tag;
unsigned int loopstart, loopend;
// A proper LOOPPOINT is its own tag, stupid.
e = FMOD_Sound_GetTag(music_stream, "LOOPPOINT", 0, &tag);
if (e != FMOD_ERR_TAGNOTFOUND)
2014-03-15 16:59:03 +00:00
{
FMR(e);
loopstart = atoi((char *)tag.data); // assumed to be a string data tag.
FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_PCM, &loopend, FMOD_TIMEUNIT_PCM));
if (loopstart > 0)
FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM));
return true;
}
// todo
// if(music type == MIDI)
// {
// FMR(FMOD_Sound_SetMode(music_stream, FMOD_LOOP_NORMAL));
// return true;
// }
2014-03-15 16:59:03 +00:00
// Use LOOPMS for time in miliseconds.
e = FMOD_Sound_GetTag(music_stream, "LOOPMS", 0, &tag);
if (e != FMOD_ERR_TAGNOTFOUND)
{
FMR(e);
loopstart = atoi((char *)tag.data); // assumed to be a string data tag.
FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_MS, &loopend, FMOD_TIMEUNIT_PCM));
if (loopstart > 0)
FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_MS, loopend, FMOD_TIMEUNIT_PCM));
return true;
}
// Try to fetch it from the COMMENT tag, like A.J. Freda
e = FMOD_Sound_GetTag(music_stream, "COMMENT", 0, &tag);
if (e != FMOD_ERR_TAGNOTFOUND)
{
char *loopText;
// Handle any errors that arose, first
FMR(e);
// Figure out where the number starts
loopText = strstr((char *)tag.data,"LOOPPOINT=");
if (loopText != NULL)
2014-03-15 16:59:03 +00:00
{
// Skip the "LOOPPOINT=" part.
loopText += 10;
// Convert it to our looppoint
// FMOD seems to ensure the tag is properly NULL-terminated.
// atoi will stop when it reaches anything that's not a number.
loopstart = atoi(loopText);
// Now do the rest like above
2014-03-15 16:59:03 +00:00
FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_PCM, &loopend, FMOD_TIMEUNIT_PCM));
if (loopstart > 0)
FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM));
}
return true;
2014-03-15 16:59:03 +00:00
}
// No special loop point
2014-03-15 16:59:03 +00:00
return true;
}
void I_SetDigMusicVolume(UINT8 volume)
{
// volume is 0 to 31.
music_volume = volume;
if (I_GetMusicType() != MU_MID && music_stream)
2014-03-15 16:59:03 +00:00
FMR_MUSIC(FMOD_Channel_SetVolume(music_channel, volume / 31.0));
}
boolean I_SetSongSpeed(float speed)
{
FMOD_RESULT e;
float frequency;
if (!music_stream)
return false;
if (speed > 250.0f)
speed = 250.0f; //limit speed up to 250x
#ifdef HAVE_LIBGME
// Try to set GME speed
if (gme)
{
gme_set_tempo(gme, speed);
return true;
}
#endif
// Try to set Mod/Midi speed
e = FMOD_Sound_SetMusicSpeed(music_stream, speed);
if (e == FMOD_ERR_FORMAT)
{
// Just change pitch instead for Ogg/etc.
FMR(FMOD_Sound_GetDefaults(music_stream, &frequency, NULL, NULL, NULL));
FMR_MUSIC(FMOD_Channel_SetFrequency(music_channel, speed*frequency));
}
else
FMR_MUSIC(e);
return true;
}
boolean I_SetSongTrack(INT32 track)
{
if (track != current_track) // If the track's already playing, then why bother?
{
FMOD_RESULT e;
#ifdef HAVE_LIBGME
// If the specified track is within the number of tracks playing, then change it
if (gme)
{
if (track >= 0
&& track < gme_track_count(gme))
{
gme_err_t gme_e = gme_start_track(gme,track);
if (gme_e == NULL)
{
current_track = track;
return true;
}
else
CONS_Alert(CONS_ERROR, "Encountered GME error: %s\n", gme_e);
}
return false;
}
#endif // HAVE_LIBGME
// Try to set it via FMOD
e = FMOD_Channel_SetPosition(music_channel, (UINT32)track, FMOD_TIMEUNIT_MODORDER);
if (e == FMOD_OK) // We good
{
current_track = track;
return true;
}
else if (e == FMOD_ERR_UNSUPPORTED // Only music modules, numbnuts!
|| e == FMOD_ERR_INVALID_POSITION) // Out-of-bounds!
return false;
else // Congrats, you horribly broke it somehow
{
FMR_MUSIC(e);
return false;
}
}
return false;
}
//
// Fuck MIDI. ... Okay fine, you can have your silly D_-only mode.
//
void I_SetMIDIMusicVolume(UINT8 volume)
{
// volume is 0 to 31.
midi_volume = volume;
if (I_GetMusicType() != MU_MID && music_stream)
2014-03-15 16:59:03 +00:00
FMR_MUSIC(FMOD_Channel_SetVolume(music_channel, volume / 31.0));
}
boolean I_PlaySong(boolean looping)
2014-03-15 16:59:03 +00:00
{
#ifdef HAVE_LIBGME
if (gme)
2014-03-15 16:59:03 +00:00
{
gme_start_track(gme, 0);
current_track = 0;
2014-03-15 16:59:03 +00:00
FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
FMR(FMOD_Channel_SetPriority(music_channel, 0));
return true;
2014-03-15 16:59:03 +00:00
}
#endif
FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel));
if (I_GetMusicType() != MU_MID)
FMR(FMOD_Channel_SetVolume(music_channel, midi_volume / 31.0));
else
FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
FMR(FMOD_Channel_SetPriority(music_channel, 0));
current_track = 0;
2014-03-15 16:59:03 +00:00
return true;
}
void I_StopSong(void)
2014-03-15 16:59:03 +00:00
{
#ifdef HAVE_LIBGME
if (gme)
gme_delete(gme);
gme = NULL;
#endif
current_track = -1;
I_UnloadSong();
2014-03-15 16:59:03 +00:00
}
void I_UnloadSong(void)
2014-03-15 16:59:03 +00:00
{
UNREFERENCED_PARAMETER(handle);
if (music_stream)
FMR(FMOD_Sound_Release(music_stream));
music_stream = NULL;
}