On 43folders, I learned that a to-do list is more effective when broken up into tasks that can be done in one sitting, each of the form "use X to do Y, resulting in Z". It's also easier to make a music engine if you can factor it out into a separate module that exposes specific functions to the game. I wrote a separate test harness that calls the same API that I use in the game.
External API used by the game:
init_sound() - Inits the audio subsystem. Called only at system startup.
init_music(A=song_id) - Starts a song.
stop_music() - Stops a song.
start_sound(A=fx_id) - Picks a channel for a PSG sound effect and starts it.
start_sample(A=dpcm_id) - Starts a DPCM sound effect.
update_sound() - Called once per vertical blank, after the PPU update code, to update the PSG registers.
Compare to the NSF API:
nsf_init_music(A=song_id) - Calls init_sound() and init_music(A=song_id)
update_sound()
Internal API used by update_sound():
update_music() - Interprets sequence data.
update_instrument(X=channel_id)
update_sound_effect(X=channel_id)
Then it's easy to have sound effects interrupt music properly: First call update_instrument(), then have update_sound_effect() overwrite the duty+volume and pitch if a sound effect is running on that channel, then write the duty+volume and pitch to the channel's register. In fact, I'm planning on doing drums using the sound effect system, which would make triangle drums easy as pie.
Tetramino 0.33 implements all of this except music. Because I had broken up the tasks, I was able to sketch out init_music(), update_music() and update_instrument() in about four fifteen-minute sessions on the bus today, and you'll see the result in Tetramino 0.37 once I get some non-trivial sequences and instruments developed.
External API used by the game:
init_sound() - Inits the audio subsystem. Called only at system startup.
init_music(A=song_id) - Starts a song.
stop_music() - Stops a song.
start_sound(A=fx_id) - Picks a channel for a PSG sound effect and starts it.
start_sample(A=dpcm_id) - Starts a DPCM sound effect.
update_sound() - Called once per vertical blank, after the PPU update code, to update the PSG registers.
Compare to the NSF API:
nsf_init_music(A=song_id) - Calls init_sound() and init_music(A=song_id)
update_sound()
Internal API used by update_sound():
update_music() - Interprets sequence data.
update_instrument(X=channel_id)
update_sound_effect(X=channel_id)
Then it's easy to have sound effects interrupt music properly: First call update_instrument(), then have update_sound_effect() overwrite the duty+volume and pitch if a sound effect is running on that channel, then write the duty+volume and pitch to the channel's register. In fact, I'm planning on doing drums using the sound effect system, which would make triangle drums easy as pie.
Tetramino 0.33 implements all of this except music. Because I had broken up the tasks, I was able to sketch out init_music(), update_music() and update_instrument() in about four fifteen-minute sessions on the bus today, and you'll see the result in Tetramino 0.37 once I get some non-trivial sequences and instruments developed.