[GUIDE] Introduction to the Portal2 Sound Operator Stacks
Quote from josepezdj on April 30, 2015, 9:09 amPersonal Notes
[spoiler]I decided to write all this HUGE wall of text because I simply wanted this to be written down for future mappers interested in this system and its extraordinary amount of features. It's been many months digging into Valve's soundscript files and in the internet to be able to understand this minimally, and there are a lot of things I haven't found anywhere, misleading information and many mixed concepts, which is very frustrating.This is from all points of view only an introduction guide. I must admit I still have to understand a LOT of this system myself, so I'd like to remark the humbleness contained in this guide. I hope it's all well written though and that it sounds clear, because it has been a real pain to give all this a certain order.
My intention is only to share what I've found and what I know so far, but this is only the tip of the iceberg, I truly hope the community will help this thread to continue being written. I think music is a very interesting part of mapping, and this sound system based in the so called "operator stacks" is amazingly powerful to immerse players in your map's environments. It deserves a cooperation between all of us to clarify and define these "stacks" and to understand them better and to use them in new ways.
Even being an introduction guide, it's an advanced type of guide, meaning that I'll be assuming you are an experienced Hammer editor user, and won't stop much in "mapping details". Nonetheless please don't hesitate to comment anything that you think I obviated; thanks.[/spoiler]
1. OverviewVALVe created the Sound Operators to produce complex behaviors with the music and sounds as the player gets through the levels. When they implemented it into Portal2, this advanced sound system was only the first prototype, and in Mike Morasky words:
"...the sound operator stacks are therefore somewhat different from those used in Dota2 or CSGO and will be even more different from the newer versions going forward".
This guide will try to explain how this complex sound system works in Portal2. Nonetheless, let me insist: it is only an 'Introduction guide' given the complexity of this system and my lack of knowledge about it so far.
This guide will try to cover:
- what's possible to achieve with this sound system
- how to prepare your soundscript files
- how to implement the system into your hammer editor
- how to pack your custom music and soundscripts into your map file in order to make it ready for the Workshop.MASSIVE THANKS to Mr. Mike Morasky from VALVe for answering my e-mails and for helping me to understand this sound system, even being a top busy person. Eternally grateful for coming up with a patch for making Portal2 to read a soundscript file per map automatically, like the rest of VALVe games already do.
MANY THANKS AS WELL to Mr. Alex Vlachos from VALVe for his valuable support and help on each and every single time that I wrote to him asking for help!
2. Why using the Operator Stacks System
The main advantage of this system is the possibility of programming your map's entire music to be played automatically and mixed softly and synced as the player goes through the map. You can write group of instructions called 'soundentries' into a soundscript file and combine them in maaaany different ways... Due to the introductory nature of this guide, I can only spot some of the most interesting or known features of this system in Portal2, however, the scope these operator cover is truly large, being possible to achieve almost whatever sound effect, mix or music behaviour!
Let me briefly summarize some of the key features we'll be talking about:
1. We'll take a look at the operator stacks that will let us sync our cached music tracks or music variations or loops automatically. These stacks can automatically get information from the cached music like your tracks' total length or the elapsed time that a track is playing for... you can set a delay or effects for one entry to take effect right in the moment you decide. Your custom music will be played automatically and synchronized... as if it was just one long song.
2. You can add your custom music for funnels. All of us know how cool is to enter a funnel and start hearing a different music track in there automatically and softly mixed up with the main music, and then stepping out of it and listen to the main music track back again. Well, now you'll be able to use your custom music for the funnels in your maps by means of a couple of operator stacks...
3. The same goes for the "musical lightbridges", you can make them play your custom sounds automatically when the player is near a lightbridge or standing on one of them.
4. Apart from that, you can program a ton of effects for your music tracks to be applied automatically, delays, speakers spatialization, dsp (echoes, reberveration,...), fade in and out, etc.
Of course, you can still make use of custom (or stock) soundscapes and play them altogether with your custom soundscripts combined in the same map...
3. Some Important Concepts First
First off, it's very important to have a clear idea of the different concepts related to the sound system in order to avoid misunderstandings, as well as the main differences between the entities you can use into your hammer editor to play the music scripts.
- Soundscripts vs Soundscapes: Soundscapes are simpler, intended to play background ambient music or sounds that we won't want to alter during a gameplay. Soundscripts are more complex, they contain the instructions and parameters to play, stop, sync, mix, delay, tweak... your .WAV music files (.MP3 sound files can also be played, however, due to they cannot be looped and therefore they're more limited, these won't be considered in this guide). These instructions are grouped into sound entries , that consist of code blocks delimited by curly braces { }. While both, Soundscripts and Soundscapes, use 'sound entries', the main difference between them is the complexity in their code, being far more complex the soundentries inside the Soundscripts.
Soundscapes look like this:
scroll down to see the full code
[spoiler]
- Code: Select all
"ug_lake_top_01"
"playlooping"
{
"dsp" "1"
"fadetime" ".5"
{
"volume" ".95"
"wave" "ambient/wind/Underground_wind_lp_01.wav"
"pitch" "95"}"playlooping"
{
"volume" ".3"
"pitch" "100"
"wave" "ambient/nature/water/amb_water_pipes_lp_01.wav"
"origin" "574.951233, 722.183228, 98.359047;"
"soundlevel" "SNDLVL_75dB"
}"playlooping"
{
"volume" ".7"
"pitch" "100"
"wave" "ambient/nature/water/amb_water_pipes_lp_01.wav"
"origin" "-3238.034424, 1662.389038, -2383.910156;"
"soundlevel" "SNDLVL_80dB"
}"playsoundscape"
{
"name" "util.random.boomer"
"volume" ".6"
}"playsoundscape"
{
"name" "utility.metal.imp.lo"
"volume" ".8"
}"playsoundscape"
{
"name" "util.random.metal_groans"
"volume" ".8"
}"playsoundscape"
{
"name" "TestChamber_Industrial.WarehouseImpact_01"
"volume" ".23"
}"playsoundscape"
{
"name" "util.random.rockdebris"
"volume" ".4"
}"playsoundscape"
{
"name" "util.random.rockfall"
"volume" "1"
}
}[/spoiler]
A list of the Portal2 stock soundscapes is available in the wiki.Soundscripts look like this:
scroll down to see the full code
[spoiler]
- Code: Select all
"music.sp_a4_tb_intro_tbin"
"wave" "*music/sp_a4_tb_intro_tbin.wav""soundentry_version" "2"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_NONE"
"volume" "0.5"
"operator_stacks"
{
"start_stack"
{
"random_offset"
{
"operator" "math_random"
"input_min" "0.0"
"input_max" "126"
}"negative_delay"
{
"operator" "math_float"
"apply" "mult"
"input1" "@random_offset.output"
"input2" "-1.0"
}
"delay_output"
{
"operator" "sys_output"
"input_float" "@negative_delay.output"
"output" "delay"
}
}"update_stack"
{
"import_stack" "update_music_stereo"
"mixer"
{
"mixgroup" "unduckedMusic"
}"volume_fade_in"
{
"input_max" "3.0"
"input_map_min" "0.05"
}
"volume_fade_out"
{
"input_max" "0.75"
"input_map_min" "0.05"
}
"volume_lfo_time_scale"
{
"input2" "0.3"
}
"volume_lfo_scale"
{
"input2" "0.4"
}}
}
}[/spoiler]
As you can see, both soundscapes and soundscripts share some commands like "wave", "volume" or "pitch"; but then there is a lot of code that is specific for the Soundscripts from the "soundentry_version" "2" line and downwards: all of that specific code is the "sound operator stacks" system.- Soundentries: As mentioned above this concept must be used in the sense of "a number of instructions grouped in code blocks relative to one or several music files to be played automatically and following a certain order, rules, conditions, effects and whatnot". Remember that both, soundscripts and soundscapes use what it's known as 'sound entries'.
- Ambient_generic vs Env_soundscape: The following points are very important in this guide:
a) The ambient_generic entity can play both, a direct music file (.WAV or .MP3) AND also a pre-cached sound entry (i.e. "music.sp_a4_intro_b3").
b) The ambient_generic is the only entity capable to play soundscripts (the env_soundscapes can't) due to the complex code inside their sound entries.
c) Use the env_soundscapes only when you actually want to play 'soundscapes', simple looped background music.
d) A custom soundscape file per map can be automatically loaded by the game if it's located and named like so: "scripts/soundscapes_custom_<mapname>.txt" or "scripts/soundscapes_<mapname>.txt".
e) A custom soundscript file per map can be automatically loaded by the game if it's located and named like so: "maps/<mapname>_level_sounds.txt" ONLY FROM 2014, DECEMBER ONWARDS (this wasn't working before!)
f) For the ambient_generics, always remember this:* Tick the flag "Play Everywhere". This will make your music environmental, and hearable everywhere.
* Tick the flag "Start Silent". This way we'll have always full control of the moment we'll want our tracks to be played by triggers.
* Untick the flag "Is NOT Looped". We'll want our music to keep on playing, and in fact we'll loop our tracks in advance.Your custom tracks must be placed within the /sound folder to work. Usually, we will be placing our custom music into a /sound/music/ folder.
4. The SoundscriptFile
Our reference folder will be the portal2/scripts/ folder. Here you'll find all music soundscript files for each chapter of the campaign, single and multiplayer, scripted sequences, soundscripts for weapons npcs or events, all soundscapes used in the game, and so on.
Apart from those files, there are some important reference files to note here:
- sound_operator_stacks.txt -> this one contains ALL operator stacks within the sound system and all their pre-defined parameters we can choose to use. We'll be checking out many parts of this file little by little to discuss about their main functions.
- soundmixers.txt -> this file contains all "mixgroups" and sound mixers, and their main default values for parameters like max/min soundlevel, threshold or priority.
These are the main parts of a soundentry inside a Soundscript file (it's high resolution, click two times on the image to make it bigger):
As you can see, the soundentry name is enclosed between double quotes. Then all the code is below and delimited by curly braces {}.
Regarding our soundentry names, well, you could potentially use whatever ones. VALVe seems to name them identifying if it's a music track or any other sound at the beginning of the name, then a part relative to the map name, then a number or a key suffix that is used or interpreted by the game sound system and certain entities in special ways (_tbin, _tbout, _lbout, etc.). This means that the soundentry names might be more important than at a first glance we could think since some mixers, entities or stacks may refer to them by part of their name (a common usage is for example when you star playing a new soundentry, you stop all sound entries containing the word "music").
For the soundscript file name, as mentioned above, from 2014 December onwards the game will read a soundscript named "[map_name]_level_sounds.txt" from the /maps folder automatically. However, there is a problem in the workshop: due to the map is put into a long-string-of-numbers random new folder when published to the workshop, the game returns an error message failing to load the custom soundscript file, since it is no longer located into /maps, but into a random /maps/workshop/??????????????????/ folder. Therefore we will use a workaround to fix this for workshop maps, and the soundscript file name no longer needs to be necessarily "[map_name]_level_sounds.txt", but whatever one, as long as we declare it into the game_sounds_manifest.txt file. All this will be explained later in detail.
The basic commands are located always at the beginning of each soundentry and they are the following ones:
- Channel: They are used as a way to categorise the sounds. The most commonly used is the CHAN_STATIC one (to play a constant music that doesn't require any reaction). You can select from a variety of 'channels' explained in the wiki.
- Volume: It's a value between 0 and 1, where 1 is the loudest and 0 means no sound.
- Pitch: It's the sound tone in the scale. A value of 100 is the standard speed/tone the music file is recorded at. A higher value means a high-pitched sound, and a lower one, a low-pitched sound.
- Soundlevel: this is the attenuation we want for the sound when it's stopped and should fade/drop away.
- Wave and Rndwave: these 2 commands lead to the sound file to be played, and the path is relative to the /sound folder. They should point to .WAV files. With the command 'Wave' you only specify 1 single .Wav sound file, while with the command 'Rndwave' you can specify a list of sounds to be played randomly (hence the name of the command: rnd(random)wave ).All of that has no secret and it's pretty well described on the wiki.
After setting up the values for the above main parameters, we have to include the following line to enable the sound operator stacks system:
"soundentry_version" "2"
And right after that:
"operator_stacks"
And it's now when we must open a new code block with a curly brace { and list all operator stacks, parameters and values. Then we close the block of code with another } brace. Each parameter inside each stack with its values must also be delimited by curly braces, making blocks of code.
NOTE: We'll be mainly copy/pasting certain code blocks from Valve's scripts in order to keep the same syntax and to avoid issues. You can then play with different values for the parametes, test the result in game, and post it here!
5. The Operator Stacks
As stated above, the goal of this guide is not to describe each and every sound operator stack plus all their parameters and all the different combinations you can have amongst them. As the wiki says:
"There are 28 operators used in Portal 2, but they are re-configured and combined in hundreds of different ways..."
Therefore the goal is to only introduce this system describing some of their more significative features and how they're used into Portal2.
There is a brief description of some operators in the wiki. For a complete list of the sound operator stacks and their parameters, you can check your file "portal 2/portal2/scripts/sound_operator_stacks.txt". These definitions will give us an idea of what each of them can do. This file contain all stacks and the arguments or parameters we can use set them up. We'll have to add to our custom soundscript all those stacks and parameters that we want in order to produce the mix or effect we desire.
There are 3 types of operator stacks according to their function:
- start_stack
- update_stack
- stop_stackYou can take a look to the different soundentries into the following soundscript files in order to check out how Valve has programmed each of the levels for each chapter:
- game_sounds_music_a1.txt
- game_sounds_music_a2.txt
- game_sounds_music_a3.txt
- game_sounds_music_a4.txt
- game_sounds_music_mp.txtIt is very important to know how these operators work when the game "reads" one soundentry, and how the game manages their inputs and instructions. In order to make all this clearer, I've splitted the information into the next 4 sections, one per relevant feature.
Consider as well to decompile some of Valve maps to go deeper into all this because it's extremely useful to check out how Valve places the triggers, what soundentries are played before and what code they have inside, what soundentries are played later and how, etc.
6. FEATURE: Syncing your tracks
Operator Stack: start_sync_to_entry
stack definitions
[spoiler]When we check inside the "sound_operator_stacks.txt" file, we can see there are 7 parameters or options to control and configure the "start_sync_to_entry" stack. But don't get too overwhelmed! Only 2 of those 7 params are commonly used, "elapsed_time" and "duration_div", so this is only for your reference:
- Code: Select all
"start_sync_to_entry"
{
"elapsed_time"
{
"operator" "get_entry_time"
}
"duration_div"
{
"operator" "math_float"
"apply" "div"
"input1" "@elapsed_time.output_sound_duration"
"input2" "4"
}
"time_mod"
{
"operator" "math_float"
"apply" "mod"
"input1" "@elapsed_time.output_sound_elapsed"
"input2" "@duration_div.output"
}
"div_mult"
{
"operator" "math_float"
"apply" "mult"
"input1" "0.0"
"input2" "@duration_div.output"
}
"add_offset"
{
"operator" "math_float"
"apply" "add"
"input1" "@time_mod.output"
"input2" "@div_mult.output"
}
"negative_delay"
{
"operator" "math_float"
"apply" "mult"
"input1" "@add_offset.output"
"input2" "-1.0"
}
"delay_output"
{
"operator" "sys_output"
"input_float" "@negative_delay.output"
"output" "delay"
}
}[/spoiler]
The idea behind this feature is basicly to chain our music tracks, variations or loops smoothly and synchronized. We'll want our tracks to have the same tempo or pitch in order to be able to synchronize them or to hear them as "only one song". Also we must loop our .WAV files (note that you can't loop .MP3 files) to let the music play continually without hearing any silence bit between multiple replay times. For this, you'd better get the free software Wavosaur. Open your track and press the "L" button; it'll automatically put one mark at the beginning and one mark at the end of your music track. Now Ctrl+S to save the track with the 2 loop marks. You're done looping!OK, let's see how this works. We'll take an example from the "scripts/game_sounds_music_a2.txt" file, these 2 sound entries:
"music.sp_a2_dual_lasers.laser01"
[spoiler]
- Code: Select all
"music.sp_a2_dual_lasers.laser01"
"wave" "*music/sp_a2_dual_lasers_l1.wav""soundentry_version" "2"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_70dB"
"volume" "0.78"
"operator_stacks"
{
"update_stack"
{
"import_stack" "p2_update_music_spatial_portals"
"speakers_spatialize"
{
"input_radius" "200"
}}
}
}[/spoiler]
"music.sp_a2_dual_lasers.laser02"
[spoiler]
- Code: Select all
"music.sp_a2_dual_lasers.laser02"
"wave" "*music/sp_a2_dual_lasers_l2.wav""soundentry_version" "2"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_70dB"
"volume" "0.78"
"operator_stacks"
{
"start_stack"
{
"import_stack" "start_sync_to_entry""elapsed_time"
{
"entry" "music.sp_a2_dual_lasers.laser01"
}
"duration_div"
{
"input2" "17"
}
}"update_stack"
{
"import_stack" "p2_update_music_spatial_portals"
"speakers_spatialize"
{
"input_radius" "200"
}
}
}
}[/spoiler]
So the "music.sp_a2_dual_lasers.laser01" sound entry is intended to be played first and the "music.sp_a2_dual_lasers.laser02" is played later on, once the player arrives to a different area in the map and other ambient_generic is triggered. So we'll need one ambient_generic per soundentry in our map, and for example a couple of trigger_once entities to play each of them.Following this example, we would place a trigger for example near the entry to play the first ambient_generic and first soundentry (music.sp_a2_dual_lasers.laser01). Then another trigger wherever for the second ambient_generic to play the 2nd soundentry (music.sp_a2_dual_lasers.laser02).
With regards to the first soundentry, it's up to you what stacks or effects to include. Of course, you'll need all the basic commands before adding any operator_stack. As we see in this example, the only "addition" here to the basic commands is this:
- Code: Select all
"update_stack"
}
{
"import_stack" "p2_update_music_spatial_portals"
"speakers_spatialize"
{
"input_radius" "200"
}This stack seems to apply a spatializing effect to the music with a radius of 200 units (around the source entity). You can try copying-pasting blocks of code from other of the stock soundentries to test out how they modify your track and decide about the better effect.
Now, due to this code inside the 2nd soundentry:
- Code: Select all
"elapsed_time"
"start_stack"
{
"import_stack" "start_sync_to_entry"
{
"entry" "music.sp_a2_dual_lasers.laser01"
}
"duration_div"
{
"input2" "17"
}
}whenever the player trigger the 2nd ambient_generic, there will be a soft mix between track 1 and 2, the transition from one track to the other one will be smooth and synchronized...
How exactly? Well, this is the key part, how the engine manages the information from the "start_sync_to_entry" stack. Let's check out Mr. Morasky's words on this:
"Start_sync_to_entry" syncs "this" sound entry to another sound entry that is already playing. Ie: sync "newPiece" to "playingPiece".
The way it does this is it checks the elapsed time of "playingPiece" and then does a "negative delay" when starting the "newPiece". Or in other words, "fast forwards" into the new piece so that it's starting in sync with "playingPiece".
Ie: "playingPiece" is playing at 15 seconds, "newPiece" will start at 15 seconds instead of 0 seconds.
However, this assumes that each piece, "newPiece" and "playingPiece" are the same length. Sometimes "newPiece" is shorter than "playingPiece" in which case "duration_div" or "duration division" is used.
If the length of "playingPiece" is 16 seconds and "newPiece" is 4 seconds then you set:
"duration_div"
{
"input2" "4"
}Meaning that that the duration length of "playingPiece" will be divided by 4 before determining the elapsed time.
If "playingPiece" has been playing for 15 seconds but the "newPiece" is only 4 seconds long, you can't fast forward it 15 seconds. Therefore, you set "duration_div" to 4, which divides the duration of "playingPiece" by 4 and then does a modulus function, this leaves an elapsed time of 3 seconds, which will sync "newPiece" to the 4th quarter of "playingPiece".
For example if the lengths are:
playingPiece = 8 seconds
newPiece = 8 seconds
"duration_div.input2" = 1playingPiece = 8 seconds
newPiece = 4 seconds
"duration_div.input2" = 2playingPiece = 8 seconds
newPiece = 2 seconds
"duration_div.input2" = 4
...NOTE: "newPiece" length must be an even division of "playingPiece" length.
So, basicly we will be using only the following 2 parameters for this stack:
- Code: Select all
"elapsed_time"
{
"operator" "get_entry_time"
}
"duration_div"
{
"operator" "math_float"
"apply" "div"
"input1" "@elapsed_time.output_sound_duration"
"input2" "4"
}- elapsed_time: this parameter uses the operator "get_entry_time", and from the operators list, we can see it's a "Getter" type of operator (provides values from the engine). The function of this operator is to get the amount of elapsed time for a soundentry being played. So we have to introduce here the name of the first soundentry supposed to be playing, in our example:
- Code: Select all
"elapsed_time"
{
"entry" "music.sp_a2_dual_lasers.laser01"
}- duration_div: as we read from Mr. Morasky above, we will use this parameter when our tracks are different length and we need to adjust our synchronization. In our example an "input2" of "17" is used.
- Code: Select all
"duration_div"
{
"input2" "17"
}If you go and check the tracks these 2 soundentries are playing ("sp_a2_dual_lasers_l1.wav" and "sp_a2_dual_lasers_l2.wav") you'll find that they're just a couple of short sound effects, not music tracks really, and dividing the first one into 17 parts to sync the 2nd one seems to make sense when talking of really short-length sounds: due to how the system works (fast-forwarding the 2nd soundentry into the amount of seconds that the first entry elapsed) there are more chances to hear the second soundentry entirely if we divide the first one into 17 parts... take into account that these 2 tracks are 0:01 seconds length!
That's pretty much it though. As already mentioned there are more parameters within this stack, but we won't discuss about them for now... play with them and post what you discover!
7. FEATURE: Playing our custom music in funnels
Operator Stack: p2_update_music_play_tbeam
Key soundentry suffix: "_tbin"stack definitions
[spoiler]Again, don't be overwhelmed! we will be using some of these parameters only and mainly copying/pasting code!
- Code: Select all
"p2_update_music_play_tbeam"
// "print_start_trigger"
{
"tb_mixer"
{
"operator" "get_soundmixer"
"mixgroup" "testTBin"
}
// "print_tb_mixer"
// {
// "operator" "util_print_float"
// "input" "@tb_mixer.output_volume"
// }
"tb_delta"
{
"operator" "math_delta"
"input" "@tb_mixer.output_volume"
}
"tb_mixer_zero"
{
"operator" "math_float"
"apply" "less_than_or_equal"
"input1" "@tb_mixer.output_volume"
"input2" "0.0"
}
"tb_start_trigger"
{
"operator" "math_float"
"apply" "less_than"
"input1" "@tb_delta.output"
"input2" "0.0"
}
"tb_start_trigger_test"
{
"operator" "math_float"
"apply" "min"
"input1" "@tb_start_trigger.output"
"input2" "@tb_mixer_zero.output"
}
// "print_tb_delta"
// {
// "operator" "util_print_float"
// "input" "@tb_delta.output"
// }
// {
// "input_execute" "@tb_start_trigger.output"
// "operator" "util_print_float"
// "input" "@tb_start_trigger_test.output"
// }
"tb_stop_trigger"
{
"operator" "math_float"
"apply" "greater_than"
"input1" "@tb_mixer.output_volume"
"input2" "0.0"
}"play_entry"
{
"execute_once" "false"
"operator" "sys_start_entry"
"input_start" "1"
"input_execute" "@tb_start_trigger_test.output"
"entry_name" "Default.Null"
}
"stop_entry"
{
"input_execute" "@tb_stop_trigger.output"
"operator" "sys_stop_entries"
"input_max_entries" "0.000000"
"match_entity" "false"
"match_substring" "false"
"match_entry" "Default.Null"
}
}[/spoiler]
The idea behind this feature is to play a different music than the playing one when the player is going into and out of any funnel in a given map. The guys at Valve named the soundentries related to funnels ended up in "tbout" ("tractor beam out") and "tbin" ("tractor beam in") and you can check out into the "portal 2/portal2/scripts/game_sounds_music_a4.txt" script file for the many sound entries with these 2 suffixes.OK, the same as in the previous feature, this stack relates 2 soundentries, the playing soundentry and the one we'll want inside our funnels. Let's take another couple of related soundentries as example, these 2 from the "game_sounds_music_a4.txt" script file:
"music.sp_a4_tb_intro_b1"
[spoiler]
- Code: Select all
"music.sp_a4_tb_intro_b1"
"wave" "*music/sp_a4_tb_intro_b1.wav""soundentry_version" "2"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_NONE"
"volume" "0.45"
"operator_stacks"
{
"update_stack"
{
"import_stack" "update_music_stereo"
"volume_fade_out"
{
"input_max" "3.0"
"input_map_min" "0.05"
}// "volume_lfo_time_scale"
// {
// "input2" "0.3"
// }
// "volume_lfo_scale"
// {
// "input2" "0.95"
// }"import_stack" "p2_update_music_play_tbeam"
"play_entry"
{
"entry_name" "music.sp_a4_tb_intro_tbin"
}
"stop_entry"
{
"match_entry" "music.sp_a4_tb_intro_tbin"
}
}
}
}[/spoiler]
"music.sp_a4_tb_intro_tbin"
[spoiler]
- Code: Select all
"music.sp_a4_tb_intro_tbin"
"wave" "*music/sp_a4_tb_intro_tbin.wav""soundentry_version" "2"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_NONE"
"volume" "0.5"
"operator_stacks"
{
"start_stack"
{
"random_offset"
{
"operator" "math_random"
"input_min" "0.0"
"input_max" "126"
}"negative_delay"
{
"operator" "math_float"
"apply" "mult"
"input1" "@random_offset.output"
"input2" "-1.0"
}
"delay_output"
{
"operator" "sys_output"
"input_float" "@negative_delay.output"
"output" "delay"
}
}"update_stack"
{
"import_stack" "update_music_stereo"
"mixer"
{
"mixgroup" "unduckedMusic"
}"volume_fade_in"
{
"input_max" "3.0"
"input_map_min" "0.05"
}
"volume_fade_out"
{
"input_max" "0.75"
"input_map_min" "0.05"
}
"volume_lfo_time_scale"
{
"input2" "0.3"
}
"volume_lfo_scale"
{
"input2" "0.4"
}}
}
}[/spoiler]
The first one is intended to be playing before we enter into any funnel, tipically by means of any ambient_generic triggering. Second one is the soundentry that will be played automatically when we enter into a funnel.Note here that soundentries for funnels won't be called by ambient_generics, but they will be automatically played when the player enters a funnel. This feature works this way:
- Entering the map and triggering the corresponding first ambient_generic will start the soundentry "music.sp_a4_tb_intro_b1".
- That soundentry imports and runs the "p2_update_music_play_tbeam" stack with the arguments inside the "music.sp_a4_tb_intro_tbin" soundentry. This means that from the moment the 1st soundentry is played, the sound system is "ready" to play that 2nd soundentry whenever the player meets a funnel.
About how and why this works internally, I can only quote again Mr. Mike Morasky:
"...I don't recall exactly but I'm assuming that p2_update_music_play_tbeam runs every frame looking for a tbeam to be entered (probably by testing the mixgroup state of the sound triggered when entering a tbeam) and plays music.sp_a4_tb_intro_tbin when that state is true."
We can check into whatever "_tbin" soundentry how it always updates the mixer with the following code:
- Code: Select all
"import_stack" "update_music_stereo"
"mixer"
{
"mixgroup" "unduckedMusic"
}So probably the funnels are coded to play the music within the "unduckedMusic" mixgroup... Again, by now let's copy/paste the existing code for starters; then you play with different values or code pieces to get different fade in-out or delay effects for example. However, bare in mind that the "_tbin"/"_tbout" part in the soundentry names, altogether with that "mixer" part of code both seem to be mandatory for this feature to work out properly.
8. FEATURE: Playing our custom music in lightbridges
Operator Stack: p2_update_music_spatial_portals
Key soundentry suffix: "_lbout"stack definitions
[spoiler]
- Code: Select all
// set up so it iterates over portalized sounds
"p2_update_music_spatial_portals"
{
"import_stack" "update_music_spatial"
"speakers_multi_origin"
{
"input_max_iterations" "@source_info.output_source_count"
"iterate_operator" "source_info"
}
// connecting output after operator has been created
"source_info"
{
"input_source_index" "@speakers_multi_origin.output_index"
}
// turn off position array indexing
"position_array"
{
"input_index" "0"
}
}[/spoiler]
This feature plays a different music when the player is on a lightbridge. The 2nd chapter in Portal is the one where the lightbridges are introduced, and we can check into the script file "portal 2/portal2/scripts/game_sounds_music_a2.txt" to find several soundentries examples with the suffix "_lbout"... let's take one of them:"music.sp_a2_bridge_the_gap_lbout"
[spoiler]
- Code: Select all
"music.sp_a2_bridge_the_gap_lbout"
"rndwave"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_55dB"
"volume" "0.87"
{
"wave" "*music/sp_a2_bridge_the_gap_lbout.wav"
}"soundentry_version" "2"
"operator_stacks"
{
"update_stack"
{
"import_stack" "p2_update_music_spatial_portals"
"source_info"
{
"source" "entity"
}
"volume_fade_in"
{
"input_max" "5"
"input_map_min" "0.05"
}"speakers_spatialize"
{
"input_radius" "200"
}
}
}
}[/spoiler]
As we can see, what Valve seems to apply to the music track "music/sp_a2_bridge_the_gap_lbout.wav" it's a fade-in and a spatialize effect. Also there is a "source-info" stack parameter that seems to indicate where will the music come out from, in this case, the entity (I think it refers to the prop_wall_projector prop itself, since I seem to listen the track louder as I get close to the prop...)Note again that we won't be using any ambient_generic to play any "_lbout" soundentry directly. We just need to make sure we made the "_lbout" soundentry in our soundscript.
Nothing much to add here. It's mainly based in adding the suffix "_lbout" to our soundentry and the code we can see in the example above, then the game will automatically load the soundentry with the "_lbout" by map name and will be cached and ready for when the player gets on a bridge.
9. How to make your custom soundscripts to work in the Workshop
As mentioned before there's a recent update that makes Portal2 to automatically search and load a soundscript file per map, like the rest of Valve maps do, but it must be placed inside the maps/ folder and be named relative to the map name like so:
/maps/[map_name]_level_sounds.txt
Example: "jose_soundscripts_test_level_sounds.txt" (the name of the map in this case is "jose_soundscripts_test.BSP")
This means the following statements for your reference:
1. In the case that you already have your soundscript file named relative to your map's name and inside the /maps folder, you'll be able to make your soundscript to work and therefore your custom sounds to be heard when running the map via the console command 'map [map_name]' only if you declare the name of your soundscript file inside the /scripts/game_sounds_manifest.txt file. Due to the game priority system, as you know, you'll need to edit this file: "portal 2/portal2_dlc2/scripts/game_sounds_manifest.txt". It doesn't really matters inside which /maps folder you put the soundscript in though from the current available ones:
- "portal 2/portal2/map/"
- "portal 2/portal2_dlc1/maps/"
- "portal 2/portal2_dlc2/maps/"2. The above requirement can be bypassed with simply pakratting the soundscript file inside the .BSP map file, meaning that a map with its soundscript file "[map_name]_level_sounds.txt" packed inside will load it automatically, no need to declare it into the game_sounds_manifest.txt. Inside the BSP the soundscript file must be into a /maps folder to work though.
3. Therefore, to distribute a .BSP map file with all sound customization you need to pack inside the .BSP:
- your custom sounds (always remember to fix up the paths for your sound files when packing)
- the soundscript file (always remember to fix up the path to leave it right inside the /maps folder)However, in the case we want our map published to the Workshop, things don't work out like that. Due to the workshop creates a random folder composed by a long string of numbers and places the map inside "/maps/workshop/[long-string-of-numbers]/" when we publish the map, even though we already pakratted the relevant files inside the .BSP, we'll get a console error message similar to these:
PrecacheScriptSound 'music.workshop34110908146838399jose_soundscripts_test_tbout' failed, no such sound script entry
PrecacheScriptSound 'music.workshop34110908146838399jose_soundscripts_test_lbout' failed, no such sound script entryStrangely, this problem doesn't pop up with soundscapes: even if you publish a map to your workshop with the custom soundscape file packed in and the map is stored after published inside a random folder, the soundscape file is loaded and the custom soundscapes are heard...
Workshop workaround
There is a hacky workaround that our mate HMW discovered when dealing with custom soundscripts. I'll explain the whole process step by step using an example:
1. Inside the Hammer Editor
As already mentioned above, you're supposed to place your triggers and ambient_generics pointing to your soundentries in your map at your desired places, I'm assuming you already control this part... Next you'll need to add the following entity to perform this workaround:
- point_servercommand. Name it for example "@servercommand"
Now open whatever logic_auto you surely have in your map (or add one) and add the following output:
[spoiler][/spoiler]
That will reset the soundemitter on map spawn and somehow to load the game_sounds_manifest.txt that we will pakrat inside the BSP map file; let's explain this in the next point.2. Files & Pakrat
I'll be assuming you are using the portal2_dlc2/ folder due to its priority over the rest. Let's say we've got a map called "jose_soundscripts_test" into portal2_dlc2/maps/ and and its relative soundscript file called "jose_soundscripts_test_level_sounds.txt" also inside portal2_dlc2/maps/. There is also the "game_sounds_manifest.txt" inside portal2_dlc2/scripts/
Remember that we must declare our custom soundscript inside the "game_sounds_manifest.txt" file by adding the following line in:
- Code: Select all
"precache_file" "maps/jose_soundscripts_test_level_sounds.txt"
After adding that line, it looks like so:
Now we must use pakrat to pack those 2 files in, altogether with our custom sounds.First off, a couple of advices about pakrat settings:
- Into "File > Preferences": just check all options like in the image below, select "Ask" on "Path fixup on add file" (mainly because this way you will be sure it is fixing up the paths when it asks you), and finally browse your gamedir (portal 2/portal2/) at the "Game Root Directory"
[spoiler][/spoiler]
- I suggest you to also put the gamedir whole path in the dialog box you'll find after hitting the "Scan" button at the top of the window. Pakrat will remember this path next times from now on and this way the Scan feature will be functional.Now pakrat is correctly set up.
- Next open your BSP map file in pakrat, and click on the "Add" button to start adding files.
- Now you must browse and locate the files in your computer. When browsing, notice there's a drop-down option at the bottom of the window labelled "File types" with the "All valid pak files" option set by default. To locate the .txt files sometimes you must change that option to "All files":
[spoiler][/spoiler]
- OK, select the "game_sounds_manifest.txt" file from the "portal2_dlc2/scripts/" folder. If you set pakrat to "ASK" about fixing up the path when adding a new file in Preferences, it should ask you:[spoiler][/spoiler]
Hit the "Yes" or "Yes to all" button.- Then browse again and select the soundscript file from the /maps folder. After adding this one too, you must have something like this:
[spoiler][/spoiler]
- The lack of tick at the rightmost little boxes means that even though we added the files, these 2 files aren't actually in yet, we must save the BSP to see these 2 boxes checked.- If you take a close look you'll see the paths to the 2 files inside the BSP contain a "dlc2/" part. If we leave it like that it won't work out; we must edit these paths. Select one of them and click on the "Edit" button to erase that part:
[spoiler][/spoiler]
- After doing so with the 2 files, you should have something like this:[spoiler][/spoiler]
Now the paths are correct inside the BSP. Don't forget to save (Ctrl+S or File > Save BSP) your changes!- If you prefer a clearer view inside Pakrat like me, you can select "View > As a Tree" to see it like in the image:
[spoiler][/spoiler]
- Don't forget to add your custom music files too. Their paths don't need to be editted, they will appear within the sounds/ folder right as you actually have them in your local folders.Then your map should be ready to be published to your workshop with your custom soundscripts functional.
10. Work in Progress
There are some features and stacks I'm still investigating about, and I'd like to mention them in this section just for your reference.
- Stack "p2_update_music_play_gel" / "p2_update_music_play_speed_gel"
This seems to me like we could potentially use custom sounds for gels... however I haven't been able to make it to work yet. You can check out all parameters and information about these stacks in the sound_operator_stacks.txt file. I cheer you up to try and make it to work!!
- Usage of "commonnull.wav"
In my journey through all soundscripts and Valve's decompiled maps, I noticed a continuous and interesting usage of this .WAV file. I checked how sometimes they trigger a soundentry with this track in the player's route before triggering an actual soundentry with a "real" sound. Example:
- Code: Select all
"music.sp_a2_intro_01"
// "wave" "*music/sp_a2_intro.wav"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_NONE"
"volume" "0.65"
// "wave" "npc/xray/beep.wav"
"wave" "common/null.wav""soundentry_version" "2""operator_stacks"
{
// "start_stack"
// {
// "set_mixlayer_solo_glados"
// {
// "operator" "sys_mixlayer"
// "mixlayer" "gladosVOLayer"
// "mixgroup" "Music"
// "field" "solo"
// "input" "0.35"
// }
// "set_mixlayer_solo_wheatley"
// {
// "operator" "sys_mixlayer"
// "mixlayer" "wheatleyVOLayer"
// "mixgroup" "Music"
// "field" "solo"
// "input" "0.0"
// }
//
// }
"update_stack"
{
"import_stack" "update_music_stereo""volume_fade_in"
{
"input_max" "0.0"
}
"volume_fade_out"
{
"input_max" "0.0"
// "input_max" "4.0"
}
}
// "stop_stack"
// {
// "set_mixlayer_solo_glados"
// {
// "operator" "sys_mixlayer"
// "mixlayer" "gladosVOLayer"
// "mixgroup" "Music"
// "field" "solo"
// "input" "0.0"
// }
// }
}
}To me it seemed like a sort of placeholder to do something else... a way to free the mixer or the stack... but couldn't really deduct much more. So I asked Mr. Mike Morasky and this is his reply:
There are 2 uses of "commonnull.wav".
1) A place-holder to be overridden by the entry using the stack.
2) A place-holder to "do nothing" as every soundentry must have a soundfile. This one does nothing.Still, I don't really know when exactly or where I should use it anyways... This is pending to be investigated in depth...
I beg to anyone who has dealt with this sound system to revise this post and report possible corrections, as well as any find within this system that may be included, thanks. This post is expected to be editted multiple times.
I'm working on the Steam Guide version of this, but I don't know when I will have it ready since I'm really busy. I'd like to give that one a better and more intuitive structure. Will let you know when I do.
Personal Notes
1. Overview
VALVe created the Sound Operators to produce complex behaviors with the music and sounds as the player gets through the levels. When they implemented it into Portal2, this advanced sound system was only the first prototype, and in Mike Morasky words:
"...the sound operator stacks are therefore somewhat different from those used in Dota2 or CSGO and will be even more different from the newer versions going forward".
This guide will try to explain how this complex sound system works in Portal2. Nonetheless, let me insist: it is only an 'Introduction guide' given the complexity of this system and my lack of knowledge about it so far.
This guide will try to cover:
- what's possible to achieve with this sound system
- how to prepare your soundscript files
- how to implement the system into your hammer editor
- how to pack your custom music and soundscripts into your map file in order to make it ready for the Workshop.
MASSIVE THANKS to Mr. Mike Morasky from VALVe for answering my e-mails and for helping me to understand this sound system, even being a top busy person. Eternally grateful for coming up with a patch for making Portal2 to read a soundscript file per map automatically, like the rest of VALVe games already do.
MANY THANKS AS WELL to Mr. Alex Vlachos from VALVe for his valuable support and help on each and every single time that I wrote to him asking for help!
2. Why using the Operator Stacks System
The main advantage of this system is the possibility of programming your map's entire music to be played automatically and mixed softly and synced as the player goes through the map. You can write group of instructions called 'soundentries' into a soundscript file and combine them in maaaany different ways... Due to the introductory nature of this guide, I can only spot some of the most interesting or known features of this system in Portal2, however, the scope these operator cover is truly large, being possible to achieve almost whatever sound effect, mix or music behaviour!
Let me briefly summarize some of the key features we'll be talking about:
1. We'll take a look at the operator stacks that will let us sync our cached music tracks or music variations or loops automatically. These stacks can automatically get information from the cached music like your tracks' total length or the elapsed time that a track is playing for... you can set a delay or effects for one entry to take effect right in the moment you decide. Your custom music will be played automatically and synchronized... as if it was just one long song.
2. You can add your custom music for funnels. All of us know how cool is to enter a funnel and start hearing a different music track in there automatically and softly mixed up with the main music, and then stepping out of it and listen to the main music track back again. Well, now you'll be able to use your custom music for the funnels in your maps by means of a couple of operator stacks...
3. The same goes for the "musical lightbridges", you can make them play your custom sounds automatically when the player is near a lightbridge or standing on one of them.
4. Apart from that, you can program a ton of effects for your music tracks to be applied automatically, delays, speakers spatialization, dsp (echoes, reberveration,...), fade in and out, etc.
Of course, you can still make use of custom (or stock) soundscapes and play them altogether with your custom soundscripts combined in the same map...
3. Some Important Concepts First
First off, it's very important to have a clear idea of the different concepts related to the sound system in order to avoid misunderstandings, as well as the main differences between the entities you can use into your hammer editor to play the music scripts.
- Soundscripts vs Soundscapes: Soundscapes are simpler, intended to play background ambient music or sounds that we won't want to alter during a gameplay. Soundscripts are more complex, they contain the instructions and parameters to play, stop, sync, mix, delay, tweak... your .WAV music files (.MP3 sound files can also be played, however, due to they cannot be looped and therefore they're more limited, these won't be considered in this guide). These instructions are grouped into sound entries , that consist of code blocks delimited by curly braces { }. While both, Soundscripts and Soundscapes, use 'sound entries', the main difference between them is the complexity in their code, being far more complex the soundentries inside the Soundscripts.
Soundscapes look like this:
scroll down to see the full code
A list of the Portal2 stock soundscapes is available in the wiki.
Soundscripts look like this:
scroll down to see the full code
As you can see, both soundscapes and soundscripts share some commands like "wave", "volume" or "pitch"; but then there is a lot of code that is specific for the Soundscripts from the "soundentry_version" "2" line and downwards: all of that specific code is the "sound operator stacks" system.
- Soundentries: As mentioned above this concept must be used in the sense of "a number of instructions grouped in code blocks relative to one or several music files to be played automatically and following a certain order, rules, conditions, effects and whatnot". Remember that both, soundscripts and soundscapes use what it's known as 'sound entries'.
- Ambient_generic vs Env_soundscape: The following points are very important in this guide:
a) The ambient_generic entity can play both, a direct music file (.WAV or .MP3) AND also a pre-cached sound entry (i.e. "music.sp_a4_intro_b3").
b) The ambient_generic is the only entity capable to play soundscripts (the env_soundscapes can't) due to the complex code inside their sound entries.
c) Use the env_soundscapes only when you actually want to play 'soundscapes', simple looped background music.
d) A custom soundscape file per map can be automatically loaded by the game if it's located and named like so: "scripts/soundscapes_custom_<mapname>.txt" or "scripts/soundscapes_<mapname>.txt".
e) A custom soundscript file per map can be automatically loaded by the game if it's located and named like so: "maps/<mapname>_level_sounds.txt" ONLY FROM 2014, DECEMBER ONWARDS (this wasn't working before!)
f) For the ambient_generics, always remember this:
* Tick the flag "Play Everywhere". This will make your music environmental, and hearable everywhere.
* Tick the flag "Start Silent". This way we'll have always full control of the moment we'll want our tracks to be played by triggers.
* Untick the flag "Is NOT Looped". We'll want our music to keep on playing, and in fact we'll loop our tracks in advance.
Your custom tracks must be placed within the /sound folder to work. Usually, we will be placing our custom music into a /sound/music/ folder.
4. The SoundscriptFile
Our reference folder will be the portal2/scripts/ folder. Here you'll find all music soundscript files for each chapter of the campaign, single and multiplayer, scripted sequences, soundscripts for weapons npcs or events, all soundscapes used in the game, and so on.
Apart from those files, there are some important reference files to note here:
- sound_operator_stacks.txt -> this one contains ALL operator stacks within the sound system and all their pre-defined parameters we can choose to use. We'll be checking out many parts of this file little by little to discuss about their main functions.
- soundmixers.txt -> this file contains all "mixgroups" and sound mixers, and their main default values for parameters like max/min soundlevel, threshold or priority.
These are the main parts of a soundentry inside a Soundscript file (it's high resolution, click two times on the image to make it bigger):
As you can see, the soundentry name is enclosed between double quotes. Then all the code is below and delimited by curly braces {}.
Regarding our soundentry names, well, you could potentially use whatever ones. VALVe seems to name them identifying if it's a music track or any other sound at the beginning of the name, then a part relative to the map name, then a number or a key suffix that is used or interpreted by the game sound system and certain entities in special ways (_tbin, _tbout, _lbout, etc.). This means that the soundentry names might be more important than at a first glance we could think since some mixers, entities or stacks may refer to them by part of their name (a common usage is for example when you star playing a new soundentry, you stop all sound entries containing the word "music").
For the soundscript file name, as mentioned above, from 2014 December onwards the game will read a soundscript named "[map_name]_level_sounds.txt" from the /maps folder automatically. However, there is a problem in the workshop: due to the map is put into a long-string-of-numbers random new folder when published to the workshop, the game returns an error message failing to load the custom soundscript file, since it is no longer located into /maps, but into a random /maps/workshop/??????????????????/ folder. Therefore we will use a workaround to fix this for workshop maps, and the soundscript file name no longer needs to be necessarily "[map_name]_level_sounds.txt", but whatever one, as long as we declare it into the game_sounds_manifest.txt file. All this will be explained later in detail.
The basic commands are located always at the beginning of each soundentry and they are the following ones:
- Channel: They are used as a way to categorise the sounds. The most commonly used is the CHAN_STATIC one (to play a constant music that doesn't require any reaction). You can select from a variety of 'channels' explained in the wiki.
- Volume: It's a value between 0 and 1, where 1 is the loudest and 0 means no sound.
- Pitch: It's the sound tone in the scale. A value of 100 is the standard speed/tone the music file is recorded at. A higher value means a high-pitched sound, and a lower one, a low-pitched sound.
- Soundlevel: this is the attenuation we want for the sound when it's stopped and should fade/drop away.
- Wave and Rndwave: these 2 commands lead to the sound file to be played, and the path is relative to the /sound folder. They should point to .WAV files. With the command 'Wave' you only specify 1 single .Wav sound file, while with the command 'Rndwave' you can specify a list of sounds to be played randomly (hence the name of the command: rnd(random)wave ).
All of that has no secret and it's pretty well described on the wiki.
After setting up the values for the above main parameters, we have to include the following line to enable the sound operator stacks system:
"soundentry_version" "2"
And right after that:
"operator_stacks"
And it's now when we must open a new code block with a curly brace { and list all operator stacks, parameters and values. Then we close the block of code with another } brace. Each parameter inside each stack with its values must also be delimited by curly braces, making blocks of code.
NOTE: We'll be mainly copy/pasting certain code blocks from Valve's scripts in order to keep the same syntax and to avoid issues. You can then play with different values for the parametes, test the result in game, and post it here!
5. The Operator Stacks
As stated above, the goal of this guide is not to describe each and every sound operator stack plus all their parameters and all the different combinations you can have amongst them. As the wiki says:
"There are 28 operators used in Portal 2, but they are re-configured and combined in hundreds of different ways..."
Therefore the goal is to only introduce this system describing some of their more significative features and how they're used into Portal2.
There is a brief description of some operators in the wiki. For a complete list of the sound operator stacks and their parameters, you can check your file "portal 2/portal2/scripts/sound_operator_stacks.txt". These definitions will give us an idea of what each of them can do. This file contain all stacks and the arguments or parameters we can use set them up. We'll have to add to our custom soundscript all those stacks and parameters that we want in order to produce the mix or effect we desire.
There are 3 types of operator stacks according to their function:
- start_stack
- update_stack
- stop_stack
You can take a look to the different soundentries into the following soundscript files in order to check out how Valve has programmed each of the levels for each chapter:
- game_sounds_music_a1.txt
- game_sounds_music_a2.txt
- game_sounds_music_a3.txt
- game_sounds_music_a4.txt
- game_sounds_music_mp.txt
It is very important to know how these operators work when the game "reads" one soundentry, and how the game manages their inputs and instructions. In order to make all this clearer, I've splitted the information into the next 4 sections, one per relevant feature.
Consider as well to decompile some of Valve maps to go deeper into all this because it's extremely useful to check out how Valve places the triggers, what soundentries are played before and what code they have inside, what soundentries are played later and how, etc.
6. FEATURE: Syncing your tracks
Operator Stack: start_sync_to_entry
stack definitions
The idea behind this feature is basicly to chain our music tracks, variations or loops smoothly and synchronized. We'll want our tracks to have the same tempo or pitch in order to be able to synchronize them or to hear them as "only one song". Also we must loop our .WAV files (note that you can't loop .MP3 files) to let the music play continually without hearing any silence bit between multiple replay times. For this, you'd better get the free software Wavosaur. Open your track and press the "L" button; it'll automatically put one mark at the beginning and one mark at the end of your music track. Now Ctrl+S to save the track with the 2 loop marks. You're done looping!
OK, let's see how this works. We'll take an example from the "scripts/game_sounds_music_a2.txt" file, these 2 sound entries:
"music.sp_a2_dual_lasers.laser01"
"music.sp_a2_dual_lasers.laser02"
So the "music.sp_a2_dual_lasers.laser01" sound entry is intended to be played first and the "music.sp_a2_dual_lasers.laser02" is played later on, once the player arrives to a different area in the map and other ambient_generic is triggered. So we'll need one ambient_generic per soundentry in our map, and for example a couple of trigger_once entities to play each of them.
Following this example, we would place a trigger for example near the entry to play the first ambient_generic and first soundentry (music.sp_a2_dual_lasers.laser01). Then another trigger wherever for the second ambient_generic to play the 2nd soundentry (music.sp_a2_dual_lasers.laser02).
With regards to the first soundentry, it's up to you what stacks or effects to include. Of course, you'll need all the basic commands before adding any operator_stack. As we see in this example, the only "addition" here to the basic commands is this:
- Code: Select all
"update_stack"
}
{
"import_stack" "p2_update_music_spatial_portals"
"speakers_spatialize"
{
"input_radius" "200"
}
This stack seems to apply a spatializing effect to the music with a radius of 200 units (around the source entity). You can try copying-pasting blocks of code from other of the stock soundentries to test out how they modify your track and decide about the better effect.
Now, due to this code inside the 2nd soundentry:
- Code: Select all
"elapsed_time""start_stack"
{
"import_stack" "start_sync_to_entry"
{
"entry" "music.sp_a2_dual_lasers.laser01"
}
"duration_div"
{
"input2" "17"
}
}
whenever the player trigger the 2nd ambient_generic, there will be a soft mix between track 1 and 2, the transition from one track to the other one will be smooth and synchronized...
How exactly? Well, this is the key part, how the engine manages the information from the "start_sync_to_entry" stack. Let's check out Mr. Morasky's words on this:
"Start_sync_to_entry" syncs "this" sound entry to another sound entry that is already playing. Ie: sync "newPiece" to "playingPiece".
The way it does this is it checks the elapsed time of "playingPiece" and then does a "negative delay" when starting the "newPiece". Or in other words, "fast forwards" into the new piece so that it's starting in sync with "playingPiece".
Ie: "playingPiece" is playing at 15 seconds, "newPiece" will start at 15 seconds instead of 0 seconds.
However, this assumes that each piece, "newPiece" and "playingPiece" are the same length. Sometimes "newPiece" is shorter than "playingPiece" in which case "duration_div" or "duration division" is used.
If the length of "playingPiece" is 16 seconds and "newPiece" is 4 seconds then you set:
"duration_div"
{
"input2" "4"
}
Meaning that that the duration length of "playingPiece" will be divided by 4 before determining the elapsed time.
If "playingPiece" has been playing for 15 seconds but the "newPiece" is only 4 seconds long, you can't fast forward it 15 seconds. Therefore, you set "duration_div" to 4, which divides the duration of "playingPiece" by 4 and then does a modulus function, this leaves an elapsed time of 3 seconds, which will sync "newPiece" to the 4th quarter of "playingPiece".
For example if the lengths are:
playingPiece = 8 seconds
newPiece = 8 seconds
"duration_div.input2" = 1
playingPiece = 8 seconds
newPiece = 4 seconds
"duration_div.input2" = 2
playingPiece = 8 seconds
newPiece = 2 seconds
"duration_div.input2" = 4
...
NOTE: "newPiece" length must be an even division of "playingPiece" length.
So, basicly we will be using only the following 2 parameters for this stack:
- Code: Select all
"elapsed_time"
{
"operator" "get_entry_time"
}
"duration_div"
{
"operator" "math_float"
"apply" "div"
"input1" "@elapsed_time.output_sound_duration"
"input2" "4"
}
- elapsed_time: this parameter uses the operator "get_entry_time", and from the operators list, we can see it's a "Getter" type of operator (provides values from the engine). The function of this operator is to get the amount of elapsed time for a soundentry being played. So we have to introduce here the name of the first soundentry supposed to be playing, in our example:
- Code: Select all
"elapsed_time"
{
"entry" "music.sp_a2_dual_lasers.laser01"
}
- duration_div: as we read from Mr. Morasky above, we will use this parameter when our tracks are different length and we need to adjust our synchronization. In our example an "input2" of "17" is used.
- Code: Select all
"duration_div"
{
"input2" "17"
}
If you go and check the tracks these 2 soundentries are playing ("sp_a2_dual_lasers_l1.wav" and "sp_a2_dual_lasers_l2.wav") you'll find that they're just a couple of short sound effects, not music tracks really, and dividing the first one into 17 parts to sync the 2nd one seems to make sense when talking of really short-length sounds: due to how the system works (fast-forwarding the 2nd soundentry into the amount of seconds that the first entry elapsed) there are more chances to hear the second soundentry entirely if we divide the first one into 17 parts... take into account that these 2 tracks are 0:01 seconds length!
That's pretty much it though. As already mentioned there are more parameters within this stack, but we won't discuss about them for now... play with them and post what you discover!
7. FEATURE: Playing our custom music in funnels
Operator Stack: p2_update_music_play_tbeam
Key soundentry suffix: "_tbin"
stack definitions
The idea behind this feature is to play a different music than the playing one when the player is going into and out of any funnel in a given map. The guys at Valve named the soundentries related to funnels ended up in "tbout" ("tractor beam out") and "tbin" ("tractor beam in") and you can check out into the "portal 2/portal2/scripts/game_sounds_music_a4.txt" script file for the many sound entries with these 2 suffixes.
OK, the same as in the previous feature, this stack relates 2 soundentries, the playing soundentry and the one we'll want inside our funnels. Let's take another couple of related soundentries as example, these 2 from the "game_sounds_music_a4.txt" script file:
"music.sp_a4_tb_intro_b1"
"music.sp_a4_tb_intro_tbin"
The first one is intended to be playing before we enter into any funnel, tipically by means of any ambient_generic triggering. Second one is the soundentry that will be played automatically when we enter into a funnel.
Note here that soundentries for funnels won't be called by ambient_generics, but they will be automatically played when the player enters a funnel. This feature works this way:
- Entering the map and triggering the corresponding first ambient_generic will start the soundentry "music.sp_a4_tb_intro_b1".
- That soundentry imports and runs the "p2_update_music_play_tbeam" stack with the arguments inside the "music.sp_a4_tb_intro_tbin" soundentry. This means that from the moment the 1st soundentry is played, the sound system is "ready" to play that 2nd soundentry whenever the player meets a funnel.
About how and why this works internally, I can only quote again Mr. Mike Morasky:
"...I don't recall exactly but I'm assuming that p2_update_music_play_tbeam runs every frame looking for a tbeam to be entered (probably by testing the mixgroup state of the sound triggered when entering a tbeam) and plays music.sp_a4_tb_intro_tbin when that state is true."
We can check into whatever "_tbin" soundentry how it always updates the mixer with the following code:
- Code: Select all
"import_stack" "update_music_stereo"
"mixer"
{
"mixgroup" "unduckedMusic"
}
So probably the funnels are coded to play the music within the "unduckedMusic" mixgroup... Again, by now let's copy/paste the existing code for starters; then you play with different values or code pieces to get different fade in-out or delay effects for example. However, bare in mind that the "_tbin"/"_tbout" part in the soundentry names, altogether with that "mixer" part of code both seem to be mandatory for this feature to work out properly.
8. FEATURE: Playing our custom music in lightbridges
Operator Stack: p2_update_music_spatial_portals
Key soundentry suffix: "_lbout"
stack definitions
This feature plays a different music when the player is on a lightbridge. The 2nd chapter in Portal is the one where the lightbridges are introduced, and we can check into the script file "portal 2/portal2/scripts/game_sounds_music_a2.txt" to find several soundentries examples with the suffix "_lbout"... let's take one of them:
"music.sp_a2_bridge_the_gap_lbout"
As we can see, what Valve seems to apply to the music track "music/sp_a2_bridge_the_gap_lbout.wav" it's a fade-in and a spatialize effect. Also there is a "source-info" stack parameter that seems to indicate where will the music come out from, in this case, the entity (I think it refers to the prop_wall_projector prop itself, since I seem to listen the track louder as I get close to the prop...)
Note again that we won't be using any ambient_generic to play any "_lbout" soundentry directly. We just need to make sure we made the "_lbout" soundentry in our soundscript.
Nothing much to add here. It's mainly based in adding the suffix "_lbout" to our soundentry and the code we can see in the example above, then the game will automatically load the soundentry with the "_lbout" by map name and will be cached and ready for when the player gets on a bridge.
9. How to make your custom soundscripts to work in the Workshop
As mentioned before there's a recent update that makes Portal2 to automatically search and load a soundscript file per map, like the rest of Valve maps do, but it must be placed inside the maps/ folder and be named relative to the map name like so:
/maps/[map_name]_level_sounds.txt
Example: "jose_soundscripts_test_level_sounds.txt" (the name of the map in this case is "jose_soundscripts_test.BSP")
This means the following statements for your reference:
1. In the case that you already have your soundscript file named relative to your map's name and inside the /maps folder, you'll be able to make your soundscript to work and therefore your custom sounds to be heard when running the map via the console command 'map [map_name]' only if you declare the name of your soundscript file inside the /scripts/game_sounds_manifest.txt file. Due to the game priority system, as you know, you'll need to edit this file: "portal 2/portal2_dlc2/scripts/game_sounds_manifest.txt". It doesn't really matters inside which /maps folder you put the soundscript in though from the current available ones:
- "portal 2/portal2/map/"
- "portal 2/portal2_dlc1/maps/"
- "portal 2/portal2_dlc2/maps/"
2. The above requirement can be bypassed with simply pakratting the soundscript file inside the .BSP map file, meaning that a map with its soundscript file "[map_name]_level_sounds.txt" packed inside will load it automatically, no need to declare it into the game_sounds_manifest.txt. Inside the BSP the soundscript file must be into a /maps folder to work though.
3. Therefore, to distribute a .BSP map file with all sound customization you need to pack inside the .BSP:
- your custom sounds (always remember to fix up the paths for your sound files when packing)
- the soundscript file (always remember to fix up the path to leave it right inside the /maps folder)
However, in the case we want our map published to the Workshop, things don't work out like that. Due to the workshop creates a random folder composed by a long string of numbers and places the map inside "/maps/workshop/[long-string-of-numbers]/" when we publish the map, even though we already pakratted the relevant files inside the .BSP, we'll get a console error message similar to these:
PrecacheScriptSound 'music.workshop34110908146838399jose_soundscripts_test_tbout' failed, no such sound script entry
PrecacheScriptSound 'music.workshop34110908146838399jose_soundscripts_test_lbout' failed, no such sound script entry
Strangely, this problem doesn't pop up with soundscapes: even if you publish a map to your workshop with the custom soundscape file packed in and the map is stored after published inside a random folder, the soundscape file is loaded and the custom soundscapes are heard...
Workshop workaround
There is a hacky workaround that our mate HMW discovered when dealing with custom soundscripts. I'll explain the whole process step by step using an example:
1. Inside the Hammer Editor
As already mentioned above, you're supposed to place your triggers and ambient_generics pointing to your soundentries in your map at your desired places, I'm assuming you already control this part... Next you'll need to add the following entity to perform this workaround:
- point_servercommand. Name it for example "@servercommand"
Now open whatever logic_auto you surely have in your map (or add one) and add the following output:
That will reset the soundemitter on map spawn and somehow to load the game_sounds_manifest.txt that we will pakrat inside the BSP map file; let's explain this in the next point.
2. Files & Pakrat
I'll be assuming you are using the portal2_dlc2/ folder due to its priority over the rest. Let's say we've got a map called "jose_soundscripts_test" into portal2_dlc2/maps/ and and its relative soundscript file called "jose_soundscripts_test_level_sounds.txt" also inside portal2_dlc2/maps/. There is also the "game_sounds_manifest.txt" inside portal2_dlc2/scripts/
Remember that we must declare our custom soundscript inside the "game_sounds_manifest.txt" file by adding the following line in:
- Code: Select all
"precache_file" "maps/jose_soundscripts_test_level_sounds.txt"
After adding that line, it looks like so:
Now we must use pakrat to pack those 2 files in, altogether with our custom sounds.
First off, a couple of advices about pakrat settings:
- Into "File > Preferences": just check all options like in the image below, select "Ask" on "Path fixup on add file" (mainly because this way you will be sure it is fixing up the paths when it asks you), and finally browse your gamedir (portal 2/portal2/) at the "Game Root Directory"
- I suggest you to also put the gamedir whole path in the dialog box you'll find after hitting the "Scan" button at the top of the window. Pakrat will remember this path next times from now on and this way the Scan feature will be functional.
Now pakrat is correctly set up.
- Next open your BSP map file in pakrat, and click on the "Add" button to start adding files.
- Now you must browse and locate the files in your computer. When browsing, notice there's a drop-down option at the bottom of the window labelled "File types" with the "All valid pak files" option set by default. To locate the .txt files sometimes you must change that option to "All files":
- OK, select the "game_sounds_manifest.txt" file from the "portal2_dlc2/scripts/" folder. If you set pakrat to "ASK" about fixing up the path when adding a new file in Preferences, it should ask you:
Hit the "Yes" or "Yes to all" button.
- Then browse again and select the soundscript file from the /maps folder. After adding this one too, you must have something like this:
- The lack of tick at the rightmost little boxes means that even though we added the files, these 2 files aren't actually in yet, we must save the BSP to see these 2 boxes checked.
- If you take a close look you'll see the paths to the 2 files inside the BSP contain a "dlc2/" part. If we leave it like that it won't work out; we must edit these paths. Select one of them and click on the "Edit" button to erase that part:
- After doing so with the 2 files, you should have something like this:
Now the paths are correct inside the BSP. Don't forget to save (Ctrl+S or File > Save BSP) your changes!
- If you prefer a clearer view inside Pakrat like me, you can select "View > As a Tree" to see it like in the image:
- Don't forget to add your custom music files too. Their paths don't need to be editted, they will appear within the sounds/ folder right as you actually have them in your local folders.
Then your map should be ready to be published to your workshop with your custom soundscripts functional.
10. Work in Progress
There are some features and stacks I'm still investigating about, and I'd like to mention them in this section just for your reference.
- Stack "p2_update_music_play_gel" / "p2_update_music_play_speed_gel"
This seems to me like we could potentially use custom sounds for gels... however I haven't been able to make it to work yet. You can check out all parameters and information about these stacks in the sound_operator_stacks.txt file. I cheer you up to try and make it to work!!
- Usage of "commonnull.wav"
In my journey through all soundscripts and Valve's decompiled maps, I noticed a continuous and interesting usage of this .WAV file. I checked how sometimes they trigger a soundentry with this track in the player's route before triggering an actual soundentry with a "real" sound. Example:
- Code: Select all
"music.sp_a2_intro_01"
// "wave" "*music/sp_a2_intro.wav"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_NONE"
"volume" "0.65"
// "wave" "npc/xray/beep.wav"
"wave" "common/null.wav""soundentry_version" "2""operator_stacks"
{
// "start_stack"
// {
// "set_mixlayer_solo_glados"
// {
// "operator" "sys_mixlayer"
// "mixlayer" "gladosVOLayer"
// "mixgroup" "Music"
// "field" "solo"
// "input" "0.35"
// }
// "set_mixlayer_solo_wheatley"
// {
// "operator" "sys_mixlayer"
// "mixlayer" "wheatleyVOLayer"
// "mixgroup" "Music"
// "field" "solo"
// "input" "0.0"
// }
//
// }
"update_stack"
{
"import_stack" "update_music_stereo""volume_fade_in"
{
"input_max" "0.0"
}
"volume_fade_out"
{
"input_max" "0.0"
// "input_max" "4.0"
}
}
// "stop_stack"
// {
// "set_mixlayer_solo_glados"
// {
// "operator" "sys_mixlayer"
// "mixlayer" "gladosVOLayer"
// "mixgroup" "Music"
// "field" "solo"
// "input" "0.0"
// }
// }
}
}
To me it seemed like a sort of placeholder to do something else... a way to free the mixer or the stack... but couldn't really deduct much more. So I asked Mr. Mike Morasky and this is his reply:
There are 2 uses of "commonnull.wav".
1) A place-holder to be overridden by the entry using the stack.
2) A place-holder to "do nothing" as every soundentry must have a soundfile. This one does nothing.
Still, I don't really know when exactly or where I should use it anyways... This is pending to be investigated in depth...
I beg to anyone who has dealt with this sound system to revise this post and report possible corrections, as well as any find within this system that may be included, thanks. This post is expected to be editted multiple times.
I'm working on the Steam Guide version of this, but I don't know when I will have it ready since I'm really busy. I'd like to give that one a better and more intuitive structure. Will let you know when I do.
Quote from srs bsnss on April 30, 2015, 2:30 pmWow - what an effort Jose! This is a HUGELY helpful document - the amount of people who have asked how to put music in funnels and light bridges (I'm one of them) is enormous. If I were a mod, I'd go to the point of doing a blog post linking to this that was titled "If you've ever wanted music to play in a funnel..." (And maybe bump you up to Community Contributor status )
Thank you Jose!
Wow - what an effort Jose! This is a HUGELY helpful document - the amount of people who have asked how to put music in funnels and light bridges (I'm one of them) is enormous. If I were a mod, I'd go to the point of doing a blog post linking to this that was titled "If you've ever wanted music to play in a funnel..." (And maybe bump you up to Community Contributor status )
Thank you Jose!
Quote from josepezdj on May 1, 2015, 5:21 amThank you Steve! thanks for such a nice comment! I really hope ppl make use of this and it's clear enough so all of you can manage these features
srs bsnss wrote:(And maybe bump you up to Community Contributor status )Hehe... I do feel like a community contributor already
And for some years now I've been feeling that my rank in this site won't change ever; but I'm fine with that anyways. I feel the warmth of the people I help
Thank you Steve! thanks for such a nice comment! I really hope ppl make use of this and it's clear enough so all of you can manage these features
Hehe... I do feel like a community contributor already
And for some years now I've been feeling that my rank in this site won't change ever; but I'm fine with that anyways. I feel the warmth of the people I help
Quote from Arachnaphob on May 1, 2015, 10:07 amJebus, that's like an essay on soundscripts. I've bookmarked this page for the future. Thanks so much!
Jebus, that's like an essay on soundscripts. I've bookmarked this page for the future. Thanks so much!
Musical website Moddb
Quote from quaternary on May 2, 2015, 12:18 am"Community Contributor" sounds lame compared to "Olympian and/or War Hero". Just sayin.
Fantastic article!! Now the only thing preventing me from filling my maps with shitty music is... I don't know which ones to add. Heh. But really though, I always wondered how in e.g. mods, there can be an entire new remix of the song played in funnels, and also at the beginning of chapter 9 where the off-key version of the funnel music plays. Now I can do that too c:
Also, how... how did you get an email from valve
"Community Contributor" sounds lame compared to "Olympian and/or War Hero". Just sayin.
Fantastic article!! Now the only thing preventing me from filling my maps with shitty music is... I don't know which ones to add. Heh. But really though, I always wondered how in e.g. mods, there can be an entire new remix of the song played in funnels, and also at the beginning of chapter 9 where the off-key version of the funnel music plays. Now I can do that too c:
Also, how... how did you get an email from valve
Quote from Arachnaphob on May 2, 2015, 2:16 amquaternary wrote:StufWith the tractor beam sounds, I believe those are all pre made beforehand and just normal audio files. The difference is that there are a few variations on the pitches as different files. Chances are, they were all constructed from one or various presets of Native Instruments' Synths, as a lot of the Portal 2 soundtrack was. I have discovered a few in the tens of thousands of presets.
With the tractor beam sounds, I believe those are all pre made beforehand and just normal audio files. The difference is that there are a few variations on the pitches as different files. Chances are, they were all constructed from one or various presets of Native Instruments' Synths, as a lot of the Portal 2 soundtrack was. I have discovered a few in the tens of thousands of presets.
Musical website Moddb
Quote from Inspirata on May 5, 2015, 1:55 amIn part 2: "sync our cached music tracks"
Worth mentioning: this is useful for making those musical laser catchers/relays sync up with one another and/or the background music (e.g. the laser relay intro map (sp_a2_laser_relays) or Wheatley's map with the platform and laserfields (sp_a4_laser_platform)). An ambient_generic with a raw .WAV can't do that.sv_soundemitter_flush... that's gold right there. Everything else is secondary
In part 2: "sync our cached music tracks"
Worth mentioning: this is useful for making those musical laser catchers/relays sync up with one another and/or the background music (e.g. the laser relay intro map (sp_a2_laser_relays) or Wheatley's map with the platform and laserfields (sp_a4_laser_platform)). An ambient_generic with a raw .WAV can't do that.
sv_soundemitter_flush... that's gold right there. Everything else is secondary
Quote from josepezdj on May 5, 2015, 5:39 amThanks for the support, guys!
quaternary wrote:Also, how... how did you get an email from valveWell, to be honest, I always felt they are very responsive people. First time I thought of writting to them was when I was creating custom water materials and read Alex Vlachos' "Water Flow in Portal2" article... Tired of trial and error and issues everywhere, I wrote to Alex Vlachos. He is very kind and polite, and tried to help me out. For all this soundscript adventure, I wrote him again, and he handed over my e-mail to Mr. Morasky and another sound technician. Mr. Morasky has been amazingly responsive, patient and helpful It's incredible that they have come out with such a complex system to control music in games...
Arachnaphob wrote:With the tractor beam sounds, I believe those are all pre made beforehand and just normal audio files. The difference is that there are a few variations on the pitches as different files. Chances are, they were all constructed from one or various presets of Native Instruments' Synths, as a lot of the Portal 2 soundtrack was. I have discovered a few in the tens of thousands of presets.Well, yeah, I guess it's a mix of everything. In one hand, you are not the first person mentioning about those presets found in the Portal2 soundtrack. I am a Reason user though, haven't got any of those synths so I can't know for sure.
However, apart from that, there are always some effects they use for the tbeams music. Example:
- Code: Select all
"music.sp_a4_tb_intro_tbin"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_NONE"
"volume" "0.5""wave" "*music/sp_a4_tb_intro_tbin.wav"
"soundentry_version" "2"
"operator_stacks"
{
"start_stack"
{
"random_offset"
{
"operator" "math_random"
"input_min" "0.0"
"input_max" "126"
}"negative_delay"
{
"operator" "math_float"
"apply" "mult"
"input1" "@random_offset.output"
"input2" "-1.0"
}
"delay_output"
{
"operator" "sys_output"
"input_float" "@negative_delay.output"
"output" "delay"
}
}
"update_stack"
{
"import_stack" "update_music_stereo"
"mixer"
{
"mixgroup" "unduckedMusic"
}"volume_fade_in"
{
"input_max" "3.0"
"input_map_min" "0.05"
}
"volume_fade_out"
{
"input_max" "0.75"
"input_map_min" "0.05"
}
"volume_lfo_time_scale"
{
"input2" "0.3"
}
"volume_lfo_scale"
{
"input2" "0.4"
}}
}
}You can go to your pak01_dir.VPK and listen to "music/sp_a4_tb_intro_tbin.wav", and then compare it with how it sounds in game...
What I do is starting with the same code than above, and I make a simpler version of my custom main track, with less instruments. Then depending on how it sounds, I start to change the parameters and listen carefully to the difference it makes... until I'm happy with it.
Inspirata wrote:In part 2: "sync our cached music tracks"
Worth mentioning: this is useful for making those musical laser catchers/relays sync up with one another and/or the background music (e.g. the laser relay intro map (sp_a2_laser_relays) or Wheatley's map with the platform and laserfields (sp_a4_laser_platform)). An ambient_generic with a raw .WAV can't do that.Indeed. How they do is to use the vscript "scripts/laser_target/musical_laser_catcher.nut", and depending on the catcher name and a counter there's in the vscript, a given sound is played from a pre-cached list. All of these sounds are quite simple though (not really music tracks), probably because the laser catchers can be constantly powered on and off, so it's not needed to sync them with the main music.
The "start_sync_to_entry" seems an useful stack to achieve a good synchronization here though in the case you really want it
Inspirata wrote:sv_soundemitter_flush... that's gold right there. Everything else is secondaryHehe... totally! To be honest, I had this guide stopped and waiting in my pendrive for a few months because I didn't want to explain a thing that you couldn't really put into practice completely due to the workshop limitations. I wish Valve could make the soundscripts to work in the workshop just as soundscapes do, simply by name, but soundscripts are still affected by the random-numbers-name folder the map is located into after it has been published... So luckily there's this workaround
Thanks for the support, guys!
Well, to be honest, I always felt they are very responsive people. First time I thought of writting to them was when I was creating custom water materials and read Alex Vlachos' "Water Flow in Portal2" article... Tired of trial and error and issues everywhere, I wrote to Alex Vlachos. He is very kind and polite, and tried to help me out. For all this soundscript adventure, I wrote him again, and he handed over my e-mail to Mr. Morasky and another sound technician. Mr. Morasky has been amazingly responsive, patient and helpful It's incredible that they have come out with such a complex system to control music in games...
Well, yeah, I guess it's a mix of everything. In one hand, you are not the first person mentioning about those presets found in the Portal2 soundtrack. I am a Reason user though, haven't got any of those synths so I can't know for sure.
However, apart from that, there are always some effects they use for the tbeams music. Example:
- Code: Select all
"music.sp_a4_tb_intro_tbin"
{
"channel" "CHAN_STATIC"
"soundlevel" "SNDLVL_NONE"
"volume" "0.5""wave" "*music/sp_a4_tb_intro_tbin.wav"
"soundentry_version" "2"
"operator_stacks"
{
"start_stack"
{
"random_offset"
{
"operator" "math_random"
"input_min" "0.0"
"input_max" "126"
}"negative_delay"
{
"operator" "math_float"
"apply" "mult"
"input1" "@random_offset.output"
"input2" "-1.0"
}
"delay_output"
{
"operator" "sys_output"
"input_float" "@negative_delay.output"
"output" "delay"
}
}
"update_stack"
{
"import_stack" "update_music_stereo"
"mixer"
{
"mixgroup" "unduckedMusic"
}"volume_fade_in"
{
"input_max" "3.0"
"input_map_min" "0.05"
}
"volume_fade_out"
{
"input_max" "0.75"
"input_map_min" "0.05"
}
"volume_lfo_time_scale"
{
"input2" "0.3"
}
"volume_lfo_scale"
{
"input2" "0.4"
}}
}
}
You can go to your pak01_dir.VPK and listen to "music/sp_a4_tb_intro_tbin.wav", and then compare it with how it sounds in game...
What I do is starting with the same code than above, and I make a simpler version of my custom main track, with less instruments. Then depending on how it sounds, I start to change the parameters and listen carefully to the difference it makes... until I'm happy with it.
Worth mentioning: this is useful for making those musical laser catchers/relays sync up with one another and/or the background music (e.g. the laser relay intro map (sp_a2_laser_relays) or Wheatley's map with the platform and laserfields (sp_a4_laser_platform)). An ambient_generic with a raw .WAV can't do that.
Indeed. How they do is to use the vscript "scripts/laser_target/musical_laser_catcher.nut", and depending on the catcher name and a counter there's in the vscript, a given sound is played from a pre-cached list. All of these sounds are quite simple though (not really music tracks), probably because the laser catchers can be constantly powered on and off, so it's not needed to sync them with the main music.
The "start_sync_to_entry" seems an useful stack to achieve a good synchronization here though in the case you really want it
Hehe... totally! To be honest, I had this guide stopped and waiting in my pendrive for a few months because I didn't want to explain a thing that you couldn't really put into practice completely due to the workshop limitations. I wish Valve could make the soundscripts to work in the workshop just as soundscapes do, simply by name, but soundscripts are still affected by the random-numbers-name folder the map is located into after it has been published... So luckily there's this workaround
Quote from DaMaGepy on May 5, 2015, 4:56 pmIf you can write to them, ask them to add an option to the workshop for custom loading screens!
Of course its impossible to add any pic into a bsp, since the loading screen is displayed while loading it, but at the publisher they could add another picture upload option, or just to use the uploaded (preview) pic from the workshop while loading the map (or with PTI maps the mapeditor pic). Max a day of work for them
If you can write to them, ask them to add an option to the workshop for custom loading screens!
Of course its impossible to add any pic into a bsp, since the loading screen is displayed while loading it, but at the publisher they could add another picture upload option, or just to use the uploaded (preview) pic from the workshop while loading the map (or with PTI maps the mapeditor pic). Max a day of work for them
Quote from Inspirata on May 11, 2015, 12:01 amNow here's a riddle for you: I checked the contents of Valve's hard light bridge maps, and they don't use an ambient_generic to create the music, which leaves me wondering how exactly to get a custom sound to work on a hard light bridge if it's not via an ambient_generic....
Side note: why is it that the PeTI hard light bridge sounds work offline, but when published to the workshop, they're gone?
Now here's a riddle for you: I checked the contents of Valve's hard light bridge maps, and they don't use an ambient_generic to create the music, which leaves me wondering how exactly to get a custom sound to work on a hard light bridge if it's not via an ambient_generic....
Side note: why is it that the PeTI hard light bridge sounds work offline, but when published to the workshop, they're gone?