Skip to content

From what point in time are VGM 'wait' commands based?

Technical discussion about the VGM format, and all the software you need to handle VGM files.

Moderator: Staff

  • jaholmes Offline
  • Posts: 2
  • Joined: 2018-04-27, 19:40:58

From what point in time are VGM 'wait' commands based?

Post by jaholmes »

This feels like a silly question, but I'm not finding an obvious answer in the VGM format description. I'm working on a hardware VGM player and looking to "do it right."

Are the "wait" commands in the VGM format based on the time at the end of the previous command (if any, and whatever that command may be), or the end of the previous "wait" command (if any)? Or are all non-wait commands simply assumed to take zero time?

In other words, if I have a command sequence like this:

Code: Select all

wait 10 samples
write dd to memory location aaaa
wait 10 samples
...does this represent 20 samples worth of time (where the write simply falls somewhere in the second 10-sample "window"), or does it represent 20 samples worth of time plus whatever (unspecified) amount of time the write takes? I suppose I'm hoping it's the former, as at least the duration of a sequence would then be deterministic, even if the timing of the writes wouldn't be. The latter would seem rather sloppy, perhaps acceptable on emulated hardware where the timing of a write is (potentially) so fast that it might reasonably be looked on as an "instant in time," but when playback uses parts of the original hardware that operate at only a couple of MHz, suddenly the duration of a write can be nontrivial. (It might even be a variable where other hardware events are involved.)

Thanks in advance for the help!
  • ctr Offline
  • Posts: 492
  • Joined: 2013-07-17, 23:32:39

Post by ctr »

In theory, all commands are instantaneous except the wait commands. This is also the case in VGMPlay and probably most other software players. But on hardware, writing to the chip does take some time and usually there is a delay required by the chip between two writes. In that case, the player has to compensate for the delay time lost, or it will simply play the file slower and thus be off sync with software players. Some hardware players deal with this by accumulating wait commands and only execute a wait once a specified wait count is reached (for example matching a timer interrupt)
  • jaholmes Offline
  • Posts: 2
  • Joined: 2018-04-27, 19:40:58

Post by jaholmes »

ctr wrote:In theory, all commands are instantaneous except the wait commands. This is also the case in VGMPlay and probably most other software players. But on hardware, writing to the chip does take some time and usually there is a delay required by the chip between two writes. In that case, the player has to compensate for the delay time lost, or it will simply play the file slower and thus be off sync with software players. Some hardware players deal with this by accumulating wait commands and only execute a wait once a specified wait count is reached (for example matching a timer interrupt)
Thank you, ctr, this is pretty much what I was planning on doing. Waits would effectively be windows of time during which any non-wait commands that followed the previous wait command (if any) would be executed as quickly as possible.
  • User avatar
  • grauw Offline
  • Posts: 150
  • Joined: 2015-02-22, 3:40:22

Post by grauw »

My VGM playback loop is as follows; I call the player with how much time has passed since the last call. The player will then play whatever command it encounters immediately, and when it encounters a wait, it deducts it from the time passed, until it goes below zero. Then I return, determine the time that has passed since the previous call to the player, add that, and if it goes above zero invoke the player again.
  • octorian Offline
  • Posts: 7
  • Joined: 2018-04-28, 18:14:49

Post by octorian »

I'm also working on a hardware VGM player (for the NES APU), and have been wondering about this. My current architecture involves having the NES CPU (RP2A03) listen as an I2C slave, where a modern processor reads VGM commands and sends them to it via I2C. This is obviously not instant, and actually slower than the time it takes to simply write to an APU register. But in practice, it works really well.

Right now, my playback loop is really simple, and works something like this:
  • Get next VGM command
  • If the command is a register write, send it to the NES APU and track how long it took
  • If the command is a wait, subtract the duration of the previous write from it. If the resulting value is non-zero, then sleep for that much time
There's probably a better (or more correct) way of doing this, but for now the output sounds pretty good.

FWIW, I sometimes wonder how many of the "short" waits are actually necessary, and how many are simply an artifact of game code and write logging. While attempting to capture a game sound effect (something I wish this site collected), I hacked Mednafen to log APU writes and inter-write delays. While the long delays were clearly important, it made absolutely no difference in the resulting sound whether or not I bothered implementing the short (~15 sample) delays in my playback.
  • User avatar
  • ValleyBell Offline
  • Posts: 4768
  • Joined: 2011-12-01, 20:20:07
  • Location: Germany

Post by ValleyBell »

In any case, the wait times are an artifact of capturing the writes with accurate timing.

For PSG and PCM sound chips, I think being "frame accurate" should be enough.
The envelope generator of FM chips is a bit more sensitive to timing though and executing [KeyOff + changing parameters + KeyOn] can result in a different sound, depending on the time you wait between these actions. (There is a certain song that I always use as a prime example of what can go wrong when FM timing is not accurate enough. Hoot records with 100 Hz, btw.)
Post Reply