Mapping & Hammer

Soundscapes Guide: Ambient Sound for GMod Maps in Hammer

How Source soundscapes work: env_soundscape, proxy and trigger, a script per map, packing it into the BSP and fixes when the map stays silent.

A soundscape is a named entry in a script file that describes a background mix: looping sounds, random one-shots and a reverb effect. In the map you place one env_soundscape per area and enter the name. The engine decides for each player separately which entity currently applies and crossfades when it changes. You write your own soundscapes into the file scripts/soundscapes_<mapname>.txt and pack it into the BSP together with its sounds.

How a soundscape works

The work is split. The server checks for each player which soundscape entity is active and tells the client. The client reads the entry from the script and plays the sounds locally. This means:

  • Server and client need the same script. If one side does not know the name, there is silence.
  • On a change, loops are crossfaded. The duration is set by soundscape_fadetime, which is 3 seconds in the Source SDK.

Placing the entities: env_soundscape, proxy and trigger

EntityPurposeImportant keyvalues
env_soundscapePoint entity that becomes active through radius and line of sightRadius (radius, default 128), Soundscape (soundscape), Sound Position 0 to 7 (position0 to position7)
env_soundscape_proxyOwn location and radius, takes soundscape and positions from another entitySoundscape Entity (MainSoundscapeName), Radius (radius)
env_soundscape_triggerableLike env_soundscape, and additionally switched by a trigger_soundscapeSame as env_soundscape
trigger_soundscapeBrush trigger that turns on the named entity when enteredSoundscape (soundscape): name of an env_soundscape_triggerable

The selection rule is stated in the FGD: the active soundscape is the last one that had line of sight to the player while the player was within its radius. The Source SDK source code shows the details:

  • Line of sight is checked from the entity to the player. Solid geometry and water block it.
  • If several entities meet both conditions, the nearest one wins.
  • If none meets the conditions any more, the last one keeps playing.
  • A radius of -1 means: line of sight is enough, distance does not matter.

So place the entity in open space and choose a radius that does not reach through doors into the next room. The spawn points need a soundscape too, otherwise players hear nothing until they reach the first entity. For rooms with corners, place several env_soundscape_proxy entities that point to the same main entity. Where line of sight does not match the room layout, for example with glass walls or open courtyards, use trigger_soundscape. According to the Valve Developer Community, an entity that reaches the player through radius and line of sight takes precedence over the trigger, and the env_soundscape_triggerable itself still reacts to radius and line of sight. So place it where players do not come within its radius.

Using existing soundscapes

Which scripts are loaded is listed in scripts/soundscapes_manifest.txt. GMod's manifest lists its own entries such as construct.outside as well as the scripts from Half-Life 2, the episodes, Portal, Counter-Strike: Source, Day of Defeat: Source and Team Fortress 2. The dropdown in the entity only shows a few entries such as GenericIndoor, but you can type any name.

A name in the manifest does not mean the sounds are there. On a current installation, the packages GMod itself ships contain almost all sound files used by the scripts from Half-Life 2, the episodes and Counter-Strike: Source, but only a small part of those from Team Fortress 2 and Day of Defeat: Source. More on this in CS:S and HL2 content in GMod maps.

To audition one, enter sv_cheats 1 in the console and then playsoundscape with a name.

Writing your own script

Create the file scripts/soundscapes_<mapname>.txt, where <mapname> is the name of the BSP without the extension. The engine loads it in addition to the manifest as soon as it exists. Leave the manifest itself untouched. If you rename the map, rename the file with it.

"mymap.hall"
{
	"dsp"	"1"

	"playlooping"
	{
		"volume"	"0.4"
		"wave"		"ambient/atmosphere/indoor2.wav"
	}

	"playlooping"
	{
		"volume"	"0.6"
		"soundlevel"	"SNDLVL_70dB"
		"position"	"0"
		"wave"		"yourname/mymap/generator_loop.wav"
	}

	"playrandom"
	{
		"time"		"8,20"
		"volume"	"0.3,0.6"
		"pitch"		"90,110"
		"soundlevel"	"SNDLVL_75dB"
		"position"	"random"
		"rndwave"
		{
			"wave"	"ambient/levels/canals/drip1.wav"
			"wave"	"ambient/levels/canals/drip2.wav"
		}
	}
}
  • Name: Soundscape names are shared across all loaded files. A prefix such as mymap. prevents collisions.
  • dsp: the reverb effect of the area. In the stock scripts, 0 stands for off and 1 for the automatic choice.
  • playlooping: a continuous loop. The path given for wave is relative to the sound/ folder. Always state volume: in the Source SDK code the initial value is 0, and a loop with volume 0 is skipped.
  • playrandom: plays one of the sounds from rndwave at random intervals. time, volume and pitch are ranges written as minimum,maximum, time in seconds.
  • position: Without it, the sound comes evenly from everywhere. The number 0 refers to the keyvalue Sound Position 0 of the entity, where you enter the name of a target entity, for example an info_target at the generator. random only works in playrandom and places the sound at a different spot near the player each time. A positioned sound in playrandom also needs soundlevel or attenuation: in the Source SDK code the level otherwise stays at 0 and the sound plays without falloff, as if it had no position.

Packing the script and sounds into the BSP

Server and players need the script and every custom sound file. As a loose file in a Workshop addon the script does not come along: gmad's whitelist allows only scripts/vehicles/*.txt in the scripts/ folder. The usual way is therefore the map's pakfile. The procedure is described in Pack custom content into a BSP. Two points are specific to soundscapes:

  • Sounds that only appear in the script are not referenced anywhere in the map itself. Check the list of an automatic packer by hand for that reason.
  • For playlooping you need a WAV with a loop marker (cue point), as the stock loops have. Source recognises a loop only by this marker, without it the WAV plays once.

Whether script and sounds have arrived in the pakfile is shown by bspzip -dir or by the Map Scanner, which lists the embedded files.

When ambient_generic fits better

Soundscapeambient_generic
Meant forthe base mood of an areaa single sound source
Controlautomatic, by the player's locationthrough inputs such as PlaySound, StopSound, Volume, FadeIn, FadeOut
Who hears itevery player their own mixall players in range the same

Use ambient_generic for everything the map is supposed to switch: a machine with an off switch, an alarm, music at the press of a button. According to the FGD, the flags "Start Silent" and "Is NOT Looped" are set on a new entity. It therefore stays silent until an output fires PlaySound. How to wire that up is covered in Hammer inputs and outputs.

Typical problems

  • Silence everywhere: Usually the name is wrong, or the file is not named like the map. With developer 1 the console reports Can't find soundscape: <name>. According to the Valve Developer Community, soundscapes also need a map compiled with VVIS, at least with -fast.
  • The soundscape does not switch, or switches in the wrong place: The new entity has no line of sight, its radius is too small, or a nearer entity sees into the room through a door. soundscape_debug 1 draws lines to all entities: green for the active one, white for entities in range, red for entities out of range.
  • Other players are missing sounds: The script or sound files are not packed, or the soundscape uses sounds from a game the players do not have. Test the packed map without your loose files.
  • Changes to the script have no effect: soundscape_flush reloads the scripts without restarting the map.
sv_cheats 1
soundscape_debug 1
playsoundscape mymap.hall
stopsoundscape
soundscape_flush

Commands two to four need sv_cheats 1. The server accepts soundscape_flush only from its own console or from the host of a local server, and in the Source SDK the client-side part it triggers (cl_soundscape_flush) is a cheat command as well.

FAQ

Do all players hear the same soundscape?

No. The active soundscape is determined for each player separately and played on their client. Everyone hears the mix of their own location.

What do the numbers for dsp mean?

Each number stands for a reverb preset. The comments in Valve's own script list, besides 0 (off) and 1 (generic), among others 2 to 4 for metal rooms, 5 to 7 for tunnels, 17 to 19 for concrete rooms and 23 to 25 for caverns, each as small, medium and large, plus 14 to 16 for water.

Do I have to edit soundscapes_manifest.txt?

No. The engine loads a file named scripts/soundscapes_<mapname>.txt by itself, in addition to everything listed in the manifest. The manifest applies to all maps and stays unchanged.