mirror of https://github.com/PCSX2/pcsx2.git
386 lines
8.1 KiB
C++
386 lines
8.1 KiB
C++
//GiGaHeRz's SPU2 Driver
|
|
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
|
//
|
|
//This library is free software; you can redistribute it and/or
|
|
//modify it under the terms of the GNU Lesser General Public
|
|
//License as published by the Free Software Foundation; either
|
|
//version 2.1 of the License, or (at your option) any later version.
|
|
//
|
|
//This library 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
|
|
//Lesser General Public License for more details.
|
|
//
|
|
//You should have received a copy of the GNU Lesser General Public
|
|
//License along with this library; if not, write to the Free Software
|
|
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
#include "spu2.h"
|
|
|
|
extern "C" {
|
|
#include "liba52/inttypes.h"
|
|
#include "liba52/a52.h"
|
|
#include "liba52/mm_accel.h"
|
|
}
|
|
|
|
extern u32 spdif_read_data(u8 *buff, u32 max_data);
|
|
|
|
#define DATA_SIZE 0x100000
|
|
|
|
u32 use51=0;
|
|
|
|
u8 databuffer[DATA_SIZE];
|
|
u32 data_in_buffer;
|
|
|
|
s32 output_buffer[0x10000][6];
|
|
s32 output_write_cursor=0;
|
|
s32 output_read_cursor=0;
|
|
s32 output_buffer_data=0;
|
|
|
|
int flags,srate,bitrate;
|
|
|
|
#define CHANNEL_CENTER 1
|
|
#define CHANNEL_STEREO 2
|
|
#define CHANNEL_SURROUND 4
|
|
#define CHANNEL_LFE 8
|
|
|
|
int sample_flags = 0;
|
|
|
|
u32 frame_size;
|
|
|
|
a52_state_t* ac3dec;
|
|
sample_t *decode_buffer = NULL;
|
|
|
|
s32 data_rate=4;
|
|
|
|
int state=0;
|
|
|
|
FILE *fSpdifDump;
|
|
|
|
extern u32 core;
|
|
void __fastcall ReadInput(V_Core& thiscore, s32& PDataL,s32& PDataR);
|
|
|
|
union spdif_frame { // total size: 32bits
|
|
struct {
|
|
u32 preamble:4; //4
|
|
u32 databits:24; //28
|
|
u32 valid:1; //29
|
|
u32 subcode:1; //30
|
|
u32 chanstat:1; //31
|
|
u32 parity:1; //32 // parity not including preamble
|
|
} bits;
|
|
u32 whole;
|
|
};
|
|
|
|
union spdif_block {
|
|
spdif_frame frames[192];
|
|
u8 bytes [192*4];
|
|
};
|
|
|
|
/*
|
|
spdif_block bbuffer[2];
|
|
u8 *bbuff = bbuffer[0].bytes;
|
|
u32 bbuff_bytes = 0;
|
|
*/
|
|
|
|
bool check_frame(spdif_frame f)
|
|
{
|
|
u32 w = f.whole>>4;
|
|
u32 t = 0;
|
|
|
|
for(int i=0;i>28;i++)
|
|
{
|
|
t=t^(w&1);
|
|
w>>=1;
|
|
}
|
|
|
|
return (t==0)&&(f.bits.valid);
|
|
}
|
|
|
|
void spdif_Write(s32 data)
|
|
{
|
|
spdif_frame f;
|
|
|
|
f.whole=data;
|
|
|
|
if(check_frame(f))
|
|
{
|
|
int dec = f.bits.databits;
|
|
databuffer[data_in_buffer++]=(dec )&0xFF;
|
|
databuffer[data_in_buffer++]=(dec>> 8)&0xFF;
|
|
databuffer[data_in_buffer++]=(dec>>16)&0xFF;
|
|
}
|
|
}
|
|
|
|
void spdif_remove_data(unsigned int bytes)
|
|
{
|
|
if(bytes<data_in_buffer)
|
|
{
|
|
memcpy(databuffer,databuffer+bytes,data_in_buffer-bytes);
|
|
}
|
|
data_in_buffer-=bytes;
|
|
}
|
|
|
|
s32 stoi(sample_t n) //input: [-1..1]
|
|
{
|
|
s32 sign=(n<0) + (n>0)*-1; //1 if positive, -1 if negative, 0 otherwise
|
|
n=abs(n)+1; //make it [1..2]
|
|
s32 k=*(s32*)&n;
|
|
k=k&0x7FFFFF;
|
|
return k*sign;
|
|
}
|
|
|
|
void spdif_update()
|
|
{
|
|
s32 Data,Zero;
|
|
|
|
core=0;
|
|
V_Core& thiscore( Cores[core] );
|
|
for(int i=0;i<data_rate;i++)
|
|
{
|
|
ReadInput(thiscore, Data,Zero);
|
|
|
|
if(fSpdifDump)
|
|
{
|
|
fwrite(&Data,4,1,fSpdifDump);
|
|
fwrite(&Zero,4,1,fSpdifDump);
|
|
}
|
|
|
|
if(ac3dec)
|
|
spdif_Write(Data);
|
|
}
|
|
|
|
if(!ac3dec) return;
|
|
|
|
if(state==0)
|
|
{
|
|
if(data_in_buffer<7) return;
|
|
|
|
if(databuffer[0]!=0)
|
|
{
|
|
flags=0;
|
|
}
|
|
frame_size = a52_syncinfo (databuffer, &flags, &srate, &bitrate);
|
|
if(frame_size==0)
|
|
{
|
|
spdif_remove_data(1);
|
|
}
|
|
else
|
|
{
|
|
state=1;
|
|
}
|
|
}
|
|
if(state==1)
|
|
{
|
|
if(data_in_buffer<frame_size) return;
|
|
|
|
flags = A52_ADJUST_LEVEL;
|
|
|
|
if(use51) flags|=A52_3F2R|A52_LFE;
|
|
else flags|=A52_STEREO;
|
|
|
|
sample_t level=1;
|
|
a52_frame(ac3dec,databuffer,&flags,&level,0);
|
|
|
|
//decode
|
|
for(int i=0;i<6;i++)
|
|
{
|
|
a52_block(ac3dec);
|
|
|
|
// processing
|
|
for(int j=0;j<256;j++)
|
|
{
|
|
s32* samples = output_buffer[output_write_cursor];
|
|
sample_flags=0;
|
|
|
|
int output_cursor=j;
|
|
|
|
int n=0;
|
|
if(flags&A52_LFE)
|
|
{
|
|
sample_flags|=CHANNEL_LFE;
|
|
|
|
samples[3] = stoi(decode_buffer[(0<<8)+output_cursor]); //lfe
|
|
n=1;
|
|
}
|
|
|
|
switch(flags&(~A52_LFE))
|
|
{
|
|
case A52_STEREO:
|
|
sample_flags |= CHANNEL_STEREO;
|
|
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
|
samples[1] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //r
|
|
samples[2] = 0; //c
|
|
samples[4] = 0; //sl
|
|
samples[5] = 0; //sr
|
|
break;
|
|
case A52_2F1R:
|
|
sample_flags |= CHANNEL_STEREO|CHANNEL_SURROUND;
|
|
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
|
samples[1] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //r
|
|
samples[2] = 0; //c
|
|
samples[4] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sl
|
|
samples[5] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sr
|
|
break;
|
|
case A52_2F2R:
|
|
sample_flags |= CHANNEL_STEREO|CHANNEL_SURROUND;
|
|
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
|
samples[1] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //r
|
|
samples[2] = 0; //c
|
|
samples[4] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sl
|
|
samples[5] = stoi(decode_buffer[((n+3)<<8)+output_cursor]); //sr
|
|
break;
|
|
case A52_3F:
|
|
sample_flags |= CHANNEL_STEREO|CHANNEL_CENTER;
|
|
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
|
samples[1] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //r
|
|
samples[2] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //c
|
|
samples[4] = 0; //sl
|
|
samples[5] = 0; //sr
|
|
break;
|
|
case A52_3F1R:
|
|
sample_flags |= CHANNEL_STEREO|CHANNEL_SURROUND|CHANNEL_CENTER;
|
|
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
|
samples[1] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //r
|
|
samples[2] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //c
|
|
samples[4] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sl
|
|
samples[5] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sr
|
|
break;
|
|
case A52_3F2R:
|
|
sample_flags |= CHANNEL_STEREO|CHANNEL_SURROUND|CHANNEL_CENTER;
|
|
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
|
samples[1] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //r
|
|
samples[2] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //c
|
|
samples[4] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sl
|
|
samples[5] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sr
|
|
break;
|
|
default:
|
|
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
|
samples[1] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //r
|
|
samples[2] = 0; //c
|
|
samples[4] = 0; //sl
|
|
samples[5] = 0; //sr
|
|
break;
|
|
}
|
|
|
|
output_write_cursor=(output_write_cursor+1)&0xFFFF;
|
|
output_buffer_data++;
|
|
}
|
|
}
|
|
spdif_remove_data(frame_size);
|
|
}
|
|
}
|
|
|
|
u32 spdif_init()
|
|
{
|
|
data_rate=1; // words/tick
|
|
|
|
ac3dec = a52_init(0);
|
|
if(!ac3dec) return 1;
|
|
|
|
decode_buffer = a52_samples(ac3dec);
|
|
a52_dynrng (ac3dec, NULL, NULL);
|
|
data_in_buffer=0;
|
|
|
|
fSpdifDump = fopen("logs/spdif.dat","wb");
|
|
return 0;
|
|
}
|
|
|
|
void spdif_set51(u32 is_5_1_out)
|
|
{
|
|
use51 = is_5_1_out;
|
|
}
|
|
|
|
void spdif_shutdown()
|
|
{
|
|
if(ac3dec) a52_free(ac3dec);
|
|
ac3dec=NULL;
|
|
if(fSpdifDump)
|
|
fclose(fSpdifDump);
|
|
}
|
|
|
|
int spdif_decode_one(s32 *channels)
|
|
{
|
|
channels[0]=0;
|
|
channels[1]=0;
|
|
channels[2]=0;
|
|
channels[3]=0;
|
|
channels[4]=0;
|
|
channels[5]=0;
|
|
|
|
if(output_buffer_data==0) return 0;
|
|
|
|
memcpy(channels,output_buffer[output_read_cursor],4*6);
|
|
output_read_cursor=(output_read_cursor+1)&0xFFFF;
|
|
output_buffer_data--;
|
|
|
|
return sample_flags;
|
|
}
|
|
|
|
void spdif_get_samples(s32*samples)
|
|
{
|
|
s32 channels[6];
|
|
s32 flags = spdif_decode_one(channels);
|
|
|
|
if(use51)
|
|
{
|
|
samples[0]=0;
|
|
samples[1]=0;
|
|
samples[2]=0;
|
|
samples[3]=0;
|
|
samples[4]=0;
|
|
samples[5]=0;
|
|
|
|
if(flags&CHANNEL_STEREO)
|
|
{
|
|
samples[0]=channels[0];
|
|
samples[1]=channels[1];
|
|
}
|
|
|
|
if(flags&CHANNEL_CENTER)
|
|
{
|
|
samples[2]=channels[2];
|
|
}
|
|
|
|
if(flags&CHANNEL_LFE)
|
|
{
|
|
samples[3]=channels[3];
|
|
}
|
|
|
|
if(flags&CHANNEL_SURROUND)
|
|
{
|
|
samples[4]=channels[4];
|
|
samples[5]=channels[5];
|
|
}
|
|
}
|
|
else // downmix to stereo (no DPL2 encoding... yet ;)
|
|
{
|
|
samples[0]=0;
|
|
samples[1]=0;
|
|
|
|
if(flags&CHANNEL_STEREO)
|
|
{
|
|
samples[0]=channels[0];
|
|
samples[1]=channels[1];
|
|
}
|
|
|
|
if(flags&CHANNEL_CENTER)
|
|
{
|
|
samples[0]+=channels[2];
|
|
samples[1]+=channels[2];
|
|
}
|
|
|
|
if(flags&CHANNEL_LFE)
|
|
{
|
|
samples[0]+=channels[3];
|
|
samples[1]+=channels[3];
|
|
}
|
|
|
|
if(flags&CHANNEL_SURROUND)
|
|
{
|
|
samples[0]+=channels[4];
|
|
samples[1]+=channels[5];
|
|
}
|
|
}
|
|
}
|