diff options
Diffstat (limited to 'src/doom/s_sound.c')
-rw-r--r-- | src/doom/s_sound.c | 684 |
1 files changed, 684 insertions, 0 deletions
diff --git a/src/doom/s_sound.c b/src/doom/s_sound.c new file mode 100644 index 00000000..7f4411dd --- /dev/null +++ b/src/doom/s_sound.c @@ -0,0 +1,684 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// 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: none +// +//----------------------------------------------------------------------------- + +#include <stdio.h> +#include <stdlib.h> + +#include "i_sound.h" +#include "i_system.h" + +#include "doomfeatures.h" +#include "deh_str.h" + +#include "doomstat.h" +#include "doomtype.h" + +#include "sounds.h" +#include "s_sound.h" + +#include "m_random.h" +#include "m_argv.h" + +#include "p_local.h" +#include "w_wad.h" +#include "z_zone.h" + +// when to clip out sounds +// Does not fit the large outdoor areas. + +#define S_CLIPPING_DIST (1200 * FRACUNIT) + +// Distance tp origin when sounds should be maxed out. +// This should relate to movement clipping resolution +// (see BLOCKMAP handling). +// In the source code release: (160*FRACUNIT). Changed back to the +// Vanilla value of 200 (why was this changed?) + +#define S_CLOSE_DIST (200 * FRACUNIT) + +// The range over which sound attenuates + +#define S_ATTENUATOR ((S_CLIPPING_DIST - S_CLOSE_DIST) >> FRACBITS) + +// Stereo separation + +#define S_STEREO_SWING (96 * FRACUNIT) + +#define NORM_PITCH 128 +#define NORM_PRIORITY 64 +#define NORM_SEP 128 + +typedef struct +{ + // sound information (if null, channel avail.) + sfxinfo_t *sfxinfo; + + // origin of sound + mobj_t *origin; + + // handle of the sound being played + int handle; + +} channel_t; + +// The set of channels available + +static channel_t *channels; + +// Maximum volume of a sound effect. +// Internal default is max out of 0-15. + +int sfxVolume = 8; + +// Maximum volume of music. + +int musicVolume = 8; + +// Internal volume level, ranging from 0-127 + +static int snd_SfxVolume; + +// Whether songs are mus_paused + +static boolean mus_paused; + +// Music currently being played + +static musicinfo_t *mus_playing = NULL; + +// Number of channels to use + +int snd_channels = 8; + +// +// Initializes sound stuff, including volume +// Sets channels, SFX and music volume, +// allocates channel buffer, sets S_sfx lookup. +// + +void S_Init(int sfxVolume, int musicVolume) +{ + int i; + + I_InitSound(true); + I_InitMusic(); + + I_PrecacheSounds(S_sfx, NUMSFX); + + S_SetSfxVolume(sfxVolume); + S_SetMusicVolume(musicVolume); + + // Allocating the internal channels for mixing + // (the maximum numer of sounds rendered + // simultaneously) within zone memory. + channels = Z_Malloc(snd_channels*sizeof(channel_t), PU_STATIC, 0); + + // Free all channels for use + for (i=0 ; i<snd_channels ; i++) + { + channels[i].sfxinfo = 0; + } + + // no sounds are playing, and they are not mus_paused + mus_paused = 0; + + // Note that sounds have not been cached (yet). + for (i=1 ; i<NUMSFX ; i++) + { + S_sfx[i].lumpnum = S_sfx[i].usefulness = -1; + } + + I_AtExit(S_Shutdown, true); +} + +void S_Shutdown(void) +{ + I_ShutdownSound(); + I_ShutdownMusic(); +} + +static void S_StopChannel(int cnum) +{ + int i; + channel_t *c; + + c = &channels[cnum]; + + if (c->sfxinfo) + { + // stop the sound playing + + if (I_SoundIsPlaying(c->handle)) + { + I_StopSound(c->handle); + } + + // check to see if other channels are playing the sound + + for (i=0; i<snd_channels; i++) + { + if (cnum != i && c->sfxinfo == channels[i].sfxinfo) + { + break; + } + } + + // degrade usefulness of sound data + + c->sfxinfo->usefulness--; + c->sfxinfo = NULL; + } +} + +// +// Per level startup code. +// Kills playing sounds at start of level, +// determines music if any, changes music. +// + +void S_Start(void) +{ + int cnum; + int mnum; + + // kill all playing sounds at start of level + // (trust me - a good idea) + for (cnum=0 ; cnum<snd_channels ; cnum++) + { + if (channels[cnum].sfxinfo) + { + S_StopChannel(cnum); + } + } + + // start new music for the level + mus_paused = 0; + + if (gamemode == commercial) + { + mnum = mus_runnin + gamemap - 1; + } + else + { + int spmus[]= + { + // Song - Who? - Where? + + mus_e3m4, // American e4m1 + mus_e3m2, // Romero e4m2 + mus_e3m3, // Shawn e4m3 + mus_e1m5, // American e4m4 + mus_e2m7, // Tim e4m5 + mus_e2m4, // Romero e4m6 + mus_e2m6, // J.Anderson e4m7 CHIRON.WAD + mus_e2m5, // Shawn e4m8 + mus_e1m9, // Tim e4m9 + }; + + if (gameepisode < 4) + { + mnum = mus_e1m1 + (gameepisode-1)*9 + gamemap-1; + } + else + { + mnum = spmus[gamemap-1]; + } + } + + S_ChangeMusic(mnum, true); +} + +void S_StopSound(mobj_t *origin) +{ + int cnum; + + for (cnum=0 ; cnum<snd_channels ; cnum++) + { + if (channels[cnum].sfxinfo && channels[cnum].origin == origin) + { + S_StopChannel(cnum); + break; + } + } +} + +// +// S_GetChannel : +// If none available, return -1. Otherwise channel #. +// + +static int S_GetChannel(mobj_t *origin, sfxinfo_t *sfxinfo) +{ + // channel number to use + int cnum; + + channel_t* c; + + // Find an open channel + for (cnum=0 ; cnum<snd_channels ; cnum++) + { + if (!channels[cnum].sfxinfo) + { + break; + } + else if (origin && channels[cnum].origin == origin) + { + S_StopChannel(cnum); + break; + } + } + + // None available + if (cnum == snd_channels) + { + // Look for lower priority + for (cnum=0 ; cnum<snd_channels ; cnum++) + { + if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) + { + break; + } + } + + if (cnum == snd_channels) + { + // FUCK! No lower priority. Sorry, Charlie. + return -1; + } + else + { + // Otherwise, kick out lower priority. + S_StopChannel(cnum); + } + } + + c = &channels[cnum]; + + // channel is decided to be cnum. + c->sfxinfo = sfxinfo; + c->origin = origin; + + return cnum; +} + +// +// Changes volume and stereo-separation variables +// from the norm of a sound effect to be played. +// If the sound is not audible, returns a 0. +// Otherwise, modifies parameters and returns 1. +// + +static int S_AdjustSoundParams(mobj_t *listener, mobj_t *source, + int *vol, int *sep) +{ + fixed_t approx_dist; + fixed_t adx; + fixed_t ady; + angle_t angle; + + // calculate the distance to sound origin + // and clip it if necessary + adx = abs(listener->x - source->x); + ady = abs(listener->y - source->y); + + // From _GG1_ p.428. Appox. eucledian distance fast. + approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); + + if (gamemap != 8 && approx_dist > S_CLIPPING_DIST) + { + return 0; + } + + // angle of source to listener + angle = R_PointToAngle2(listener->x, + listener->y, + source->x, + source->y); + + if (angle > listener->angle) + { + angle = angle - listener->angle; + } + else + { + angle = angle + (0xffffffff - listener->angle); + } + + angle >>= ANGLETOFINESHIFT; + + // stereo separation + *sep = 128 - (FixedMul(S_STEREO_SWING, finesine[angle]) >> FRACBITS); + + // volume calculation + if (approx_dist < S_CLOSE_DIST) + { + *vol = snd_SfxVolume; + } + else if (gamemap == 8) + { + if (approx_dist > S_CLIPPING_DIST) + { + approx_dist = S_CLIPPING_DIST; + } + + *vol = 15+ ((snd_SfxVolume-15) + *((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) + / S_ATTENUATOR; + } + else + { + // distance effect + *vol = (snd_SfxVolume + * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) + / S_ATTENUATOR; + } + + return (*vol > 0); +} + +void S_StartSound(void *origin_p, int sfx_id) +{ + sfxinfo_t *sfx; + mobj_t *origin; + int rc; + int sep; + int priority; + int cnum; + int volume; + + origin = (mobj_t *) origin_p; + volume = snd_SfxVolume; + + // check for bogus sound # + if (sfx_id < 1 || sfx_id > NUMSFX) + { + I_Error("Bad sfx #: %d", sfx_id); + } + + sfx = &S_sfx[sfx_id]; + + // Initialize sound parameters + if (sfx->link) + { + priority = sfx->priority; + volume += sfx->volume; + + if (volume < 1) + { + return; + } + + if (volume > snd_SfxVolume) + { + volume = snd_SfxVolume; + } + } + else + { + priority = NORM_PRIORITY; + } + + + // Check to see if it is audible, + // and if not, modify the params + if (origin && origin != players[consoleplayer].mo) + { + rc = S_AdjustSoundParams(players[consoleplayer].mo, + origin, + &volume, + &sep); + + if (origin->x == players[consoleplayer].mo->x + && origin->y == players[consoleplayer].mo->y) + { + sep = NORM_SEP; + } + + if (!rc) + { + return; + } + } + else + { + sep = NORM_SEP; + } + + // kill old sound + S_StopSound(origin); + + // try to find a channel + cnum = S_GetChannel(origin, sfx); + + if (cnum < 0) + { + return; + } + + // increase the usefulness + if (sfx->usefulness++ < 0) + { + sfx->usefulness = 1; + } + + if (sfx->lumpnum < 0) + { + sfx->lumpnum = I_GetSfxLumpNum(sfx); + } + + channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep); +} + +// +// Stop and resume music, during game PAUSE. +// + +void S_PauseSound(void) +{ + if (mus_playing && !mus_paused) + { + I_PauseSong(); + mus_paused = true; + } +} + +void S_ResumeSound(void) +{ + if (mus_playing && mus_paused) + { + I_ResumeSong(); + mus_paused = false; + } +} + +// +// Updates music & sounds +// + +void S_UpdateSounds(mobj_t *listener) +{ + int audible; + int cnum; + int volume; + int sep; + sfxinfo_t* sfx; + channel_t* c; + + for (cnum=0; cnum<snd_channels; cnum++) + { + c = &channels[cnum]; + sfx = c->sfxinfo; + + if (c->sfxinfo) + { + if (I_SoundIsPlaying(c->handle)) + { + // initialize parameters + volume = snd_SfxVolume; + sep = NORM_SEP; + + if (sfx->link) + { + volume += sfx->volume; + if (volume < 1) + { + S_StopChannel(cnum); + continue; + } + else if (volume > snd_SfxVolume) + { + volume = snd_SfxVolume; + } + } + + // check non-local sounds for distance clipping + // or modify their params + if (c->origin && listener != c->origin) + { + audible = S_AdjustSoundParams(listener, + c->origin, + &volume, + &sep); + + if (!audible) + { + S_StopChannel(cnum); + } + else + { + I_UpdateSoundParams(c->handle, volume, sep); + } + } + } + else + { + // if channel is allocated but sound has stopped, + // free it + S_StopChannel(cnum); + } + } + } +} + +void S_SetMusicVolume(int volume) +{ + if (volume < 0 || volume > 127) + { + I_Error("Attempt to set music volume at %d", + volume); + } + + I_SetMusicVolume(volume); +} + +void S_SetSfxVolume(int volume) +{ + if (volume < 0 || volume > 127) + { + I_Error("Attempt to set sfx volume at %d", volume); + } + + snd_SfxVolume = volume; +} + +// +// Starts some music with the music id found in sounds.h. +// + +void S_StartMusic(int m_id) +{ + S_ChangeMusic(m_id, false); +} + +void S_ChangeMusic(int musicnum, int looping) +{ + musicinfo_t *music = NULL; + char namebuf[9]; + void *handle; + + // The Doom IWAD file has two versions of the intro music: d_intro + // and d_introa. The latter is used for OPL playback. + + if (musicnum == mus_intro && (snd_musicdevice == SNDDEVICE_ADLIB + || snd_musicdevice == SNDDEVICE_SB)) + { + musicnum = mus_introa; + } + + if (musicnum <= mus_None || musicnum >= NUMMUSIC) + { + I_Error("Bad music number %d", musicnum); + } + else + { + music = &S_music[musicnum]; + } + + if (mus_playing == music) + { + return; + } + + // shutdown old music + S_StopMusic(); + + // get lumpnum if neccessary + if (!music->lumpnum) + { + sprintf(namebuf, "d_%s", DEH_String(music->name)); + music->lumpnum = W_GetNumForName(namebuf); + } + + music->data = W_CacheLumpNum(music->lumpnum, PU_STATIC); + + handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); + music->handle = handle; + I_PlaySong(handle, looping); + + mus_playing = music; +} + +boolean S_MusicPlaying(void) +{ + return I_MusicIsPlaying(); +} + +void S_StopMusic(void) +{ + if (mus_playing) + { + if (mus_paused) + { + I_ResumeSong(); + } + + I_StopSong(); + I_UnRegisterSong(mus_playing->handle); + W_ReleaseLumpNum(mus_playing->lumpnum); + mus_playing->data = NULL; + mus_playing = NULL; + } +} + |