There is a set of problems related to sound update routine calling in a more complex project than a simple demo. I still can't find nice NES-specific solutions to these, so maybe someone can give me some ideas or input how it was solved in existing games.
Main problem is that you need to call sound update routine with constant rate. Usually it is TV frame rate. There are two common ways to sync code with TV frames on NES - NMI or some flag polling (either PPU status or one changed in NMI handler). However, you can't just call sound update right when new frame starts if you need to make some things with VRAM first, otherwise you just miss the VRAM access time.
So I see few ways to call the sound update, all of them has drawbacks:
- Flag polling, then VRAM update, then sound update. Easy to work with, however, it is not acceptable if there is lag possibility (i.e. more than one TV frame between the polls).
- NMI handler, all the code that updates VRAM should be there, and after it sound update routine called. If it is a game, it could need many different pieces of code that updates VRAM depending from situation, that could make a lot of headache to organize and debug (compared to previous case).
- IRQ from frame counter. Interrupts disabled before polling flag, then VRAM update, then interrupts enabled, so IRQ handler never called in VRAM access time. This works, but because frame counter rate is a bit slower than a TV frame, and VRAM updates delays IRQ handler call, the update rate is not stable, and it is noticeable, at least in tests I've done in emulators.
- IRQ from DMC probably could be used too, and even could allow to compensate problems of previous way, but it limits or disables DPCM samples use. I guess, it is also the only way that allows to compensate NTSC/PAL frame rate difference without sound artefacts and without additional hardware (second problem).
Second problem is the NTSC/PAL frame rate difference. Pitch difference is easy to compensate, and not that important anyway (most of people don't care, they only notice it when compare side-by-side). Speed difference is very noticeable, though.
Simple way to compensate it is 'frame skip', either make 50hz music and call the update once per frame for PAL, but once per frame with skipping every sixth frame for NTSC; or make 60hz music and call the update once per frame for NTSC and once per frame but twice every fifth frame for PAL. However, this produces noticeable sound artifacts, and only works fine with simple music and sounds.
Better way is to have interrupt with better resolution than 50/60 hz (scanline counter, for example), but it is only available with some mappers.
Main problem is that you need to call sound update routine with constant rate. Usually it is TV frame rate. There are two common ways to sync code with TV frames on NES - NMI or some flag polling (either PPU status or one changed in NMI handler). However, you can't just call sound update right when new frame starts if you need to make some things with VRAM first, otherwise you just miss the VRAM access time.
So I see few ways to call the sound update, all of them has drawbacks:
- Flag polling, then VRAM update, then sound update. Easy to work with, however, it is not acceptable if there is lag possibility (i.e. more than one TV frame between the polls).
- NMI handler, all the code that updates VRAM should be there, and after it sound update routine called. If it is a game, it could need many different pieces of code that updates VRAM depending from situation, that could make a lot of headache to organize and debug (compared to previous case).
- IRQ from frame counter. Interrupts disabled before polling flag, then VRAM update, then interrupts enabled, so IRQ handler never called in VRAM access time. This works, but because frame counter rate is a bit slower than a TV frame, and VRAM updates delays IRQ handler call, the update rate is not stable, and it is noticeable, at least in tests I've done in emulators.
- IRQ from DMC probably could be used too, and even could allow to compensate problems of previous way, but it limits or disables DPCM samples use. I guess, it is also the only way that allows to compensate NTSC/PAL frame rate difference without sound artefacts and without additional hardware (second problem).
Second problem is the NTSC/PAL frame rate difference. Pitch difference is easy to compensate, and not that important anyway (most of people don't care, they only notice it when compare side-by-side). Speed difference is very noticeable, though.
Simple way to compensate it is 'frame skip', either make 50hz music and call the update once per frame for PAL, but once per frame with skipping every sixth frame for NTSC; or make 60hz music and call the update once per frame for NTSC and once per frame but twice every fifth frame for PAL. However, this produces noticeable sound artifacts, and only works fine with simple music and sounds.
Better way is to have interrupt with better resolution than 50/60 hz (scanline counter, for example), but it is only available with some mappers.