Controlling Looping Sounds System

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
Post Reply
Batty
Posts: 509
Joined: Sun Apr 15, 2012 7:04 pm

Controlling Looping Sounds System

Post by Batty »

Looping sounds ("sounds" in this post) have the following problems:
  • 1. Can't be stopped
    2. Disappear when player loads savegame
    3. Disappear when party changes level
    4. Audible on every level in square they're played
I use door objects to produce sounds instead of playSoundAt() and it works well. The definition looks like this:

Code: Select all

cloneObject{
	name = "sound_wind",
	baseObject = "dungeon_wall_grating",
	--class = "Door",
	model = "mod_assets/models/vertex.fbx",
	openSound = "silence",
	closeSound = "silence",
	lockSound = "wind",
	openVelocity = 100,
	--closeVelocity = 0,
	--closeAcceleration = -10,
	--sparse = true,
	--placement = "wall",
	editorIcon = 88,
}
The door is invisible & is spawned then immediately opened thus producing the lockSound which is your defined sound. This fixes problem #4 as door produced sound does not occur on every level as playSoundAt() does. The sound can be stopped at anytime by simply destroying the door which fixes problem #1. The door never blocks anything as it's opened the moment it's spawned at a velocity of 100.

Note the following:
  • openSound & closeSound are silent, you need your own silent .wav because you can't have the standard door sounds playing.
  • You must make the door model disappear. There are several ways to do this, I use a null model (very small), I leave that up to you.
  • openVelocity must be set high. I use 100, a nice round number, it opens the door instantaneously.
  • You define one "sound door" for every defined sound. Easy to do, 2 steps only: change the name and set the lockSound as your defined sound.
Problem #2 was solved in this thread. That leaves #3 and overall sound management so I have this function:

Code: Select all

function detectParty(obj)
	if obj.level == party.level then
		current = party.level
		if notAtStart then
			if not findEntity("loopy") then
				spawn("fx", 2, 25, 9, 0, "loopy"):setLight(0, 0, 0, 0, 0, 1800000, false)
				restoreSound(current)		
			end		
		end	
		notAtStart = true
		if previous and current ~= previous then
			restoreSound(current)	
		end
		previous = current	
	end
end
I have a timer on every level set to same interval (0.5 seconds 8-)) calling this function. This function detects when the player loads a savegame and when a level change occurs and then calls restoreSound() to restart the sounds only on the current party level.

Note the following:
  • The if not findEntity("loopy") section is the check for a loaded savegame, again, covered in this thread.
  • Current & previous handle when the party changes level. This works well. In testing, I noticed that the level change registers before you arrive (e.g. midpoint down a pit) so there is no interruption in sound as they are restored before you arrive.
  • The check for if notAtStart and if previous are required to handle the special case of the player first starting the game. I don't want restoreSound() called at game start, notAtStart & previous will only be nil once, at start, the first time the function is called. They will have values from then on and their respective tests will pass for all following function calls thus calling restoreSound().
Remaining functions:

Code: Select all

function restoreSound(current)		
	local l, x, y
	for sound, state in pairs(sounds[current]) do
		if state == "on" then
			l = findEntity(sound).level
			x = findEntity(sound).x
			y = findEntity(sound).y		
			for entity in entitiesAt(l, x, y) do		
				if entity.name == findEntity(sound).name then		
					entity:setDoorState("closed")
					entity:open()			
				end			
			end	
		end		
	end
end

sounds = { [1] = { },
		     [2] = { },
		     [3] = { } }
		
function soundStateOn(id, l)
	sounds[l][id] = "on"
end

function soundStateOff(id, l)
	sounds[l][id] = "off"
end

function startSound(sound, l, x, y, f, id)
	spawn(sound, l, x, y, f, id):open()
	soundStateOn(id, l)	
end

function stopSound(id)
	soundStateOff(id, findEntity(id).level)
	findEntity(id):destroy()
end
The restoreSound() function iterates through the sounds on the current party level & if it finds them, restarts them. Note how sounds are restarted by setting the door state to closed, then opening it again thus restarting the lockSound (your defined sound).

The sounds table stores references to all the sound doors. The example is a 3 level dungeon, you would add tables within the sounds table, one each for each level of your dungeon. Level 7 would be [7] = { },

startSound(sound, l, x, y, f, id) is called with the exact same parameters as spawn() (that's what it does, after all). Sound is the name of the sound door, not the defined sound.
stopSound(id) simply pass it the unique ID of the sound door.

Important: Door object produced sound emanates from the edge of the square (between squares). This doesn't change no matter where you place the door. You may want this, in which case, you're fine. However, if you want the sound to emanate "centrally" from the entire square you must do this:

Code: Select all

startSound("sound_wind", 1, 27, 4, 0, "sw_1_27_4_0")
startSound("sound_wind", 1, 27, 4, 2, "sw_1_27_4_2")
Two sound doors are spawned at opposite facings in the same square. You need to lower the volume ~50% of your defined sound since it is played twice. This works surprisingly well when compared to a single sound with playSoundAt(), no significant distortion.

Disclaimer: We may get a nice sound object from AH making this all for naught. There may be other ways, better ways, there usually is. Nevertheless, this system is working well for me (could be bugged, of course). You can implement these sound doors any way you want, my system is here for example. :D
Last edited by Batty on Fri Dec 14, 2012 12:06 am, edited 7 times in total.
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Controlling Looping Sounds System

Post by Komag »

nice work! This is quite the workaround! I hope (in a good way) that ultimately it's a wasted effort due to AH fixing the bug properly, but at least it's possible to deal with in the mean time :)
Finished Dungeons - complete mods to play
User avatar
Phitt
Posts: 442
Joined: Tue Aug 14, 2012 9:43 am

Re: Controlling Looping Sounds System

Post by Phitt »

Nice idea! Didn't think of that. Also hope AH will fix it eventually, but definitely better than the playSoundAt approach. Thanks!

EDIT: ...
EDIT2: Nevermind, pits/trapdoors don't have a sound you can define, thought I remembered something like that.
EDIT3: Maybe a spell without particle effect/light/screen effect and with 0 damage?
EDIT4: Nope, spells also play their sound on all levels.
Batty
Posts: 509
Joined: Sun Apr 15, 2012 7:04 pm

Re: Controlling Looping Sounds System

Post by Batty »

Thanks guys. Before I used doors to produce sound, I tried a few other objects (forget which, blockage was one) & attached looping sounds to them. Destroying them destroyed the object but the sound kept going. Doors were the first object that allowed me to stop sounds. So, there may be others, I haven't tried them all.

The thing with the doors is that they normally have sounds that are played for different durations depending on open/close velocity and are, I guess, hardcoded to be stopped so the next sound (lock, close) can be played. I *think* this may be unique to the doors, but again, I only tried a few objects.
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Controlling Looping Sounds System

Post by Komag »

I think the door movement sounds are simply the right length, not that they stop per se
Finished Dungeons - complete mods to play
User avatar
zimberzimber
Posts: 432
Joined: Fri Feb 08, 2013 8:06 pm

Re: Controlling Looping Sounds System

Post by zimberzimber »

question:
Where do I put the:

Code: Select all

cloneObject{
   name = "sound_wind",
   baseObject = "dungeon_wall_grating",
   --class = "Door",
   model = "mod_assets/models/vertex.fbx",
   openSound = "silence",
   closeSound = "silence",
   lockSound = "wind",
   openVelocity = 100,
   --closeVelocity = 0,
   --closeAcceleration = -10,
   --sparse = true,
   --placement = "wall",
   editorIcon = 88,
}
I tried looking at the post but I found nothing about where to place this, ad how.
maybe its just me being stupidly blind, but hey...
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Controlling Looping Sounds System

Post by Komag »

In the original game the base thing is in objects.lua so that is a good place for your mod version. But honestly it can go anywhere.
Finished Dungeons - complete mods to play
User avatar
AdrTru
Posts: 223
Joined: Sat Jan 19, 2013 10:10 pm
Location: Trutnov, Czech Republic

Re: Controlling Looping Sounds System

Post by AdrTru »

Super idea. THX
I using it in new version of MultiAlcoveManager for Torch burning sound.
(This new version will be published soon :) )
My LOG2 projects: virtual money, Forge recipes, liquid potions and
MultiAlcoveManager, Toolbox, Graphic text,
User avatar
Eightball
Posts: 48
Joined: Thu Jan 09, 2014 8:21 am

Re: Controlling Looping Sounds System

Post by Eightball »

Thank you! I had thought the sound bug was my fault and spend a few hours looking for the non-existent error. But making invisible doors? This is going to take me a while. Time to invest, uh, time in a session of graphic editing forums to make that tiny door "model". Wish AH had made the fix, but as of yet still hasn't, I guess. Must be all that work on Grim2.
Post Reply