Mercurial > audlegacy-plugins
diff src/psf2/peops/spu.c @ 2737:62cc6d667119
Import a bunch of stuff for new psf2 plugin.
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Mon, 30 Jun 2008 20:20:53 -0500 |
parents | |
children | f1482af6384c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/psf2/peops/spu.c Mon Jun 30 20:20:53 2008 -0500 @@ -0,0 +1,641 @@ +/*************************************************************************** + spu.c - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2003/03/01 - linuzappz +// - libraryName changes using ALSA +// +// 2003/02/28 - Pete +// - added option for type of interpolation +// - adjusted spu irqs again (Thousant Arms, Valkyrie Profile) +// - added MONO support for MSWindows DirectSound +// +// 2003/02/20 - kode54 +// - amended interpolation code, goto GOON could skip initialization of gpos and cause segfault +// +// 2003/02/19 - kode54 +// - moved SPU IRQ handler and changed sample flag processing +// +// 2003/02/18 - kode54 +// - moved ADSR calculation outside of the sample decode loop, somehow I doubt that +// ADSR timing is relative to the frequency at which a sample is played... I guess +// this remains to be seen, and I don't know whether ADSR is applied to noise channels... +// +// 2003/02/09 - kode54 +// - one-shot samples now process the end block before stopping +// - in light of removing fmod hack, now processing ADSR on frequency channel as well +// +// 2003/02/08 - kode54 +// - replaced easy interpolation with gaussian +// - removed fmod averaging hack +// - changed .sinc to be updated from .iRawPitch, no idea why it wasn't done this way already (<- Pete: because I sometimes fail to see the obvious, haharhar :) +// +// 2003/02/08 - linuzappz +// - small bugfix for one usleep that was 1 instead of 1000 +// - added iDisStereo for no stereo (Linux) +// +// 2003/01/22 - Pete +// - added easy interpolation & small noise adjustments +// +// 2003/01/19 - Pete +// - added Neill's reverb +// +// 2003/01/12 - Pete +// - added recording window handlers +// +// 2003/01/06 - Pete +// - added Neill's ADSR timings +// +// 2002/12/28 - Pete +// - adjusted spu irq handling, fmod handling and loop handling +// +// 2002/08/14 - Pete +// - added extra reverb +// +// 2002/06/08 - linuzappz +// - SPUupdate changed for SPUasync +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#define _IN_SPU + +#include "../peops/stdafx.h" +#include "../peops/externals.h" +#include "../peops/regs.h" +#include "../peops/registers.h" +#include "../peops/spu.h" + +void SPUirq(void) ; + +//#include "PsxMem.h" +//#include "driver.h" + +//////////////////////////////////////////////////////////////////////// +// globals +//////////////////////////////////////////////////////////////////////// + +// psx buffer / addresses + +static u16 regArea[0x200]; +static u16 spuMem[256*1024]; +static u8 * spuMemC; +static u8 * pSpuIrq=0; +static u8 * pSpuBuffer; + +// user settings +static int iVolume; + +// MAIN infos struct for each channel + +static SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling) +static REVERBInfo rvb; + +static u32 dwNoiseVal=1; // global noise generator + +static u16 spuCtrl=0; // some vars to store psx reg infos +static u16 spuStat=0; +static u16 spuIrq=0; +static u32 spuAddr=0xffffffff; // address into spu mem +static int bSPUIsOpen=0; + +static const int f[5][2] = { + { 0, 0 }, + { 60, 0 }, + { 115, -52 }, + { 98, -55 }, + { 122, -60 } }; +s16 * pS; +static s32 ttemp; + +//////////////////////////////////////////////////////////////////////// +// CODE AREA +//////////////////////////////////////////////////////////////////////// + +// dirty inline func includes + +#include "../peops/reverb.c" +#include "../peops/adsr.c" + +// Try this to increase speed. +#include "../peops/registers.c" +#include "../peops/dma.c" + +//////////////////////////////////////////////////////////////////////// +// helpers for so-called "gauss interpolation" + +#define gval0 (((int *)(&s_chan[ch].SB[29]))[gpos]) +#define gval(x) (((int *)(&s_chan[ch].SB[29]))[(gpos+x)&3]) + +#include "gauss_i.h" + +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// START SOUND... called by main thread to setup a new sound on a channel +//////////////////////////////////////////////////////////////////////// + +static INLINE void StartSound(int ch) +{ + StartADSR(ch); + + s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start + + s_chan[ch].s_1=0; // init mixing vars + s_chan[ch].s_2=0; + s_chan[ch].iSBPos=28; + + s_chan[ch].bNew=0; // init channel flags + s_chan[ch].bStop=0; + s_chan[ch].bOn=1; + + s_chan[ch].SB[29]=0; // init our interpolation helpers + s_chan[ch].SB[30]=0; + + s_chan[ch].spos=0x40000L;s_chan[ch].SB[28]=0; // -> start with more decoding +} + +//////////////////////////////////////////////////////////////////////// +// MAIN SPU FUNCTION +// here is the main job handler... thread, timer or direct func call +// basically the whole sound processing is done in this fat func! +//////////////////////////////////////////////////////////////////////// + +static u32 sampcount; +static u32 decaybegin; +static u32 decayend; + +// Counting to 65536 results in full volume offage. +void setlength(s32 stop, s32 fade) +{ + if(stop==~0) + { + decaybegin=~0; + } + else + { + stop=(stop*441)/10; + fade=(fade*441)/10; + + decaybegin=stop; + decayend=stop+fade; + } +} + +#define CLIP(_x) {if(_x>32767) _x=32767; if(_x<-32767) _x=-32767;} +int SPUasync(u32 cycles) +{ + int volmul=iVolume; + static s32 dosampies; + s32 temp; + + ttemp+=cycles; + dosampies=ttemp/384; + if(!dosampies) return(1); + ttemp-=dosampies*384; + temp=dosampies; + + while(temp) + { + s32 revLeft=0, revRight=0; + s32 sl=0, sr=0; + int ch,fa; + + temp--; + //--------------------------------------------------// + //- main channel loop -// + //--------------------------------------------------// + { + for(ch=0;ch<MAXCHAN;ch++) // loop em all. + { + if(s_chan[ch].bNew) StartSound(ch); // start new sound + if(!s_chan[ch].bOn) continue; // channel not playing? next + + + if(s_chan[ch].iActFreq!=s_chan[ch].iUsedFreq) // new psx frequency? + { + s_chan[ch].iUsedFreq=s_chan[ch].iActFreq; // -> take it and calc steps + s_chan[ch].sinc=s_chan[ch].iRawPitch<<4; + if(!s_chan[ch].sinc) s_chan[ch].sinc=1; + } + + while(s_chan[ch].spos>=0x10000L) + { + if(s_chan[ch].iSBPos==28) // 28 reached? + { + int predict_nr,shift_factor,flags,d,s; + u8* start;unsigned int nSample; + int s_1,s_2; + + start=s_chan[ch].pCurr; // set up the current pos + + if (start == (u8*)-1) // special "stop" sign + { + s_chan[ch].bOn=0; // -> turn everything off + s_chan[ch].ADSRX.lVolume=0; + s_chan[ch].ADSRX.EnvelopeVol=0; + goto ENDX; // -> and done for this channel + } + + s_chan[ch].iSBPos=0; // Reset buffer play index. + + //////////////////////////////////////////// spu irq handler here? mmm... do it later + + s_1=s_chan[ch].s_1; + s_2=s_chan[ch].s_2; + + predict_nr=(int)*start;start++; + shift_factor=predict_nr&0xf; + predict_nr >>= 4; + flags=(int)*start;start++; + + // -------------------------------------- // + // Decode new samples into s_chan[ch].SB[0 through 27] + for (nSample=0;nSample<28;start++) + { + d=(int)*start; + s=((d&0xf)<<12); + if(s&0x8000) s|=0xffff0000; + + fa=(s >> shift_factor); + fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); + s_2=s_1;s_1=fa; + s=((d & 0xf0) << 8); + + s_chan[ch].SB[nSample++]=fa; + + if(s&0x8000) s|=0xffff0000; + fa=(s>>shift_factor); + fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); + s_2=s_1;s_1=fa; + + s_chan[ch].SB[nSample++]=fa; + } + + //////////////////////////////////////////// irq check + + if(spuCtrl&0x40) // irq active? + { + if((pSpuIrq > start-16 && // irq address reached? + pSpuIrq <= start) || + ((flags&1) && // special: irq on looping addr, when stop/loop flag is set + (pSpuIrq > s_chan[ch].pLoop-16 && + pSpuIrq <= s_chan[ch].pLoop))) + { + //extern s32 spuirqvoodoo; + s_chan[ch].iIrqDone=1; // -> debug flag + SPUirq(); + //puts("IRQ"); + //if(spuirqvoodoo!=-1) + //{ + // spuirqvoodoo=temp*384; + // temp=0; + //} + } + } + + //////////////////////////////////////////// flag handler + + if((flags&4) && (!s_chan[ch].bIgnoreLoop)) + s_chan[ch].pLoop=start-16; // loop adress + + if(flags&1) // 1: stop/loop + { + // We play this block out first... + //if(!(flags&2)) // 1+2: do loop... otherwise: stop + if(flags!=3 || s_chan[ch].pLoop==NULL) // PETE: if we don't check exactly for 3, loop hang ups will happen (DQ4, for example) + { // and checking if pLoop is set avoids crashes, yeah + start = (u8*)-1; + } + else + { + start = s_chan[ch].pLoop; + } + } + + s_chan[ch].pCurr=start; // store values for next cycle + s_chan[ch].s_1=s_1; + s_chan[ch].s_2=s_2; + + //////////////////////////////////////////// + } + + fa=s_chan[ch].SB[s_chan[ch].iSBPos++]; // get sample data + + if((spuCtrl&0x4000)==0) fa=0; // muted? + else CLIP(fa); + + { + int gpos; + gpos = s_chan[ch].SB[28]; + gval0 = fa; + gpos = (gpos+1) & 3; + s_chan[ch].SB[28] = gpos; + } + s_chan[ch].spos -= 0x10000L; + } + + //////////////////////////////////////////////// + // noise handler... just produces some noise data + // surely wrong... and no noise frequency (spuCtrl&0x3f00) will be used... + // and sometimes the noise will be used as fmod modulation... pfff + + if(s_chan[ch].bNoise) + { + //puts("Noise"); + if((dwNoiseVal<<=1)&0x80000000L) + { + dwNoiseVal^=0x0040001L; + fa=((dwNoiseVal>>2)&0x7fff); + fa=-fa; + } + else fa=(dwNoiseVal>>2)&0x7fff; + + // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val + fa=s_chan[ch].iOldNoise+((fa-s_chan[ch].iOldNoise)/((0x001f-((spuCtrl&0x3f00)>>9))+1)); + if(fa>32767L) fa=32767L; + if(fa<-32767L) fa=-32767L; + s_chan[ch].iOldNoise=fa; + + } //---------------------------------------- + else // NO NOISE (NORMAL SAMPLE DATA) HERE + { + int vl, vr, gpos; + vl = (s_chan[ch].spos >> 6) & ~3; + gpos = s_chan[ch].SB[28]; + vr=(gauss[vl]*gval0)>>9; + vr+=(gauss[vl+1]*gval(1))>>9; + vr+=(gauss[vl+2]*gval(2))>>9; + vr+=(gauss[vl+3]*gval(3))>>9; + fa = vr>>2; + } + + s_chan[ch].sval = (MixADSR(ch) * fa)>>10; // / 1023; // add adsr + if(s_chan[ch].bFMod==2) // fmod freq channel + { + int NP=s_chan[ch+1].iRawPitch; + NP=((32768L+s_chan[ch].sval)*NP)>>15; ///32768L; + + if(NP>0x3fff) NP=0x3fff; + if(NP<0x1) NP=0x1; + + // mmmm... if I do this, all is screwed + // s_chan[ch+1].iRawPitch=NP; + + NP=(44100L*NP)/(4096L); // calc frequency + + s_chan[ch+1].iActFreq=NP; + s_chan[ch+1].iUsedFreq=NP; + s_chan[ch+1].sinc=(((NP/10)<<16)/4410); + if(!s_chan[ch+1].sinc) s_chan[ch+1].sinc=1; + + // mmmm... set up freq decoding positions? + // s_chan[ch+1].iSBPos=28; + // s_chan[ch+1].spos=0x10000L; + } + else + { + ////////////////////////////////////////////// + // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff) + int tmpl,tmpr; + + if (1) //ao_channel_enable[ch+PSF_1]) { + { + tmpl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)>>14; + tmpr=(s_chan[ch].sval*s_chan[ch].iRightVolume)>>14; + } else { + tmpl = 0; + tmpr = 0; + } + sl+=tmpl; + sr+=tmpr; + + if(((rvb.Enabled>>ch)&1) && (spuCtrl&0x80)) + { + revLeft+=tmpl; + revRight+=tmpr; + } + } + + s_chan[ch].spos += s_chan[ch].sinc; + ENDX: ; + } + } + + /////////////////////////////////////////////////////// + // mix all channels (including reverb) into one buffer + MixREVERBLeftRight(&sl,&sr,revLeft,revRight); +// printf("sampcount %d decaybegin %d decayend %d\n", sampcount, decaybegin, decayend); + if(sampcount>=decaybegin) + { + s32 dmul; + if(decaybegin!=~0) // Is anyone REALLY going to be playing a song + // for 13 hours? + { + if(sampcount>=decayend) + { +// ao_song_done = 1; + return(0); + } + dmul=256-(256*(sampcount-decaybegin)/(decayend-decaybegin)); + sl=(sl*dmul)>>8; + sr=(sr*dmul)>>8; + } + } + + sampcount++; + sl=(sl*volmul)>>8; + sr=(sr*volmul)>>8; + + //{ + // static double asl=0; + // static double asr=0; + + // asl+=(sl-asl)/5; + // asr+=(sl-asr)/5; + + //sl-=asl; + //sr-=asr; + + // if(sl>32767 || sl < -32767) printf("Left: %d, %f\n",sl,asl); + // if(sr>32767 || sr < -32767) printf("Right: %d, %f\n",sl,asl); + //} + + if(sl>32767) sl=32767; if(sl<-32767) sl=-32767; + if(sr>32767) sr=32767; if(sr<-32767) sr=-32767; + + *pS++=sl; + *pS++=sr; + } + + return(1); +} + +void SPU_flushboot(void) +{ + if((u8*)pS>((u8*)pSpuBuffer+1024)) + { + spu_update((u8*)pSpuBuffer,(u8*)pS-(u8*)pSpuBuffer); + pS=(s16 *)pSpuBuffer; + } +} + +#ifdef TIMEO +static u64 begintime; +static u64 gettime64(void) +{ + struct timeval tv; + u64 ret; + + gettimeofday(&tv,0); + ret=tv.tv_sec; + ret*=1000000; + ret+=tv.tv_usec; + return(ret); +} +#endif +//////////////////////////////////////////////////////////////////////// +// INIT/EXIT STUFF +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// SPUINIT: this func will be called first by the main emu +//////////////////////////////////////////////////////////////////////// + +int SPUinit(void) +{ + spuMemC=(u8*)spuMem; // just small setup + memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN)); + memset((void *)&rvb,0,sizeof(REVERBInfo)); + memset(regArea,0,sizeof(regArea)); + memset(spuMem,0,sizeof(spuMem)); + InitADSR(); + sampcount=ttemp=0; + #ifdef TIMEO + begintime=gettime64(); + #endif + return 0; +} + +//////////////////////////////////////////////////////////////////////// +// SETUPSTREAMS: init most of the spu buffers +//////////////////////////////////////////////////////////////////////// + +void SetupStreams(void) +{ + int i; + + pSpuBuffer=(u8*)malloc(32768); // alloc mixing buffer + pS=(s16 *)pSpuBuffer; + + for(i=0;i<MAXCHAN;i++) // loop sound channels + { + s_chan[i].ADSRX.SustainLevel = 1024; // -> init sustain + s_chan[i].iIrqDone=0; + s_chan[i].pLoop=spuMemC; + s_chan[i].pStart=spuMemC; + s_chan[i].pCurr=spuMemC; + } +} + +//////////////////////////////////////////////////////////////////////// +// REMOVESTREAMS: free most buffer +//////////////////////////////////////////////////////////////////////// + +void RemoveStreams(void) +{ + free(pSpuBuffer); // free mixing buffer + pSpuBuffer=NULL; + + #ifdef TIMEO + { + u64 tmp; + tmp=gettime64(); + tmp-=begintime; + if(tmp) + tmp=(u64)sampcount*1000000/tmp; + printf("%lld samples per second\n",tmp); + } + #endif +} + + +//////////////////////////////////////////////////////////////////////// +// SPUOPEN: called by main emu after init +//////////////////////////////////////////////////////////////////////// + +int SPUopen(void) +{ + if(bSPUIsOpen) return 0; // security for some stupid main emus + spuIrq=0; + + spuStat=spuCtrl=0; + spuAddr=0xffffffff; + dwNoiseVal=1; + + spuMemC=(u8*)spuMem; + memset((void *)s_chan,0,(MAXCHAN+1)*sizeof(SPUCHAN)); + pSpuIrq=0; + + iVolume=255; //85; + SetupStreams(); // prepare streaming + + bSPUIsOpen=1; + + return 1; +} + +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// SPUCLOSE: called before shutdown +//////////////////////////////////////////////////////////////////////// + +int SPUclose(void) +{ + if(!bSPUIsOpen) return 0; // some security + + bSPUIsOpen=0; // no more open + + RemoveStreams(); // no more streaming + + return 0; +} + +//////////////////////////////////////////////////////////////////////// +// SPUSHUTDOWN: called by main emu on final exit +//////////////////////////////////////////////////////////////////////// + +int SPUshutdown(void) +{ + return 0; +} + +void SPUinjectRAMImage(u16 *pIncoming) +{ + int i; + + for (i = 0; i < (256*1024); i++) + { + spuMem[i] = pIncoming[i]; + } +}