mirror of
https://github.com/fabiangreffrath/woof.git
synced 2025-08-03 12:47:01 -04:00
1045 lines
25 KiB
C
1045 lines
25 KiB
C
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id: i_sound.c,v 1.15 1998/05/03 22:32:33 killough Exp $
|
|
//
|
|
// Copyright (C) 1999 by
|
|
// id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// 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., 59 Temple Place - Suite 330, Boston, MA
|
|
// 02111-1307, USA.
|
|
//
|
|
// DESCRIPTION:
|
|
// System interface for sound.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static const char
|
|
rcsid[] = "$Id: i_sound.c,v 1.15 1998/05/03 22:32:33 killough Exp $";
|
|
|
|
// haleyjd
|
|
#include "SDL.h"
|
|
#include "SDL_audio.h"
|
|
#include "SDL_mixer.h"
|
|
#include <math.h>
|
|
|
|
#include "z_zone.h"
|
|
#include "doomstat.h"
|
|
#include "mmus2mid.h" //jff 1/16/98 declarations for MUS->MIDI converter
|
|
#include "i_sound.h"
|
|
#include "w_wad.h"
|
|
#include "g_game.h" //jff 1/21/98 added to use dprintf in I_RegisterSong
|
|
#include "d_main.h"
|
|
#include "d_io.h"
|
|
|
|
// Needed for calling the actual sound output.
|
|
int SAMPLECOUNT = 512;
|
|
|
|
void I_CacheSound(sfxinfo_t *sound);
|
|
|
|
// haleyjd
|
|
#define MAX_CHANNELS 32
|
|
#define NUM_CHANNELS 256
|
|
|
|
int snd_card; // default.cfg variables for digi and midi drives
|
|
int mus_card; // jff 1/18/98
|
|
|
|
int default_snd_card; // killough 10/98: add default_ versions
|
|
int default_mus_card;
|
|
|
|
// haleyjd: safety variables to keep changes to *_card from making
|
|
// these routines think that sound has been initialized when it hasn't
|
|
boolean snd_init = false;
|
|
boolean mus_init = false;
|
|
|
|
int detect_voices; //jff 3/4/98 enables voice detection prior to install_sound
|
|
//jff 1/22/98 make these visible here to disable sound/music on install err
|
|
|
|
// haleyjd 10/28/05: updated for Julian's music code, need full quality now
|
|
int snd_samplerate = 44100;
|
|
|
|
// The actual output device.
|
|
int audio_fd;
|
|
|
|
typedef struct {
|
|
// SFX id of the playing sound effect.
|
|
// Used to catch duplicates (like chainsaw).
|
|
sfxinfo_t *id;
|
|
// The channel step amount...
|
|
unsigned int step;
|
|
// ... and a 0.16 bit remainder of last step.
|
|
unsigned int stepremainder;
|
|
unsigned int samplerate;
|
|
// The channel data pointers, start and end.
|
|
unsigned char* data;
|
|
unsigned char* enddata;
|
|
// Time/gametic that the channel started playing,
|
|
// used to determine oldest, which automatically
|
|
// has lowest priority.
|
|
// In case number of active sounds exceeds
|
|
// available channels.
|
|
int starttime;
|
|
// Hardware left and right channel volume lookup.
|
|
int *leftvol_lookup;
|
|
int *rightvol_lookup;
|
|
// haleyjd 06/16/08: channel lock -- do not modify when locked!
|
|
volatile int lock;
|
|
// haleyjd 06/16/08: unique id number
|
|
int idnum;
|
|
} channel_info_t;
|
|
|
|
channel_info_t channelinfo[MAX_CHANNELS];
|
|
|
|
// Pitch to stepping lookup, unused.
|
|
int steptable[256];
|
|
|
|
// Volume lookups.
|
|
int vol_lookup[128*256];
|
|
|
|
//
|
|
// stopchan
|
|
//
|
|
// cph
|
|
// Stops a sound, unlocks the data
|
|
//
|
|
static void stopchan(int handle)
|
|
{
|
|
int cnum;
|
|
|
|
#ifdef RANGECHECK
|
|
// haleyjd 02/18/05: bounds checking
|
|
if(handle < 0 || handle >= MAX_CHANNELS)
|
|
return;
|
|
#endif
|
|
|
|
// haleyjd 02/18/05: sound channel locking in case of
|
|
// multithreaded access to channelinfo[].data. Make Eternity
|
|
// sleep for the minimum timeslice to give another thread
|
|
// chance to clear the lock.
|
|
while(channelinfo[handle].lock)
|
|
SDL_Delay(1);
|
|
|
|
if(channelinfo[handle].data)
|
|
{
|
|
channelinfo[handle].data = NULL;
|
|
|
|
if(channelinfo[handle].id)
|
|
{
|
|
// haleyjd 06/03/06: see if we can free the sound
|
|
for(cnum = 0; cnum < MAX_CHANNELS; ++cnum)
|
|
{
|
|
if(cnum == handle)
|
|
continue;
|
|
if(channelinfo[cnum].id &&
|
|
channelinfo[cnum].id->data == channelinfo[handle].id->data)
|
|
return; // still being used by some channel
|
|
}
|
|
|
|
// set sample to PU_CACHE level
|
|
Z_ChangeTag(channelinfo[handle].id->data, PU_CACHE);
|
|
}
|
|
}
|
|
|
|
channelinfo[handle].id = NULL;
|
|
}
|
|
|
|
#define SOUNDHDRSIZE 8
|
|
|
|
//
|
|
// addsfx
|
|
//
|
|
// 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.
|
|
//
|
|
// haleyjd: needs to take a sfxinfo_t ptr, not a sound id num
|
|
// haleyjd 06/03/06: changed to return boolean for failure or success
|
|
//
|
|
static boolean addsfx(sfxinfo_t *sfx, int channel)
|
|
{
|
|
size_t lumplen;
|
|
int lump;
|
|
|
|
#ifdef RANGECHECK
|
|
if(channel < 0 || channel >= MAX_CHANNELS)
|
|
I_Error("addsfx: channel out of range!\n");
|
|
#endif
|
|
|
|
// haleyjd 02/18/05: null ptr check
|
|
if(!snd_init || !sfx)
|
|
return false;
|
|
|
|
stopchan(channel);
|
|
|
|
// We will handle the new SFX.
|
|
// Set pointer to raw data.
|
|
|
|
// haleyjd: Eternity sfxinfo_t does not have a lumpnum field
|
|
lump = I_GetSfxLumpNum(sfx);
|
|
|
|
// replace missing sounds with a reasonable default
|
|
if(lump == -1)
|
|
lump = W_GetNumForName("DSPISTOL");
|
|
|
|
lumplen = W_LumpLength(lump);
|
|
|
|
// haleyjd 10/08/04: do not play zero-length sound lumps
|
|
if(lumplen <= SOUNDHDRSIZE)
|
|
return false;
|
|
|
|
// haleyjd 06/03/06: rewrote again to make sound data properly freeable
|
|
if(sfx->data == NULL)
|
|
{
|
|
byte *data;
|
|
Uint32 samplerate, samplelen;
|
|
|
|
// haleyjd: this should always be called (if lump is already loaded,
|
|
// W_CacheLumpNum handles that for us).
|
|
data = (byte *)W_CacheLumpNum(lump, PU_STATIC);
|
|
|
|
// Check the header, and ensure this is a valid sound
|
|
if(data[0] != 0x03 || data[1] != 0x00)
|
|
{
|
|
Z_ChangeTag(data, PU_CACHE);
|
|
return false;
|
|
}
|
|
|
|
samplerate = (data[3] << 8) | data[2];
|
|
samplelen = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
|
|
|
|
// don't play sounds that think they're longer than they really are
|
|
if(samplelen > lumplen - SOUNDHDRSIZE)
|
|
{
|
|
Z_ChangeTag(data, PU_CACHE);
|
|
return false;
|
|
}
|
|
|
|
sfx->alen = (Uint32)(((ULong64)samplelen * snd_samplerate) / samplerate);
|
|
sfx->data = Z_Malloc(sfx->alen, PU_STATIC, &sfx->data);
|
|
|
|
// haleyjd 04/23/08: Convert sound to target samplerate
|
|
if(sfx->alen != samplelen)
|
|
{
|
|
unsigned int i;
|
|
byte *dest = (byte *)sfx->data;
|
|
byte *src = data + SOUNDHDRSIZE;
|
|
|
|
unsigned int step = (samplerate << 16) / snd_samplerate;
|
|
unsigned int stepremainder = 0, j = 0;
|
|
|
|
// do linear filtering operation
|
|
for(i = 0; i < sfx->alen && j < samplelen - 1; ++i)
|
|
{
|
|
int d = (((unsigned int)src[j ] * (0x10000 - stepremainder)) +
|
|
((unsigned int)src[j+1] * stepremainder)) >> 16;
|
|
|
|
if(d > 255)
|
|
dest[i] = 255;
|
|
else if(d < 0)
|
|
dest[i] = 0;
|
|
else
|
|
dest[i] = (byte)d;
|
|
|
|
stepremainder += step;
|
|
j += (stepremainder >> 16);
|
|
|
|
stepremainder &= 0xffff;
|
|
}
|
|
// fill remainder (if any) with final sample byte
|
|
for(; i < sfx->alen; ++i)
|
|
dest[i] = src[j];
|
|
}
|
|
else
|
|
{
|
|
// sound is already at target samplerate, copy data
|
|
memcpy(sfx->data, data + SOUNDHDRSIZE, samplelen);
|
|
}
|
|
|
|
// haleyjd 06/03/06: don't need original lump data any more
|
|
Z_ChangeTag(data, PU_CACHE);
|
|
}
|
|
else
|
|
Z_ChangeTag(sfx->data, PU_STATIC); // reset to static cache level
|
|
|
|
channelinfo[channel].data = sfx->data;
|
|
|
|
// Set pointer to end of raw data.
|
|
channelinfo[channel].enddata = (byte *)sfx->data + sfx->alen - 1;
|
|
|
|
channelinfo[channel].stepremainder = 0;
|
|
|
|
// Preserve sound SFX id
|
|
channelinfo[channel].id = sfx;
|
|
|
|
return true;
|
|
}
|
|
|
|
int forceFlipPan;
|
|
|
|
//
|
|
// updateSoundParams
|
|
//
|
|
// Changes sound parameters in response to stereo panning and relative location
|
|
// change.
|
|
//
|
|
static void updateSoundParams(int handle, int volume, int separation, int pitch)
|
|
{
|
|
int slot = handle;
|
|
int rightvol;
|
|
int leftvol;
|
|
int step = steptable[pitch];
|
|
|
|
if(!snd_init)
|
|
return;
|
|
|
|
#ifdef RANGECHECK
|
|
if(handle < 0 || handle >= MAX_CHANNELS)
|
|
I_Error("I_UpdateSoundParams: handle out of range");
|
|
#endif
|
|
|
|
// Set stepping
|
|
// MWM 2000-12-24: Calculates proportion of channel samplerate
|
|
// to global samplerate for mixing purposes.
|
|
// Patched to shift left *then* divide, to minimize roundoff errors
|
|
// as well as to use SAMPLERATE as defined above, not to assume 11025 Hz
|
|
if(pitched_sounds)
|
|
channelinfo[slot].step = step;
|
|
else
|
|
channelinfo[slot].step = 1 << 16;
|
|
|
|
// Separation, that is, orientation/stereo.
|
|
// range is: 1 - 256
|
|
separation += 1;
|
|
|
|
// SoM 7/1/02: forceFlipPan accounted for here
|
|
if(forceFlipPan)
|
|
separation = 257 - separation;
|
|
|
|
// Per left/right channel.
|
|
// x^2 seperation,
|
|
// adjust volume properly.
|
|
//volume *= 8;
|
|
|
|
leftvol = volume - ((volume*separation*separation) >> 16);
|
|
separation = separation - 257;
|
|
rightvol= volume - ((volume*separation*separation) >> 16);
|
|
|
|
// Sanity check, clamp volume.
|
|
if(rightvol < 0 || rightvol > 127)
|
|
I_Error("rightvol out of bounds");
|
|
|
|
if(leftvol < 0 || leftvol > 127)
|
|
I_Error("leftvol out of bounds");
|
|
|
|
// Get the proper lookup table piece
|
|
// for this volume level???
|
|
channelinfo[slot].leftvol_lookup = &vol_lookup[leftvol*256];
|
|
channelinfo[slot].rightvol_lookup = &vol_lookup[rightvol*256];
|
|
}
|
|
|
|
//
|
|
// SFX API
|
|
//
|
|
|
|
//
|
|
// I_UpdateSoundParams
|
|
//
|
|
// Update the sound parameters. Used to control volume,
|
|
// pan, and pitch changes such as when a player turns.
|
|
//
|
|
void I_UpdateSoundParams(int handle, int vol, int sep, int pitch)
|
|
{
|
|
if(!snd_init)
|
|
return;
|
|
|
|
updateSoundParams(handle, vol, sep, pitch);
|
|
}
|
|
|
|
//
|
|
// I_SetChannels
|
|
//
|
|
// Init internal lookups (raw data, mixing buffer, channels).
|
|
// This function sets up internal lookups used during
|
|
// the mixing process.
|
|
//
|
|
void I_SetChannels(void)
|
|
{
|
|
int i;
|
|
int j;
|
|
|
|
int *steptablemid = steptable + 128;
|
|
|
|
// Okay, reset internal mixing channels to zero.
|
|
for(i = 0; i < MAX_CHANNELS; ++i)
|
|
{
|
|
memset(&channelinfo[i], 0, sizeof(channel_info_t));
|
|
}
|
|
|
|
// This table provides step widths for pitch parameters.
|
|
for(i=-128 ; i<128 ; i++)
|
|
{
|
|
steptablemid[i] = (int)(pow(1.2, (double)i / 64.0) * 65536.0);
|
|
}
|
|
|
|
|
|
// 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++)
|
|
{
|
|
// proff - made this a little bit softer, because with
|
|
// full volume the sound clipped badly (191 was 127)
|
|
vol_lookup[i*256+j] = (i*(j-128)*256)/191;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// I_SetSfxVolume
|
|
//
|
|
void I_SetSfxVolume(int volume)
|
|
{
|
|
// Identical to DOS.
|
|
// Basically, this should propagate
|
|
// the menu/config file setting
|
|
// to the state variable used in
|
|
// the mixing.
|
|
|
|
snd_SfxVolume = volume;
|
|
}
|
|
|
|
// jff 1/21/98 moved music volume down into MUSIC API with the rest
|
|
|
|
//
|
|
// I_GetSfxLumpNum
|
|
//
|
|
// Retrieve the raw data lump index
|
|
// for a given SFX name.
|
|
//
|
|
int I_GetSfxLumpNum(sfxinfo_t *sfx)
|
|
{
|
|
char namebuf[16];
|
|
|
|
memset(namebuf, 0, sizeof(namebuf));
|
|
|
|
strcpy(namebuf, "DS");
|
|
strcpy(namebuf+2, sfx->name);
|
|
|
|
return W_CheckNumForName(namebuf);
|
|
}
|
|
|
|
// Almost all of the sound code from this point on was
|
|
// rewritten by Lee Killough, based on Chi's rough initial
|
|
// version.
|
|
|
|
//
|
|
// I_StartSound
|
|
//
|
|
// This function adds a sound to the list of currently
|
|
// active sounds, which is maintained as a given number
|
|
// of internal channels. Returns a handle.
|
|
//
|
|
int I_StartSound(sfxinfo_t *sound, int cnum, int vol, int sep, int pitch, int pri)
|
|
{
|
|
static unsigned int id = 0;
|
|
int handle;
|
|
|
|
if(!snd_init)
|
|
return -1;
|
|
|
|
// haleyjd: turns out this is too simplistic. see below.
|
|
/*
|
|
// SoM: reimplement hardware channel wrap-around
|
|
if(++handle >= MAX_CHANNELS)
|
|
handle = 0;
|
|
*/
|
|
|
|
// haleyjd 06/03/06: look for an unused hardware channel
|
|
for(handle = 0; handle < MAX_CHANNELS; ++handle)
|
|
{
|
|
if(channelinfo[handle].data == NULL)
|
|
break;
|
|
}
|
|
|
|
// all used? don't play the sound. It's preferable to miss a sound
|
|
// than it is to cut off one already playing, which sounds weird.
|
|
if(handle == MAX_CHANNELS)
|
|
return -1;
|
|
|
|
// haleyjd 02/18/05: cannot proceed until channel is unlocked
|
|
while(channelinfo[handle].lock)
|
|
SDL_Delay(1);
|
|
|
|
if(addsfx(sound, handle))
|
|
{
|
|
channelinfo[handle].idnum = id++; // give the sound a unique id
|
|
updateSoundParams(handle, vol, sep, pitch);
|
|
}
|
|
else
|
|
handle = -1;
|
|
|
|
return handle;
|
|
}
|
|
|
|
//
|
|
// I_StopSound
|
|
//
|
|
// Stop the sound. Necessary to prevent runaway chainsaw,
|
|
// and to stop rocket launches when an explosion occurs.
|
|
//
|
|
void I_StopSound(int handle)
|
|
{
|
|
if(!snd_init)
|
|
return;
|
|
|
|
#ifdef RANGECHECK
|
|
if(handle < 0 || handle >= MAX_CHANNELS)
|
|
I_Error("I_StopSound: handle out of range");
|
|
#endif
|
|
|
|
stopchan(handle);
|
|
}
|
|
|
|
//
|
|
// I_SoundIsPlaying
|
|
//
|
|
// haleyjd: wow, this can actually do something in the Windows version :P
|
|
//
|
|
int I_SoundIsPlaying(int handle)
|
|
{
|
|
if(!snd_init)
|
|
return false;
|
|
|
|
#ifdef RANGECHECK
|
|
if(handle < 0 || handle >= MAX_CHANNELS)
|
|
I_Error("I_SoundIsPlaying: handle out of range");
|
|
#endif
|
|
|
|
return (channelinfo[handle].data != NULL);
|
|
}
|
|
|
|
//
|
|
// I_SoundID
|
|
//
|
|
// haleyjd: returns the unique id number assigned to a specific instance
|
|
// of a sound playing on a given channel. This is required to make sure
|
|
// that the higher-level sound code doesn't start updating sounds that have
|
|
// been displaced without it noticing.
|
|
//
|
|
int I_SoundID(int handle)
|
|
{
|
|
if(!snd_init)
|
|
return 0;
|
|
|
|
#ifdef RANGECHECK
|
|
if(handle < 0 || handle >= MAX_CHANNELS)
|
|
I_Error("I_SoundID: handle out of range\n");
|
|
#endif
|
|
|
|
return channelinfo[handle].idnum;
|
|
}
|
|
|
|
// This function loops all active (internal) sound
|
|
// channels, retrieves a given number of samples
|
|
// from the raw sound data, modifies it according
|
|
// to the current (internal) channel parameters,
|
|
// mixes the per channel samples into the global
|
|
// mixbuffer, clamping it to the allowed range,
|
|
// and sets up everything for transferring the
|
|
// contents of the mixbuffer to the (two)
|
|
// hardware channels (left and right, that is).
|
|
//
|
|
// allegro does this now
|
|
|
|
void I_UpdateSound(void)
|
|
{
|
|
}
|
|
|
|
|
|
#define STEP sizeof(Sint16)
|
|
#define STEPSHIFT 1
|
|
|
|
//
|
|
// I_SDLUpdateSound
|
|
//
|
|
// SDL_mixer postmix callback routine. Possibly dispatched asynchronously.
|
|
// We do our own mixing on up to 32 digital sound channels.
|
|
//
|
|
static void I_SDLUpdateSound(void *userdata, Uint8 *stream, int len)
|
|
{
|
|
// Mix current sound data.
|
|
// Data, from raw sound, for right and left.
|
|
register Uint8 sample;
|
|
register Sint32 dl;
|
|
register Sint32 dr;
|
|
|
|
// Pointers in audio stream, left, right, end.
|
|
Sint16 *leftout;
|
|
Sint16 *rightout;
|
|
Sint16 *leftend;
|
|
|
|
// Mixing channel index.
|
|
int chan;
|
|
|
|
// Left and right channel
|
|
// are in audio stream, alternating.
|
|
leftout = (Sint16 *)stream;
|
|
rightout = leftout + 1;
|
|
|
|
// Determine end, for left channel only
|
|
// (right channel is implicit).
|
|
leftend = leftout + len / STEP;
|
|
|
|
// Mix sounds into the mixing buffer.
|
|
// Loop over step*SAMPLECOUNT,
|
|
// that is 512 values for two channels.
|
|
while(leftout != leftend)
|
|
{
|
|
// 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 < MAX_CHANNELS; ++chan)
|
|
{
|
|
// Check channel, if active.
|
|
if(!channelinfo[chan].data)
|
|
continue;
|
|
|
|
// haleyjd 02/18/05: lock the channel to prevent possible race
|
|
// conditions in the below loop that could modify
|
|
// channelinfo[chan].data while it's being used here.
|
|
channelinfo[chan].lock = 1;
|
|
|
|
// Get the raw data from the channel.
|
|
// Sounds are now prefiltered.
|
|
sample = *(channelinfo[chan].data);
|
|
|
|
// Add left and right part
|
|
// for this channel (sound)
|
|
// to the current data.
|
|
// Adjust volume accordingly.
|
|
dl += channelinfo[chan].leftvol_lookup[sample];
|
|
dr += channelinfo[chan].rightvol_lookup[sample];
|
|
|
|
// Increment index ???
|
|
channelinfo[chan].stepremainder += channelinfo[chan].step;
|
|
|
|
// MSB is next sample???
|
|
channelinfo[chan].data += channelinfo[chan].stepremainder >> 16;
|
|
|
|
// Limit to LSB???
|
|
channelinfo[chan].stepremainder &= 0xffff;
|
|
|
|
// Check whether we are done.
|
|
if(channelinfo[chan].data >= channelinfo[chan].enddata)
|
|
{
|
|
// haleyjd 02/18/05: unlock channel
|
|
channelinfo[chan].lock = 0;
|
|
stopchan(chan);
|
|
}
|
|
else // haleyjd 02/18/05: unlock channel
|
|
channelinfo[chan].lock = 0;
|
|
}
|
|
|
|
// Clamp to range. Left hardware channel.
|
|
if(dl > SHRT_MAX)
|
|
*leftout = SHRT_MAX;
|
|
else if(dl < SHRT_MIN)
|
|
*leftout = SHRT_MIN;
|
|
else
|
|
*leftout = (short)dl;
|
|
|
|
// Same for right hardware channel.
|
|
if(dr > SHRT_MAX)
|
|
*rightout = SHRT_MAX;
|
|
else if(dr < SHRT_MIN)
|
|
*rightout = SHRT_MIN;
|
|
else
|
|
*rightout = (short)dr;
|
|
|
|
// Increment current pointers in stream
|
|
leftout += STEP;
|
|
rightout += STEP;
|
|
}
|
|
}
|
|
|
|
|
|
// This would be used to write out the mixbuffer
|
|
// during each game loop update.
|
|
// Updates sound buffer and audio device at runtime.
|
|
// It is called during Timer interrupt with SNDINTR.
|
|
|
|
void I_SubmitSound(void)
|
|
{
|
|
//this should no longer be necessary because
|
|
//allegro is doing all the sound mixing now
|
|
}
|
|
|
|
//
|
|
// I_ShutdownSound
|
|
//
|
|
// atexit handler.
|
|
//
|
|
void I_ShutdownSound(void)
|
|
{
|
|
if(snd_init)
|
|
{
|
|
Mix_CloseAudio();
|
|
snd_init = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// I_CacheSound
|
|
//
|
|
// haleyjd 11/05/03: fixed for SDL sound engine
|
|
// haleyjd 09/24/06: added sound aliases
|
|
//
|
|
void I_CacheSound(sfxinfo_t *sound)
|
|
{
|
|
if(sound->link)
|
|
I_CacheSound(sound->link);
|
|
else
|
|
{
|
|
int lump = I_GetSfxLumpNum(sound);
|
|
|
|
// replace missing sounds with a reasonable default
|
|
if(lump == -1)
|
|
lump = W_GetNumForName("DSPISTOL");
|
|
|
|
W_CacheLumpNum(lump, PU_CACHE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// I_InitSound
|
|
//
|
|
// SoM 9/14/02: Rewrite. code taken from prboom to use SDL_Mixer
|
|
//
|
|
void I_InitSound(void)
|
|
{
|
|
if(!nosfxparm)
|
|
{
|
|
int audio_buffers;
|
|
|
|
printf("I_InitSound: ");
|
|
|
|
/* Initialize variables */
|
|
audio_buffers = SAMPLECOUNT * snd_samplerate / 11025;
|
|
|
|
// haleyjd: the docs say we should do this
|
|
if(SDL_InitSubSystem(SDL_INIT_AUDIO))
|
|
{
|
|
printf("Couldn't initialize SDL audio.\n");
|
|
snd_card = 0;
|
|
mus_card = 0;
|
|
return;
|
|
}
|
|
|
|
if(Mix_OpenAudio(snd_samplerate, MIX_DEFAULT_FORMAT, 2, audio_buffers) < 0)
|
|
{
|
|
printf("Couldn't open audio with desired format.\n");
|
|
snd_card = 0;
|
|
mus_card = 0;
|
|
return;
|
|
}
|
|
|
|
SAMPLECOUNT = audio_buffers;
|
|
Mix_SetPostMix(I_SDLUpdateSound, NULL);
|
|
printf("Configured audio device with %d samples/slice.\n", SAMPLECOUNT);
|
|
|
|
atexit(I_ShutdownSound);
|
|
|
|
snd_init = true;
|
|
|
|
// haleyjd 04/11/03: don't use music if sfx aren't init'd
|
|
// (may be dependent, docs are unclear)
|
|
if(!nomusicparm)
|
|
I_InitMusic();
|
|
}
|
|
}
|
|
|
|
///
|
|
// MUSIC API.
|
|
//
|
|
|
|
// julian (10/25/2005): rewrote (nearly) entirely
|
|
|
|
#include "mmus2mid.h"
|
|
#include "m_misc.h"
|
|
|
|
// Only one track at a time
|
|
static Mix_Music *music = NULL;
|
|
|
|
// Some tracks are directly streamed from the RWops;
|
|
// we need to free them in the end
|
|
static SDL_RWops *rw = NULL;
|
|
|
|
// Same goes for buffers that were allocated to convert music;
|
|
// since this concerns mus, we could do otherwise but this
|
|
// approach is better for consistency
|
|
static void *music_block = NULL;
|
|
|
|
// Macro to make code more readable
|
|
#define CHECK_MUSIC(h) ((h) && music != NULL)
|
|
|
|
//
|
|
// I_ShutdownMusic
|
|
//
|
|
// atexit handler.
|
|
//
|
|
void I_ShutdownMusic(void)
|
|
{
|
|
I_UnRegisterSong(1);
|
|
}
|
|
|
|
//
|
|
// I_InitMusic
|
|
//
|
|
void I_InitMusic(void)
|
|
{
|
|
switch(mus_card)
|
|
{
|
|
case -1:
|
|
printf("I_InitMusic: Using SDL_mixer.\n");
|
|
mus_init = true;
|
|
break;
|
|
default:
|
|
printf("I_InitMusic: Music is disabled.\n");
|
|
break;
|
|
}
|
|
|
|
atexit(I_ShutdownMusic);
|
|
}
|
|
|
|
// jff 1/18/98 changed interface to make mididata destroyable
|
|
|
|
void I_PlaySong(int handle, int looping)
|
|
{
|
|
if(!mus_init)
|
|
return;
|
|
|
|
if(CHECK_MUSIC(handle) && Mix_PlayMusic(music, looping ? -1 : 0) == -1)
|
|
{
|
|
dprintf("I_PlaySong: Mix_PlayMusic failed\n");
|
|
return;
|
|
}
|
|
|
|
// haleyjd 10/28/05: make sure volume settings remain consistent
|
|
I_SetMusicVolume(snd_MusicVolume);
|
|
}
|
|
|
|
//
|
|
// I_SetMusicVolume
|
|
//
|
|
void I_SetMusicVolume(int volume)
|
|
{
|
|
// haleyjd 09/04/06: adjust to use scale from 0 to 15
|
|
Mix_VolumeMusic((volume * 128) / 15);
|
|
}
|
|
|
|
static int paused_midi_volume;
|
|
|
|
//
|
|
// I_PauseSong
|
|
//
|
|
void I_PauseSong(int handle)
|
|
{
|
|
if(CHECK_MUSIC(handle))
|
|
{
|
|
// Not for mids
|
|
if(Mix_GetMusicType(music) != MUS_MID)
|
|
Mix_PauseMusic();
|
|
else
|
|
{
|
|
// haleyjd 03/21/06: set MIDI volume to zero on pause
|
|
paused_midi_volume = Mix_VolumeMusic(-1);
|
|
Mix_VolumeMusic(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// I_ResumeSong
|
|
//
|
|
void I_ResumeSong(int handle)
|
|
{
|
|
if(CHECK_MUSIC(handle))
|
|
{
|
|
// Not for mids
|
|
if(Mix_GetMusicType(music) != MUS_MID)
|
|
Mix_ResumeMusic();
|
|
else
|
|
Mix_VolumeMusic(paused_midi_volume);
|
|
}
|
|
}
|
|
|
|
//
|
|
// I_StopSong
|
|
//
|
|
void I_StopSong(int handle)
|
|
{
|
|
if(CHECK_MUSIC(handle))
|
|
Mix_HaltMusic();
|
|
}
|
|
|
|
//
|
|
// I_UnRegisterSong
|
|
//
|
|
void I_UnRegisterSong(int handle)
|
|
{
|
|
if(CHECK_MUSIC(handle))
|
|
{
|
|
// Stop and free song
|
|
I_StopSong(handle);
|
|
Mix_FreeMusic(music);
|
|
|
|
// Free RWops
|
|
if(rw != NULL)
|
|
SDL_FreeRW(rw);
|
|
|
|
// Free music block
|
|
if(music_block != NULL)
|
|
free(music_block);
|
|
|
|
// Reinitialize all this
|
|
music = NULL;
|
|
rw = NULL;
|
|
music_block = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// I_RegisterSong
|
|
//
|
|
int I_RegisterSong(void *data, int size)
|
|
{
|
|
if(music != NULL)
|
|
I_UnRegisterSong(1);
|
|
|
|
rw = SDL_RWFromMem(data, size);
|
|
music = Mix_LoadMUS_RW(rw);
|
|
|
|
// It's not recognized by SDL_mixer, is it a mus?
|
|
if(music == NULL)
|
|
{
|
|
int err;
|
|
MIDI mididata;
|
|
UBYTE *mid;
|
|
int midlen;
|
|
|
|
SDL_FreeRW(rw);
|
|
rw = NULL;
|
|
|
|
memset(&mididata, 0, sizeof(MIDI));
|
|
|
|
if((err = mmus2mid((byte *)data, &mididata, 89, 0)))
|
|
{
|
|
// Nope, not a mus.
|
|
dprintf("Error loading music: %d", err);
|
|
return 0;
|
|
}
|
|
|
|
// Hurrah! Let's make it a mid and give it to SDL_mixer
|
|
MIDIToMidi(&mididata, &mid, &midlen);
|
|
rw = SDL_RWFromMem(mid, midlen);
|
|
music = Mix_LoadMUS_RW(rw);
|
|
|
|
if(music == NULL)
|
|
{
|
|
// Conversion failed, free everything
|
|
SDL_FreeRW(rw);
|
|
rw = NULL;
|
|
free(mid);
|
|
}
|
|
else
|
|
{
|
|
// Conversion succeeded
|
|
// -> save memory block to free when unregistering
|
|
music_block = mid;
|
|
}
|
|
}
|
|
|
|
// the handle is a simple boolean
|
|
return music != NULL;
|
|
}
|
|
|
|
//
|
|
// I_QrySongPlaying
|
|
//
|
|
// Is the song playing?
|
|
//
|
|
int I_QrySongPlaying(int handle)
|
|
{
|
|
// haleyjd: this is never called
|
|
// julian: and is that a reason not to code it?!?
|
|
// haleyjd: ::shrugs::
|
|
return CHECK_MUSIC(handle);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// $Log: i_sound.c,v $
|
|
// Revision 1.15 1998/05/03 22:32:33 killough
|
|
// beautification, use new headers/decls
|
|
//
|
|
// Revision 1.14 1998/03/09 07:11:29 killough
|
|
// Lock sound sample data
|
|
//
|
|
// Revision 1.13 1998/03/05 00:58:46 jim
|
|
// fixed autodetect not allowed in allegro detect routines
|
|
//
|
|
// Revision 1.12 1998/03/04 11:51:37 jim
|
|
// Detect voices in sound init
|
|
//
|
|
// Revision 1.11 1998/03/02 11:30:09 killough
|
|
// Make missing sound lumps non-fatal
|
|
//
|
|
// Revision 1.10 1998/02/23 04:26:44 killough
|
|
// Add variable pitched sound support
|
|
//
|
|
// Revision 1.9 1998/02/09 02:59:51 killough
|
|
// Add sound sample locks
|
|
//
|
|
// Revision 1.8 1998/02/08 15:15:51 jim
|
|
// Added native midi support
|
|
//
|
|
// Revision 1.7 1998/01/26 19:23:27 phares
|
|
// First rev with no ^Ms
|
|
//
|
|
// Revision 1.6 1998/01/23 02:43:07 jim
|
|
// Fixed failure to not register I_ShutdownSound with atexit on install_sound error
|
|
//
|
|
// Revision 1.4 1998/01/23 00:29:12 killough
|
|
// Fix SSG reload by using frequency stored in lump
|
|
//
|
|
// Revision 1.3 1998/01/22 05:55:12 killough
|
|
// Removed dead past changes, changed destroy_sample to stop_sample
|
|
//
|
|
// Revision 1.2 1998/01/21 16:56:18 jim
|
|
// Music fixed, defaults for cards added
|
|
//
|
|
// Revision 1.1.1.1 1998/01/19 14:02:57 rand
|
|
// Lee's Jan 19 sources
|
|
//
|
|
//----------------------------------------------------------------------------
|