Streaming Music(SD, SPI ram, Network, etc. source)

Topics related to the API, programming discussions & questions, coding tips, bugs, etc. should go here.
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by nicksen782 »

There is a link on the Bubble Bobble thread where the latest (might not be stable) version can be seen.

This is how I detect if the song is finished.

Code: Select all

if( !playSong && SongBufFull() )
I see that the sound engine flips the playSong flag to false when it sees that the song is finished. I'm also checking the buffer too that it is full... full of junk probably but it isn't being played since playSong is off. My SONG_BUFFER_SIZE is 16.

At present this is where I stand on usage. I still need sound effects and AI interactions, oh, and bubbles!

Code: Select all

AVR Memory Usage
----------------
Device: atmega644

Program:   55582 bytes (84.8% Full)
(.text + .data + .bootloader)

Data:       3574 bytes (87.3% Full)
(.data + .bss + .noinit)
There are a couple more bugs to fix.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by D3thAdd3r »

Don't quote me on this, but from memory without the S and E tags, mconvert will not put meaningful information (256 bytes copied from the beginning of the song) after the end of the song, since it does not know where the loop is intended to start. In that case, if you read past the end of the song, it will be garbage that is either sector padding or song data for the next song.

Seems like you should ne good on flash as you have what looks like full graphical load, and code load for most parts. Guess it depends on code space for bubbles, but I am sure you can fit this game somehow or another.
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by nicksen782 »

I haven't added the S and E tags to any of the MIDIs but I am still encountering what I would see as "weird" bugs. Usually near the very start of a song.

Perhaps I should just finish the job and add the S and E tags. At least to remove some uncertainty. I can do that with mconvert from the command line, right? I just have to count the ticks in Anvil Studio?

What do you mean about 256 bytes from the beginning of the song?

I've thought about it and only the songs played during gameplay need to auto repeat. (main theme, fast main theme, final boss.) Also, the intros are separate such as the main theme intro, the hurry intro, etc. I play one and then play the other on repeat. I'm starting to question my method of detecting end of song. I would want the S and E tags at the exact beginning and exact end of each song respectively.

Quite a bit of the game is graphical load. But, at least the only musical load is for the sound kernel and the ram buffer. I did find that adding sound took away a chunk of RAM. Would less RAM be used if I minimized channel usage or something else?

This is pretty cool. I hope to have another official demo posted within a week. The change log is rather long.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by D3thAdd3r »

S and E are added in midiconv not mconvert. I would highly recommend doing so, and don't be troubled if looping seems a bit early or late...a lot of times you have to fiddle with E to be right. Usually S will be the tick the first note starts in the song, and E will be the tick of the last note plus the length of that note (the note off). Sometimes you need to make E past that even, as if there is a music rest between the last note and the first...experimenting is unfortunately the only good way I know to get a feel for this since it varies upon the timing of the MIDI.

Did you have the sound engine disabled in the makefile before? Then it makes sense you now have less ram, otherwise look for something that ought to be PROGMEM, but isn't. I think you gain a bit of ram by disabling some channels. If you lost a bunch of ram over 500 bytes, then you have vsync mixer instead of inline (the SPIRamMusicDemo latest version has vsync mixer...that is unisual and only required for specific purposes there).

Typing on my phone, so hard to give a good visual. Imagine a song that has S and E which is 400 bytes long and has S at the 11th byte, and E at the 400th. So bytes 0,1,2,3,4,5,....399 are the song data. mconvert will store that like: 0,1,2,3,4,5,...399,11,12,13,14,15...266. So 256 bytes are copied from the loop start, to the end of the song. Now data you buffered before you realized the song is over is still valid. That is the reason for the song offset manipulations to adjust for how far we read past. If we buffered 10 bytes after the end of the song, they match the 10 bytes after the loop start, and we adjust the read position to be loop start+10. That makes the buffered data the same linear sequence regardless of how long it was before we discovered the E (which is always after the song player we are buffering ahead of). Without E, that doesn't happen since the intent is assumed to be no loop, so save space.

Edit - fixed detail on indexing. TODO SELF REMINDER add mconvrrt option to add less than 256 bytes to save space, and also multiple copies of data for songs shorter than 256 (or the shorter specified value..it would be garbage currently).
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by nicksen782 »

I do not see where I can determine the number of ticks in a song. I want a start and and end and also a way to detect that the song end has been reached.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by D3thAdd3r »

IsSongPlaying() should tell you when a song is over if it is a one shot no loop thing. For number of ticks, I assume you mean for the pre-converted MIDI itself to calculate S and E. In that case you have to look at that in the MIDI editor, and I am not sure how to do it in RoseGarden yet. In AnvilStudio you look at the event list, then find the channel with the last event(which could be a tie), and that is the effective end of the song. I suppose mconvert could display how many frames a song lasts as opposed to jut seconds, though that does nothing to help with S and E since those are based on the timing of the MIDI not the 60hz timing of the Uzebox version. The music tutorial I put on the wiki is about the only thing I know of that really covers that process, and it is AS only so far.
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by nicksen782 »

I couldn't get RoseGarden to work so I've been using Anvil Studio.

IsSongPlaying() just returns the value of playSong which means the music engine is actively doing something. I need to know when the song actually ends so I know when to start over. I have set -e values for each song I convert with midiconv. I followed your advice. I'm not sure what the value for -s should be (I'm using 1) but I'm handling the looping myself so it doesn't matter for me.

Really important it seems that you can't mess with the SPI bus while it is in use. So, I have an additional flag now, mplayerActive.

I can repeatedly start a song now and mash the keyboard choosing songs randomly in my music test. No crash now. Here is the code presently. Am I on track?

Code: Select all

// Songs by song number.
#define bb01_introfirstlevel 0  //
#define bb02_hurry           1  //
#define bb03_maintheme       2  //
#define bb04_mainthemefast   3  //
#define bb05_titleintro      4  //
#define bb06_invincible      5  //
#define bb07_theyrehere      6  //
#define bb08_thankyou        7  //
#define bb09_finalboss       8  //
#define bb10_badend          9  //
#define bb11_credits         10 //
#define bb12_happyend        11 //
#define bb13_victory         12 //
#define bb14_secretroom      13 //
#define bb15_gameover        14 //

// Required by sound engine.
extern s8 songSpeed            ;
extern bool playSong           ;
extern volatile u16 songPos    ;
extern u8 songBuf              ;
extern u8 songBufIn            ;
extern u8 songBufOut           ;
extern volatile u16 songStalls ;
extern volatile u16 loopStart  ;
extern volatile u16 loopEnd    ;

// Required by game engine.
unsigned long songBase  = 0               ;
unsigned long songOff   = 0               ;
bool mplayerActive      = false           ;
bool autoRepeatSong     = false           ;
u8 songCount            = 0               ;
u16 loopEndFound        = 0               ;
u8 songNum              = bb05_titleintro ;

/*
*/

void N782_StopSong(){
	// Make sure the current song isn't playing.
	mplayerActive=false;
	StopSong();

	// Reset pointers and values.
	songBufIn   = 0x00;
	songBufOut  = 0x00;
	songOff     = 0;
	songStalls  = 0;
	loopEnd     = false;
}

void N782_fillStreamingMusicBuffer(u8 frames){
	// In a game loop there should be a WaitVsync(1) either at the top or the bottom.
	// This limits the speed of the screen updates and game logic.
	// This function performs similar but will use that wasted time to refill the music buffer.
	// This function could likely also work from a post vsync callback.
	// By clearing the vsync flag and waiting for the next vsync flag this emulates WaitVsync.
	// Frames will likely always be 1. It doesn't actually have to be 1 though.

	// _emu_whisper(0, loopEnd);
	// _emu_whisper(1, playSong);

	// Skip the buffering if the mplayer is set to not active or if the SPI bus is in use.
	if(
		   !mplayerActive    // Music player set to inactive.
		|| !(PORTD & (1<<6)) // SD Card chip select.
		|| !(PORTA & (1<<4)) // SPIRAM chip select.
	){
		return;
	}
	// End of song and the player is still actively playing?
	else if(loopEnd && playSong){
		// Repeat? Play the song again.
		if(autoRepeatSong){
			N782_changeSong(songNum, true);
		}

		// No repeat? Stop the song and reset the music player settings.
		else{ N782_StopSong(); }

		return;
	}
	else{
		// The song is not over. Can we read in some more bytes?
		// Address the SPI RAM for the sequential read.
		uint8_t  bank = (uint8_t)  ((((uint32_t)( songBase + songOff ))) >> 16);
		uint16_t addr = (uint16_t) ((uint32_t) (( songBase + songOff )));
		SpiRamSeqReadStart(bank, addr);

		// Read data from the SPI RAM into the music buffer while waiting for the next vsync flag.
		// Do this for the specified number of frames.
		while(frames){
			// Vsync flag clear.
			ClearVsyncFlag();

			// Decrement the remaining frames count.
			frames--;

			// Keep on reading and sending bytes until the next vsync.
			// If the song buffer is NOT full.
			while(!GetVsyncFlag() && !SongBufFull()){
				// Write a byte to the song buffer.
				SongBufWrite(SpiRamSeqReadU8());

				// Increase the songBase offset.
				songOff++;
			}
		}

		// Deactivate the SPI RAM since we are done with it for now.
		SpiRamSeqReadEnd();

	}
}

void N782_changeSong(uint8_t songNumber, uint8_t autoStart){
	// // Make sure the current song is stopped.
	N782_StopSong();

	// Get the base address of the next song from the beginning of the SPIRAM file.
	SpiRamSeqReadEnd();

	// Address the SPI RAM for a sequential read.
	uint8_t  bank = (uint8_t)  ((((uint32_t)( songNumber*4 ))) >> 16);
	uint16_t addr = (uint16_t) ((uint32_t) (( songNumber*4 )));

	// Set the new song base address.
	songBase = SpiRamReadU32(bank, addr);

	if(autoStart){
		// Reactivate the music player. It will play whatever is in the buffer.
		mplayerActive=true;
		N782_fillStreamingMusicBuffer(1);
		StartSong();
	}
}

u8 N782_getSongCount(){
	// Determine how many songs are included within our datafile.
	u8 theSongCount=0;
	for(uint8_t i=0;i<255;i++){
		if(SpiRamReadU32(0,i*4) == 0) { break; }
		theSongCount++;
	}

	return theSongCount;
}

void steamingMusicInit(){
	// Init the music player
	InitMusicPlayer(patches);

	// Set the master volume.
	SetMasterVolume(4);

	songCount = N782_getSongCount();
}
** EDIT: I've updated the inline code here. I'm respecting the E markers now and stopping and restarting songs more correctly. I have not encountered any music related crashes since the new changes. I still encourage comments from you all. I think I'm passed the music part of my game and need to return to the game itself.

Of course, I still need sound effects. :?:
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by D3thAdd3r »

If using AS you should be able to see the start tick of the very first note event(depends on the song which channel has the first event of course). In most cases this will be 0, unless there is an intro that plays before the looping part. So the vast majority of times I end up doing -s as 0. Otherwise it might miss the first note event in the song.
nicksen782 wrote:I think I'm passed the music part of my game and need to return to the game itself.
Sweet, keep at it!

Development on streaming music will be stopped for a bit not due to lack of interest, but literally every waking second of every day has been directed at work lately and probably so for the next couple weeks as it is critical. I think for streaming music there is not a lot else that really needs to be done, but I manage a glance at the forums each day so if there is some show stopper bug or something I will smash it. But otherwise I will probably be sparse for a while, and come back to wrap up any loose ends on this.
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by nicksen782 »

Could this be put into master yet? I'm pretty sure that it works so long as you feed the buffer correctly.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Streaming Music(SD, SPI ram, Network, etc. source)

Post by D3thAdd3r »

I am not aware of any bugs to fix with it at this point, but I have really fell behind on what I thought the final delivery date would be on this :oops: I want to call it totally done, but work has been all consuming lately keeping me from even staying up to date on the forums. I have some time off coming, and I am honestly very excited to finally get some fun time and get back to Uzebox stuff interleaved with wine drinking. I mean to add flash based bufferless "streaming" music to Zooming Secretary as an example of it's usage as the final feature. More or less, with that implemented, it seems there can be an almost universal appeal to developers here, where in almost all cases they are getting at least some flash back.

After that feature is completed, I am out of any possible idea to improve it. So unless anyone objects, lets call it final the moment bufferless streaming is tested, then no one developing with it has to be using non-standard branches, which I do know is a hassle sometimes when new feature zoom in on the main branch. Should say, pretty much last call if anyone has any feature requests on this, I am going to finish hammering this nail.
Post Reply