Sound in Frotz on DOS

Hello,

I am having trouble with sound in Frotz on DOS, but it is likely operator error…

I recently learned to create my own Infocom-style SND files by creating a RAW file and adding the proper header. While testing, I found that Frotz 2.40 did not seem to care whether the repeats bit value was 0 or 1 (or what have you) in a Z3 story. Instead, it uses the third parameter from the opcode, just like Z5+.

So, I downloaded the latest (Frotz 2.55), and it can’t find any sound files at all. At this point, I decided the issue might be my game (and/or sounds), and I tried Lurking Horror in 2.55 – to find that it has the same issue.

Looking at the source code, Frotz seems to throw the missing sound error if it hasn’t loaded a blorb:

	if (f_setup.blorb_file == NULL) {
		runtime_error(ERR_SOUND_MISSING);
		return;
	}

It seems that the code concerning any SND files in a “sound” sub-folder will never run, since this error is thrown before that point (I think?). So, I created a blorb, but Frotz 2.55 still doesn’t find the sounds with a properly named blorb in the same directory.

I then commented that bit of code out and built Frotz 2.55 for DOS using Watcom on DOSBox, hoping it might load my SND files after that change. It seemed like it could work, because I see the code that uses the story name to build a string for the path, but it still played no sound (with no error message this time, since I’d removed this if statement). So, I changed the code to just use my actual path for this specific test game, but there was still no sound.

At this point, I decided to find out when code was added to handle repeats based on Lurking Horror, but that code was there all the way back in 2.32 (if memory serves). So, I decided to pivot a little and test things in an even earlier version of Frotz. I ended up with frotzpl3 (which identifies itself as FROTZ V2.01+), and it reads the repeat bit from my SND files. It also lets me “stack” my calls to play sounds like so:

<SOUND 3 2 8> ;"This plays once"
<TELL "This \"music\" is terrible!" CR>
<SOUND 4 2 8> ;"This plays once after sound 3 finishes"
<TELL "...and it keeps getting worse!" CR> ;"This prints when SOUND 4 plays"
<SOUND 3 2 8> ;"This loops after sound 4 finishes, because the bit in its header is 0"
<TELL "MAKE IT STOP!!!" CR> ;"This prints when SOUND 3 plays the 2nd time"

That is the bee’s knees! I assume that’s how it used to work back when Lurking Horror was written? That seems to be the case, based on the routine with sounds 8 and 9 (and the routine with sounds 11 and 16).

I’m guessing the way this was handled needed to be changed when Windows Frotz was created.

Anyway, if I use Frotz 2.40, it works with SND files – just not the repeats bit from the headers when the story file is v3. Instead, v3 sounds are handled like v5 sounds (only we can’t include the fourth parameter for the “callback” routine when compiling to Z3, which is why I thought the “stacking” was so cool).

So, I need to change my <SOUND 3 2 8> to <SOUND 3 2 264> in the Z3 story’s code, adding 256 to tell it to play once. This is a little counter-intuitive to me, because in Z5+, <SOUND 3 2 8> and <SOUND 3 2 264> both only play the sound once. If we want a loop, we use <SOUND 3 2 -1>.

This is fine, really, now that I know that’s how it works. My goal was to convert my own audio to a SND file that worked like the ones in The Lurking Horror, and I succeeded in that.

Now I’m just wondering if there’s something I’m missing when using Frotz 2.55. I built it using Watcom after commenting out the option to exclude blorbs. My soundt.z3 and soundt.blb files are in a folder along with frotz.exe. I don’t see a way to include the blorb from the command line on DOS (like the way it can be done using dfrotz from PowerShell), and I see in the source code where it builds the “filename**.blb**” string to look for the file.

2 Likes

After adding calls to os_warn to sprinkle in some debugging messages, I found that my sounds are not working for me because sample_data is null when checked by os_prepare_sample. There is an audible BEEP every time os_warn displays a message, though.

(Note: I commented out the bit that returns if there is no blorb, as I am trying to use the SND files in a SOUND folder.)

I also failed to mention needing to remove all the prefixes for non-existent folders from the Makefile.ow file. All the files are in SNAVIG; so I have to fix all the bits of code to remove $(OW_DOS_DIR), $(CORE_DIR), and $(BLORB_DIR) from the paths.

…and I am new to this (attempting to build anything for DOS). So, I’m probably failing to do something during the build process. I don’t think it’s my DOSBox setup, though, because sounds work with DJGPPFrotz 2.40 (and with FROTZPL3).

(Also, all the other builds (dfrotz, frotz, and sfrotz) all work correctly on Linux from this same source.)

This isn’t going to address your actual problem (sorry), but I thought that ERR_SOUND_MISSING code looked very familiar: I reported a similar issue for graphics, where Frotz assumed that no “extra” Blorb meant no graphics. @DavidG addressed that issue so I imagine he’d be open to doing the same for sounds.

Turns out that we may be the only two people in existence to try using Frotz this way!

2 Likes

Nice, thanks!

I’ll look at those changes to try to find similar code concerning sound, and I’ll switch back to the main branch before trying anything else, too.

I went down an “Infocom sound” rabbit hole…

I’ve already shared most of my findings with the one other person I know who is interested in creating Z-machine games with sound, but this might be helpful to someone else out there, too.

Concerning the bit which is set in the header to enable sound in the terp…

Short version:

  • Only Amiga (and Atari?) releases of Lurking Horror and Sherlock included sound (as far as I can tell)
  • None of those story files from those Amiga disks have anything set in “Flags 1” (MODE from ZVERSION)
  • Lurking Horror (Amiga) r219 and r221 (v3 story files) both have bit 4 set in FLAGS (“Flags 2”)
  • Sherlock (Amiga) (v5 story file) has bit 7 set in FLAGS (“Flags 2”).
  • None of the DOS versions of either game had sound functionality. So no bit 4 in Lurking Horror, and no bit 7 in Sherlock. (Nothing at all ever seems to be set in MODE on DOS, either.)

My notes (including header info from unz, source code, etc): zmachine sound (notes).md · GitHub


Also, here are my notes concerning SND files for DOS: The Infocom SND Format in 2025.md · GitHub

WARNING: Somewhere in those notes concerning the SND format, there is code to convert a RAW audio file to SND, and it was generated by Google Gemini. (Cue dramatic music!) I had to test and tweak/change things at least a hundred times to get that working. I do not know how to code in C (yet), and I got tired of opening RAW files in HxD to add the headers manually to convert to SND files during testing; so, I had Gemini whip something up. I also used Gemini and Claude to learn some fundamentals I’d never had a chance to learn, but most of the help I got came from actual people: asking numerous questions here and on Discord, reading the docs, scouring through old code on ifarchive, etc. I thank those kind people wholeheartedly. Any mistakes in any of this came from me, not them. I would like to correct any mistakes that are found, too; so, be sure to point them out if/when anyone finds any. =)


Also also, I did not get sound working in Frotz 2.55 (or the current code from the repo) on DOS yet. I decided to learn as much as I could about all of this before giving it another shot. I shall report back here if I get anywhere with that, though.

Still working on this. Sound works for me on DOS until Frotz 2.52.

The first sound issue I run into is this in sound.c:

	if (f_setup.blorb_file == NULL) {
		runtime_error(ERR_SOUND_MISSING);
		return;

I don’t know if this is the proper way to handle this, but changing that to this seems to completely fix it:

#ifndef MSDOS_16BIT
	if (f_setup.blorb_file == NULL) {
		runtime_error(ERR_SOUND_MISSING);
		return;
	}
#endif

Next, it seems the sound board isn’t set up properly. I sprinkled debugging messages throughout, and this one (fishing in dossampl.c) got a bite:

void os_start_sample(int number, int volume, int repeats, zword eos)
{
	eos = eos;		/* not used in DOS Frotz */

	os_stop_sample(0);

	/* Exit if the sound board isn't set up properly */
	if (sample_data == NULL) {
    os_display_string("os_start_sample: the sound board isn't set up properly");
    new_line();
		return;
  }
 ...


I found changing void os_init_sound(void) { /* do nothing */ } in dossampl.c to this fixes that:

void os_init_sound(void) { 
  dos_init_sound(); /* Modified by KV */
}

Those two changes to the files currently in the master branch make sound work on DOS.

…but Turbo C can’t compile Frotz because of the hack for Zork Zero in screen.c:

void z_put_wind_prop(void)
{
	flush_buffer();

	/* Hack for Zork0_r242 */
	//if (story_id == ZORK_ZERO && z_header.release == 242 && zargs[1] >= 16) {
		//if (zargs[2] >= 16)
			//runtime_error(ERR_ILL_WIN_PROP);

		//zword cnt, i, value;
		//LOW_WORD(zargs[1], cnt)		/* Get number of values */
		//for (i = 0; i < cnt; i++) {
			//LOW_WORD(zargs[1] + (i + 1) * 2, value)
			//((zword *) (wp + winarg0()))[zargs[2] + i] = value;
		//}
	//} else
		//((zword *) (wp + winarg0()))[zargs[1]] = zargs[2];
  
  // Turbo C will not compile with the hack included - KV

	if (zargs[1] >= 16)
		runtime_error(ERR_ILL_WIN_PROP);

	((zword *) (wp + winarg0()))[zargs[1]] = zargs[2];
} /* z_put_wind_prop */

It complains that variables can’t be declared at the beginning of the function. So, I moved zword cnt, i, value; to the beginning of this script; then it complained about needing a constant for the first line in the for loop…

I don’t understand at least one third of what is going on in that hack; so I just commented the “hacky” bits out to make it compile so I could test my sound fix.


I am posting this here before creating an issue in the Frotz repo to be sure I haven’t made any obvious errors. I am always very grateful when corrected.

1 Like

I updated those changes to the sound functionality, to make it work as I believe it was intended:

diff -uNr "snavig - clean/dosinit.c" "snavig - patched/dosinit.c"
--- "snavig - clean/dosinit.c"	2025-10-16 14:26:02.237172800 -0500
+++ "snavig - patched/dosinit.c"	2025-10-16 15:01:51.592534800 -0500
@@ -779,14 +779,18 @@
 			z_header.flags &= ~GRAPHICS_FLAG;
 	if (z_header.version == V3 && (z_header.flags & OLD_SOUND_FLAG))
 #ifdef NO_SOUND
-		if (!dos_init_sound())
-#endif
+		z_header.flags &= ~OLD_SOUND_FLAG;
+#else
+    if (!dos_init_sound())
 			z_header.flags &= ~OLD_SOUND_FLAG;
+#endif
 	if (z_header.flags & SOUND_FLAG)
 #ifdef NO_SOUND
+		z_header.flags &= ~SOUND_FLAG;
+#else
 		if (!dos_init_sound())
+      z_header.flags &= ~SOUND_FLAG;
 #endif
-			z_header.flags &= ~SOUND_FLAG;
 	if (z_header.version >= V5 && (z_header.flags & UNDO_FLAG))
 		if (!f_setup.undo_slots)
 			z_header.flags &= ~UNDO_FLAG;
diff -uNr "snavig - clean/sound.c" "snavig - patched/sound.c"
--- "snavig - clean/sound.c"	2025-10-16 14:26:02.385228900 -0500
+++ "snavig - patched/sound.c"	2025-10-16 14:34:55.682462000 -0500
@@ -187,10 +187,12 @@
 	if (!f_setup.sound)
 		runtime_error(ERR_PLAY_SOUND);
 
+#ifndef MSDOS_16BIT /* Added by KV */
 	if (f_setup.blorb_file == NULL) {
 		runtime_error(ERR_SOUND_MISSING);
 		return;
 	}
+#endif /* Added by KV */
 
 	if (number >= 3 || number == 0) {
 		locked = TRUE;

(I have omitted the change to z_put_wind_prop in that diff, seeming how I don’t understand the code and am simply commenting out the hack/fix for Zork Zero – only to appease Turbo C 3.0.)
EDIT: Also, this is working strictly with the “snavig” directory which created when running make dos.