The Lurking Horror source code loop handling

I’m looking at the ZIL code that handles the audio in The Lurking Horror, trying to work out how it handles the looping sounds.

The interpreters that currently support sound effects in The Lurking Horror, Frotz and Bocfel, both have a hard-coded list in the source of the sound effects that should loop. But looking at the code, it seems that the special handling of the looping effects (S-DRONE, S-ATTACK, S-PSYCHO, S-MONSTR, S-VOICE, S-ZOMBIE, S-CRETIN) is in the game code rather than built into the interpreter or the sound files themselves.

All sound effects are listed here.

Does anyone understand what <PUT ,SOUND-FLAG 0 <+ <* .N 16> .VOL>> and <PUT ,SOUND-FLAG 1 <+ <* .N 16> .VOL>> actually do here?

"SOUND sound-id,[action],[volume]"

<ROUTINE SOUNDS (N "OPT" (OP ,S-START) (VOL 8))
	 <COND (<ZERO? .OP> <SET OP ,S-START>)>
	 <COND (<AND <L? <GET ,SOUND-FLAG 0> 0>
		     <EQUAL? .OP ,S-START>>
		<PUT ,SOUND-FLAG 0 1>
		<TELL CR
"[Use $SOUND to toggle sound usage on and off.]" CR CR>)>
	 <COND (<EQUAL? .N ,S-DRONE ,S-ATTACK ,S-PSYCHO ,S-MONSTR
			,S-VOICE ,S-ZOMBIE ,S-CRETIN>
		<PUT ,SOUND-FLAG 1 <+ <* .N 16> .VOL>>)>
	 <COND (<GET ,SOUND-FLAG 0>
		<COND (<EQUAL? .OP ,S-STOP>
		       <PUT ,SOUND-FLAG 0 1>
		       <PUT ,SOUND-FLAG 1 0>)
		      (<EQUAL? .N ,S-DRONE ,S-ATTACK ,S-PSYCHO ,S-MONSTR
			       ,S-VOICE ,S-ZOMBIE ,S-CRETIN>
		       <PUT ,SOUND-FLAG 0 <+ <* .N 16> .VOL>>
		       <PUT ,SOUND-FLAG 1 <+ <* .N 16> .VOL>>)
		      (ELSE
		       <PUT ,SOUND-FLAG 0 1>
		       <PUT ,SOUND-FLAG 1 0>)>
		<COND (<EQUAL? .OP ,S-START>
		       <SOUND .N .OP .VOL>)
		      (ELSE
		       <SOUND .N .OP>)>)>>

SOUND-FLAG is a table (array) with two slots

<GLOBAL SOUND-FLAG <TABLE -1 0>>

Normally it contains the values [1, 0] but for the sounds ,S-DRONE ,S-ATTACK ,S-PSYCHO ,S-MONSTR,S-VOICE ,S-ZOMBIE and ,S-CRETIN it contains the values [sound-id*16+volume, sound-id*16+volume]. As far as I can see the SOUND-FLAG is only used with RESTORE and when you toggle sound on/off with $SOUND. I assume this is to ensure that continuously sound is started when you restore/toogle sound on and are in a location that should play sound. The one-off sounds is not repeated under these circumstances.

The actual sound is played in the snippet below (as far as I can see the only call to SOUND). The third parameter is actually both volume and repetitions (rep*256+vol), rep=255 is infinitive) but I can’t see that VOL ever has a value that includes repetitions. I guess that the looping property of the sound are either definied inside the sound-file or in the terp

<COND (<EQUAL? .OP ,S-START>
		       <SOUND .N .OP .VOL>)
		      (ELSE
		       <SOUND .N .OP>)>)>>

Well, that is my reading of the code and I could absolutely be completely wrong…

2 Likes

The SOUND-FLAG is initially [-1,0] and the code

	 <COND (<AND <L? <GET ,SOUND-FLAG 0> 0>
		     <EQUAL? .OP ,S-START>>
		<PUT ,SOUND-FLAG 0 1>
		<TELL CR
"[Use $SOUND to toggle sound usage on and off.]" CR CR>)>

Prints the message [Use $SOUND to toggle sound usage on and off.] the first time a sound is played.

[Shameless plug]
I’ve written a document modestly called “ZILF Reference Guide” were I have tried to collect info about all ZIL and MDL syntax you can use inside ZILF. It’s not finished but the latest version is here.
[/Shameless plug]

1 Like

Yup. Frotz has hardcoded loop counts for the Lurking Horror sound effects:

I wonder why Lurking is written this way. The definition of the SOUND command allows for the repetitions to be defined inside the ZIL-code instead.

Maybe the API for the SOUND command wasn’t finalized until late in the process and the repetitions was already hard-coded so nobody at Infocom bothered to change the code.

Not until Version 5.

Thanks a lot! It makes a little more sense now.

Of course… You’re right, and I need to clarify that in the ZRG. Thanks!

1 Like

Now we just need a new version that re-instates the cut storm sound and the microwave beep.

There also the S-SQUEAL

;"sounds cut for reasons of space"
;<CONSTANT S-SQUEAL 5>	;"23K single rat"
;<CONSTANT S-STORMY 14>	;"54K * storm"

There’s no code related to the squeal but it shouldn’t be to hard to find where to insert it. The microwave sound is the default BEEP sound but are the other sounds available somewhere?

If not, suitable free-to-use sound effects are not hard to find.

In the IF Archive the sound-files 05 & 14 are, of course, missing (they where, as specified, cut for lack of space)…

I recommend freesound.org – they use standard Creative Commons licenses which will not cause problems in the future. (I.e., the zapsplat site says “Our sound effects must not be distributed in any form including on other websites.”)

As a mildly interesting note, The Lurking Horror produces, near the end of the game, the following sequence during one game round (i.e. in between @read calls):

@sound_effect 11 2 8
@sound_effect 16 2 8
@sound_effect 16 3

In short, start effect 11, then start effect 16, then stop effect 16. As noted by the standard, the slowness of the Amiga allowed for effect 11 to play for a bit, then 16 to play for a bit, before the final “stop” request.

In order to sort-of reproduce the Amiga behavior, interpreters are supposed to play effect 11 for one complete cycle, queueing effect 16 to start afterward; if this didn’t happen, sound effect 11 wouldn’t ever be heard on fast, modern systems, because sound effect 16 would be started almost simultaneously with sound effect 11, forcing it to stop.

The problem with that approach is that the “stop” request will come in before the queued effect 16 is played… and effect 16 is a looping effect. So unless an interpreter queues the stop request (and assuming it supports looping effects), effect 16 will start looping, even though it is supposed to have been stopped.

Petter actually discovered this odd behavior while testing (and finding bugs in) Bocfel’s sound support. He also came up with an elegant solution: if an effect is queued, don’t queue its “repeat” status: play it only one time regardless of whether it’s a repeating effect. An alternative would be to not mark effect 16 as looping: the end result is the same, since (I believe) sound 16 is not used anywhere else.

1 Like