Sega Saturn Development > General Jo Engine Help

Why is this skipping?

<< < (3/4) > >>

ponut64:
Just did a quick real hardware test.
Again, memcpy_w is the final transfer from the buffers (already in sound RAM from start to finish) to the PCM playback buffer of channel 0 (also in sound RAM).
GFS_NwFread is pushing it to sound RAM set to SCU transfer.
It works very well, with only one flaw, that being stutter. I imagine that stutter is simply caused by there being too many DMAs going on, but it can be overlooked for the time being.


I'm gonna burn another disc with memcpy_w at VBLANK and see what the Saturn does.
That's where the transfer should be, but SSF and Bizhawk immediately crash if it is there.

To literally nobodies surprise, it does crash on real hardware like that. I guess I had to try. Maybe I can find a workaround.
Thanks again XL2.

/e: I have of course not yet tested whether or not this whole extended buffer ordeal actually achieves my intentions of having interruptable file system of music playback, but I digress.

ponut64:
I have come here once again to announce that this is driving me insane.

When you typo your work buffer where your first buffer should be, things might work more or less correctly on emulators, but utterly crash and burn on real hardware and you kinda lose all faith in everything.

Also, SDMA0 and SDMA1 as file system access DMAs aren't playing nice on real hardware right now.

I still have hope about finishing this... I've got the concept working on real hardware but there are major bugs and skippings and oh hey

ponut64:
So I have something cool to share. Not entirely finished yet, but, it is real.

I have finally managed to make an uninterrupted music playback system that can also load files while music is playing.
I've also found the Saturn's CD system can reliably read and fetch 8 sectors per frame at 30 FPS. It could do a little more, unsure.

This system has a lot of problems and maybe some things you can benefit from.
One, SGL has a command called "slSoundRequest" to send commands directly to the MC68EC000. I use that to play sound.
Two, something is injecting static during playback. I don't know what the cause is and thus have no clue about a solution. I had to expand the buffer from a potential 80 KB with more frequent sector switches to 180KB with less frequent buffer sector switches (less times the slSoundRequest command is sent), since this mitigates the static. It's still there and its really annoying.
Three, this buffer is in sound RAM, and as far as I figure, with directly addressed commands all but the first 4KB of sound RAM are usable (because your playback memory address is 16 high-order bits of 20-bit data, meaning it assumes 4 lower order bits are zero).
Four, this potentially uses less CPU grunt than SGL and SBL PCM playback methods, since there are zero DMAs (outside of the command) happening between the main CPUs and the SCSP during playback. The file system writes the new sound data into the ring buffer directly using SCU DMA.

Also, it's calibrated ONLY for 30.72KHz mono sound.


--- Code: ---
//Pseudo-code: Assemble if you want to explore or ask me to post the files
#define M3072KHZ (31122)

/**MUSIC BUFFER REGION 160KB / 80 SECTORS / 5 * 32768 **/
///THIS IS UNNECESSARILY LARGE
#define PCMBUF1 (631304192)
#define PCMBUF2 (631336960)
#define PCMBUF3 (631369728)
#define PCMBUF4 (631402496)
#define PCMBUF5 (631435264)

#define MAP_TO_SCSP(sh2map_snd_adr) ((sh2map_snd_adr - SNDRAM)>>4)

typedef struct{
void* rd_pcmbuf;
int play_pcmbuf;
} snd_ring;

//Rate of data reading
static Sint16 m_step = (8 * 2048);
static Sint16 m_sector = 8;
static Sint8 mcpy_factor = 2;
static Sint16 rt_step = (8 * 2048);
static Sint16 rt_sector = 8;

//Buffered space between buffer segments is important. This is why this takes up so much RAM. Lower buffer segment size = lesser space consumed by buffer segmentation
// = lower overall size possible.
music_buf[0].rd_pcmbuf = (void*)PCMBUF4;
music_buf[0].play_pcmbuf = MAP_TO_SCSP(PCMBUF1);
music_buf[1].rd_pcmbuf = (void*)PCMBUF5;
music_buf[1].play_pcmbuf = MAP_TO_SCSP(PCMBUF2);
music_buf[2].rd_pcmbuf = (void*)PCMBUF1;
music_buf[2].play_pcmbuf = MAP_TO_SCSP(PCMBUF3);
music_buf[3].rd_pcmbuf = (void*)PCMBUF2;
music_buf[3].play_pcmbuf = MAP_TO_SCSP(PCMBUF4);
music_buf[4].rd_pcmbuf = (void*)PCMBUF3;
music_buf[4].play_pcmbuf = MAP_TO_SCSP(PCMBUF5);

/**There are variables and functions left out for the file system**/
/**
MAIN MUSIC-GAME LOOP
Fault Tolerance needs study.
Sample clipping needs correction.
Data M_TRIG : Music trigger. Sets Vblank buffer playback toggle ON.
Data music_frames : Frames read from music file system under copy factor.
Data mcpy_factor : Frames to read from music file to complete an 16-sector buffer.
Data mrd_pos : Last or current read case in music buffer. It is the last position in the case of file system interrupting music.
Data buf_pos : The current playback case in music buffer. Synchronized with mrd_pos at file system interrupt (NOTE: not system IRQ, see break)
Data music_fs : GFS handle of music file. [Gets closed and re-opened. Music and file system can operate with max open files of 2]
Data m_sector : Number of sectors to fetch from CD block buffer.
Data music_buf : Music ring buffer struct array. Ring buffer is comprised of numerous 16-sector buffers. [At 30.72KHz mono, that is 8 frames of sound]
Data m_step : Bytes to fetch from CD block buffer. Also used as address offset (in the case of sub-8 sectors used as m_sector ; > 1 used as mcpy_factor)
Data play_ref : Seek reference when music file is re-opened.
Data rdsize : Fetch size of single read loop increment.
Data stat : Execution error/status information. If it is ever 2, you have a problem.
Function game_code : Your game.
Function slSynch : Frame limit synchronization.
buffers_filled : Unnecessary, but notifies system how many buffers have new data. It is incremented down at Vblank IRQ.

FILE SYSTEM INTERRUPT
Bool process_requested : Trigger in game code as to whether or not file process is requested. [TODO: sound_requested, etc]
Data fetch_timer : Buffer switch timer. See Vblank IRQ. This timer will increment buf_pos which triggers reads until mrd_pos equals buf_pos.
**/
for( ; ; ){
if(play_ref < nsct_m){
m_trig = true;
} else if(play_ref >= nsct_m){
m_trig = false;
if(m_trig){
slSoundRequest("b", SND_PCM_STOP, 0);
}
}
  if(music_frames < mcpy_factor && mrd_pos != buf_pos){
GFS_NwFread(music_fs, m_sector, (void*)music_buf[mrd_pos].rd_pcmbuf + (music_frames * m_step), m_step);
play_ref += m_sector;
music_frames++;
}
do{
game_code();
slSynch();
GFS_NwExecOne(music_fs);
GFS_NwGetStat(music_fs, &stat, &rdsize);
if(music_frames == mcpy_factor){
buffers_filled += 1;
if(mrd_pos != buf_pos){
mrd_pos++;
}
music_frames = 0;
}
jo_printf(0, 20, "(49) loop label");
jo_printf(0, 22, "(%i) cur rd case", mrd_pos);
jo_printf(0, 23, "(%i) cur play case", buf_pos);
jo_printf(0, 7, "(%i)", music_frames);
jo_printf(7, 7, "(%i) sct off", play_ref);
jo_printf(0, 10, "(%i) fs stat", stat);
jo_printf(0, 11, "(%i) fetch", fetch_timer);
}while(stat != GFS_SVR_COMPLETED && rdsize < m_step);
if(mrd_pos > 4){
mrd_pos = 0;
}

if(process_requested == true && fetch_timer >= (mcpy_factor * 1) && fetch_timer <= 32){
if(mrd_pos == buf_pos){
break;
}
}
}
/**END MUSIC SYSTEM SETUP**/
GFS_Close(music_fs);

//Ensure file handler loop is handled like this:
//for( ; fetch_timer >= (mcpy_factor * 1) && fetch_timer <= 32 && mrd_pos == buf_pos ; ){
game_code();
slSynch();
}

void my_vlank(void){
    slGouraudTblCopy();
if(m_trig == true){
if(fetch_timer == 0){
slSoundRequest("bbwwwbb", SND_PCM_START, 0, 224, (music_buf[buf_pos].play_pcmbuf), (32768), (M3072KHZ), 0, 0);
}
fetch_timer++;
if(fetch_timer >= 32){
buffers_filled -= 1;
buf_pos++;
///Ring buffer wrap
if(buf_pos > 4){
buf_pos = 0;
}
fetch_timer = 0;
}
}
}


--- End code ---

XL2:
Wow, nice!
Good to know you are getting it to work.
I'm mainly interested in streaming data, so to know that you can expect 4 sectors per frame at 60 fps is great.
You could do something a bit like Crash Bandicoot where you stream the visibility data to have 0 overdraw, which means you could fill the screen with polygons at a decent framerate.

ponut64:
In that case, you need to be careful about the lead time of closing, seeking in a file (GFS_Seek), or changing directories.
All of these things can take up to a full second to complete, which is a circumstance where you would want to structure your visibility data in a way where you can buffer 60 frames of the nearest said visibility data in memory. In Crash Bandicoot's case, the linear structure of the game makes it obvious how they did that.

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Sitemap 1 2 3 4 5 6 7 8 9 10 
Go to full version