doom

a minimalistic implementation of doom
git clone git://ssnf.xyz/doom
Log | Files | Refs

s_sound.c (8232B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 
      4 #include "i_system.h"
      5 #include "i_sound.h"
      6 #include "sounds.h"
      7 #include "s_sound.h"
      8 #include "z_zone.h"
      9 #include "m_random.h"
     10 #include "w_wad.h"
     11 #include "doomdef.h"
     12 #include "p_local.h"
     13 #include "doomstat.h"
     14 
     15 const char snd_prefixen[] = { 'P', 'P', 'A', 'S', 'S', 'S', 'M', 'M', 'M', 'S', 'S', 'S' };
     16 
     17 #define S_MAX_VOLUME		127
     18 #define S_CLIPPING_DIST		(1200*0x10000)
     19 #define S_CLOSE_DIST		(160*0x10000)
     20 #define S_ATTENUATOR		((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS)
     21 #define NORM_VOLUME    		snd_MaxVolume
     22 #define NORM_PITCH     		128
     23 #define NORM_PRIORITY		64
     24 #define NORM_SEP		128
     25 #define S_PITCH_PERTURB		1
     26 #define S_STEREO_SWING		(96*0x10000)
     27 #define S_IFRACVOL		30
     28 #define NA			0
     29 #define S_NUMCHANNELS		2
     30 
     31 typedef struct {
     32 	sfxinfo_t*	sfxinfo;
     33 	void*	origin;
     34 	int		handle;
     35 } channel_t;
     36 
     37 extern int snd_MusicDevice;
     38 extern int snd_SfxDevice;
     39 extern int snd_DesiredMusicDevice;
     40 extern int snd_DesiredSfxDevice;
     41 
     42 static channel_t*	channels;
     43 int 		snd_SfxVolume = 15;
     44 int 		snd_MusicVolume = 15;
     45 static boolean		mus_paused;
     46 static musicinfo_t*	mus_playing=0;
     47 int			numChannels;
     48 static int		nextcleanup;
     49 
     50 int
     51 S_getChannel(void* origin, sfxinfo_t* sfxinfo);
     52 
     53 int
     54 S_AdjustSoundParams(mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch);
     55 void S_StopChannel(int cnum);
     56 
     57 void
     58 S_Init(int sfxVolume, int musicVolume)
     59 {
     60 	int i;
     61 
     62 	fprintf(stderr, "S_Init: default sfx volume %d\n", sfxVolume);
     63 	I_SetChannels();
     64 	S_SetSfxVolume(sfxVolume);
     65 	S_SetMusicVolume(musicVolume);
     66 	channels = (channel_t*)Z_Malloc(numChannels*sizeof(channel_t), PU_STATIC, 0);
     67 	for (i = 0; i < numChannels; ++i)
     68 	channels[i].sfxinfo = 0;
     69 	mus_paused = 0;
     70 	for (i = 1; i < NUMSFX; ++i)
     71 	S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
     72 }
     73 
     74 void
     75 S_Start()
     76 {
     77 	int cnum, mnum;
     78 	int spmus[] = {
     79 	  mus_e3m4,
     80 	  mus_e3m2,
     81 	  mus_e3m3,
     82 	  mus_e1m5,
     83 	  mus_e2m7,
     84 	  mus_e2m4,
     85 	  mus_e2m6,
     86 	  mus_e2m5,
     87 	  mus_e1m9
     88 	};
     89 
     90 	for (cnum = 0; cnum < numChannels; ++cnum)
     91 		if (channels[cnum].sfxinfo)
     92 			S_StopChannel(cnum);
     93 	mus_paused = 0;
     94 	if (gameepisode < 4)
     95 		mnum = mus_e1m1 + (gameepisode-1)*9 + gamemap-1;
     96 	else
     97 		mnum = spmus[gamemap-1];
     98 	S_ChangeMusic(mnum, true);
     99 	nextcleanup = 15;
    100 }
    101 
    102 void
    103 S_StartSoundAtVolume(void* origin_p, int sfx_id, int volume)
    104 {
    105 	sfxinfo_t* sfx;
    106 	mobj_t*    origin;
    107 	int        rc, sep, pitch, priority, cnum;
    108 	origin = (mobj_t *) origin_p;
    109 	if (sfx_id < 1 || sfx_id > NUMSFX)
    110 		I_Error("Bad sfx #: %d", sfx_id);
    111 	sfx = &S_sfx[sfx_id];
    112 	if (sfx->link) {
    113 		pitch = sfx->pitch;
    114 		priority = sfx->priority;
    115 		volume += sfx->volume;
    116 		if (volume < 1)
    117 			return;
    118 		if (volume > snd_SfxVolume)
    119 			volume = snd_SfxVolume;
    120 	} else {
    121 		pitch = NORM_PITCH;
    122 		priority = NORM_PRIORITY;
    123 	}
    124 	if (origin && origin != players[consoleplayer].mo) {
    125 		rc = S_AdjustSoundParams(players[consoleplayer].mo, origin, &volume, &sep, &pitch);
    126 		if (origin->x == players[consoleplayer].mo->x && origin->y == players[consoleplayer].mo->y)
    127 			sep = NORM_SEP;
    128 		if (!rc)
    129 			return;
    130 	} else sep = NORM_SEP;
    131 	if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit) {
    132 		pitch += 8 - (M_Random()&15);
    133 		if (pitch<0)
    134 			pitch = 0;
    135 		if (pitch>255)
    136 			pitch = 255;
    137 	}
    138 	if (sfx_id != sfx_itemup && sfx_id != sfx_tink) {
    139 		pitch += 16 - (M_Random()&31);
    140 		if (pitch < 0)
    141 		  pitch = 0;
    142 		if (pitch > 255)
    143 		  pitch = 255;
    144 	}
    145 	S_StopSound(origin);
    146 	cnum = S_getChannel(origin, sfx);
    147 	if (cnum<0) return;
    148 	if (sfx->lumpnum < 0) sfx->lumpnum = I_GetSfxLumpNum(sfx);
    149 	if (!sfx->data) fprintf(stderr, "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n");
    150 	if (sfx->usefulness++ < 0) sfx->usefulness = 1;
    151 	channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority);
    152 }
    153 
    154 void
    155 S_StartSound(void* origin, int sfx_id)
    156 {
    157 	S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume);
    158 }
    159 
    160 void
    161 S_StopSound(void *origin)
    162 {
    163 	int cnum;
    164 
    165 	for (cnum = 0; cnum < numChannels; ++cnum)
    166 		if (channels[cnum].sfxinfo && channels[cnum].origin == origin) {
    167 			S_StopChannel(cnum);
    168 			break;
    169 		}
    170 }
    171 
    172 void
    173 S_PauseSound()
    174 {
    175 	if (mus_playing && !mus_paused) {
    176 		I_PauseSong(mus_playing->handle);
    177 		mus_paused = true;
    178 	}
    179 }
    180 
    181 void
    182 S_ResumeSound()
    183 {
    184 	if (mus_playing && mus_paused) {
    185 		I_ResumeSong(mus_playing->handle);
    186 		mus_paused = false;
    187 	}
    188 }
    189 
    190 void
    191 S_UpdateSounds(void* listener_p)
    192 {
    193 	sfxinfo_t*	sfx;
    194 	channel_t*	c;
    195 	mobj_t*	listener;
    196 	int		audible, cnum, volume, sep, pitch;
    197 
    198 	listener = (mobj_t*)listener_p;
    199 	for (cnum = 0; cnum < numChannels; ++cnum) {
    200 		c = &channels[cnum];
    201 		sfx = c->sfxinfo;
    202 		if (c->sfxinfo) {
    203 			if (I_SoundIsPlaying(c->handle)) {
    204 				volume = snd_SfxVolume;
    205 				pitch = NORM_PITCH;
    206 				sep = NORM_SEP;
    207 				if (sfx->link) {
    208 					pitch = sfx->pitch;
    209 					volume += sfx->volume;
    210 					if (volume < 1) {
    211 						S_StopChannel(cnum);
    212 						continue;
    213 					}
    214 					if (volume > snd_SfxVolume)
    215 						volume = snd_SfxVolume;
    216 				}
    217 				if (c->origin && listener_p != c->origin) {
    218 					audible = S_AdjustSoundParams(listener, c->origin, &volume, &sep, &pitch);
    219 				if (!audible)
    220 					S_StopChannel(cnum);
    221 				else I_UpdateSoundParams(c->handle, volume, sep, pitch);
    222 				}
    223 			}
    224 			else S_StopChannel(cnum);
    225 		}
    226 	}
    227 }
    228 
    229 void
    230 S_SetMusicVolume(int volume)
    231 {
    232 	if (volume < 0 || volume > 127)
    233 		I_Error("Attempt to set music volume at %d", volume);
    234 	I_SetMusicVolume(127);
    235 	I_SetMusicVolume(volume);
    236 	snd_MusicVolume = volume;
    237 }
    238 
    239 void
    240 S_SetSfxVolume(int volume)
    241 {
    242 	if (volume < 0 || volume > 127)
    243 		I_Error("Attempt to set sfx volume at %d", volume);
    244 	snd_SfxVolume = volume;
    245 }
    246 
    247 void
    248 S_StartMusic(int m_id)
    249 {
    250 	S_ChangeMusic(m_id, false);
    251 }
    252 
    253 void
    254 S_ChangeMusic(int musicnum, int looping)
    255 {
    256 	musicinfo_t* music;
    257 	char         namebuf[9];
    258 
    259 	if ((musicnum <= mus_None) || (musicnum >= NUMMUSIC))
    260 		I_Error("Bad music number %d", musicnum);
    261 	music = &S_music[musicnum];
    262 	if (mus_playing == music)
    263 		return;
    264 	S_StopMusic();
    265 	if (!music->lumpnum) {
    266 		sprintf(namebuf, "D_%s", music->name);
    267 		music->lumpnum = W_GetNumForName(namebuf);
    268 	}
    269 	music->data = (void *) W_CacheLumpNum(music->lumpnum, PU_MUSIC);
    270 	music->handle = I_RegisterSong(music->data);
    271 	I_PlaySong(music->handle, looping);
    272 	mus_playing = music;
    273 }
    274 
    275 void
    276 S_StopMusic()
    277 {
    278 	if (mus_playing) {
    279 		if (mus_paused)
    280 			I_ResumeSong(mus_playing->handle);
    281 		I_StopSong(mus_playing->handle);
    282 		I_UnRegisterSong(mus_playing->handle);
    283 		Z_ChangeTag(mus_playing->data, PU_CACHE);
    284 		mus_playing->data = 0;
    285 		mus_playing = 0;
    286 	}
    287 }
    288 
    289 void
    290 S_StopChannel(int cnum)
    291 {
    292 	channel_t*	c;
    293 	int       i;
    294 
    295 	c = &channels[cnum];
    296 	if (c->sfxinfo) {
    297 		if (I_SoundIsPlaying(c->handle))
    298 			I_StopSound(c->handle);
    299 		for (i = 0; i < numChannels; ++i)
    300 			if (cnum != i && c->sfxinfo == channels[i].sfxinfo)
    301 				break;
    302 		c->sfxinfo->usefulness--;
    303 		c->sfxinfo = 0;
    304 	}
    305 }
    306 
    307 int
    308 S_AdjustSoundParams(mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch)
    309 {
    310 	fixed_t	approx_dist, adx, ady;
    311 	angle_t	angle;
    312 
    313 	adx = abs(listener->x - source->x);
    314 	ady = abs(listener->y - source->y);
    315 	approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
    316 	if (gamemap != 8 && approx_dist > S_CLIPPING_DIST) 
    317 		return 0;
    318 	angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y);
    319 	if (angle > listener->angle)
    320 		angle = angle - listener->angle;
    321 	else angle = angle + (0xffffffff - listener->angle);
    322 	angle >>= ANGLETOFINESHIFT;
    323 	*sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS);
    324 	if (approx_dist < S_CLOSE_DIST) 
    325 		*vol = snd_SfxVolume;
    326 	else if (gamemap == 8) {
    327 		if (approx_dist > S_CLIPPING_DIST)
    328 			approx_dist = S_CLIPPING_DIST;
    329 		*vol = 15+ ((snd_SfxVolume-15) * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR;
    330 	} else *vol = (snd_SfxVolume * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR;
    331 	return (*vol > 0);
    332 }
    333 
    334 int
    335 S_getChannel(void* origin, sfxinfo_t* sfxinfo)
    336 {
    337 	channel_t* c;
    338 	int        cnum;
    339 	for (cnum = 0; cnum < numChannels; ++cnum) {
    340 		if (!channels[cnum].sfxinfo)
    341 			break;
    342 		if (origin &&  channels[cnum].origin ==  origin) {
    343 			S_StopChannel(cnum);
    344 			break;
    345 		}
    346 	}
    347 	if (cnum == numChannels) {
    348 		for (cnum = 0; cnum < numChannels; ++cnum)
    349 			if (channels[cnum].sfxinfo->priority >= sfxinfo->priority)
    350 				break;
    351 		if (cnum == numChannels)
    352 			return -1;
    353 		else S_StopChannel(cnum);
    354 	}
    355 	c = &channels[cnum];
    356 	c->sfxinfo = sfxinfo;
    357 	c->origin = origin;
    358 	return cnum;
    359 }