2009-02-06 19:52:59 +00:00
/* ZeroSPU2
* Copyright ( C ) 2006 - 2007 zerofrog
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <assert.h>
# include <stdlib.h>
# include "zerospu2.h"
# include "SoundTouch/SoundTouch.h"
# include "SoundTouch/WavFile.h"
// VOICE_PROCESSED definitions
SPU_CONTROL_ * VOICE_PROCESSED : : GetCtrl ( )
{
return ( ( SPU_CONTROL_ * ) ( spu2regs + memoffset + REG_C0_CTRL ) ) ;
}
2009-05-17 21:57:24 +00:00
void VOICE_PROCESSED : : SetVolume ( s32 iProcessRight )
2009-02-06 19:52:59 +00:00
{
u16 vol = iProcessRight ? pvoice - > right . word : pvoice - > left . word ;
if ( vol & 0x8000 ) // sweep not working
{
short sInc = 1 ; // -> sweep up?
if ( vol & 0x2000 ) sInc = - 1 ; // -> or down?
if ( vol & 0x1000 ) vol ^ = 0xffff ; // -> mmm... phase inverted? have to investigate this
vol = ( ( vol & 0x7f ) + 1 ) / 2 ; // -> sweep: 0..127 -> 0..64
vol + = vol / ( 2 * sInc ) ; // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
vol * = 128 ;
}
else // no sweep:
{
if ( vol & 0x4000 ) vol = 0x3fff - ( vol & 0x3fff ) ; // -> mmm... phase inverted? have to investigate this
}
if ( iProcessRight )
rightvol = vol & 0x3fff ;
else
leftvol = vol & 0x3fff ;
bVolChanged = true ;
}
void VOICE_PROCESSED : : StartSound ( )
{
ADSRX . lVolume = 1 ; // and init some adsr vars
ADSRX . State = 0 ;
ADSRX . EnvelopeVol = 0 ;
if ( bReverb & & GetCtrl ( ) - > reverb )
{
// setup the reverb effects
}
pCurr = pStart ; // set sample start
s_1 = 0 ; // init mixing vars
s_2 = 0 ;
iSBPos = 28 ;
bNew = false ; // init channel flags
bStop = false ;
bOn = true ;
SB [ 29 ] = 0 ; // init our interpolation helpers
SB [ 30 ] = 0 ;
spos = 0x10000L ;
SB [ 31 ] = 0 ;
}
void VOICE_PROCESSED : : VoiceChangeFrequency ( )
{
iUsedFreq = iActFreq ; // -> take it and calc steps
sinc = ( u32 ) pvoice - > pitch < < 4 ;
if ( ! sinc ) sinc = 1 ;
// -> freq change in simle imterpolation mode: set flag
SB [ 32 ] = 1 ;
}
void VOICE_PROCESSED : : InterpolateUp ( )
{
if ( SB [ 32 ] = = 1 ) // flag == 1? calc step and set flag... and don't change the value in this pass
{
2009-05-17 21:57:24 +00:00
const s32 id1 = SB [ 30 ] - SB [ 29 ] ; // curr delta to next val
const s32 id2 = SB [ 31 ] - SB [ 30 ] ; // and next delta to next-next val :)
2009-02-06 19:52:59 +00:00
SB [ 32 ] = 0 ;
if ( id1 > 0 ) // curr delta positive
{
if ( id2 < id1 )
{
SB [ 28 ] = id1 ;
SB [ 32 ] = 2 ;
}
else if ( id2 < ( id1 < < 1 ) )
SB [ 28 ] = ( id1 * sinc ) / 0x10000L ;
else
SB [ 28 ] = ( id1 * sinc ) / 0x20000L ;
}
else // curr delta negative
{
if ( id2 > id1 )
{
SB [ 28 ] = id1 ;
SB [ 32 ] = 2 ;
}
else if ( id2 > ( id1 < < 1 ) )
SB [ 28 ] = ( id1 * sinc ) / 0x10000L ;
else
SB [ 28 ] = ( id1 * sinc ) / 0x20000L ;
}
}
else if ( SB [ 32 ] = = 2 ) // flag 1: calc step and set flag... and don't change the value in this pass
{
SB [ 32 ] = 0 ;
SB [ 28 ] = ( SB [ 28 ] * sinc ) / 0x20000L ;
if ( sinc < = 0x8000 )
SB [ 29 ] = SB [ 30 ] - ( SB [ 28 ] * ( ( 0x10000 / sinc ) - 1 ) ) ;
else
SB [ 29 ] + = SB [ 28 ] ;
}
else // no flags? add bigger val (if possible), calc smaller step, set flag1
SB [ 29 ] + = SB [ 28 ] ;
}
//
// even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm
//
void VOICE_PROCESSED : : InterpolateDown ( )
{
if ( sinc > = 0x20000L ) // we would skip at least one val?
{
SB [ 29 ] + = ( SB [ 30 ] - SB [ 29 ] ) / 2 ; // add easy weight
if ( sinc > = 0x30000L ) // we would skip even more vals?
SB [ 29 ] + = ( SB [ 31 ] - SB [ 30 ] ) / 2 ; // add additional next weight
}
}
2009-05-17 21:57:24 +00:00
void VOICE_PROCESSED : : FModChangeFrequency ( s32 ns )
2009-02-06 19:52:59 +00:00
{
2009-05-17 21:57:24 +00:00
s32 NP = pvoice - > pitch ;
2009-02-06 19:52:59 +00:00
NP = ( ( 32768L + iFMod [ ns ] ) * NP ) / 32768L ;
if ( NP > 0x3fff ) NP = 0x3fff ;
if ( NP < 0x1 ) NP = 0x1 ;
NP = ( SAMPLE_RATE * NP ) / ( 4096L ) ; // calc frequency
iActFreq = NP ;
iUsedFreq = NP ;
sinc = ( ( ( NP / 10 ) < < 16 ) / 4800 ) ;
2009-05-17 21:57:24 +00:00
if ( ! sinc ) sinc = 1 ;
2009-02-06 19:52:59 +00:00
// freq change in simple interpolation mode
SB [ 32 ] = 1 ;
iFMod [ ns ] = 0 ;
}
2009-02-13 09:57:15 +00:00
static void __forceinline GetNoiseValues ( s32 & VD )
{
static s32 Seed = 0x41595321 ;
if ( Seed & 0x100 )
VD = ( s32 ) ( ( Seed & 0xff ) < < 8 ) ;
2009-05-17 21:57:24 +00:00
else if ( ! ( Seed & 0xffff ) )
2009-02-13 09:57:15 +00:00
VD = ( s32 ) 0x8000 ;
else
VD = ( s32 ) 0x7fff ;
# ifdef _WIN32
__asm {
MOV eax , Seed
ROR eax , 5
XOR eax , 0x9a
MOV ebx , eax
ROL eax , 2
ADD eax , ebx
XOR eax , ebx
ROR eax , 3
MOV Seed , eax
}
# else
__asm__ (
" .intel_syntax \n "
" MOV %%eax,%0 \n "
" ROR %%eax,5 \n "
" XOR %%eax,0x9a \n "
" MOV %%ebx,%%eax \n "
" ROL %%eax,2 \n "
" ADD %%eax,%%ebx \n "
" XOR %%eax,%%ebx \n "
" ROR %%eax,3 \n "
" MOV %0,%%eax \n "
" .att_syntax \n " : : " r " ( Seed ) ) ;
# endif
}
2009-02-06 19:52:59 +00:00
// fixme - 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
int VOICE_PROCESSED : : iGetNoiseVal ( )
{
2009-05-17 21:57:24 +00:00
s32 fa ;
2009-02-06 19:52:59 +00:00
2009-02-13 09:57:15 +00:00
/*if ((dwNoiseVal<<=1)&0x80000000L)
2009-02-06 19:52:59 +00:00
{
dwNoiseVal ^ = 0x0040001L ;
fa = ( ( dwNoiseVal > > 2 ) & 0x7fff ) ;
fa = - fa ;
}
else
2009-02-13 09:57:15 +00:00
fa = ( dwNoiseVal > > 2 ) & 0x7fff ; */
GetNoiseValues ( fa ) ;
2009-02-06 19:52:59 +00:00
// mmm... depending on the noise freq we allow bigger/smaller changes to the previous val
fa = iOldNoise + ( ( fa - iOldNoise ) / ( ( 0x001f - ( GetCtrl ( ) - > noiseFreq ) ) + 1 ) ) ;
clamp16 ( fa ) ;
iOldNoise = fa ;
SB [ 29 ] = fa ; // -> store noise val in "current sample" slot
return fa ;
}
2009-05-17 21:57:24 +00:00
void VOICE_PROCESSED : : StoreInterpolationVal ( s32 fa )
2009-02-06 19:52:59 +00:00
{
if ( bFMod = = 2 ) // fmod freq channel
SB [ 29 ] = fa ;
else
{
if ( ! GetCtrl ( ) - > spuUnmute )
fa = 0 ; // muted?
else // else adjust
{
clamp16 ( fa ) ;
}
SB [ 28 ] = 0 ;
SB [ 29 ] = SB [ 30 ] ; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour'
SB [ 30 ] = SB [ 31 ] ;
SB [ 31 ] = fa ;
SB [ 32 ] = 1 ; // -> flag: calc new interolation
}
}
2009-05-17 21:57:24 +00:00
s32 VOICE_PROCESSED : : iGetInterpolationVal ( )
2009-02-06 19:52:59 +00:00
{
2009-05-17 21:57:24 +00:00
s32 fa ;
2009-02-06 19:52:59 +00:00
if ( bFMod = = 2 ) return SB [ 29 ] ;
if ( sinc < 0x10000L ) // -> upsampling?
InterpolateUp ( ) ; // --> interpolate up
else
InterpolateDown ( ) ; // --> else down
fa = SB [ 29 ] ;
return fa ;
}
void VOICE_PROCESSED : : Stop ( )
{
}