Ambient sounds

Discuss programming topics for the various GPL'd game engine sources.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Ambient sounds

Post by frag.machine »

Vanilla Quake supports 4 "ambient" sound loops (I think "content" sounds would be more correct): sky/wind, water, slime and lava. Only two of these ambient sounds are used in the game though (water and wind).
So, I got FitzQuake 0.85 source and altered it to read ambient sounds from 4 cvars (_sfx_envwind, _sfx_envlava, _sfx_envslime and _sfx_envwater). Added a small function to reload the sfx every time one of these cvars are changed, so I can change the sounds at my will. Also, I produced a small test map consisting of 4 isolated rooms, each one containing one of the "content" brushes that triggers ambient sounds (lava, slime, water, sky). And I noticed a funny thing that at first I thought was my fault messing something:

a) if you set _gfx_envlava or _gfxenvwind, they work as expected;
b) if you set _gfx_envslime, nothing changes;
c) water brushes don' t emit the water ambient sound or any sound at all;
d) slime brushes emit the _gfx_envwater sound instead the expected.

As I said, at first I thought I should have messed something, but then I tested the same map with vanilla GlQuake and the behavior persists. I double checked the FitzQuake source and was unable to find anything wrong. So, I want to ask if anyone already observed such behavior ? Could it be a bug in the QBSP/VIS/LIGHT tools I am using (Bengt Jardrup's Quake map utilities) ?

EDIT:

Yeah, it's a "feature" from the VIS tool, after all:

Code: Select all

void CalcAmbientSounds (void)
{
	int		i, j, k, l;
	dleaf_t	*leaf, *hit;
	byte	*vis;
	dface_t	*surf;
	vec3_t	mins, maxs;
	float	d, maxd;
	int		ambient_type;
	texinfo_t	*info;
	miptex_t	*miptex;
	int		ofs;
	float	dists[NUM_AMBIENTS];
	float	vol;
	
	for (i=0 ; i< portalleafs ; i++)
	{
		leaf = &dleafs[i+1];

	//
	// clear ambients
	//
		for (j=0 ; j<NUM_AMBIENTS ; j++)
			dists[j] = 1020;

		vis = &uncompressed[i*bitbytes];
		
		for (j=0 ; j< portalleafs ; j++)
		{
			if ( !(vis[j>>3] & (1<<(j&7))) )
				continue;
		
		//
		// check this leaf for sound textures
		//	
			hit = &dleafs[j+1];

			for (k=0 ; k< hit->nummarksurfaces ; k++)
			{
				surf = &dfaces[dmarksurfaces[hit->firstmarksurface + k]];
				info = &texinfo[surf->texinfo];
				ofs = ((dmiptexlump_t *)dtexdata)->dataofs[info->miptex];
				miptex = (miptex_t *)(&dtexdata[ofs]);

				if ( !Q_strncasecmp (miptex->name, "*water", 6) )
					ambient_type = AMBIENT_WATER;
				else if ( !Q_strncasecmp (miptex->name, "sky", 3) )
					ambient_type = AMBIENT_SKY;
				else if ( !Q_strncasecmp (miptex->name, "*slime", 6) )
					ambient_type = AMBIENT_WATER; // AMBIENT_SLIME;
				else if ( !Q_strncasecmp (miptex->name, "*lava", 6) )
					ambient_type = AMBIENT_LAVA;
				else if ( !Q_strncasecmp (miptex->name, "*04water", 8) )
					ambient_type = AMBIENT_WATER;
				else
					continue;

			// find distance from source leaf to polygon
				SurfaceBBox (surf, mins, maxs);
				maxd = 0;
				for (l=0 ; l<3 ; l++)
				{
					if (mins[l] > leaf->maxs[l])
						d = mins[l] - leaf->maxs[l];
					else if (maxs[l] < leaf->mins[l])
						d = leaf->mins[l] - mins[l];
					else
						d = 0;
					if (d > maxd)
						maxd = d;
				}
				
				maxd = 0.25;
				if (maxd < dists[ambient_type])
					dists[ambient_type] = maxd;
			}
		}
		
		for (j=0 ; j<NUM_AMBIENTS ; j++)
		{
			if (dists[j] < 100)
				vol = 1.0;
			else
			{
				vol = 1.0 - dists[2]*0.002;
				if (vol < 0)
					vol = 0;
			}
			leaf->ambient_level[j] = vol*255;
		}
	}
}
I wonder why the lava ambient sound was left untouched though.

EDIT2: just to clarify: it's working this way since the original VIS tool.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Ambient sounds

Post by frag.machine »

Well, I digged a bit more and looks like I managed to fix this really old bug. (AFAIK it comes from the original tools - most id maps have this problem)

Besides the above mentioned CONTENTS_SLIME being replaced by CONTENTS_WATER, there was another bit of code messed up in the same file preventing things to work as expected, so I am posting here the patched soundpvs.c (from Bengt Jardrup's VIS source, but the fix can be applied to other versions of the tool without changes AFAIK).
As a bonus, now the VIS tool will detect water, slime, lava and sky textures with any capitalization and with the keywords starting in any position on the texture name (in other words, "*04awater" now can emit water sounds, too), provided the texture start with "*" (or "s" or "S" for skies). Here is the complete soundpvs.c:

Code: Select all


#include <string.h>
#include "vis.h"

void CalcAmbientSounds (void)
{
	int	    i, j, k;
	dleaf_t	    *leaf, *hit;
	byte	    *vis;
	dface_t	    *surf;
	float	    maxd;
	int	    ambient_type, *Ambient;
	texinfo_t   *info;
	miptex_t    *miptex;
	int	    ofs, nummiptex;
	float	    dists[NUM_AMBIENTS];
	float	    vol;
	qboolean    AmbientUsed[NUM_AMBIENTS];

	// frag.machine / ambient sounds fix
	char		texname[64];
	int			r, len;

	nummiptex = ((dmiptexlump_t *)dtexdata)->nummiptex;
	Ambient = malloc (nummiptex * sizeof(int));

	// Any textures at all ?
	if (texdatasize != 0)
	{
		for (i = 0; i < NUM_AMBIENTS; ++i)
			AmbientUsed[i] = false;

		// Calculate ambient type only once
		for (i = 0; i < nummiptex; i++)
		{
			Ambient[i] = -1;

			if (NoAmbient)
				continue; // All sounds disabled

			ofs = ((dmiptexlump_t *)dtexdata)->dataofs[i];
			
			if (ofs == -1)
				continue; // Missing texture

			miptex = (miptex_t *)(&dtexdata[ofs]);

			if (miptex->name[0] != '*' && miptex->name[0] != 's' && miptex->name[0] != 'S')
				continue;

			// frag.machine: converts the texture name to lowercase
			len = strlen (miptex->name);
			if (len > 64) {len = 64;}
			memset (texname, '\0', 64);
			for (r = 0; r < len; r++)
			{
				texname[r] = tolower(miptex->name[r]);
			}

			// frag.machine - we check ocurrences of the keywords in any point of the texture name, so "*04awater" and other non-standard texture names will work now
			if (strstr (texname, "water") != NULL)
				Ambient[i] = NoAmbientWater ? -1 : AMBIENT_WATER;
			else if (strstr (texname, "sky") != NULL)
				Ambient[i] = NoAmbientSky ? -1 : AMBIENT_SKY;
			else if (strstr (texname, "slime") != NULL)				
				Ambient[i] = NoAmbientSlime ? -1 : AMBIENT_SLIME; // frag.machine - Used to be AMBIENT_WATER
			else if (strstr (texname, "lava") != NULL)
				Ambient[i] = NoAmbientLava ? -1 : AMBIENT_LAVA;
			
			if (Ambient[i] != -1)
				AmbientUsed[Ambient[i]] = true;
		}
		
		if (!NoAmbient)
		{
			for (i = 0; i < NUM_AMBIENTS; ++i)
			{
				if (AmbientUsed[i])
					break;
			}

			if (i == NUM_AMBIENTS)
				NoAmbient = true; // No ambients used; disable sounds
		}
	}
	
	for (i=0 ; i< portalleafs ; i++)
	{
		leaf = &dleafs[i+1];

	//
	// clear ambients
	//
		for (j=0 ; j<NUM_AMBIENTS ; j++)
			dists[j] = 1020;

		// Any textures at all ?
		if (texdatasize != 0 && !NoAmbient)
		{
			vis = &uncompressed[i*bitbytes];
			
			for (j=0 ; j< portalleafs ; j++)
			{
				if ( !(vis[j>>3] & (1<<(j&7))) )
					continue;
			
			//
			// check this leaf for sound textures
			//	
				hit = &dleafs[j+1];

				for (k=0 ; k< hit->nummarksurfaces ; k++)
				{
					surf = &dfaces[dmarksurfaces[hit->firstmarksurface + k]];
					info = &texinfo[surf->texinfo];
					
					if (Ambient[info->miptex] == -1)
						continue;

					ambient_type = Ambient[info->miptex];

					maxd = 0.25; // Visible => very close
					if (maxd < dists[ambient_type])
						dists[ambient_type] = maxd;
				}
				
				// NOTE: The logic below only works when maxd is fixed (as in original logic)
				for (k = 0; k < NUM_AMBIENTS; ++k)
				{
					if (AmbientUsed[k] && dists[k] == 1020)
						break;
				}

				if (k == NUM_AMBIENTS)
					break; // All used ambient positions done
			}
		}
		
		for (j=0 ; j<NUM_AMBIENTS ; j++)
		{
			if (dists[j] < 100)
				vol = 1.0; // Very close => full volume
			else
			{
				vol = 1.0 - dists[j]*0.002; // frag.machine - used to be hardcoded to "vol = 1.0 - dists[2]*0.002;" 
				if (vol < 0)
					vol = 0;
			}
			leaf->ambient_level[j] = vol*255;
		}
	}
	
	free (Ambient);
}
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Ambient sounds

Post by frag.machine »

Oh yeah, almost forgot: there's a small work to do in the engine side. Open up snd_dma.c and find S_Init (). At this end, there's these 2 lines:

Code: Select all

	ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav");
	ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav");
You need to add precaches for slime and lava sounds, too:

Code: Select all

	ambient_sfx[AMBIENT_LAVA] = S_PrecacheSound ("ambience/lava1.wav");
	ambient_sfx[AMBIENT_SLIME] = S_PrecacheSound ("ambience/slime1.wav");
Where "ambience/lava1.wav" and "ambience/slime1.wav" are sounds you need to provide since they don't exist in regular Quake. I suggest a visit to freesound.org or any other public domain sound source.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: Ambient sounds

Post by r00k »

if your precaches are hardcoded whats the point of cvars?

I was tinkering in snd.dma and changed this for shitz and grinz

Code: Select all

void S_UpdateAmbientSounds (void)
{
	mleaf_t		*l;
	float		vol;
	int			ambient_channel = 0;
	channel_t	*chan;

	if (!cl.worldmodel)
		return;

	if (!ambient_level.value)//R00k: if levels are off dont 'Mod_PointInLeaf' every frame!
	{
		if (channels[ambient_channel].sfx != NULL)//R00k, if there's an sfx in use then clear all...
		{
			for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
				channels[ambient_channel].sfx = NULL;
		}
		return;
	}

	l = Mod_PointInLeaf (listener_origin, cl.worldmodel);

	if (!l)
	{
		for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
			channels[ambient_channel].sfx = NULL;
		return;
	}
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Ambient sounds

Post by frag.machine »

r00k wrote:if your precaches are hardcoded whats the point of cvars?
I'm sorry, I should have made explicit I was using standard FitzQuake code as reference to the fix.
Of course they aren't fixed (at least, not in this way - the default values of _sfx_env* cvars are the original wav files, so things will work as expected without further intervention).
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Ambient sounds

Post by mankrip »

I suggest adding a fallback for the engine to use the ambient water sound in the place of the slime/lava sound if the slime/lava sound file isn't found.

Also, frag.machine, dunno why you've used the names "slime1" and "lava1" instead of just "slime" and "lava".
"water1" and "wind2" have numbers, but for new files this doesn't seem necessary.

Would be nice to hunt some official ambient sounds for these in other Id games. Maybe from Q2, QIII or Doom 3.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Ambient sounds

Post by frag.machine »

mankrip wrote:I suggest adding a fallback for the engine to use the ambient water sound in the place of the slime/lava sound if the slime/lava sound file isn't found.

Also, frag.machine, dunno why you've used the names "slime1" and "lava1" instead of just "slime" and "lava".
"water1" and "wind2" have numbers, but for new files this doesn't seem necessary.

Would be nice to hunt some official ambient sounds for these in other Id games. Maybe from Q2, QIII or Doom 3.
/d'oh!
You're correct, "lava1.wav" and "slime1.wav" are sounds I got from freesound.org for testing purposes, I should have changed that.
IMHO the sanest approach would be to use "misc/null.wav" as default for both cvars, since stock Quake doesn't provide useful sounds.
This would have the benefit of behaving exactly like stock Quake if unmodified.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Ambient sounds

Post by revelator »

Heh dunno but im thinking in terms of a parser that could change the abients based on numerical values :) so the idea is not exactly bad something nice might come out of it. Not quite sure how to get the environment data for say a cave so that the ambients have an Echo effect though. If someone knows how i think it might make for an interresting improvement to quake.
Productivity is a state of mind.
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Ambient sounds

Post by mankrip »

reckless wrote:Not quite sure how to get the environment data for say a cave so that the ambients have an Echo effect though.
That depends not only on the shape of the environment, but also on what it's made of. Individual resonance values would have to be assigned to each texture, and then the thickness and the shape of the walls would determine how to calculate the echo. But the BSP format also have infinitely thick walls (because everything outside of the map is solid), which there's no proper way to handle automatically.

At least, that's my guess.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: Ambient sounds

Post by r00k »

maybe add an echo to the wav file using audacity since say cave1.wav would always echo?
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Ambient sounds

Post by revelator »

Yep nothing quite as complicated but it would add some umph to it :)
Productivity is a state of mind.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: Ambient sounds

Post by frag.machine »

I can't imagine a way to automatically switch to an "echo" effect; I suppose the easiest way to do that is using some kind of "trigger_environment" (or some sort of metadata added to the BSP format saying something like "if you're inside the volumetric area defined by coord1 and coord2 enviromental sounds use an echo effect". In other words, the map needs to have some "hint" about to activate the effect in the engine. Another solution would require some sort of shader support where you could hint about sound effects, and again it would be a task to the mapper to mark "echo" areas thru the use of shaders.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Ambient sounds

Post by revelator »

Hmm ill take a look at the bsp defines might be something we could use in them for it :)
Productivity is a state of mind.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Ambient sounds

Post by Spike »

halflife had triggers for its EAX areas...
echo regions only really apply where you have a confined space, you can send out 6 tracelines or so, but you'd need to repeat that process at least one extra time to try to avoid it leaking too badly (distant open areas should be considered closed if they're the corner of pipes or whatever) . And you'd have quite a bit of error in that, so some temporal coherance wouldn't go a miss (weighting it over about 10 seconds with some randomized angles, monte carlo style), but you perhaps can't easily do that inside the vis tool.

need a mixer reverb effect or something. eax for xp users, or switch to openal for linux+vista+ users I guess, or write one in software that works in anything.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Ambient sounds

Post by revelator »

I´nterresting so we can get the distances from tracelines (allbeit not completely error proof) and i seem to remember a worldtype spawn type from the entity definitions allthough i think its only used to tell the bsp parser to stop parsing gonna have a look.
Productivity is a state of mind.
Post Reply