Team Fortress 2/Scripting/Script Functions/EmitSoundEx

From Valve Developer Community
Jump to navigation Jump to search

Function Description

void EmitSoundEx(table parameters)

Flexible way of playing sounds. All parameters except sound_name are optional. Sounds must be precached first using PrecacheSound or PrecacheScriptSound, or they won't play. Sounds can be played in raw or soundscript form, and filtered to everyone, on a per-team or per-player basis. Volume, pitch, channel and attenuation level can be controlled, and the sound can play from a fixed position or relative to an entity. It is also possible to stop or adjust sounds already playing on the entity using special flags such as SND_CHANGE_VOL or SND_CHANGE_PITCH.

Note.pngNote:If the sound is playing while a player is outside of the sound's PAS/PVS/attenuation, and then enters its visiblity, the player will not hear the sound. This may be a problem for looping sounds or sounds with long durations, in which case the following workarounds can be used:
  • Use the RECIPIENT_FILTER_GLOBAL/RECIPIENT_FILTER_TEAM filter. The sound will still "play" outside of the range, but at no volume.
  • Play the sound to the player individually using RECIPIENT_FILTER_SINGLE_PLAYER when they enter a given area, e.g. via a trigger.
  • Create a soundscape which automatically deals with this on a per-player basis
Warning.pngWarning:Looping sounds will NOT stop automatically on round restart or when an entity is destroyed. You must manually stop them with the SND_STOP flag.
Tip.pngTip:The best time to stop looping sounds is during the scorestats_accumulated_update event. This event is fired right before all map entities are cleaned up. On players, stop the sound in the player_disconnect event.

Parameters

Name Type Default Description
sound_name string Name of sound. Can be raw filename or soundscript.
Note.pngNote:Soundscripts override the channel, volume and pitch parameters. However the latter two can be forcefully overriden by adding the SND_CHANGE_VOL or SND_CHANGE_PITCH flags respectively.
channel int 0 Channel ID on which to play the sound. Channel values below have special behavior, all other channels behave normally. List of special channels:
Name Value Description
CHAN_REPLACE -1 Used by console commands
CHAN_AUTO 0 Automatic channel [Clarify]
CHAN_WEAPON 1 Weapon sounds
CHAN_VOICE 2 Character voices
CHAN_ITEM 3 Items [Clarify]
CHAN_BODY 4 Impacts, ragdolls and footsteps
CHAN_STREAM 5 "Stream from static or dynamic area" [Clarify]
CHAN_STATIC 6 Constant or background sounds. Valve recommends this to be used for looping ambient sounds, uninterrupted looping sounds or "one-shot" sounds
Note.pngNote:Allows multiple of the same sounds to play without cutting out
Confirm:Code comments imply that this channel continues to work outside of the sound radius
CHAN_VOICE2 7 Team Fortress 2 Team Fortress 2 Announcer
CHAN_VOICE_BASE 8 - 107 Allocated for player voice chat (100 channels)
Note.pngNote:Allows multiple of the same sounds to play without cutting out
volume float 1.0 Volume of sound, in normalized 0 - 1 range.
Tip.pngTip:Sounds may be hard to hear even at full volume. Naming custom sounds according to the soundmixer can be used to make them naturally louder.
sound_level
Note.pngNote:Type the name of this parameter carefully. This has been a frequent mistake!
int 0 Sound attenuation in decibels. The default value of 0 will make it play globally. See this page for real world equivalents. If the sound has non-zero attenuation, it will play from the entity's origin if present, otherwise it uses the origin parameter.
Tip.pngTip:To convert radius in Hammer units to decibels (similar to ambient_generic), use the following formula:
local soundlevel = (40 + (20 * log10(radius / 36.0))).tointeger();
flags int 0 Special sound flags. List of flags available:
Name Value Description
SND_NOFLAGS 0 Nothing special.
SND_CHANGE_VOL 1 Changes volume of an already-playing sound on this entity. Sounds will not restart playback if already active.
SND_CHANGE_PITCH 2 Changes pitch of an already-playing sound on this entity. Sounds will not restart playback if already active.
SND_STOP 4 Stops the specified sound if playing on this entity.
Confirm:This may be channel-specific.
Warning.pngWarning:ALWAYS use the RECIPIENT_FILTER_GLOBAL filter when stopping sounds. Otherwise, looping sounds may not be stopped for players outside of the sound radius and will persist forever.
SND_SPAWNING 8 Writes sound info into net message for connecting clients.
Confirm:This probably doesn't do anything at runtime.
SND_DELAY 16 Specifies the sound should be scheduled with a delay.
SND_STOP_LOOPING 32 Stop all looping sounds on the entity.
Icon-Bug.pngBug:Not implemented by the engine.  [todo tested in?]
SND_SPEAKER 64 Used by audio played through env_microphone. Applies special mixing rules.
SND_SHOULDPAUSE 128 Sound will be paused when the game is paused. Useless in multiplayer.
SND_IGNORE_PHONEMES 256 Prevents flex animation from being driven by this sound, if available.
SND_IGNORE_NAME 512 Changes all sounds emitted on the entity, regardless of actual name.
SND_DO_NOT_OVERWRITE_EXISTING_ON_CHANNEL 1024 If an existing sound is found on the channel, the sound will not play instead of overwriting it.
pitch int 100 Pitch of the sound.
special_dsp int 0 DSP effect to use on this sound.
origin Vector 0 0 0 Overwrites sound origin if specified.
Note.pngNote:If not specified, the origin will be fetched from the entity parameter for RECIPIENT_FILTER_PAS and RECIPIENT_FILTER_PVS filters.
delay float How far to skip into the sound playback, in seconds. The value must be negative!
Icon-Bug.pngBug:Many (but not all) MP3 files cannot be skipped past a certain point depending on how the file was saved/compressed. The amount you are able to skip is not consistent, however it is usually around 30-35 seconds. "music/hl2_song2.mp3" for example will seemingly allow you to skip to any point in the song, while "music/hl2_song31.mp3" will only allow you to skip up to 35.8 seconds, and "music/hl2_song26_trainstation1.mp3" will only allow you to skip up to 32.9 seconds. WAV files are not affected by this.  [todo tested in?]
Todo: Is there any practical effect from a positive delay value?
sound_time float Absolute time to delay sound until, NOT the duration. Overrides delay if specified.
Todo: Might require the SND_DELAY flag to be set.
entity handle null Entity to emit the sound from. This is also used by certain filters. e.g. RECIPIENT_FILTER_SINGLE_PLAYER will play sounds only to the specified player here, or RECIPIENT_FILTER_PVS will build a PVS filter originating from this entity.
Note.pngNote:The entity specified must be an edict, i.e. clients are aware of it's existence.
speaker_entity handle null Entity which will *speak* the sound. This is not the sound as the actual owner of the sound, see above.
filter_type
Note.pngNote:Type the name of this parameter carefully. This has been frequently written as filter by mistake!
int 0 Specifies what entities can hear this entity. See Constants.EScriptRecipientFilter.
filter_param int -1 Specifies an extra option to the filter. Currently only used by RECIPIENT_FILTER_TEAM, which uses this to specify which team to play to.

Flags

It may be useful to define these flags for extra readability.

::SND_NOFLAGS <- 0
::SND_CHANGE_VOL <- 1
::SND_CHANGE_PITCH <- 2
::SND_STOP <- 4
::SND_SPAWNING <- 8
::SND_DELAY <- 16
::SND_STOP_LOOPING <- 32
::SND_SPEAKER <- 64
::SND_SHOULDPAUSE <- 128
::SND_IGNORE_PHONEMES <- 256
::SND_IGNORE_NAME <- 512
::SND_DO_NOT_OVERWRITE_EXISTING_ON_CHANNEL <- 1024

Example Usage

// sounds should be precached once before use
PrecacheScriptSound("Game.Domination");

// play the sound to everyone around yourself
EmitSoundEx({
	sound_name = "Game.Domination",
	origin = GetListenServerHost().GetCenter(),
	filter_type = Constants.EScriptRecipientFilter.RECIPIENT_FILTER_GLOBAL
});

Playing to specific players

It may be useful to play a sound to a specific list of players only, and/or with the sound originating from a source rather than globally.

This abuses a feature of PAS filtering, where players that are too far away won't hear the sound. This moves their view very, very far away and then sets it back after sending the sound to achieve arbitrary filtering.

function EmitSoundFiltered(params, included_players)
{
	local origin = Vector()
	local exclude_offset = Vector(99999, 99999, 99999)
	
	local players = {}
	for (local player; player = Entities.FindByClassname(player, "player");)
	{
		if (included_players.find(player) == null)
		{
			players[player] <- NetProps.GetPropVector(player, "m_vecViewOffset")
			NetProps.SetPropVector(player, "m_vecViewOffset", exclude_offset)
		}
	}
	
	params.filter_type <- 1 // RECIPIENT_FILTER_PAS_ATTENUATION
	EmitSoundEx(params)
	
	foreach (player, offset in players)
		NetProps.SetPropVector(player, "m_vecViewOffset", offset)
}

// example usage that plays the sound only to the host and originating from an entity named "speaker"
local players = []
players.append(GetListenServerHost())

EmitSoundFiltered
({
	sound_name  = "vo/engineer_medic03.mp3"
	sound_level = 80
	entity      = Entities.FindByName(null, "speaker")
}, players)