Streaming Music

From Uzebox Wiki
Revision as of 22:07, 23 September 2017 by D3thAdd3r (talk | contribs) (Created page with "==General Description== Streaming Music is a technique useful for playing music back from ram instead of flash as the standard player does. This cost a small amount of ram,...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

General Description

Streaming Music is a technique useful for playing music back from ram instead of flash as the standard player does. This cost a small amount of ram, but enables the music to be stored in places other than the limited flash on the '644. Offloading this data to storage that has "virtually unlimited" storage like SD or "quite a bit" like SPI Ram can allow a great deal of music in a single game, without requiring extra flash space. This can help to finish a big game, where traditionally music can represent a large percentage of flash usage.

Streaming Music has the same general capabilities as the flash player, but internally it has many differences. To enable it requires a makefile flag:

-DMUSIC_ENGINE=STREAM

The most obvious difference is the use of a circular buffer to store music data that will be played. The required size of this buffer might range from 8 bytes to perhaps 32 depending on the demand of the music, and the speed at which you can continually fill the buffer. The size of this buffer can be specified in the makefile like so:

-DSONG_BUFFER_SIZE=24

You should probably start with a conservative value like this, and after your programs ram usage is set and the music done, roll this value back until it causes noticeable slow down of the music. It would also be best to test thoroughly during heavy CPU loads when there is a lot going on in the game. Increase slightly over that and you should have the most efficient use of ram, as well as being able to handle worst case scenarios that could arise without missing a beat. The required buffer size is directly related to how rapidly events will happen in the music.

Another item to consider is the speed at which you can fill the buffer, which is correlated to how large of a buffer you need. Basically if you can be guaranteed that you can fill it fast(SD card), you might require more buffer so that you are ahead of the curve with buffered data when a computationally expensive part of the game occurs. The Streaming Music system should never crash due to running out of data, but the music will "stretch" in time as gracefully as possible to fill the gaps until more data is there. This can range from not noticeable, to very harsh sounding depending on the music, where it happens, and how long the data drought is. The most likely culprit of this, if streaming from SD, would be the inter sector delay which could stop buffering for multiple frames while the card gets ready.


Other Considerations

Because the song player relies on a buffer in this scheme, you will need to make provisions in your game to allow it to be filled every frame or at least every few frames. The recommended method is to use a customized version of WaitVsync(). The stock WaitVsync() does just what it should, it waits. Ideally we can use these wait cycles that would otherwise just be wasted, to do something useful like keep the buffer full. In the ideal situation where game logic always finishes on time, this would have virtually no performance impact on the game. It can still work for games that sometimes miss a frame, although it then potentially aggravates the worst case scenario. Basically that would require more testing to make sure it never crashes. An example of a custom WaitVsync() might look like this:

//Using SPI Ram:
void CustomWaitVsync(u8 frames){//we do a best effort to keep up to the demand of the song player.

	while(frames){
		if(loopEnd){//we read past the end of the song..luckily it is padded with bytes from the loop start

			songOff = (songOff-loopEnd)+loopStart;
			loopEnd = 0;//since we immediately zero it so we don't keep doing it
			SpiRamSeqReadEnd();
			SpiRamSeqReadStart(0,(uint16_t)(songBase+songOff));//read from the start, plus the offset we "read past the end"
		}

		while(!GetVsyncFlag()){//try to use cycles that we would normally waste

			if(doSongBuffer && !SongBufFull())
				SongBufWrite(SpiRamSeqReadU8());						
		}

		ClearVsyncFlag();
		frames--;
	}
}