/*
* Java port of ogg demultiplexer.
* Copyright (c) 2004 Jonathan Hueber.
*
* License conditions are the same as OggVorbis. See README.
* 1a39e335700bec46ae31a38e2156a898
*/
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
* by the XIPHOPHORUS Company http://www.xiph.org/ *
* *
********************************************************************/
package net.sourceforge.jffmpeg.codecs.audio.vorbis.mapping;
import net.sourceforge.jffmpeg.codecs.audio.vorbis.VorbisDecoder;
import net.sourceforge.jffmpeg.codecs.audio.vorbis.OggReader;
import javax.media.Buffer;
public class Mapping0 extends Mapping {
private int channels;
private int submaps;
private int coupling_steps;
private int[] coupling_mag = new int[ 257 ];
private int[] coupling_ang = new int[ 257 ];
private int[] chmuxlist = new int[ 6 ];
private int[] floorsubmap = new int[ 17 ];
private int[] residuesubmap = new int[ 17 ];
private float[][] pcm;
private int pcmend;
private static final int ilog( long v ) {
int ret=0;
if ( v > 0 ) v--;
while( v > 0 ){
ret++;
v >>= 1;
}
return(ret);
}
public void unpack( OggReader oggRead, int channels ) {
this.channels = channels;
if ( oggRead.getBits( 1 ) == 1 ) {
submaps = (int)oggRead.getBits(4) + 1;
} else {
submaps = 1;
}
if ( oggRead.getBits( 1 ) == 1 ) {
coupling_steps = (int)oggRead.getBits(8) + 1;
for ( int i = 0; i < coupling_steps; i++ ) {
coupling_mag[i] = (int)oggRead.getBits( ilog(channels) );
coupling_ang[i] = (int)oggRead.getBits( ilog(channels) );
}
}
if ( oggRead.getBits(2) > 0 ) throw new Error( "Reserved" );
if ( submaps> 1 ) {
for ( int i = 0; i < channels; i++ ) {
chmuxlist[i] = (int)oggRead.getBits( 4 );
}
}
for ( int i = 0; i < submaps; i++ ) {
oggRead.getBits(8);
floorsubmap[i] = (int)oggRead.getBits( 8 );
residuesubmap[i] = (int)oggRead.getBits( 8 );
}
pcm = new float[ channels ][ 4096 ]; //TODO moveme
pcmb = new float[ channels ][ 4096 ]; //TODO moveme
}
public void inverse( OggReader oggRead, VorbisDecoder vorbis ) {
pcmend = vorbis.getBlockSize( vorbis.getW() ? 1:0 );
long n = pcmend;
// System.out.println( "mapping0_inverse " + n );
float[][] pcmbundle = new float[ channels ][ (int)n ];
int[] zerobundle = new int[ channels ];
int[] nonzero = new int[ channels ];
Object[] floormemo = new Object[ channels ];
/* recover the spectral envelope; store it in the PCM vector for now */
for(int i = 0; i < channels; i++ ) {
int submap = chmuxlist[ i ];
floormemo[i]=vorbis.getFloor( floorsubmap[submap] ).inverse1(oggRead, vorbis);
if( floormemo[i] != null) {
nonzero[i]=1;
} else {
nonzero[i]=0;
}
// TODO clear channel pcm
for ( int j = 0; j < n; j++ ) {
pcm[i][j] = 0;
}
}
/* channel coupling can 'dirty' the nonzero listing */
for( int i = 0; i < coupling_steps; i++ ){
if( nonzero[coupling_mag[i]] != 0 ||
nonzero[coupling_ang[i]] != 0 ){
nonzero[coupling_mag[i]]=1;
nonzero[coupling_ang[i]]=1;
}
}
/* recover the residue into our working vectors */
for(int i = 0; i < submaps; i++) {
int ch_in_bundle = 0;
for(int j = 0; j < channels; j++) {
if(chmuxlist[j]==i) {
if( nonzero[j] != 0 ) {
zerobundle[ch_in_bundle]=1;
} else {
zerobundle[ch_in_bundle]=0;
}
pcmbundle[ ch_in_bundle++ ] = pcm[j];
}
}
vorbis.getResidue(residuesubmap[i]).inverse( oggRead,
pcmbundle, zerobundle, ch_in_bundle);
}
/* channel coupling */
for(int i = coupling_steps-1; i >= 0; i-- ){
float[] pcmM=pcm[ coupling_mag[i] ];
float[] pcmA=pcm[ coupling_ang[i] ];
for( int j = 0; j < n/2; j++ ) {
float mag=pcmM[j];
float ang=pcmA[j];
if(mag>0) {
if(ang>0){
pcmM[j]=mag;
pcmA[j]=mag-ang;
} else {
pcmA[j]=mag;
pcmM[j]=mag+ang;
}
} else {
if(ang>0){
pcmM[j]=mag;
pcmA[j]=mag+ang;
} else {
pcmA[j]=mag;
pcmM[j]=mag-ang;
}
}
}
}
/* compute and apply spectral envelope */
for( int i = 0; i < channels; i++ ) {
float[] pcmt = pcm[i];
int submap = chmuxlist[i];
vorbis.getFloor( floorsubmap[submap] ).inverse2( floormemo[i], pcmt, vorbis );
}
/* transform the PCM data; takes PCM vector, vb; modifies PCM vector */
/* only MDCT right now.... */
for ( int i = 0; i < channels; i++ ) {
float[] pcmt = pcm[i];
vorbis.getMdct().mdct_backward(pcmt,pcmt);
}
/* window the data */
for( int i = 0; i < channels; i++ ) {
float[] pcmt = pcm[i];
if ( nonzero[i] != 0 ) {
_vorbis_apply_window(pcmt, vorbis);
} else {
for( int j = 0; j < n; j++ ) {
pcmt[j] = 0.f;
}
}
{ /*
int q;
System.out.println( "after window" );
for ( q = 0; q < n; q++ ) {
System.out.print( ((int)(pcmt[q] * 10000000)) + " " );
}
System.out.println();
*/ }
}
//System.out.println( "mapping 0 done" );
}
// public abstract void forward();
private float[][] window = new float[ 2 ][];
public Mapping0( VorbisDecoder vorbis ) {
for ( int w = 0; w < 2; w++ ) {
int left = vorbis.getBlockSize(w) / 2;
window[w] = new float[ left ];
/* The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi) */
for( int i = 0; i < left; i++ ) {
double x = ((double)i + 0.5f) / ((double)left) * Math.PI / 2.;
x = Math.sin(x);
x *= x;
x *= Math.PI / 2.f;
x = Math.sin(x);
window[w][i] = (float)x;
}
}
}
private void _vorbis_apply_window( float[] d, VorbisDecoder vorbis ) {
int lW = vorbis.getW() ? vorbis.getlW() : 0;
int nW = vorbis.getW() ? vorbis.getnW() : 0;
int n = vorbis.getBlockSize( vorbis.getW() ? 1 : 0 );
int ln = vorbis.getBlockSize( lW );
int rn = vorbis.getBlockSize( nW );
int leftbegin=n/4-ln/4;
int leftend=leftbegin+ln/2;
int rightbegin=n/2+n/4-rn/4;
int rightend=rightbegin+rn/2;
int i,p;
for(i=0;i<leftbegin;i++) {
d[i] = 0.f;
}
for(p=0;i<leftend;i++,p++) {
d[i] *= window[ lW ][p];
}
for(i=rightbegin,p=rn/2-1;i<rightend;i++,p--) {
d[i] *= window[ nW ][p];
}
for( ; i < n; i++ ) {
d[i] =0.f;
}
}
private static boolean lastWindow;
private static boolean thisWindow = false;
private static int centerW = -1;
private static int pcm_returned = -1;
private static int pcm_current = -1;
private static float[][] pcmb;
public void vorbis_synthesis_blockin( VorbisDecoder vorbis ) {
// System.out.println( "VORBIS_SYNTHESIS_BLOCKIN" );
lastWindow = thisWindow;
thisWindow = vorbis.getW();
int n = vorbis.getBlockSize( thisWindow ? 1 : 0 )/ 2;
int n0 = vorbis.getBlockSize( 0 ) / 2;
int n1 = vorbis.getBlockSize( 1 ) / 2;
int thisCenter;
int prevCenter;
if ( centerW != 0 ) {
thisCenter = n1;
prevCenter = 0;
} else {
thisCenter = 0;
prevCenter = n1;
}
//System.out.println( "Wd " + (lastWindow?1:0) + " " + (thisWindow?1:0) );
//System.out.println( "B " + n + " " + n0 + " " + n1 + " " + thisCenter + " " + prevCenter );
/* v->pcm is now used like a two-stage double buffer. We don't want
to have to constantly shift *or* adjust memory usage. Don't
accept a new block until the old is shifted out */
/* overlap/add PCM */
for( int j = 0; j < channels; j++ ) {
{/*
int q;
System.out.println( "before overlap" );
for ( q = 0; q < n; q++ ) {
System.out.print( ((int)(pcm[j][q] * 10000000)) + " " );
}
System.out.println(); */
}
// System.out.println( "do overlap" );
/* the overlap/add section */
if( lastWindow ) {
if ( thisWindow ) {
/* large/large */
int pcmPointer = prevCenter;
int pcmPointer2 = 0;
for ( int i = 0; i < n1; i++ ) {
//System.out.print( " " + ((int)(pcm[j][i + pcmPointer2 ] *10000000 ) ) );
pcmb[j][i + pcmPointer] += pcm[j][i + pcmPointer2 ];
}
} else {
/* large/small */
int pcmPointer = prevCenter + n1 / 2 - n0 / 2;
int pcmPointer2 = 0;
for( int i = 0; i < n0; i++ ) {
//System.out.print( " " + ((int)(pcm[j][i + pcmPointer2 ] *10000000 ) ) );
pcmb[j][i + pcmPointer] += pcm[j][i + pcmPointer2 ];
}
}
} else {
if ( thisWindow ) {
/* small/large */
int pcmPointer = prevCenter;
int pcmPointer2 = n1 / 2 - n0 / 2;
int i;
for ( i = 0; i < n0; i++ ) {
//System.out.print( " " + ((int)(pcm[j][i + pcmPointer2 ] *10000000 ) ) );
pcmb[j][i + pcmPointer ] += pcm[j][i + pcmPointer2];
}
for ( ; i < n1 / 2 + n0 / 2; i++ ) {
//System.out.print( " " + ((int)(pcm[j][i + pcmPointer2 ] *10000000 ) ) );
pcmb[j][i + pcmPointer ] = pcm[j][i + pcmPointer2];
}
} else {
/* small/small */
int pcmPointer = prevCenter;
int pcmPointer2 = 0;
for( int i = 0; i < n0; i++ ) {
//System.out.print( " " + ((int)(pcm[j][i + pcmPointer2 ] *10000000 ) ) );
pcmb[j][i + pcmPointer ] += pcm[j][i + pcmPointer2];
}
}
}
// System.out.println();
/* the copy section */
int pcmPointer = thisCenter;
int pcmPointer2 = n;
for( int i = 0; i < n; i++ ) {
pcmb[j][i + pcmPointer]=pcm[j][i + pcmPointer2];
}
}
if( centerW != 0 ) {
centerW = 0;
} else {
centerW = n1;
}
/* deal with initial packet state; we do this using the explicit
pcm_returned==-1 flag otherwise we're sensitive to first block
being short or long */
if ( pcm_returned ==- 1 ) {
pcm_returned = thisCenter;
pcm_current = thisCenter;
} else {
pcm_returned=prevCenter;
pcm_current= prevCenter
+ vorbis.getBlockSize(lastWindow ? 1 : 0)/4
+ vorbis.getBlockSize(thisWindow ? 1 : 0)/4;
}
}
public void soundOutput( Buffer output ) {
int size = (pcm_current - pcm_returned);
byte[] out = (byte[])output.getData();
if ( out == null || out.length < output.getLength() + size * 4 ) {
byte[] t = new byte[ output.getLength() + size * 4 ];
if ( out != null ) System.arraycopy( t, 0, out, 0, output.getLength() );
output.setData( t );
out = t;
}
// System.out.println( " " + pcm_current + " " + size );
int offset = output.getLength();
// System.out.println( "Channel" );
for ( int i = 0; i < size; i++ ) {
int s = scale( pcmb[ 0 ][ i + pcm_returned ] );
out[ offset + i * 4 + 1 ] = (byte)((s & 0xff00) >> 8);
out[ offset + i * 4 + 0 ] = (byte)(s & 0xff);
// System.out.print( " " + s );
s = scale( pcmb[ 1 ][ i + pcm_returned ] );
out[ offset + i * 4 + 3 ] = (byte)((s & 0xff00) >> 8);
out[ offset + i * 4 + 2 ] = (byte)(s & 0xff);
}
// System.out.println();
output.setLength( offset + (size) * 4 );
}
private static final int scale( double in ) {
int val = (int)(in * 32767.f);
if(val>32767){
val=32767;
}
if(val<-32768){
val=-32768;
}
return val;
}
}