#define PCMRAM (631734272)
#define LWRAM (2097152)
#define HIMEM (100679680)
//HIMEM has 376 sectors (752 KB)
#define HIEND (101449728)
//Each buffer is 8 sectors / 16 KB, a total of 32 sectors / 64 KB
#define PCMBUF1 (101433344)
#define PCMBUF2 (101416960)
#define PCMBUF3 (101400576)
#define PCMWORK (101384192)
//THIS IS TESTED
#define M3072KHZ (31118)
//
/**MUSIC SYSTEM SETUP**/
Sint8* music = "SCARE.AIFF";
TESTLOOP:
music_pcmfx.mode = JoSoundMono16Bit;
//This function is supposed to play a mono PCM sound effect constantly.
//Some HIMEM is consumed in the process (32 sectors / 64KB at the END OF HIMEM)
Sint32 fid_m = GFS_NameToId(music);
music_fs = GFS_Open(fid_m);
Sint32 msize;
Sint32 nsct_m;
Sint32 stat;
Sint32 rdsize;
Sint32 nsct_f;
Sint32 fsize;
//GFS Error Information
Sint32 a_stat;
//Get sectors
//HEY! SBL DOCUMENTATION IS WRONG! THIRD ITEM nzect IS GFS SECTOR COUNT. SECOND ITEM IS CD SECTOR SIZE.
//TIP: MEMORY MUST BE MANAGED IN SECTORS (2KB)
GFS_GetFileSize(music_fs, NULL, &nsct_m, NULL);
GFS_GetFileInfo(music_fs, NULL, NULL, &msize, NULL);
//This determines playback time?
music_pcmfx.data_length = (msize);
_pcm[(int)0].mode = (Uint8)music_pcmfx.mode;
_pcm[(int)0].pitch = M3072KHZ;
//How many frames are we reading?
if(rt_step < msize){
music_sectors = (msize + (rt_step - 1))/(rt_step);
}
jo_printf(0, 18, "(%i)", msize);
GFS_SetReadPara(music_fs, rt_step);
GFS_SetTransPara(music_fs, rt_sector);
//music_fs should be SCU type since it's going to high memory.
GFS_SetTmode(music_fs, GFS_TMODE_SCU);
//Seek music to the desired location
GFS_Seek(music_fs, play_ref, GFS_SEEK_SET);
//MUSIC loading follows
GFS_NwCdRead(music_fs, (20 * 2048));
//Let's make a pre-playback loop to read from the CD to fill a series of buffers using a work buffer.
for( ; buffers_filled < 3 ; ){
if(buffers_filled == 0){
rd_pcmbuf = PCMBUF1;
}
if(buffers_filled == 1){
rd_pcmbuf = PCMBUF2;
}
if(buffers_filled == 2){
rd_pcmbuf = PCMBUF3;
}
if(music_frames == 4){
slDMACopy(PCMWORK, rd_pcmbuf, (8 * 2048));
slDMAWait();
buffers_filled += 1;
music_frames = 0;
}
if(music_frames < 4){
music_frames++;
GFS_NwFread(music_fs, rt_sector, PCMWORK + (music_frames * rt_step), rt_step);
play_ref += 2;
}
do{
// game_code();
slSynch();
if(music_frames < 4){
GFS_NwExecOne(music_fs);
GFS_NwGetStat(music_fs, &stat, &rdsize);
}
jo_printf(0, 20, "(27)");
jo_printf(0, 7, "(%i)", music_frames);
jo_printf(7, 7, "(%i)", play_ref);
jo_printf(0, 10, "(%i)", stat);
jo_printf(0, 11, "(%i)", fetch_timer);
jo_printf(10, 11, "(fetched filesize)");
}while(stat != GFS_SVR_COMPLETED && rdsize < rt_step);
jo_printf(0, 22, "(%i)", buffers_filled);
}
GFS_Close(music_fs);
//Let's then make a test loop to playback in sequence the three playback buffers that have been filled.
for( ; buffers_filled > 0 ; ){
// game_code();
slSynch();
jo_printf(0, 20, "(49)");
jo_printf(0, 11, "(%i)", fetch_timer);
jo_printf(0, 22, "(%i)", buffers_filled);
fetch_timer++;
if(fetch_timer == 8){
buffers_filled -= 1;
fetch_timer = 0;
}
if(buffers_filled == 0){
curpcmbuf = PCMBUF3;
}
if(buffers_filled == 1){
curpcmbuf = PCMBUF2;
}
if(buffers_filled == 2){
curpcmbuf = PCMBUF1;
}
slSndFlush();
music_pcmfx.data = curpcmbuf; //this line does not matter
music_pcmfx.current_playing_channel = 0;
slSoundRequest("bbbbwbb", SND_PCM_START, 0, 0, 631767039, (0), 0, 0, 0);
slPCMOn(&_pcm[(int)0], music_pcmfx.data, music_pcmfx.data_length);
slPCMParmChange(&_pcm[(int)0]);
}
//buffers_filled = 0;
goto TESTLOOP;
void my_vlank(void){
//A new music buffer system is needed.
//A filled-by-request buffer of 4x8 sectors, 32 sectors, 64 KB, at the end of high memory...
//A system managed .. how? The core issue is the file system slows down the entire Saturn, causing skipping.
//Tenets: GFS Seek has a variable completion time (but does not halt the system). GFS_Close does not complete immediately.
//Management of the buffer system must never close or re-open the music file, unless the buffer is completely full.
//Copy memory from work area buffer directly into the PCM stream buffer
slDMACopy(curpcmbuf, PCMRAM, (8 * 2048));
slDMAWait();
slGouraudTblCopy();
}
;************************************************************************
; HOST Command = $85 : PCM stream play start *
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
; P1 : D7 = L ( mono ) / H ( stereo ) *
; D6 = no care *
; D5 = no care *
; D4 = L ( 16bit PCM ) / H ( 8bit PCM ) *
; D3 = no care *
; D2~D0 = PCM Stream# *
; P2 : D7~D5 = Direct level [DISDL] *
; D4~D0 = Direct Pan [DIPAN] *
; P3 : D7~D0 = PCM start address bit19~12 *
; P4 : D7~D0 = // bit11~4 ( bit3~0 = always "0" ) *
; P5,6 : D15~D0 = PCM data sample ( memory use ) size High byte per 1ch*
; P7,8 : D14~D11 = [OCT] *
; D9 ~D0 = [FNS] *
; P9 : D6~D3 = [ISEL] Rch ( stereo/mono ) *
; D2~D0 = [IMXL] Rch ( stereo/mono ) *
; P10 : D6~D3 = [ISEL] Lch ( stereo ) *
; D2~D0 = [IMXL] Lch ( stereo ) *
; P11 : D7~D0 = total level *
;************************************************************************
global drv_PCM_st
drv_PCM_st:
lea Pcm_Strm(a6),a3 ; PCM stream# stack area
lea slot_work(a6),a2 ;
move.b HOST_P1(a0),d7 ; P1 : bit7 H = stereo
bpl drv_85_mono
;===============================;
drv_85_stereo: andi.w #7,d7 ; = PCM Stream#
add.w d7,d7 ;
move.b 0(a3,d7.w),d4 ; R : play flag & slot#
bset.b #7,0(a3,d7.w) ;
andi.w #$1F,d4 ;
lsl.w #6,d4 ;
move.b 1(a3,d7.w),d5 ; L : play flag & slot#
bset.b #7,1(a3,d7.w) ;
andi.w #$1F,d5 ;
lsl.w #6,d5 ;
;-------------------------------;
; d4.w = ( R ) slot work offset
; d5.w = ( L ) slot work offset
drv_85_st1: move.b HOST_P1(a0),d0 ; P1 : D4 = 8bit / D210 = #
move.b d0,d7 ;
andi.w #7,d0 ; = PCM Stream#
ori.b #$80,d0 ;
move.b d0,PSPN(a2,d4.w) ; save PCM Stream# & play flag
move.b d0,PSPN(a2,d5.w) ; save PCM Stream# & play flag
lsr.w #1,d4 ; $00,$20,..,$3E0
lsr.w #1,d5 ;
move.b #$10,$00(a5,d4.w) ; <<< key off >>>
move.b #$10,$00(a5,d5.w) ; <<< key off >>>
andi.w #00010000B,d7 ; 16/8 bit PCM ?
ori.w #00100000B,d7 ; set Forward loop
swap d7 ;
clr.w d7 ;
moveq #0,d0 ;
move.w d0,SCSP_PCM_LSA(a5,d4.w) ; [LSA] R = 0000
move.w d0,SCSP_PCM_LSA(a5,d5.w) ; [LSA] L = 0000
move.w HOST_P3(a0),d0 ; P3,4 : [SA] 上位16bits
lsl.l #4,d0 ; *10H
or.l d7,d0 ;
move.l d0,(a5,d4.w) ; <<< [SA] R >>>
moveq #0,d1 ;
move.b d1,SCSP_SISD(a5,d4.w) ; <<< [TL/MDL] = 0 : R >>>
;@ move.b HOST_P7(a0),SCSP_TLVL(a5,d4.w) ;
move.b HOST_P11(a0),SCSP_TLVL(a5,d4.w) ;
move.w d1,SCSP_MDLSL(a5,d4.w) ; <<< [TL/MDL] = 0 : R >>>
move.b d1,SCSP_SISD(a5,d5.w) ; <<< [TL/MDL] = 0 : L >>>
;@ move.b HOST_P7(a0),SCSP_TLVL(a5,d5.w) ;
move.b HOST_P11(a0),SCSP_TLVL(a5,d5.w) ;
move.w d1,SCSP_MDLSL(a5,d5.w) ; <<< [TL/MDL] = 0 : L >>>
move.w d1,SCSP_RELFO(a5,d4.w) ; <<< [LFO] : R >>>
move.w d1,SCSP_RELFO(a5,d5.w) ; <<< [LFO] : L >>>
move.w HOST_P5(a0),d1 ; = 1,2,...,FFFF,0000
subq.w #1,d1 ; = 0,1,...,FFFE,FFFF
move.w d1,SCSP_PCM_LEA(a5,d4.w) ; <<< [LEA] R >>>
move.w d1,SCSP_PCM_LEA(a5,d5.w) ; <<< [LEA] L >>>
addq.l #1,d1 ; = 1,2,...,FFFF,10000
; d0.l = [LPCTL][8BIT][SA] R
; d1.l = PCM sample size / slot
add.l d1,d0 ; + data sample size
btst.l #20,d7 ; 8/16 bit ?
bne.s drv_85_1 ; jump if 8bit data
add.l d1,d0 ; + data sample size
drv_85_1: move.l d0,(a5,d5.w) ; <<< [SA] L >>>
move.b HOST_P2(a0),d0 ; P2 : [DISDL],[DIPAN]
andi.b #$E0,d0 ;
ori.b #$0F,d0 ; Pan = [R]
; == for monoral TV 対応 ==
btst.b #7,SND_OUT_ST(a6) ; MONO/STEREO status
beq.s drv_85_3ST ; jump if STEREO mode
andi.b #$F0,d0 ; set Center
drv_85_3ST: move.b d0,SCSP_DISDLPN(a5,d4.w) ; <<< [DISDL],[DIPAN] R >>>
add.w d4,d4
move.b d0,sl_DISDLPAN(a2,d4.w)
lsr.w #1,d4
ori.b #$10,d0 ; set Pan = [L]
move.b d0,SCSP_DISDLPN(a5,d5.w) ; <<< [DISDL],[DIPAN] L >>>
add.w d5,d5
move.b d0,sl_DISDLPAN(a2,d5.w)
lsr.w #1,d5
move.l #$001F001F,d0 ; AR,RR = 1FH
move.l d0,$08(a5,d4.w) ; <<< [D2R][D1R][EHLD][AR] >>>
move.l d0,$08(a5,d5.w) ; <<< [D2R][D1R][EHLD][AR] >>>
move.b HOST_P9(a0),d0
move.b d0,$15(a5,d4.w) ; <<< [ISEL],[IMXL] : R >>>
move.b HOST_P10(a0),d0
move.b d0,$15(a5,d5.w) ; <<< [ISEL],[IMXL] : L >>>
move.w HOST_P7(a0),d0
move.w d0,$10(a5,d4.w) ; <<< [OCT],[FNS] : R >>>
move.w d0,$10(a5,d5.w) ; <<< [OCT],[FNS] : L >>>
move.b #$08,$00(a5,d4.w) ; <<< key on 準備 >>>
move.b #$18,$00(a5,d5.w) ; <<< 同時 key on >>>
rts
;===============================;
; d7.b = P1:mode
drv_85_mono: andi.w #7,d7 ; = PCM Stream#
add.w d7,d7 ;
move.b 0(a3,d7.w),d4 ; R : play flag & slot#
bset.b #7,0(a3,d7.w) ;
andi.w #$1F,d4 ;
lsl.w #6,d4 ;
move.b 1(a3,d7.w),d5 ; L : play flag & slot#
bclr.b #7,1(a3,d7.w) ;
andi.w #$1F,d5 ;
lsl.w #6,d5 ;
;-------------------------------;
; d4.w = ( R ) slot work offset
; d5.w = ( L ) slot work offset
drv_85_mn1: move.b HOST_P1(a0),d0 ; P1 : D4 = 8bit / D210 = #
move.b d0,d7 ;
andi.w #7,d0 ; = PCM Stream#
ori.b #$80,d0 ;
move.b d0,PSPN(a2,d4.w) ; save PCM Stream# & play flag
move.b PSPN(a2,d5.w),d0 ; L 成分
move.b #0,PSPN(a2,d5.w) ; clear PCM Stream# & play flag
lsr.w #1,d4 ; $00,$20,..,$3E0
lsr.w #1,d5 ;
move.b #$10,$00(a5,d4.w) ; <<< key off >>>
move.b #$10,$00(a5,d5.w) ; <<< key off >>>
andi.w #00010000B,d7 ; 16/8 bit PCM ?
ori.w #00100000B,d7 ; set forward loop
swap d7 ;
clr.w d7 ;
moveq #0,d0 ;
move.w d0,SCSP_PCM_LSA(a5,d4.w) ; <<< [LSA] >>>
move.w HOST_P3(a0),d0 ; P3,4 : [SA] 上位16bits
lsl.l #4,d0 ;
or.l d7,d0 ;
move.l d0,(a5,d4.w) ; <<< [SA] >>>
;@ move.l d0,d3 ; stack d3.l = [SA] R
moveq #0,d1 ;
move.b d1,SCSP_SISD(a5,d4.w) ; <<< [SI],[SD] = 0 >>>
move.b HOST_P11(a0),SCSP_TLVL(a5,d4.w) ;
move.w d1,SCSP_MDLSL(a5,d4.w) ; <<< [MSL],[MDXYSL] = 0 >>>
move.w d1,SCSP_RELFO(a5,d4.w) ; <<< [LFO] >>>
move.w HOST_P5(a0),d1 ; = 1,2,...,FFFF,0000
subq.w #1,d1 ; = 0,1,...,FFFE,FFFF
move.w d1,SCSP_PCM_LEA(a5,d4.w) ; <<< [LEA] >>>
move.b HOST_P2(a0),d0 ; P2 : [DISDL],[DIPAN]
btst.b #7,SND_OUT_ST(a6) ; MONO/STEREO status
beq.s drv_85_6ST ; jump if STEREO mode
andi.b #$F0,d0 ; set Center
drv_85_6ST:
move.b d0,SCSP_DISDLPN(a5,d4.w) ; <<< [DISDL],[DIPAN] >>>
move.l #$001F001F,SCSP_D2R1R(a5,d4.w) ; <<< AR,RR = 1FH >>>
move.b HOST_P9(a0),SCSP_ISEL+1(a5,d4.w) ; <<< [ISEL],[IMXL] >>>
move.w HOST_P7(a0),SCSP_OCTFNS(a5,d4.w) ; <<< [OCT],[FNS] >>>
move.b #$18,SCSP_KXKB(a5,d4.w) ; <<< key on >>>
add.w d4,d4
move.b d0,sl_DISDLPAN(a2,d4.w)
rts
//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;
}
}
}