pcsx2/plugins/spu2ghz/decoder.cpp

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];
}
}
}