Glulx Audio question- can audio keep playing over a restore?

I’ve noticed that if I’ve got audio playing, it will keep playing while the player saves the game. But if one restores a game, playing audio stops. I didn’t expect the game to retrieve where the audio was at the moment of saving without me doing any glk work, but I expected (and vaguely hoped) that by the same token, any playing audio might not be affected by the restore action.

Might there be a way to allow the audio to not be cut off at the moment a restore is carried out? I am using just the background channel.

Thanks.

-Wade

Audio is part of the Glk state, not part of the game state, so it’s not affected by save and restore. If you keep a variable for the currently playing sound you can start it again after a restore.

As Draconis says, the Glk interface (including the sound channels) are not part of the Glulx save/restore mechanism, so a Glulx VM restore does not, on its own, stop any sounds playing. However, that’s not the whole story, as otherwise you wouldn’t be asking the question …

The issue is that, as part of handling the restore, the I7 library calls an I6 routine called GGRecoverObjects() (it is in “Internal/I6T/Glulx.i6” under the Inform 7 installation directory). If you look at this code, one of the last things it does it stop any sounds currently playing on the foreground and background sound channels that the I7 library sets up by default.

To change this, you’d need to get rid of this code from GGRecoverObjects(). However, that’s awkward (though not impossible, if I recall - this has been a while, and what follows is not tested): you’d need to copy all the I6 code in this file after the “@p Starting Up.” line down to just before the “@p Enable Acceleration.” line, and include it withInclude (- ! Copied code goes here ... -) instead of "Starting Up" in "Glulx.i6t". then edit the GGRecoverObjects() in that copied code to not stop the background channel. Look at Erik Temple’s old “Glulx Status Window Control” extension for an example of this technique. This has the disadvantage that you’d need to check this code each time you moved to a new build of Inform.

An alternative approach would be to not use the I7 library sound channels and instead set up your own ones - that way you can guarantee that the I7 library won’t be messing with them. The down side to this is that you have to do all the hard work of checking if sound is supported, creating the channels, etc.

Thanks a lot David. That gives me two approaches to the problem.

Draconis, the issue for me is that I don’t want the audio to stop at all. I’ve got a digital music player in the game that’s out of world, and it’s a game where people are likely to be restoring a lot, and quickly. So I want the music player to be able to keep running across a restore.

-Wade

YesYesYesYesYes!

I made a new audio channel, pointed all the audio and audio commands to it instead of the background channel, and now the music keeps playing over a restore. It’s just been one of those really happy-making moments.

-Wade

One catch with creating a new channel is that you need to be sure everything works with restore and restart. The number returned by the glk_schannel_create() identifies the sound channel, but you have to remember that that only has meaning for the interpreter process that the game is currently running in, so you’ve got to watch out for what restore and restart will do with this number. I suspect that the above isn’t very clear, so I’ll try to be clearer with an example, based on what you’re trying to do.

So, imagine the player starts up the interpreter and starts your game. The game creates its sound channel by calling glk_schannel_create(), which returns some number to identify the sound channel, say (for example) 123. You store that in some variable and use it to start a sound playing, and its all fine. Later on the player saves the game, which generates a save file containing details of the values of all variables, including your variable for the sound channel, set to 123.

Okay, now the player closes the interpreter and then some time later starts it up again. Again the game creates its sound channel, but this time the Glk layer might give back a different number to identify the sound channel. Lets’ say it gives back 245 this time. The game stores this in its variable, and uses it to start a sound playing - all fine.

But now the player restores the previously saved game. The saved game state has the ‘old’ value for identifying the sound channel of 123. While restoring leaves the sound playing, any later attempts by the game to change the sound channel all fail - because the game is passing 123 as the number for the channel, while the Glk layer knows that the only sound channel its got has an identifying value of 245.

Just to make things even more difficult, some Glk layers might always return the same sequence of numbers for created channels (e.g. 1,2,3 …) while some might not.

To solve this, you’ve got to have a way to get the value of the sound channel right after restore. There is a mechanism to do this: see “IdentifyGlkObject” in Glulx Entry Points. Unfortunately it’s not particularly well documented or easy to use, as you end up switching between I6 and I7. Emily’s “Simple Graphical Window” extension uses this for the purpose of finding the number to refer to the extra graphics window it creates - have a look at Section 2 of it, particularly “A glulx zeroing-reference rule” and “A glulx resetting-windows rule”. (You would need to make the later “A glulx object-updating rule”.)

Thanks heaps David.

I think I’ve worked out all the code for this. My question is - once my slots into HandleGlkEvent and IdentifyGlkObject are in place, ala the Simple Graphical Window extension (I’ve changed it to apply to my third audio channel) do they run automatically after both a restore and a restart? Or do I have to somehow tell them to in each case?

-Wade

EDIT: OK, pretty clearly I have to do more stuff, as my basic additions actually make the sound work less well than it did. But I can tell I’m moving in the right direction.

Here’s all the stuff I’ve got. The rock for the third audio channel, the command that creates it. The bits that I figured would retrieve the reference number if it changed:

[code]Include (-

Global gg_thirdchan = 0;
Constant GG_THIRDCHAN_ROCK 415;

-) before “Glulx.i6t”.

Include (-

[ ThirdCreate;
if (glk_gestalt(gestalt_Sound, 0)) {
if (gg_thirdchan == 0)
gg_thirdchan = glk_schannel_create(GG_THIRDCHAN_ROCK);
}
];

-).

To create the third channel:
(- ThirdCreate(GG_THIRDCHAN_ROCK); -)

A glulx zeroing-reference rule (this is the default removing reference to thirdchan rule):
zero thirdchan.

To zero thirdchan:
(- gg_thirdchan = 0; -)

A glulx object-updating rule (this is the default choosing thirdchan rule):
identify glulx rock.

To identify glulx rock:
(- RockSwitchingSGW(); -)

Include (-

[ RockSwitchingSGW;
switch( (+current glulx rock+) )
{
GG_THIRDCHAN_ROCK: gg_thirdchan = (+ current glulx rock-ref +);
}
];

-)[/code]

After restoring a saved game (and the third channel has already been created at this point), I ‘zero thirdchan’ and ‘identify glulx rock’. I thought that would do it, but maybe I’ve got the timing wrong or the wrong code. What’s happening after I do these two things is that sound commands no longer control the existing sound channel or anything else.

However, if I ‘restart’, a new sound channel is born, playing atop the old one. This last thing didn’t happen before when I wasn’t messing with the rocks.

-Wade

Yes, it’s convoluted enough that I think you’re along the right lines, but I’m not quite sure what’s going wrong. I will look into this and come up with a simple example (probably tomorrow when I have some time).

Okay, I can see at least one serious problem: the IdentifyGlkObject mechanism used by Glulx.i6t and “Glulx Entry Points” doesn’t handle sound channels in the list of things it iterates over. Urgh, what a mess. I will file a Mantis bug and work out how to work round it, but it will take me a bit longer than intended.

Thanks a lot David.

Do you think it’s worth me looking into using the foreground channel for my sound and trying to disable the code which stops the fore and background channels playing at the moment of restore?

EDIT: I tried this method and because I’m already using Glulx Status Window Control, it was very easy to enact. So I put all my sound back on the foreground channel. It doesn’t stop on a restore and I’m not seeing any problems happening when I restore a game after completely closing and reopening the interpreter. So I think it’s working. I’ll continue to test it.

-Wade

Yes, that should do the trick. I assume you’ve just commented out or deleted the lines calling glk_schannel_stop() in the GGRecoverObjects() I6 function in “Glulx Status Window Control”? That is the simplest approach (given that the extension is already replacing that code) and should work fine.

I’ve also filed a bug (http://inform7.com/mantis/view.php?id=1502) about the fact that doing this is impossible without template layer hacking.

Yep, that’s what I did.

-Wade

Sorry for the delays, but I’ve finally updated GEP at Github: github.com/i7/extensions/blob/m … Points.i7x

Oh, thank you.