view src/sexypsf/spu/spu.c @ 493:31d21ad70903 trunk

[svn] flac 113 plugin: stability fixes and a new option to disable bitrate update in player window during playback (saves cpu, this is the solution for most people playing flac and reporting high cpu usage)
author giacomo
date Sun, 21 Jan 2007 16:08:19 -0800
parents 3da1b8942b8b
children
line wrap: on
line source

/***************************************************************************
                            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 "stdafx.h"
#include "externals.h"
#include "spu.h"
#include "regs.h"
#include "registers.h"

#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;

////////////////////////////////////////////////////////////////////////
// CODE AREA
////////////////////////////////////////////////////////////////////////

// dirty inline func includes

#include "reverb.c"        
#include "adsr.c"

// Try this to increase speed.
#include "registers.c"
#include "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 SPUsetlength(s32 stop, s32 fade)
{
 if(stop==~0)
 {
  decaybegin=~0;
 }
 else
 {
  stop=(stop*441)/10;
  fade=(fade*441)/10;

  decaybegin=stop;
  decayend=stop+fade;
 }
}

static u32 seektime;
static s32 poo;
int sexypsf_seek(u32 t)
{
 seektime=t*441/10;
 if(seektime>sampcount) return(1);
 return(0);
}

#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;

 poo+=cycles;
 dosampies=poo/384;
 if(!dosampies) return(1);
 poo-=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;

	   tmpl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)>>14;
	   tmpr=(s_chan[ch].sval*s_chan[ch].iRightVolume)>>14;

	   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);
  if(sampcount>=decaybegin)
  {
   s32 dmul;
   if(decaybegin!=(u32)~0) // Is anyone REALLY going to be playing a song
		      // for 13 hours?
   {
    if(sampcount>=decayend) 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 sexypsf_stop(void)
{
 decaybegin=decayend=0;
}

void SPUendflush(void)
{
   if((seektime!=(u32)~0) && seektime>sampcount)
   {
    pS=(s16 *)pSpuBuffer;
    sexypsf_update(0,0);
   }
   else if((u8*)pS>((u8*)pSpuBuffer+1024))
   {
    sexypsf_update((u8*)pSpuBuffer,(u8*)pS-(u8*)pSpuBuffer);
    pS=(s16 *)pSpuBuffer;
   }
}   

#ifdef TIMEO
static u64 begintime;
static u64 SexyTime64(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=poo=0;
 seektime=(u32)~0;
 #ifdef TIMEO
 begintime=SexyTime64();
 #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=SexyTime64();
  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=128; //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;
}