Kart-Public/src/sdl12/sdl_sound.c
Sryder 6184f91dd3 Add an int to I_PlaySound to tell an interface which channel number SRB2 is using.
I've voided this out on other sound interfaces than SDL Mixer ones because I'm both not sure whether they need it, and not sure how to make them work with it if they do.
2018-10-13 23:01:11 +01:00

2031 lines
49 KiB
C

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// The source 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.
//-----------------------------------------------------------------------------
/// \file
/// \brief SDL interface for sound
#include <math.h>
#include "../doomdef.h"
#ifdef _MSC_VER
#pragma warning(disable : 4214 4244)
#endif
#if defined(HAVE_SDL) && SOUND==SOUND_SDL
#include "SDL.h"
#ifdef _MSC_VER
#pragma warning(default : 4214 4244)
#endif
#ifdef HAVE_MIXER
#include "SDL_mixer.h"
/* This is the version number macro for the current SDL_mixer version: */
#ifndef SDL_MIXER_COMPILEDVERSION
#define SDL_MIXER_COMPILEDVERSION \
SDL_VERSIONNUM(MIX_MAJOR_VERSION, MIX_MINOR_VERSION, MIX_PATCHLEVEL)
#endif
/* This macro will evaluate to true if compiled with SDL_mixer at least X.Y.Z */
#ifndef SDL_MIXER_VERSION_ATLEAST
#define SDL_MIXER_VERSION_ATLEAST(X, Y, Z) \
(SDL_MIXER_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
#endif
#else
#define MIX_CHANNELS 8
#endif
#if defined (_WIN32) && !defined (_WIN32_WCE) && !defined (_XBOX)
#include <direct.h>
#elif defined (__GNUC__)
#include <unistd.h>
#endif
#include "../z_zone.h"
#include "../m_swap.h"
#include "../i_system.h"
#include "../i_sound.h"
#include "../m_argv.h"
#include "../m_misc.h"
#include "../w_wad.h"
#include "../screen.h" //vid.WndParent
#include "../doomdef.h"
#include "../doomstat.h"
#include "../s_sound.h"
#include "../d_main.h"
#ifdef HW3SOUND
#include "../hardware/hw3dsdrv.h"
#include "../hardware/hw3sound.h"
#include "hwsym_sdl.h"
#endif
#ifdef HAVE_LIBGME
#include "gme/gme.h"
#endif
// The number of internal mixing channels,
// the samples calculated for each mixing step,
// the size of the 16bit, 2 hardware channel (stereo)
// mixing buffer, and the samplerate of the raw data.
// Needed for calling the actual sound output.
#if defined (_WIN32_WCE) || defined (DC) || defined (PSP) || defined(GP2X)
#define NUM_CHANNELS MIX_CHANNELS
#else
#define NUM_CHANNELS MIX_CHANNELS*4
#endif
#define INDEXOFSFX(x) ((sfxinfo_t *)x - S_sfx)
#if defined (_WIN32_WCE) || defined (DC) || defined (PSP)
static Uint16 samplecount = 512; //Alam: .5KB samplecount at 11025hz is 46.439909297052154195011337868481ms of buffer
#elif defined(GP2X)
static Uint16 samplecount = 128;
#else
static Uint16 samplecount = 1024; //Alam: 1KB samplecount at 22050hz is 46.439909297052154195011337868481ms of buffer
#endif
typedef struct chan_struct
{
// The channel data pointers, start and end.
Uint8 *data; //static unsigned char *channels[NUM_CHANNELS];
Uint8 *end; //static unsigned char *channelsend[NUM_CHANNELS];
// pitch
Uint32 realstep; // The channel step amount...
Uint32 step; //static UINT32 channelstep[NUM_CHANNELS];
Uint32 stepremainder; //static UINT32 channelstepremainder[NUM_CHANNELS];
Uint32 samplerate; // ... and a 0.16 bit remainder of last step.
// Time/gametic that the channel started playing,
// used to determine oldest, which automatically
// has lowest priority.
tic_t starttic; //static INT32 channelstart[NUM_CHANNELS];
// The sound handle, determined on registration,
// used to unregister/stop/modify,
INT32 handle; //static INT32 channelhandles[NUM_CHANNELS];
// SFX id of the playing sound effect.
void *id; // Used to catch duplicates (like chainsaw).
sfxenum_t sfxid; //static INT32 channelids[NUM_CHANNELS];
INT32 vol; //the channel volume
INT32 sep; //the channel pan
// Hardware left and right channel volume lookup.
Sint16* leftvol_lookup; //static INT32 *channelleftvol_lookup[NUM_CHANNELS];
Sint16* rightvol_lookup; //static INT32 *channelrightvol_lookup[NUM_CHANNELS];
} chan_t;
static chan_t channels[NUM_CHANNELS];
// Pitch to stepping lookup
static INT32 steptable[256];
// Volume lookups.
static Sint16 vol_lookup[128 * 256];
UINT8 sound_started = false;
static SDL_mutex *Snd_Mutex = NULL;
//SDL's Audio
static SDL_AudioSpec audio;
static SDL_bool musicStarted = SDL_FALSE;
#ifdef HAVE_MIXER
static SDL_mutex *Msc_Mutex = NULL;
/* FIXME: Make this file instance-specific */
#ifdef _arch_dreamcast
#define MIDI_PATH "/ram"
#elif defined(GP2X)
#define MIDI_PATH "/mnt/sd/srb2"
#define MIDI_PATH2 "/tmp/mnt/sd/srb2"
#else
#define MIDI_PATH srb2home
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
#define MIDI_PATH2 "/tmp"
#endif
#endif
#define MIDI_TMPFILE "srb2music"
#define MIDI_TMPFILE2 "srb2wav"
static INT32 musicvol = 62;
#if SDL_MIXER_VERSION_ATLEAST(1,2,2)
#define MIXER_POS //Mix_FadeInMusicPos in 1.2.2+
static void SDLCALL I_FinishMusic(void);
static double loopstartDig = 0.0l;
static SDL_bool loopingDig = SDL_FALSE;
static SDL_bool canlooping = SDL_TRUE;
#endif
#if SDL_MIXER_VERSION_ATLEAST(1,2,7)
#define USE_RWOPS // ok, USE_RWOPS is in here
#if defined (DC) || defined (_WIN32_WCE) || defined (_XBOX) //|| defined(_WIN32) || defined(GP2X)
#undef USE_RWOPS
#endif
#endif
#if SDL_MIXER_VERSION_ATLEAST(1,2,10)
//#define MIXER_INIT
#endif
#ifdef USE_RWOPS
static void * Smidi[2] = { NULL, NULL };
static SDL_bool canuseRW = SDL_TRUE;
#endif
static const char *fmidi[2] = { MIDI_TMPFILE, MIDI_TMPFILE2};
static const INT32 MIDIfade = 500;
static const INT32 Digfade = 0;
static Mix_Music *music[2] = { NULL, NULL };
#endif
typedef struct srb2audio_s {
void *userdata;
#ifdef HAVE_LIBGME
Music_Emu *gme_emu;
UINT8 gme_pause;
UINT8 gme_loop;
#endif
} srb2audio_t;
static srb2audio_t localdata;
static void Snd_LockAudio(void) //Alam: Lock audio data and uninstall audio callback
{
if (Snd_Mutex) SDL_LockMutex(Snd_Mutex);
else if (nosound) return;
else if (nomidimusic && nodigimusic
#ifdef HW3SOUND
&& hws_mode == HWS_DEFAULT_MODE
#endif
) SDL_LockAudio();
#ifdef HAVE_MIXER
else if (musicStarted) Mix_SetPostMix(NULL, NULL);
#endif
}
static void Snd_UnlockAudio(void) //Alam: Unlock audio data and reinstall audio callback
{
if (Snd_Mutex) SDL_UnlockMutex(Snd_Mutex);
else if (nosound) return;
else if (nomidimusic && nodigimusic
#ifdef HW3SOUND
&& hws_mode == HWS_DEFAULT_MODE
#endif
) SDL_UnlockAudio();
#ifdef HAVE_MIXER
else if (musicStarted) Mix_SetPostMix(audio.callback, audio.userdata);
#endif
}
FUNCMATH static inline Uint16 Snd_LowerRate(Uint16 sr)
{
if (sr <= audio.freq) // already lowered rate?
return sr; // good then
for (;sr > audio.freq;) // not good?
{ // then let see...
if (sr % 2) // can we div by half?
return sr; // no, just use the currect rate
sr /= 2; // we can? wonderful
} // let start over again
if (sr == audio.freq) // did we drop to the desired rate?
return sr; // perfect! but if not
return sr*2; // just keep it just above the output sample rate
}
#ifdef _MSC_VER
#pragma warning(disable : 4200)
#pragma pack(1)
#endif
typedef struct
{
Uint16 header; // 3?
Uint16 samplerate; // 11025+
Uint16 samples; // number of samples
Uint16 dummy; // 0
Uint8 data[0]; // data;
} ATTRPACK dssfx_t;
#ifdef _MSC_VER
#pragma pack()
#pragma warning(default : 4200)
#endif
//
// This function loads the sound data from the WAD lump,
// for single sound.
//
static void *getsfx(lumpnum_t sfxlump, size_t *len)
{
dssfx_t *sfx, *paddedsfx;
Uint16 sr , csr;
size_t size = *len;
SDL_AudioCVT sfxcvt;
sfx = (dssfx_t *)malloc(size);
if (sfx) W_ReadLump(sfxlump, (void *)sfx);
else return NULL;
sr = SHORT(sfx->samplerate);
csr = Snd_LowerRate(sr);
if (sr > csr && SDL_BuildAudioCVT(&sfxcvt, AUDIO_U8, 1, sr, AUDIO_U8, 1, csr))
{//Alam: Setup the AudioCVT for the SFX
sfxcvt.len = (INT32)size-8; //Alam: Chop off the header
sfxcvt.buf = malloc(sfxcvt.len * sfxcvt.len_mult); //Alam: make room
if (sfxcvt.buf) M_Memcpy(sfxcvt.buf, &(sfx->data), sfxcvt.len); //Alam: copy the sfx sample
if (sfxcvt.buf && SDL_ConvertAudio(&sfxcvt) == 0) //Alam: let convert it!
{
size = sfxcvt.len_cvt + 8;
*len = sfxcvt.len_cvt;
// Allocate from zone memory.
paddedsfx = (dssfx_t *) Z_Malloc(size, PU_SOUND, NULL);
// Now copy and pad.
M_Memcpy(paddedsfx->data, sfxcvt.buf, sfxcvt.len_cvt);
free(sfxcvt.buf);
M_Memcpy(paddedsfx,sfx,8);
paddedsfx->samplerate = SHORT(csr); // new freq
}
else //Alam: the convert failed, not needed or I couldn't malloc the buf
{
if (sfxcvt.buf) free(sfxcvt.buf);
*len = size - 8;
// Allocate from zone memory then copy and pad
paddedsfx = (dssfx_t *)M_Memcpy(Z_Malloc(size, PU_SOUND, NULL), sfx, size);
}
}
else
{
// Pads the sound effect out to the mixing buffer size.
// The original realloc would interfere with zone memory.
*len = size - 8;
// Allocate from zone memory then copy and pad
paddedsfx = (dssfx_t *)M_Memcpy(Z_Malloc(size, PU_SOUND, NULL), sfx, size);
}
// Remove the cached lump.
free(sfx);
// Return allocated padded data.
return paddedsfx;
}
// used to (re)calculate channel params based on vol, sep, pitch
static void I_SetChannelParams(chan_t *c, INT32 vol, INT32 sep, INT32 step)
{
INT32 leftvol;
INT32 rightvol;
c->vol = vol;
c->sep = sep;
c->step = c->realstep = step;
if (step != steptable[128])
c->step += (((c->samplerate<<16)/audio.freq)-65536);
else if (c->samplerate != (unsigned)audio.freq)
c->step = ((c->samplerate<<16)/audio.freq);
// x^2 separation, that is, orientation/stereo.
// range is: 0 (left) - 255 (right)
// Volume arrives in range 0..255 and it must be in 0..cv_soundvolume...
vol = (vol * cv_soundvolume.value) >> 7;
// note: >> 6 would use almost the entire dynamical range, but
// then there would be no "dynamical room" for other sounds :-/
leftvol = vol - ((vol*sep*sep) >> 16); ///(256*256);
sep = 255 - sep;
rightvol = vol - ((vol*sep*sep) >> 16);
// Sanity check, clamp volume.
if (rightvol < 0)
rightvol = 0;
else if (rightvol > 127)
rightvol = 127;
if (leftvol < 0)
leftvol = 0;
else if (leftvol > 127)
leftvol = 127;
// Get the proper lookup table piece
// for this volume level
c->leftvol_lookup = &vol_lookup[leftvol*256];
c->rightvol_lookup = &vol_lookup[rightvol*256];
}
static INT32 FindChannel(INT32 handle)
{
INT32 i;
for (i = 0; i < NUM_CHANNELS; i++)
if (channels[i].handle == handle)
return i;
// not found
return -1;
}
//
// This function adds a sound to the
// list of currently active sounds,
// which is maintained as a given number
// (eight, usually) of internal channels.
// Returns a handle.
//
static INT32 addsfx(sfxenum_t sfxid, INT32 volume, INT32 step, INT32 seperation)
{
static UINT16 handlenums = 0;
INT32 i, slot, oldestnum = 0;
tic_t oldest = gametic;
// Play these sound effects only one at a time.
#if 1
if (
#if 0
sfxid == sfx_stnmov || sfxid == sfx_sawup || sfxid == sfx_sawidl || sfxid == sfx_sawful || sfxid == sfx_sawhit || sfxid == sfx_pistol
#else
( sfx_litng1 <= sfxid && sfxid >= sfx_litng4 )
|| sfxid == sfx_trfire || sfxid == sfx_alarm || sfxid == sfx_spin
|| sfxid == sfx_athun1 || sfxid == sfx_athun2 || sfxid == sfx_rainin
#endif
)
{
// Loop all channels, check.
for (i = 0; i < NUM_CHANNELS; i++)
{
// Active, and using the same SFX?
if ((channels[i].end) && (channels[i].sfxid == sfxid))
{
// Reset.
channels[i].end = NULL;
// We are sure that iff,
// there will only be one.
break;
}
}
}
#endif
// Loop all channels to find oldest SFX.
for (i = 0; (i < NUM_CHANNELS) && (channels[i].end); i++)
{
if (channels[i].starttic < oldest)
{
oldestnum = i;
oldest = channels[i].starttic;
}
}
// Tales from the cryptic.
// If we found a channel, fine.
// If not, we simply overwrite the first one, 0.
// Probably only happens at startup.
if (i == NUM_CHANNELS)
slot = oldestnum;
else
slot = i;
channels[slot].end = NULL;
// Okay, in the less recent channel,
// we will handle the new SFX.
// Set pointer to raw data.
channels[slot].data = (Uint8 *)S_sfx[sfxid].data;
channels[slot].samplerate = (channels[slot].data[3]<<8)+channels[slot].data[2];
channels[slot].data += 8; //Alam: offset of the sound header
while (FindChannel(handlenums)!=-1)
{
handlenums++;
// Reset current handle number, limited to 0..65535.
if (handlenums == UINT16_MAX)
handlenums = 0;
}
// Assign current handle number.
// Preserved so sounds could be stopped.
channels[slot].handle = handlenums;
// Restart steper
channels[slot].stepremainder = 0;
// Should be gametic, I presume.
channels[slot].starttic = gametic;
I_SetChannelParams(&channels[slot], volume, seperation, step);
// Preserve sound SFX id,
// e.g. for avoiding duplicates of chainsaw.
channels[slot].id = S_sfx[sfxid].data;
channels[slot].sfxid = sfxid;
// Set pointer to end of raw data.
channels[slot].end = channels[slot].data + S_sfx[sfxid].length;
// You tell me.
return handlenums;
}
//
// SFX API
// Note: this was called by S_Init.
// However, whatever they did in the
// old DPMS based DOS version, this
// were simply dummies in the Linux
// version.
// See soundserver initdata().
//
// Well... To keep compatibility with legacy doom, I have to call this in
// I_InitSound since it is not called in S_Init... (emanne@absysteme.fr)
static inline void I_SetChannels(void)
{
// Init internal lookups (raw data, mixing buffer, channels).
// This function sets up internal lookups used during
// the mixing process.
INT32 i;
INT32 j;
INT32 *steptablemid = steptable + 128;
if (nosound)
return;
// This table provides step widths for pitch parameters.
for (i = -128; i < 128; i++)
{
const double po = pow((double)(2.0l), (double)(i / 64.0l));
steptablemid[i] = (INT32)(po * 65536.0l);
}
// Generates volume lookup tables
// which also turn the unsigned samples
// into signed samples.
for (i = 0; i < 128; i++)
for (j = 0; j < 256; j++)
{
//From PrDoom
// proff - made this a little bit softer, because with
// full volume the sound clipped badly
vol_lookup[i * 256 + j] = (Sint16)((i * (j - 128) * 256) / 127);
}
}
void I_SetSfxVolume(UINT8 volume)
{
INT32 i;
(void)volume;
//Snd_LockAudio();
for (i = 0; i < NUM_CHANNELS; i++)
if (channels[i].end) I_SetChannelParams(&channels[i], channels[i].vol, channels[i].sep, channels[i].realstep);
//Snd_UnlockAudio();
}
void *I_GetSfx(sfxinfo_t *sfx)
{
if (sfx->lumpnum == LUMPERROR)
sfx->lumpnum = S_GetSfxLumpNum(sfx);
// else if (sfx->lumpnum != S_GetSfxLumpNum(sfx))
// I_FreeSfx(sfx);
#ifdef HW3SOUND
if (hws_mode != HWS_DEFAULT_MODE)
return HW3S_GetSfx(sfx);
#endif
if (sfx->data)
return sfx->data; //Alam: I have it done!
sfx->length = W_LumpLength(sfx->lumpnum);
return getsfx(sfx->lumpnum, &sfx->length);
}
void I_FreeSfx(sfxinfo_t * sfx)
{
// if (sfx->lumpnum<0)
// return;
#ifdef HW3SOUND
if (hws_mode != HWS_DEFAULT_MODE)
{
HW3S_FreeSfx(sfx);
}
else
#endif
{
size_t i;
for (i = 1; i < NUMSFX; i++)
{
// Alias? Example is the chaingun sound linked to pistol.
if (S_sfx[i].data == sfx->data)
{
if (S_sfx+i != sfx) S_sfx[i].data = NULL;
S_sfx[i].lumpnum = LUMPERROR;
S_sfx[i].length = 0;
}
}
//Snd_LockAudio(); //Alam: too much?
// Loop all channels, check.
for (i = 0; i < NUM_CHANNELS; i++)
{
// Active, and using the same SFX?
if (channels[i].end && channels[i].id == sfx->data)
{
channels[i].end = NULL; // Reset.
}
}
//Snd_UnlockAudio(); //Alam: too much?
Z_Free(sfx->data);
}
sfx->data = NULL;
sfx->lumpnum = LUMPERROR;
}
//
// Starting a sound means adding it
// to the current list of active sounds
// in the internal channels.
// As the SFX info struct contains
// e.g. a pointer to the raw data,
// it is ignored.
// As our sound handling does not handle
// priority, it is ignored.
// Pitching (that is, increased speed of playback)
// is set, but currently not used by mixing.
//
INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel)
{
(void)priority;
(void)pitch;
(void)channel;
if (nosound)
return 0;
if (S_sfx[id].data == NULL) return -1;
Snd_LockAudio();
id = addsfx(id, vol, steptable[pitch], sep);
Snd_UnlockAudio();
return id; // Returns a handle (not used).
}
void I_StopSound(INT32 handle)
{
// You need the handle returned by StartSound.
// Would be looping all channels,
// tracking down the handle,
// an setting the channel to zero.
INT32 i;
i = FindChannel(handle);
if (i != -1)
{
//Snd_LockAudio(); //Alam: too much?
channels[i].end = NULL;
//Snd_UnlockAudio(); //Alam: too much?
channels[i].handle = -1;
channels[i].starttic = 0;
}
}
boolean I_SoundIsPlaying(INT32 handle)
{
boolean isplaying = false;
int chan = FindChannel(handle);
if (chan != -1)
isplaying = (channels[chan].end != NULL);
return isplaying;
}
FUNCINLINE static ATTRINLINE void I_UpdateStream8S(Uint8 *stream, int len)
{
// Mix current sound data.
// Data, from raw sound
register Sint16 dr; // Right 8bit stream
register Uint8 sample; // Center 8bit sfx
register Sint16 dl; // Left 8bit stream
// Pointers in audio stream
Sint8 *rightout = (Sint8 *)stream; // currect right
Sint8 *leftout = rightout + 1;// currect left
const Uint8 step = 2; // Step in stream, left and right, thus two.
INT32 chan; // Mixing channel index.
// Determine end of the stream
len /= 2; // not 8bit mono samples, 8bit stereo ones
if (Snd_Mutex) SDL_LockMutex(Snd_Mutex);
// Mix sounds into the mixing buffer.
// Loop over len
while (len--)
{
// Reset left/right value.
dl = *leftout;
dr = *rightout;
// Love thy L2 cache - made this a loop.
// Now more channels could be set at compile time
// as well. Thus loop those channels.
for (chan = 0; chan < NUM_CHANNELS; chan++)
{
// Check channel, if active.
if (channels[chan].end)
{
#if 1
// Get the raw data from the channel.
sample = channels[chan].data[0];
#else
// linear filtering from PrDoom
sample = (((Uint32)channels[chan].data[0] *(0x10000 - channels[chan].stepremainder))
+ ((Uint32)channels[chan].data[1]) * (channels[chan].stepremainder))) >> 16;
#endif
// Add left and right part
// for this channel (sound)
// to the current data.
// Adjust volume accordingly.
dl = (Sint16)(dl+(channels[chan].leftvol_lookup[sample]>>8));
dr = (Sint16)(dr+(channels[chan].rightvol_lookup[sample]>>8));
// Increment stepage
channels[chan].stepremainder += channels[chan].step;
// Check whether we are done.
if (channels[chan].data + (channels[chan].stepremainder >> 16) >= channels[chan].end)
channels[chan].end = NULL;
else
{
// step to next sample
channels[chan].data += (channels[chan].stepremainder >> 16);
// Limit to LSB???
channels[chan].stepremainder &= 0xffff;
}
}
}
// Clamp to range. Left hardware channel.
// Has been char instead of short.
if (dl > 0x7f)
*leftout = 0x7f;
else if (dl < -0x80)
*leftout = -0x80;
else
*leftout = (Sint8)dl;
// Same for right hardware channel.
if (dr > 0x7f)
*rightout = 0x7f;
else if (dr < -0x80)
*rightout = -0x80;
else
*rightout = (Sint8)dr;
// Increment current pointers in stream
leftout += step;
rightout += step;
}
if (Snd_Mutex) SDL_UnlockMutex(Snd_Mutex);
}
FUNCINLINE static ATTRINLINE void I_UpdateStream8M(Uint8 *stream, int len)
{
// Mix current sound data.
// Data, from raw sound
register Sint16 d; // Mono 8bit stream
register Uint8 sample; // Center 8bit sfx
// Pointers in audio stream
Sint8 *monoout = (Sint8 *)stream; // currect mono
const Uint8 step = 1; // Step in stream, left and right, thus two.
INT32 chan; // Mixing channel index.
// Determine end of the stream
//len /= 1; // not 8bit mono samples, 8bit mono ones?
if (Snd_Mutex) SDL_LockMutex(Snd_Mutex);
// Mix sounds into the mixing buffer.
// Loop over len
while (len--)
{
// Reset left/right value.
d = *monoout;
// Love thy L2 cache - made this a loop.
// Now more channels could be set at compile time
// as well. Thus loop those channels.
for (chan = 0; chan < NUM_CHANNELS; chan++)
{
// Check channel, if active.
if (channels[chan].end)
{
#if 1
// Get the raw data from the channel.
sample = channels[chan].data[0];
#else
// linear filtering from PrDoom
sample = (((Uint32)channels[chan].data[0] *(0x10000 - channels[chan].stepremainder))
+ ((Uint32)channels[chan].data[1]) * (channels[chan].stepremainder))) >> 16;
#endif
// Add left and right part
// for this channel (sound)
// to the current data.
// Adjust volume accordingly.
d = (Sint16)(d+((channels[chan].leftvol_lookup[sample] + channels[chan].rightvol_lookup[sample])>>9));
// Increment stepage
channels[chan].stepremainder += channels[chan].step;
// Check whether we are done.
if (channels[chan].data + (channels[chan].stepremainder >> 16) >= channels[chan].end)
channels[chan].end = NULL;
else
{
// step to next sample
channels[chan].data += (channels[chan].stepremainder >> 16);
// Limit to LSB???
channels[chan].stepremainder &= 0xffff;
}
}
}
// Clamp to range. Left hardware channel.
// Has been char instead of short.
if (d > 0x7f)
*monoout = 0x7f;
else if (d < -0x80)
*monoout = -0x80;
else
*monoout = (Sint8)d;
// Increment current pointers in stream
monoout += step;
}
if (Snd_Mutex) SDL_UnlockMutex(Snd_Mutex);
}
FUNCINLINE static ATTRINLINE void I_UpdateStream16S(Uint8 *stream, int len)
{
// Mix current sound data.
// Data, from raw sound
register Sint32 dr; // Right 16bit stream
register Uint8 sample; // Center 8bit sfx
register Sint32 dl; // Left 16bit stream
// Pointers in audio stream
Sint16 *rightout = (Sint16 *)(void *)stream; // currect right
Sint16 *leftout = rightout + 1;// currect left
const Uint8 step = 2; // Step in stream, left and right, thus two.
INT32 chan; // Mixing channel index.
// Determine end of the stream
len /= 4; // not 8bit mono samples, 16bit stereo ones
if (Snd_Mutex) SDL_LockMutex(Snd_Mutex);
// Mix sounds into the mixing buffer.
// Loop over len
while (len--)
{
// Reset left/right value.
dl = *leftout;
dr = *rightout;
// Love thy L2 cache - made this a loop.
// Now more channels could be set at compile time
// as well. Thus loop those channels.
for (chan = 0; chan < NUM_CHANNELS; chan++)
{
// Check channel, if active.
if (channels[chan].end)
{
#if 1
// Get the raw data from the channel.
sample = channels[chan].data[0];
#else
// linear filtering from PrDoom
sample = (((Uint32)channels[chan].data[0] *(0x10000 - channels[chan].stepremainder))
+ ((Uint32)channels[chan].data[1]) * (channels[chan].stepremainder))) >> 16;
#endif
// Add left and right part
// for this channel (sound)
// to the current data.
// Adjust volume accordingly.
dl += channels[chan].leftvol_lookup[sample];
dr += channels[chan].rightvol_lookup[sample];
// Increment stepage
channels[chan].stepremainder += channels[chan].step;
// Check whether we are done.
if (channels[chan].data + (channels[chan].stepremainder >> 16) >= channels[chan].end)
channels[chan].end = NULL;
else
{
// step to next sample
channels[chan].data += (channels[chan].stepremainder >> 16);
// Limit to LSB???
channels[chan].stepremainder &= 0xffff;
}
}
}
// Clamp to range. Left hardware channel.
// Has been char instead of short.
if (dl > 0x7fff)
*leftout = 0x7fff;
else if (dl < -0x8000)
*leftout = -0x8000;
else
*leftout = (Sint16)dl;
// Same for right hardware channel.
if (dr > 0x7fff)
*rightout = 0x7fff;
else if (dr < -0x8000)
*rightout = -0x8000;
else
*rightout = (Sint16)dr;
// Increment current pointers in stream
leftout += step;
rightout += step;
}
if (Snd_Mutex) SDL_UnlockMutex(Snd_Mutex);
}
FUNCINLINE static ATTRINLINE void I_UpdateStream16M(Uint8 *stream, int len)
{
// Mix current sound data.
// Data, from raw sound
register Sint32 d; // Mono 16bit stream
register Uint8 sample; // Center 8bit sfx
// Pointers in audio stream
Sint16 *monoout = (Sint16 *)(void *)stream; // currect mono
const Uint8 step = 1; // Step in stream, left and right, thus two.
INT32 chan; // Mixing channel index.
// Determine end of the stream
len /= 2; // not 8bit mono samples, 16bit mono ones
if (Snd_Mutex) SDL_LockMutex(Snd_Mutex);
// Mix sounds into the mixing buffer.
// Loop over len
while (len--)
{
// Reset left/right value.
d = *monoout;
// Love thy L2 cache - made this a loop.
// Now more channels could be set at compile time
// as well. Thus loop those channels.
for (chan = 0; chan < NUM_CHANNELS; chan++)
{
// Check channel, if active.
if (channels[chan].end)
{
#if 1
// Get the raw data from the channel.
sample = channels[chan].data[0];
#else
// linear filtering from PrDoom
sample = (((Uint32)channels[chan].data[0] *(0x10000 - channels[chan].stepremainder))
+ ((Uint32)channels[chan].data[1]) * (channels[chan].stepremainder))) >> 16;
#endif
// Add left and right part
// for this channel (sound)
// to the current data.
// Adjust volume accordingly.
d += (channels[chan].leftvol_lookup[sample] + channels[chan].rightvol_lookup[sample])>>1;
// Increment stepage
channels[chan].stepremainder += channels[chan].step;
// Check whether we are done.
if (channels[chan].data + (channels[chan].stepremainder >> 16) >= channels[chan].end)
channels[chan].end = NULL;
else
{
// step to next sample
channels[chan].data += (channels[chan].stepremainder >> 16);
// Limit to LSB???
channels[chan].stepremainder &= 0xffff;
}
}
}
// Clamp to range. Left hardware channel.
// Has been char instead of short.
if (d > 0x7fff)
*monoout = 0x7fff;
else if (d < -0x8000)
*monoout = -0x8000;
else
*monoout = (Sint16)d;
// Increment current pointers in stream
monoout += step;
}
if (Snd_Mutex) SDL_UnlockMutex(Snd_Mutex);
}
#ifdef HAVE_LIBGME
static void I_UpdateSteamGME(Music_Emu *emu, INT16 *stream, int len, UINT8 looping)
{
#define GME_BUFFER_LEN 44100*2048
// Mix current sound data.
// Data, from raw sound
register Sint32 da;
static short gme_buffer[GME_BUFFER_LEN]; // a large buffer for gme
Sint16 *in = gme_buffer;
do
{
int out = min(GME_BUFFER_LEN, len);
if ( gme_play( emu, len, gme_buffer ) ) { } // ignore error
len -= out;
while (out--)
{
//Left
da = *in;
in++;
da += *stream;
stream++;
//Right
da = *in;
in++;
da += *stream;
stream++;
}
if (gme_track_ended( emu ))
{
if (looping)
gme_seek( emu, 0);
else
break;
}
} while ( len );
#undef GME_BUFFER_LEN
}
#endif
static void SDLCALL I_UpdateStream(void *userdata, Uint8 *stream, int len)
{
if (!sound_started || !userdata)
return;
#if SDL_VERSION_ATLEAST(1,3,0)
if (musicStarted)
memset(stream, 0x00, len); // only work in !AUDIO_U8, that needs 0x80
#endif
if ((audio.channels != 1 && audio.channels != 2) ||
(audio.format != AUDIO_S8 && audio.format != AUDIO_S16SYS))
; // no function to encode this type of stream
else if (audio.channels == 1 && audio.format == AUDIO_S8)
I_UpdateStream8M(stream, len);
else if (audio.channels == 2 && audio.format == AUDIO_S8)
I_UpdateStream8S(stream, len);
else if (audio.channels == 1 && audio.format == AUDIO_S16SYS)
I_UpdateStream16M(stream, len);
else if (audio.channels == 2 && audio.format == AUDIO_S16SYS)
{
I_UpdateStream16S(stream, len);
#ifdef HAVE_LIBGME
if (userdata)
{
srb2audio_t *sa_userdata = userdata;
if (!sa_userdata->gme_pause)
I_UpdateSteamGME(sa_userdata->gme_emu, (INT16 *)stream, len/4, sa_userdata->gme_loop);
}
#endif
}
}
void I_UpdateSoundParams(INT32 handle, UINT8 vol, UINT8 sep, UINT8 pitch)
{
// Would be using the handle to identify
// on which channel the sound might be active,
// and resetting the channel parameters.
INT32 i = FindChannel(handle);
if (i != -1 && channels[i].end)
{
//Snd_LockAudio(); //Alam: too much?
I_SetChannelParams(&channels[i], vol, sep, steptable[pitch]);
//Snd_UnlockAudio(); //Alam: too much?
}
}
#ifdef HW3SOUND
static void *soundso = NULL;
static INT32 Init3DSDriver(const char *soName)
{
if (soName) soundso = hwOpen(soName);
#if defined (_WIN32) && defined (_X86_) && !defined (STATIC3DS)
HW3DS.pfnStartup = hwSym("Startup@8",soundso);
HW3DS.pfnShutdown = hwSym("Shutdown@0",soundso);
HW3DS.pfnAddSfx = hwSym("AddSfx@4",soundso);
HW3DS.pfnAddSource = hwSym("AddSource@8",soundso);
HW3DS.pfnStartSource = hwSym("StartSource@4",soundso);
HW3DS.pfnStopSource = hwSym("StopSource@4",soundso);
HW3DS.pfnGetHW3DSVersion = hwSym("GetHW3DSVersion@0",soundso);
HW3DS.pfnBeginFrameUpdate = hwSym("BeginFrameUpdate@0",soundso);
HW3DS.pfnEndFrameUpdate = hwSym("EndFrameUpdate@0",soundso);
HW3DS.pfnIsPlaying = hwSym("IsPlaying@4",soundso);
HW3DS.pfnUpdateListener = hwSym("UpdateListener@8",soundso);
HW3DS.pfnUpdateSourceParms = hwSym("UpdateSourceParms@12",soundso);
HW3DS.pfnSetCone = hwSym("SetCone@8",soundso);
HW3DS.pfnSetGlobalSfxVolume = hwSym("SetGlobalSfxVolume@4",soundso);
HW3DS.pfnUpdate3DSource = hwSym("Update3DSource@8",soundso);
HW3DS.pfnReloadSource = hwSym("ReloadSource@8",soundso);
HW3DS.pfnKillSource = hwSym("KillSource@4",soundso);
HW3DS.pfnKillSfx = hwSym("KillSfx@4",soundso);
HW3DS.pfnGetHW3DSTitle = hwSym("GetHW3DSTitle@8",soundso);
#else
HW3DS.pfnStartup = hwSym("Startup",soundso);
HW3DS.pfnShutdown = hwSym("Shutdown",soundso);
HW3DS.pfnAddSfx = hwSym("AddSfx",soundso);
HW3DS.pfnAddSource = hwSym("AddSource",soundso);
HW3DS.pfnStartSource = hwSym("StartSource",soundso);
HW3DS.pfnStopSource = hwSym("StopSource",soundso);
HW3DS.pfnGetHW3DSVersion = hwSym("GetHW3DSVersion",soundso);
HW3DS.pfnBeginFrameUpdate = hwSym("BeginFrameUpdate",soundso);
HW3DS.pfnEndFrameUpdate = hwSym("EndFrameUpdate",soundso);
HW3DS.pfnIsPlaying = hwSym("IsPlaying",soundso);
HW3DS.pfnUpdateListener = hwSym("UpdateListener",soundso);
HW3DS.pfnUpdateSourceParms = hwSym("UpdateSourceParms",soundso);
HW3DS.pfnSetCone = hwSym("SetCone",soundso);
HW3DS.pfnSetGlobalSfxVolume = hwSym("SetGlobalSfxVolume",soundso);
HW3DS.pfnUpdate3DSource = hwSym("Update3DSource",soundso);
HW3DS.pfnReloadSource = hwSym("ReloadSource",soundso);
HW3DS.pfnKillSource = hwSym("KillSource",soundso);
HW3DS.pfnKillSfx = hwSym("KillSfx",soundso);
HW3DS.pfnGetHW3DSTitle = hwSym("GetHW3DSTitle",soundso);
#endif
// if (HW3DS.pfnUpdateListener2 && HW3DS.pfnUpdateListener2 != soundso)
return true;
// else
// return false;
}
#endif
void I_ShutdownSound(void)
{
if (nosound || !sound_started)
return;
CONS_Printf("I_ShutdownSound: ");
#ifdef HW3SOUND
if (hws_mode != HWS_DEFAULT_MODE)
{
HW3S_Shutdown();
hwClose(soundso);
return;
}
#endif
if (nomidimusic && nodigimusic)
SDL_CloseAudio();
CONS_Printf("%s", M_GetText("shut down\n"));
sound_started = false;
SDL_QuitSubSystem(SDL_INIT_AUDIO);
if (Snd_Mutex)
SDL_DestroyMutex(Snd_Mutex);
Snd_Mutex = NULL;
}
void I_UpdateSound(void)
{
}
void I_StartupSound(void)
{
#ifdef HW3SOUND
const char *sdrv_name = NULL;
#endif
#ifndef HAVE_MIXER
nomidimusic = nodigimusic = true;
#endif
#ifdef DC
//nosound = true;
#ifdef HAVE_MIXER
nomidimusic = true;
nodigimusic = true;
#endif
#endif
memset(channels, 0, sizeof (channels)); //Alam: Clean it
audio.format = AUDIO_S16SYS;
audio.channels = 2;
audio.callback = I_UpdateStream;
audio.userdata = &localdata;
if (dedicated)
{
nosound = nomidimusic = nodigimusic = true;
return;
}
// Configure sound device
CONS_Printf("I_StartupSound:\n");
// Open the audio device
if (M_CheckParm ("-freq") && M_IsNextParm())
{
audio.freq = atoi(M_GetNextParm());
if (!audio.freq) audio.freq = cv_samplerate.value;
audio.samples = (Uint16)((samplecount/2)*(INT32)(audio.freq/11025)); //Alam: to keep it around the same XX ms
CONS_Printf (M_GetText(" requested frequency of %d hz\n"), audio.freq);
}
else
{
audio.samples = samplecount;
audio.freq = cv_samplerate.value;
}
if (M_CheckParm ("-mono"))
{
audio.channels = 1;
audio.samples /= 2;
}
#if defined (_PSP) && defined (HAVE_MIXER) // Bug in PSP's SDL_OpenAudio, can not open twice
I_SetChannels();
sound_started = true;
Snd_Mutex = SDL_CreateMutex();
#else
if (nosound)
#endif
return;
#ifdef HW3SOUND
#ifdef STATIC3DS
if (M_CheckParm("-3dsound") || M_CheckParm("-ds3d"))
{
hws_mode = HWS_OPENAL;
}
#elif defined (_WIN32)
if (M_CheckParm("-ds3d"))
{
hws_mode = HWS_DS3D;
sdrv_name = "s_ds3d.dll";
}
else if (M_CheckParm("-fmod3d"))
{
hws_mode = HWS_FMOD3D;
sdrv_name = "s_fmod.dll";
}
else if (M_CheckParm("-openal"))
{
hws_mode = HWS_OPENAL;
sdrv_name = "s_openal.dll";
}
#else
if (M_CheckParm("-fmod3d"))
{
hws_mode = HWS_FMOD3D;
sdrv_name = "./s_fmod.so";
}
else if (M_CheckParm("-openal"))
{
hws_mode = HWS_OPENAL;
sdrv_name = "./s_openal.so";
}
#endif
else if (M_CheckParm("-sounddriver") && M_IsNextParm())
{
hws_mode = HWS_OTHER;
sdrv_name = M_GetNextParm();
}
if (hws_mode != HWS_DEFAULT_MODE)
{
if (Init3DSDriver(sdrv_name))
{
snddev_t snddev;
//nosound = true;
//I_AddExitFunc(I_ShutdownSound);
snddev.bps = 16;
snddev.sample_rate = audio.freq;
snddev.numsfxs = NUMSFX;
#if defined (_WIN32) && !defined (_XBOX)
snddev.cooplevel = 0x00000002;
snddev.hWnd = vid.WndParent;
#endif
if (HW3S_Init(I_Error, &snddev))
{
audio.userdata = NULL;
CONS_Printf("%s", M_GetText(" Using 3D sound driver\n"));
return;
}
CONS_Printf("%s", M_GetText(" Failed loading 3D sound driver\n"));
// Falls back to default sound system
HW3S_Shutdown();
hwClose(soundso);
}
CONS_Printf("%s", M_GetText(" Failed loading 3D sound driver\n"));
hws_mode = HWS_DEFAULT_MODE;
}
#endif
if (!musicStarted && SDL_OpenAudio(&audio, &audio) < 0)
{
CONS_Printf("%s", M_GetText(" couldn't open audio with desired format\n"));
nosound = true;
return;
}
else
{
char ad[100];
CONS_Printf(M_GetText(" Starting up with audio driver : %s\n"), SDL_AudioDriverName(ad, (int)sizeof ad));
}
samplecount = audio.samples;
CV_SetValue(&cv_samplerate, audio.freq);
CONS_Printf(M_GetText(" configured audio device with %d samples/slice at %ikhz(%dms buffer)\n"), samplecount, audio.freq/1000, (INT32) (((float)audio.samples * 1000.0f) / audio.freq));
// Finished initialization.
CONS_Printf("%s", M_GetText(" Sound module ready\n"));
//[segabor]
if (!musicStarted) SDL_PauseAudio(0);
//Mix_Pause(0);
I_SetChannels();
sound_started = true;
Snd_Mutex = SDL_CreateMutex();
}
//
// MUSIC API.
//
void I_ShutdownMIDIMusic(void)
{
nomidimusic = false;
if (nodigimusic) I_ShutdownMusic();
}
#ifdef HAVE_LIBGME
static void I_ShutdownGMEMusic(void)
{
Snd_LockAudio();
if (localdata.gme_emu)
gme_delete(localdata.gme_emu);
localdata.gme_emu = NULL;
Snd_UnlockAudio();
}
#endif
void I_ShutdownDigMusic(void)
{
nodigimusic = false;
if (nomidimusic) I_ShutdownMusic();
}
#ifdef HAVE_MIXER
static boolean LoadSong(void *data, size_t lumplength, size_t selectpos)
{
FILE *midfile;
const char *tempname;
#ifdef USE_RWOPS
if (canuseRW)
{
SDL_RWops *SDLRW;
void *olddata = Smidi[selectpos]; //quick shortcut to set
Z_Free(olddata); //free old memory
Smidi[selectpos] = NULL;
if (!data)
return olddata != NULL; //was there old data?
SDLRW = SDL_RWFromConstMem(data, (int)lumplength); //new RWops from Z_zone
if (!SDLRW) //ERROR while making RWops!
{
CONS_Printf(M_GetText("Couldn't load music lump: %s\n"), SDL_GetError());
Z_Free(data);
return false;
}
music[selectpos] = Mix_LoadMUS_RW(SDLRW); // new Mix_Chuck from RWops
if (music[selectpos])
Smidi[selectpos] = data; //all done
else //ERROR while making Mix_Chuck
{
CONS_Printf(M_GetText("Couldn't load music data: %s\n"), Mix_GetError());
Z_Free(data);
SDL_RWclose(SDLRW);
Smidi[selectpos] = NULL;
}
return true;
}
#endif
tempname = va("%s/%s", MIDI_PATH, fmidi[selectpos]);
if (!data)
{
if (FIL_FileExists(tempname))
return unlink(tempname)+1;
#ifdef MIDI_PATH2
else if (FIL_FileExists(tempname = va("%s/%s", MIDI_PATH2, fmidi[selectpos])))
return unlink(tempname)+1;
#endif
else
return false;
}
midfile = fopen(tempname, "wb");
#ifdef MIDI_PATH2
if (!midfile)
{
tempname = va("%s/%s", MIDI_PATH2, fmidi[selectpos]);
midfile = fopen(tempname, "wb");
}
#endif
if (!midfile)
{
CONS_Printf(M_GetText("Couldn't open file %s to write music in\n"), tempname);
Z_Free(data);
return false;
}
if (fwrite(data, lumplength, 1, midfile) == 0)
{
CONS_Printf(M_GetText("Couldn't write music into file %s because %s\n"), tempname, strerror(ferror(midfile)));
Z_Free(data);
fclose(midfile);
return false;
}
fclose(midfile);
Z_Free(data);
music[selectpos] = Mix_LoadMUS(tempname);
if (!music[selectpos]) //ERROR while making Mix_Chuck
{
CONS_Printf(M_GetText("Couldn't load music file %s: %s\n"), tempname, Mix_GetError());
return false;
}
return true;
}
#endif
void I_ShutdownMusic(void)
{
#ifdef HAVE_MIXER
if ((nomidimusic && nodigimusic) || !musicStarted)
return;
CONS_Printf("%s", M_GetText("I_ShutdownMusic: "));
I_UnRegisterSong(0);
I_StopDigSong();
Mix_CloseAudio();
#ifdef MIX_INIT
Mix_Quit();
#endif
CONS_Printf("%s", M_GetText("shut down\n"));
musicStarted = SDL_FALSE;
if (Msc_Mutex)
SDL_DestroyMutex(Msc_Mutex);
Msc_Mutex = NULL;
#endif
}
void I_InitMIDIMusic(void)
{
if (nodigimusic) I_InitMusic();
}
void I_InitDigMusic(void)
{
if (nomidimusic) I_InitMusic();
}
void I_InitMusic(void)
{
#ifdef HAVE_MIXER
char ad[100];
SDL_version MIXcompiled;
const SDL_version *MIXlinked;
#ifdef MIXER_INIT
const int mixstart = MIX_INIT_OGG;
int mixflags;
#endif
#endif
#ifdef HAVE_LIBGME
I_AddExitFunc(I_ShutdownGMEMusic);
#endif
if ((nomidimusic && nodigimusic) || dedicated)
return;
#ifdef HAVE_MIXER
MIX_VERSION(&MIXcompiled)
MIXlinked = Mix_Linked_Version();
I_OutputMsg("Compiled for SDL_mixer version: %d.%d.%d\n",
MIXcompiled.major, MIXcompiled.minor, MIXcompiled.patch);
#ifdef MIXER_POS
#ifndef _WII
if (MIXlinked->major == 1 && MIXlinked->minor == 2 && MIXlinked->patch < 7)
#endif
canlooping = SDL_FALSE;
#endif
#ifdef USE_RWOPS
if (M_CheckParm("-noRW"))
canuseRW = SDL_FALSE;
#endif
I_OutputMsg("Linked with SDL_mixer version: %d.%d.%d\n",
MIXlinked->major, MIXlinked->minor, MIXlinked->patch);
#if !(defined (DC) || defined (PSP) || defined(GP2X) || defined (WII))
if (audio.freq < 44100 && !M_CheckParm ("-freq")) //I want atleast 44Khz
{
audio.samples = (Uint16)(audio.samples*(INT32)(44100/audio.freq));
audio.freq = 44100; //Alam: to keep it around the same XX ms
}
#endif
if (sound_started
#ifdef HW3SOUND
&& hws_mode == HWS_DEFAULT_MODE
#endif
)
{
I_OutputMsg("Temp Shutdown of SDL Audio System");
SDL_CloseAudio();
I_OutputMsg(" Done\n");
}
CONS_Printf("%s", M_GetText("I_InitMusic:"));
#ifdef MIXER_INIT
mixflags = Mix_Init(mixstart);
if ((mixstart & MIX_INIT_FLAC) != (mixflags & MIX_INIT_FLAC))
{
CONS_Printf("%s", M_GetText(" Unable to load FLAC support\n"));
}
if ((mixstart & MIX_INIT_MOD ) != (mixflags & MIX_INIT_MOD ))
{
CONS_Printf("%s", M_GetText(" Unable to load MOD support\n"));
}
if ((mixstart & MIX_INIT_MP3 ) != (mixflags & MIX_INIT_MP3 ))
{
CONS_Printf("%s", M_GetText(" Unable to load MP3 support\n"));
}
if ((mixstart & MIX_INIT_OGG ) != (mixflags & MIX_INIT_OGG ))
{
CONS_Printf("%s", M_GetText(" Unable to load OGG support\n"));
}
#endif
if (Mix_OpenAudio(audio.freq, audio.format, audio.channels, audio.samples) < 0) //open_music(&audio)
{
CONS_Printf(M_GetText(" Unable to open music: %s\n"), Mix_GetError());
nomidimusic = nodigimusic = true;
if (sound_started
#ifdef HW3SOUND
&& hws_mode == HWS_DEFAULT_MODE
#endif
)
{
if (SDL_OpenAudio(&audio, NULL) < 0) //retry
{
CONS_Printf("%s", M_GetText(" couldn't open audio with desired format\n"));
nosound = true;
sound_started = false;
}
else
{
CONS_Printf(M_GetText(" Starting with audio driver : %s\n"), SDL_AudioDriverName(ad, (int)sizeof ad));
}
}
return;
}
else
CONS_Printf(M_GetText(" Starting up with audio driver : %s with SDL_Mixer\n"), SDL_AudioDriverName(ad, (int)sizeof ad));
samplecount = audio.samples;
CV_SetValue(&cv_samplerate, audio.freq);
if (sound_started
#ifdef HW3SOUND
&& hws_mode == HWS_DEFAULT_MODE
#endif
)
I_OutputMsg(" Reconfigured SDL Audio System");
else I_OutputMsg(" Configured SDL_Mixer System");
I_OutputMsg(" with %d samples/slice at %ikhz(%dms buffer)\n", samplecount, audio.freq/1000, (INT32) ((audio.samples * 1000.0f) / audio.freq));
Mix_SetPostMix(audio.callback, audio.userdata); // after mixing music, add sound effects
Mix_Resume(-1);
CONS_Printf("%s", M_GetText("Music initialized\n"));
musicStarted = SDL_TRUE;
Msc_Mutex = SDL_CreateMutex();
#endif
}
boolean I_PlaySong(INT32 handle, boolean looping)
{
(void)handle;
#ifdef HAVE_MIXER
if (nomidimusic || !musicStarted || !music[handle])
return false;
#ifdef MIXER_POS
if (canlooping)
Mix_HookMusicFinished(NULL);
#endif
if (Mix_FadeInMusic(music[handle], looping ? -1 : 0, MIDIfade) == -1)
CONS_Printf(M_GetText("Couldn't play song because %s\n"), Mix_GetError());
else
{
Mix_VolumeMusic(musicvol);
return true;
}
#else
(void)looping;
#endif
return false;
}
static void I_PauseGME(void)
{
#ifdef HAVE_LIBGME
localdata.gme_pause = true;
#endif
}
void I_PauseSong(INT32 handle)
{
(void)handle;
I_PauseGME();
#ifdef HAVE_MIXER
if ((nomidimusic && nodigimusic) || !musicStarted)
return;
Mix_PauseMusic();
//I_StopSong(handle);
#endif
}
static void I_ResumeGME(void)
{
#ifdef HAVE_LIBGME
localdata.gme_pause = false;
#endif
}
void I_ResumeSong(INT32 handle)
{
(void)handle;
I_ResumeGME();
#ifdef HAVE_MIXER
if ((nomidimusic && nodigimusic) || !musicStarted)
return;
Mix_VolumeMusic(musicvol);
Mix_ResumeMusic();
//I_PlaySong(handle, true);
#endif
}
void I_StopSong(INT32 handle)
{
(void)handle;
#ifdef HAVE_MIXER
if (nomidimusic || !musicStarted)
return;
Mix_FadeOutMusic(MIDIfade);
#endif
}
void I_UnRegisterSong(INT32 handle)
{
#ifdef HAVE_MIXER
if (nomidimusic || !musicStarted)
return;
Mix_HaltMusic();
while (Mix_PlayingMusic())
;
if (music[handle])
Mix_FreeMusic(music[handle]);
music[handle] = NULL;
LoadSong(NULL, 0, handle);
#else
(void)handle;
#endif
}
INT32 I_RegisterSong(void *data, size_t len)
{
#ifdef HAVE_MIXER
if (nomidimusic || !musicStarted)
return false;
if (!LoadSong(data, len, 0))
return false;
if (music[0])
return true;
CONS_Printf(M_GetText("Couldn't load MIDI: %s\n"), Mix_GetError());
#else
(void)len;
(void)data;
#endif
return false;
}
void I_SetMIDIMusicVolume(UINT8 volume)
{
#ifdef HAVE_MIXER
if ((nomidimusic && nodigimusic) || !musicStarted)
return;
if (Msc_Mutex) SDL_LockMutex(Msc_Mutex);
musicvol = volume * 2;
if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
Mix_VolumeMusic(musicvol);
#else
(void)volume;
#endif
}
#ifdef HAVE_LIBGME
static void I_CleanupGME(void *userdata)
{
Z_Free(userdata);
}
#endif
static boolean I_StartGMESong(const char *musicname, boolean looping)
{
#ifdef HAVE_LIBGME
XBOXSTATIC char filename[9];
void *data;
lumpnum_t lumpnum;
size_t lumplength;
Music_Emu *emu;
const char* gme_err;
Snd_LockAudio();
if (localdata.gme_emu)
gme_delete(localdata.gme_emu);
localdata.gme_emu = NULL;
Snd_UnlockAudio();
snprintf(filename, sizeof filename, "o_%s", musicname);
lumpnum = W_CheckNumForName(filename);
if (lumpnum == LUMPERROR)
{
return false; // No music found. Oh well!
}
else
lumplength = W_LumpLength(lumpnum);
data = W_CacheLumpNum(lumpnum, PU_MUSIC);
gme_err = gme_open_data(data, (long)lumplength, &emu, audio.freq);
if (gme_err != NULL) {
//I_OutputMsg("I_StartGMESong: error %s\n",gme_err);
return false;
}
gme_set_user_data(emu, data);
gme_set_user_cleanup(emu, I_CleanupGME);
gme_start_track(emu, 0);
gme_set_fade(emu, Digfade);
Snd_LockAudio();
localdata.gme_emu = emu;
localdata.gme_pause = false;
localdata.gme_loop = (UINT8)looping;
Snd_UnlockAudio();
return true;
#else
(void)musicname;
(void)looping;
#endif
return false;
}
boolean I_StartDigSong(const char *musicname, boolean looping)
{
#ifdef HAVE_MIXER
XBOXSTATIC char filename[9];
void *data;
lumpnum_t lumpnum;
size_t lumplength;
#endif
if(I_StartGMESong(musicname, looping))
return true;
#ifdef HAVE_MIXER
if (nodigimusic)
return false;
snprintf(filename, sizeof filename, "o_%s", musicname);
lumpnum = W_CheckNumForName(filename);
I_StopDigSong();
if (lumpnum == LUMPERROR)
{
// Alam_GBC: like in win32/win_snd.c: Graue 02-29-2004: don't worry about missing music, there might still be a MIDI
//I_OutputMsg("Music lump %s not found!\n", filename);
return false; // No music found. Oh well!
}
else
lumplength = W_LumpLength(lumpnum);
data = W_CacheLumpNum(lumpnum, PU_MUSIC);
if (Msc_Mutex) SDL_LockMutex(Msc_Mutex);
#ifdef MIXER_POS
if (canlooping && (loopingDig = looping) == SDL_TRUE && strcmp(data, "OggS") == 0)
looping = false; // Only on looping Ogg files, will we will do our own looping
// Scan the Ogg Vorbis file for the COMMENT= field for a custom
// loop point
if (!looping && loopingDig)
{
size_t scan;
const char *dataum = data;
XBOXSTATIC char looplength[64];
UINT32 loopstart = 0;
UINT8 newcount = 0;
Mix_HookMusicFinished(I_FinishMusic);
for (scan = 0; scan < lumplength; scan++)
{
if (*dataum++ == 'C'){
if (*dataum++ == 'O'){
if (*dataum++ == 'M'){
if (*dataum++ == 'M'){
if (*dataum++ == 'E'){
if (*dataum++ == 'N'){
if (*dataum++ == 'T'){
if (*dataum++ == '='){
if (*dataum++ == 'L'){
if (*dataum++ == 'O'){
if (*dataum++ == 'O'){
if (*dataum++ == 'P'){
if (*dataum++ == 'P'){
if (*dataum++ == 'O'){
if (*dataum++ == 'I'){
if (*dataum++ == 'N'){
if (*dataum++ == 'T'){
if (*dataum++ == '=')
{
while (*dataum != 1 && newcount != 63)
looplength[newcount++] = *dataum++;
looplength[newcount] = '\0';
loopstart = atoi(looplength);
}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
else
dataum--;}
}
if (loopstart > 0)
{
loopstartDig = (double)((44.1l+loopstart) / 44100.0l); //8 PCM chucks off and PCM to secs
//#ifdef GP2X//#ifdef PARANOIA
//I_OutputMsg("I_StartDigSong: setting looping point to %ul PCMs(%f seconds)\n", loopstart, loopstartDig);
//#endif
}
else
{
looping = true; // loopingDig true, but couldn't find start loop point
}
}
else
loopstartDig = 0.0l;
#else
if (looping && strcmp(data, "OggS") == 0)
I_OutputMsg("I_StartDigSong: SRB2 was not compiled with looping music support(no Mix_FadeInMusicPos)\n");
#endif
if (!LoadSong(data, lumplength, 1))
{
if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
return false;
}
// Note: LoadSong() frees the data. Let's make sure
// we don't try to use the data again.
data = NULL;
if (Mix_FadeInMusic(music[1], looping ? -1 : 0, Digfade) == -1)
{
if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
I_OutputMsg("I_StartDigSong: Couldn't play song %s because %s\n", musicname, Mix_GetError());
return false;
}
Mix_VolumeMusic(musicvol);
if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
return true;
#else
(void)looping;
(void)musicname;
return false;
#endif
}
static void I_StopGME(void)
{
#ifdef HAVE_LIBGME
Snd_LockAudio();
gme_seek(localdata.gme_emu, 0);
Snd_UnlockAudio();
#endif
}
void I_StopDigSong(void)
{
I_StopGME();
#ifdef HAVE_MIXER
if (nodigimusic)
return;
#ifdef MIXER_POS
if (canlooping)
Mix_HookMusicFinished(NULL);
#endif
Mix_HaltMusic();
while (Mix_PlayingMusic())
;
if (music[1])
Mix_FreeMusic(music[1]);
music[1] = NULL;
LoadSong(NULL, 0, 1);
#endif
}
void I_SetDigMusicVolume(UINT8 volume)
{
I_SetMIDIMusicVolume(volume);
}
boolean I_SetSongSpeed(float speed)
{
(void)speed;
return false;
}
boolean I_SetSongTrack(int track)
{
(void)track;
return false;
}
#ifdef MIXER_POS
static void SDLCALL I_FinishMusic(void)
{
if (!music[1])
return;
else if (Msc_Mutex) SDL_LockMutex(Msc_Mutex);
// I_OutputMsg("I_FinishMusic: Loopping song to %g seconds\n", loopstartDig);
if (Mix_FadeInMusicPos(music[1], loopstartDig ? 0 : -1, Digfade, loopstartDig) == 0)
Mix_VolumeMusic(musicvol);
else
I_OutputMsg("I_FinishMusic: Couldn't loop song because %s\n", Mix_GetError());
if (Msc_Mutex) SDL_UnlockMutex(Msc_Mutex);
}
#endif
#endif //HAVE_SDL