206 lines
10 KiB
C
206 lines
10 KiB
C
#include <sound.h>
|
|
#include <space.h>
|
|
|
|
// VAG playback
|
|
void initSnd(SpuCommonAttr * spuSettings, char * spu_malloc_rec, u_int mallocMax){
|
|
SpuInitMalloc(mallocMax, spu_malloc_rec); // Maximum number of blocks, mem. management table address.
|
|
spuSettings->mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR | SPU_COMMON_CDVOLL | SPU_COMMON_CDVOLR | SPU_COMMON_CDMIX ); // Mask which attributes to set
|
|
spuSettings->mvol.left = MVOL_L; // Master volume left
|
|
spuSettings->mvol.right = MVOL_R; // see libref47.pdf, p.1058
|
|
spuSettings->cd.volume.left = CDVOL_L;
|
|
spuSettings->cd.volume.right = CDVOL_R;
|
|
// Enable CD input ON
|
|
spuSettings->cd.mix = SPU_ON;
|
|
// Apply settings
|
|
SpuSetCommonAttr(spuSettings);
|
|
// Set transfer mode
|
|
SpuSetTransferMode(SPU_TRANSFER_BY_DMA);
|
|
SpuSetIRQ(SPU_OFF);
|
|
// Mute all voices
|
|
SpuSetKey(SpuOff, SPU_ALLCH);
|
|
};
|
|
u_long sendVAGtoSPU(unsigned int VAG_data_size, u_char *VAG_data){
|
|
u_long transferred;
|
|
SpuSetTransferMode(SpuTransByDMA); // DMA transfer; can do other processing during transfer
|
|
transferred = SpuWrite (VAG_data + sizeof(VAGhdr), VAG_data_size); // transfer VAG_data_size bytes from VAG_data address to sound buffer
|
|
SpuIsTransferCompleted (SPU_TRANSFER_WAIT); // Checks whether transfer is completed and waits for completion
|
|
return transferred;
|
|
};
|
|
void setVoiceAttr(SpuVoiceAttr * voiceAttributes, u_int pitch, long channel, u_long soundAddr ){
|
|
voiceAttributes->mask= //~ Attributes (bit string, 1 bit per attribute)
|
|
(
|
|
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
|
|
);
|
|
voiceAttributes->voice = channel; //~ Voice (low 24 bits are a bit string, 1 bit per voice )
|
|
voiceAttributes->volume.left = 0x0; //~ Volume
|
|
voiceAttributes->volume.right = 0x0; //~ Volume
|
|
voiceAttributes->pitch = pitch; //~ Interval (set pitch)
|
|
voiceAttributes->addr = soundAddr; //~ Waveform data start address
|
|
voiceAttributes->a_mode = SPU_VOICE_LINEARIncN; //~ Attack rate mode = Linear Increase - see libref47.pdf p.1091
|
|
voiceAttributes->s_mode = SPU_VOICE_LINEARIncN; //~ Sustain rate mode = Linear Increase
|
|
voiceAttributes->r_mode = SPU_VOICE_LINEARDecN; //~ Release rate mode = Linear Decrease
|
|
voiceAttributes->ar = 0x0; //~ Attack rate
|
|
voiceAttributes->dr = 0x0; //~ Decay rate
|
|
voiceAttributes->rr = 0x0; //~ Release rate
|
|
voiceAttributes->sr = 0x0; //~ Sustain rate
|
|
voiceAttributes->sl = 0xf; //~ Sustain level
|
|
SpuSetVoiceAttr(voiceAttributes); // set attributes
|
|
};
|
|
u_long setSPUtransfer(SpuVoiceAttr * voiceAttributes, VAGsound * sound){
|
|
// Return spu_address
|
|
u_long transferred, spu_address;
|
|
u_int pitch;
|
|
const VAGhdr * VAGheader = (VAGhdr *) sound->VAGfile;
|
|
pitch = (SWAP_ENDIAN32(VAGheader->samplingFrequency) << 12) / 44100L;
|
|
spu_address = SpuMalloc(SWAP_ENDIAN32(VAGheader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer.
|
|
SpuSetTransferStartAddr(spu_address); // Sets a starting address in the sound buffer
|
|
transferred = sendVAGtoSPU(SWAP_ENDIAN32(VAGheader->dataSize), sound->VAGfile);
|
|
setVoiceAttr(voiceAttributes, pitch, sound->spu_channel, spu_address);
|
|
return spu_address;
|
|
};
|
|
void setVAGvolume(SpuVoiceAttr * voiceAttributes, VAGsound * sound, int volumeL,int volumeR ){
|
|
voiceAttributes->mask= ( SPU_VOICE_VOLL | SPU_VOICE_VOLR );
|
|
voiceAttributes->voice = sound->spu_channel;
|
|
// Range 0 - 3fff
|
|
voiceAttributes->volume.left = volumeL;
|
|
voiceAttributes->volume.right = volumeR;
|
|
SpuSetVoiceAttr(voiceAttributes);
|
|
};
|
|
void setLvlVAG(LEVEL * level, SpuCommonAttr * spuSettings, SpuVoiceAttr * voiceAttributes, char spu_malloc_rec[]){
|
|
if (level->VAG != 0){
|
|
// Free SPU mem
|
|
for (u_short vag = 0; vag < level->VAG->index; vag++ ){
|
|
if(level->VAG->samples[vag].spu_address != 0){
|
|
SpuFree(level->VAG->samples[vag].spu_address);
|
|
}
|
|
}
|
|
// Init sound settings
|
|
initSnd(spuSettings, spu_malloc_rec, level->VAG->index );
|
|
for (u_short vag = 0; vag < level->VAG->index; vag++ ){
|
|
level->VAG->samples[vag].spu_address = setSPUtransfer(voiceAttributes, &level->VAG->samples[vag]);
|
|
}
|
|
}
|
|
};
|
|
void playSFX(SpuVoiceAttr * voiceAttributes, VAGsound * sound, int volumeL, int volumeR ){
|
|
// Set voice volume to sample volume
|
|
setVAGvolume(voiceAttributes, sound, volumeL, volumeR);
|
|
// Play voice
|
|
SpuSetKey(SpuOn, sound->spu_channel);
|
|
};
|
|
void setSFXdist(LEVEL * level, CAMERA * camera, int camMode ){
|
|
VECTOR sndPos2D = {0};
|
|
if (level->levelSounds != 0){
|
|
for(int snd = 0; snd < level->levelSounds->index; snd++){
|
|
u_int r;
|
|
// If parent is actor,
|
|
if (level->levelSounds->sounds[snd]->parent == level->actorPtr && camMode <= 1){
|
|
r = CAM_DIST_TO_ACT;
|
|
// update sound location if sound has a parent and it's not actor
|
|
} else if ( level->levelSounds->sounds[snd]->parent != 0){
|
|
VECTOR dist;
|
|
copyVector(&level->levelSounds->sounds[snd]->location, &level->levelSounds->sounds[snd]->parent->pos);
|
|
// Get distance between sound source and camera
|
|
dist.vx = -camera->pos->vx - level->levelSounds->sounds[snd]->location.vx;
|
|
dist.vz = -camera->pos->vz - level->levelSounds->sounds[snd]->location.vz;
|
|
r = psqrt((dist.vx * dist.vx) + (dist.vz * dist.vz));
|
|
// Get snd screen coordinates
|
|
// Range -1024 0 == screen left, 0 +1024 == screen right
|
|
worldToScreen(&level->levelSounds->sounds[snd]->location, &sndPos2D);
|
|
}
|
|
// Find volume base on dist
|
|
u_int volumeBase = (level->levelSounds->sounds[snd]->volume_max/r) * SND_NMALIZED > SND_MAX_VOL ? SND_MAX_VOL :
|
|
(level->levelSounds->sounds[snd]->volume_max/r) * SND_NMALIZED < 0 ? 0 :
|
|
(level->levelSounds->sounds[snd]->volume_max/r) * SND_NMALIZED;
|
|
// Avoid value of 0
|
|
sndPos2D.vx = sndPos2D.vx == 0 || sndPos2D.vx == -0 ? 1 : sndPos2D.vx;
|
|
level->levelSounds->sounds[snd]->volumeL = volumeBase / ( (sndPos2D.vx > SND_DZ ? ( sndPos2D.vx - SND_DZ >> 7) + 1 : 1) );
|
|
level->levelSounds->sounds[snd]->volumeR = volumeBase / ( (sndPos2D.vx < -SND_DZ ? ( ( -sndPos2D.vx - SND_DZ ) >> 7) + 1 : 1) );
|
|
}
|
|
}
|
|
};
|
|
void XAsetup(void){
|
|
u_char param[4];
|
|
// ORing the parameters we need to set ; drive speed, ADPCM play, Subheader filter, sector size
|
|
// If using CdlModeSpeed(Double speed), you need to load an XA file that has 8 channels.
|
|
// In single speed, a 4 channels XA is to be used.
|
|
param[0] = CdlModeSpeed|CdlModeRT|CdlModeSF|CdlModeSize1;
|
|
// Issue primitive command to CD-ROM system (Blocking-type)
|
|
// Set the parameters above
|
|
CdControlB(CdlSetmode, param, 0);
|
|
// Pause at current pos
|
|
CdControlF(CdlPause,0);
|
|
};
|
|
void getXAoffset(LEVEL * level){
|
|
// TODO : Only works for first XA file
|
|
CdlFILE XAPos = {0};
|
|
// Load XA file
|
|
//~ CdSearchFile(&XAPos, level->XA->name);
|
|
CdSearchFile(&XAPos, level->XA->banks[0]->name);
|
|
// Set cd head to start of file
|
|
//~ level->XA->offset = CdPosToInt(&XAPos.pos);
|
|
level->XA->banks[0]->offset = CdPosToInt(&XAPos.pos);
|
|
};
|
|
void setXAsample(XAsound * sound, CdlFILTER * filter){
|
|
filter->chan = sound->channel;
|
|
filter->file = sound->file;
|
|
// Set filter
|
|
CdControlF(CdlSetfilter, (u_char *)filter);
|
|
// Reset sample's cursor
|
|
sound->cursor = 0;
|
|
};
|
|
void setLvlXA(LEVEL * level, int sample){
|
|
if(sample >= 0){
|
|
// CD filter
|
|
CdlFILTER filter;
|
|
// File position in m/s/f
|
|
CdlLOC loc;
|
|
if (level->XA != 0){
|
|
// Change XA track
|
|
XAsetup();
|
|
//~ sample = !sample;
|
|
level->XA->banks[0]->samples[sample].cursor = -1;
|
|
getXAoffset(level);
|
|
setXAsample(&level->XA->banks[0]->samples[sample], &filter);
|
|
CdIntToPos(level->XA->banks[0]->samples[sample].start + level->XA->banks[0]->offset , &loc);
|
|
// Send CDROM read command
|
|
CdControlF(CdlReadS, (u_char *)&loc);
|
|
}
|
|
}
|
|
};
|
|
void XAplayback(LEVEL * level, int sample, long dt){
|
|
if (sample != -1 ){
|
|
// CD filter
|
|
CdlFILTER filter;
|
|
// File position in m/s/f
|
|
CdlLOC loc;
|
|
// Begin XA file playback...
|
|
// if sample's cursor is 0
|
|
if (level->XA->banks[0]->samples[sample].cursor == 0){
|
|
// Convert sector number to CD position in min/second/frame and set CdlLOC accordingly.
|
|
CdIntToPos(level->XA->banks[0]->samples[sample].start + level->XA->banks[0]->offset , &loc);
|
|
// Send CDROM read command
|
|
CdControlF(CdlReadS, (u_char *)&loc);
|
|
//~ *XATime = VSync(-1);
|
|
// Set playing flag
|
|
}
|
|
// if sample's cursor is close to sample's end position, stop playback
|
|
// XA playback has fixed rate
|
|
if ((level->XA->banks[0]->samples[sample].cursor += XA_CDSPEED / ((XA_RATE/(dt+1)+1)) ) >= (level->XA->banks[0]->samples[sample].end - level->XA->banks[0]->samples[sample].start) * ONE ){
|
|
//~ CdControlF(CdlStop,0);
|
|
level->XA->banks[0]->samples[sample].cursor = -1;
|
|
setXAsample(&level->XA->banks[0]->samples[sample], &filter);
|
|
}
|
|
}
|
|
};
|