I've been developing a Final Fantasy-style RPG for several months, with prerendered backgrounds. The project is open source. I’ve managed to get almost everything working, but now I’m encountering a problem with the audio. I’d like to play background music while moving between the prerendered backgrounds. Obviously, if I do it with direct play from an XA file, the audio cuts off because when I change the background, it loads data from the CD-ROM for the next background.
A solution that I think I've understood is to use .VAG files and load pieces of audio little by little into a buffer, playing them bit by bit. Currently, I’m trying with the following code, and I can correctly play a single VAG file. However, I can’t figure out how to proceed. Now I just wanted to try looping the VAG playback, I thought that by changing the data_addr in the transfer callback, it would work, but I’m missing something. Does anyone have an example?
Code: Select all
SpuStCallbackProc prepare_callback(unsigned long voice_bit, long c_status) {
SpuVoiceAttr s_attr;
s_attr.mask =
(
SPU_VOICE_VOLL |
SPU_VOICE_VOLR |
SPU_VOICE_PITCH |
SPU_VOICE_WDSA |
SPU_VOICE_ADSR_AMODE |
SPU_VOICE_ADSR_SMODE |
SPU_VOICE_ADSR_RMODE |
SPU_VOICE_ADSR_AR |
SPU_VOICE_ADSR_DR |
SPU_VOICE_ADSR_SR |
SPU_VOICE_ADSR_RR |
SPU_VOICE_ADSR_SL
);
s_attr.voice = SPU_0CH;
s_attr.addr = stenv->voice[0].buf_addr;
s_attr.volume.left = 0x3fff;
s_attr.volume.right = 0x3fff;
s_attr.pitch = 0x1000;
s_attr.a_mode = SPU_VOICE_LINEARIncN;
s_attr.s_mode = SPU_VOICE_LINEARIncN;
s_attr.r_mode = SPU_VOICE_LINEARDecN;
s_attr.ar = 0x0;
s_attr.dr = 0x0;
s_attr.sr = 0x0;
s_attr.rr = 0x0;
s_attr.sl = 0xf;
SpuSetVoiceAttr(&s_attr);
stenv->voice[0].data_addr += stenv->size / 2;
SpuStTransfer(SPU_ST_PLAY, SPU_0CH);
printf("\n\n prepare callback \n\n");
return 0;
}
SpuStCallbackProc transfer_finished_callback(unsigned long voice_bit, long c_status) {
// this does not work, it start to play in loop but just the first half of the song
//stenv->voice[0].data_addr = (u_long)current_vag_data;
//SpuStTransfer(SPU_ST_PREPARE, SPU_0CH);
// -------------------------------------------------------------
// this is working but just ends the song and play it once
printf("\n\n transfer callback \n\n");
stenv->voice[0].data_addr += stenv->size / 2;
stenv->voice[0].status = SPU_ST_STOP;;
stenv->voice[0].last_size = stenv->size / 2;
return 0;
}
SpuStCallbackProc stream_finished_callback(unsigned long voice_bit, long c_status) {
SpuStQuit();
/*spu_init();
spu_load_vag(current_vag_data, 343200, SPU_0CH);
printf("\n\n STREAM finished callback \n\n");*/
return 0;
}
// AUDIO PLAYER
void spu_init() {
SpuInit();
SpuInitMalloc(SOUND_MALLOC_MAX, (char*)(SPU_MALLOC_RECSIZ * (SOUND_MALLOC_MAX + 1)));
l_c_attr.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR);
l_c_attr.mvol.left = 0x3fff; // set master left volume
l_c_attr.mvol.right = 0x3fff; // set master right volume
SpuSetCommonAttr(&l_c_attr);
stenv = SpuStInit(0);
if(stenv == NULL){
printf("spu st init error\n");
}
}
void spu_load_vag(u_long *vag_data, u_long vag_size, int voice_channel){
if ((stenv->voice[0].buf_addr = SpuMalloc(vag_size)) == -1) {
printf("spu buf_addr malloc error\n");
}
stenv->size = vag_size;
//stenv->low_priority = SPU_ON;
if(current_vag_data == NULL)
current_vag_data = vag_data;
stenv->voice[0].data_addr = (u_long)vag_data;
SpuStSetPreparationFinishedCallback((SpuStCallbackProc)prepare_callback);
SpuStSetTransferFinishedCallback((SpuStCallbackProc)transfer_finished_callback);
SpuStSetStreamFinishedCallback((SpuStCallbackProc)stream_finished_callback);
SpuStTransfer(SPU_ST_PREPARE, SPU_0CH);
}
Maybe to do something like this I need to use more channels? maybe the approach is totally wrong?

Also, I know that there is a "simpler" way to play vag audio, like this:
Code: Select all
SpuSetTransferMode(SpuTransByDMA);
l_vag1_spu_addr = SpuMalloc(vag_size);
//SpuSetTransferCallback((SpuTransferCallbackProc)spuTransfer_callback);
SpuSetTransferStartAddr(l_vag1_spu_addr);
SpuWrite((u_char *)vag_data, vag_size);
SpuIsTransferCompleted(SPU_TRANSFER_WAIT)

my project is open source and available on github
thanks in advance