Playing sounds one after another

If you want different timer durations, I would do this.

To start a timer for (N - number) milliseconds:
    (- glk_request_timer_events({N}); -).

Well to some extent, that depends on what you’re going to do if the check fails. If you decide you want the game to refuse to run without some feature, then you’d check on start-up (and on being restored) and that would be enough. If it’s not that important you could just check when the feature is needed and do something else as appropriate. It might not even be necessary to check - for example glk_request_timer_events() will do nothing if the interpreter does not support timed events, so if you weren’t going to do anything anyway if they’re not available, you could just make the call regardless.

Again that’s a tricky call. Just because an extension is out there does not imply a guarantee of its quality, so it depends on the extension, and how tricky whatever you’re trying to do is, and how good a fit the extension is for that. All the Glk layer state is outside of the save-file, so if you save then information like whether Glk timer events have been requested is not included, and any timers (or similar) would need to recreated on restore.

Dannii has already pointed out how to parameterize such calls. However, there’s a particular caveat with glk_request_timer_events(), in that you only get one set of timer events: if you call

glk_request_timer_events(100); glk_request_timer_events(200); then timer events are only sent every 200 ms - only the most recent call has any effect. If you wanted to be notified (say) every 3 seconds and 7 seconds, you’d need to request timer events every one second and use a counter to figure out where you were in the sequence.

This is very helpful, thanks!

If I follow you, then when the game is restored (if no timer code is set up specially for restoring the game), it would be as if no timer events had been triggered. So if, say, an entire game is set on a long-term timer before some disaster happens, you’d want to do something particular to make sure the timer resumes to the correct time when the player restores. But if it’s just a matter of minor things like sound cues of a few seconds, there’s no need to do anything particular with save/restore, since it makes no difference to gameplay whether they’ve been triggered before, or when.

Yes, that’s the heart of it.

Ok, great! Thanks, all!

There’s also the case where a timer event is set and the player types “RESTORE”. Then you might load a game state where no sound is playing, but the timer remains set.

If I understand this setup correctly, you’d wind up continuing your sound sequence even though it wasn’t appropriate for the game state. But again, not a serious problem.

You have a similar situation after undo as well–the state of the timer is not saved along with the game state.

The easiest way to resolve these situations is to store the timer interval in a variable, which will be saved along with the game state, and then call the timer again with the stored interval after restoring or undoing. You will likely also want some kind of event queue variable so that the timer will trigger the appropriate event on being reinstated. The event queue will also be saved with the game state, of course, so that the two should always be in sync unless you do something unusual.

My old extension Glimmr Canvas Animation implements a full system of automatically scheduling virtual timers that is probably overkill for what you need, but you might be able to extract some useful information from the code. (It has not been updated for the new Inform.) Key sections include:

Chapter - Basic animation phrases
Section - Timer control phrases
Section - Restart the timer after restoring
Section - Restart the timer after undoing

Hmm, interesting.

Rather than preserving the state of the timer and queue, what if you wanted to cancel all the timers (or sound notifications) and start fresh when the game is restored, restarted, etc. Would you do something like this?

[code]Include Glulx Entry Points by Emily Short.

Use Direct Event Handling.

Next Queued Event is a thing.
Next Queued Event can be none or First Sound or Second Sound.

To cancel timer events:
(- glk_request_timer_events(0); -)

To cancel everything:
Now Next Queued Event is none;
Cancel timer events. [or cancel sound notifications, if there’s a way to do that. And maybe stop any currently playing sound.]

The Great Outdoors is a room.

Check saving the game:
Cancel everything.

Check restoring the game:
Cancel everything.

Check restarting the game:
Cancel everything.

First report undoing a turn: [It seems like anything involving UNDO would require an extension.]
Cancel everything;
continue the action.[/code]

That looks like a reasonable plan (I haven’t tried to compile it), though I don’t think you really need to cancel everything on save - after the save everything continues as before, so it’s not obvious to me that you’d need to cancel anything there. You’d also want an event handler to look at Next Queued Event to decide what (if anything) to do, obviously.

Depending on your needs, this could be made more complicated: you could have a list of queued events, for example, and on restoring (or undo) you could also check the state of Next Queued Event and set up the timer if needed.

By the way, sound notifications can’t be cancelled per se, but if you stop the sound (either by making a call to stop it, or closing the sound channel) then any playing sounds are stopped without any notifications triggering them.

Good point. Maybe a report rule after restoring, instead of check rules for both saving and restoring, would cover both situations (clearing the queue if the restored game was saved with something in the queue, and also cancelling anything that might have been playing before you typed “restore”.)

Is there a fairly straightforward way to do this, assuming there’s only one sound channel in use?

Hopefully this is my last question! Thanks all for your help!

If you only ever have to worry about one sound playing at a time, you can just save the currently playing track in a variable (also tracking whether or not a sound is playing), then revisit after restoring etc. to play or stop the sound channel as needed. Here’s a bit of code; the commented bits should be replaced with the :

[code]Sound of null is the file “nothing.mp3”.

The currently playing is a sound name variable. Currently playing is sound of null.

After restoring the game:
play appropriate sound.

To play appropriate sound:
if currently playing is sound of null:
[stop sound channel];
else:
[play] currently playing.[/code]

You will of course need to set the currently playing variable every time the sound is to change, i.e. whenever a sound starts or stops playing; the latter being the tricky part (see the rest of the thread).

I’ve got the actual sound sequence working (though I might fiddle with it some more). Thanks for all the help with that! What I meant to ask was, what phrasing do I use to make a sound stop playing, if it needs to be interrupted for some reason (restoring or otherwise)? The Multiple Sounds extension uses this:

[code]To stop the foreground sound:
(- SoundStop(gg_foregroundchan); -)

To stop the background sound:
(- SoundStop(gg_backgroundchan); -)

To stop the midground sound:
(- SoundStop(gg_thirdchan); -)[/code]

but this seems very specific to that extension.

To stop a sound started with “play ”, we would need a rule like

To stop the sound: (- glk_schannel_stop(gg_foregroundchan); -)
To give some explanation of how I worked this out: The lowest level of all the layers involved in this are these Glk calls, which correspond to calls to the interpreter to ask it to do something (here sound related). These sound capabilities are covered in the Glk specification, which defines what an interpreter can provide. From that I know I need to call glk_schannel_stop() with a number argument, which is the number identifying the sound channel.

The Standard Rules that come with Inform 7 has limited sound support (lots more is possible with the Glk calls). If you look in the Standard Rules at Section SR5/2/23, there is a definition “To play (SFX - sound name), one time only”, which is what the game code “play ” will call. This in turn calls an Inform 6 routine PlaySound(). A bit of searching shows this is defined in Figures.i6t and the code for it basically just calls the Inform 6 routine VM_SoundEffect(). This in turn is defined in Glulx.i6t as calling glk_schannel_play(gg_foregroundchan,). Here “gg_foregroundchan” is the number identifying the Glk sound channel that the Standard Rules have already set up for us.

Wow, thanks so much, everyone. This has been enormously helpful. :slight_smile:

Still on this topic -

If a sound is playing and there’s a sound notification cued up for it at the moment you save the game, then you restore that game, has the sound notification gone away? IE - Is it part of the glk stuff that’s not saved by default?

-Wade

Right. Sound notifications are not saved in the save file (or restored when you do a restore). No Glk state is.

Gotcha.

-Wade