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.”)

1 Like

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

I’m looking at this again to see why SDL Frotz isn’t doing the right thing with Lurking Horror. One thing that’s confusing me in the source is line 1614 in https://github.com/the-infocom-files/lurkinghorror/blob/develop/frob.zil#L1614-L1623

%<IFSOUND <SOUNDS ,S-CRETIN ,S-STOP>>

What is the purpose of that leading percent sign? It’s not a comment, which is a semicolon. Meretzky’s “Learning ZIL” seems to make no mention of it.

1 Like

The % means that it’s a read-macro that is evaluated as soon as it’s read. In this case during compilation. The IFSOUND is in the lurkinghorror.zil:

<DEFINE IFSOUND ("ARGS" FOO)
	<COND (,SOUND-EFFECTS?
	       <FORM PROG () !.FOO>)
	      (ELSE T)>>

In practice this means thst if the global variable SOUND-EFFECTS? is true the parameter <SOUNDS ,S-CRETIN ,S-STOP> is inserted here at compile time, if it’s false nothing is inserted here. I guess it’s done this way if you want to compile for a platform that don’t have sound, you can omitt all that code by changing this variable.

Extra info: “Russell Lieblich, Sovnd Engineer” is omitted from the credits inscription if the compilation lacks sound.

1 Like

I figured out why SDL Frotz doesn’t handle sound for Lurking Horror correctly. When the audio code was originally written, it was done such that the fourth parameter to @sound_effect was always considered as a possibility. Lurking Horror was always a V3 game and the fourth parameter is legal only for V5 and higher. Therefore stuff got mangled.

In the aftermath of this discovery, I realized that the audio code in the curses interface was never written to handle that fourth parameter either. This means that the other audio game that Infocom produced, Sherlock, probably isn’t having its samples played correctly when played with the curses interface for Frotz.

Now I’m looking into what sound effects in Sherlock are supposed to use that fourth parameter…

5 Likes

I’m curious how the fourth parameter causes a problem as I didn’t think Lurking Horror uses it (it is a V3 game as you pointed out, and the parameter is V5).

Another approach would be if a stop signal is sent to a queued sound, to allow it to play a single time fully before stopping it.

It doesn’t. The problem is that sfrotz seems unaware that the fourth parameter is for V5 and above. Rather that doing the right thing when effects are lined up one right after another, sfrotz seemed to go off the rails looking for a return vector or something.