/*
* $ProjectName$
* $ProjectRevision$
* -----------------------------------------------------------
* $Id: AudioPacket.java,v 1.2 2003/03/16 01:11:12 jarnbjo Exp $
* -----------------------------------------------------------
*
* $Author: jarnbjo $
*
* Description:
*
* Copyright 2002-2003 Tor-Einar Jarnbjo
* -----------------------------------------------------------
*
* Change History
* -----------------------------------------------------------
* $Log: AudioPacket.java,v $
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import java.util.*;
import de.jarnbjo.util.io.*;
class AudioPacket {
private int modeNumber;
private Mode mode;
private Mapping mapping;
private int n; // block size
private boolean blockFlag, previousWindowFlag, nextWindowFlag;
private int windowCenter, leftWindowStart, leftWindowEnd, leftN, rightWindowStart, rightWindowEnd, rightN;
private float[] window;
private float[][] pcm;
private int[][] pcmInt;
private Floor[] channelFloors;
private boolean[] noResidues;
private final static float[][] windows=new float[8][];
protected AudioPacket(final VorbisStream vorbis, final BitInputStream source) throws VorbisFormatException, IOException {
final SetupHeader sHeader=vorbis.getSetupHeader();
final IdentificationHeader iHeader=vorbis.getIdentificationHeader();
final Mode[] modes=sHeader.getModes();
final Mapping[] mappings=sHeader.getMappings();
final Residue[] residues=sHeader.getResidues();
final int channels=iHeader.getChannels();
if(source.getInt(1)!=0) {
throw new VorbisFormatException("Packet type mismatch when trying to create an audio packet.");
}
modeNumber=source.getInt(Util.ilog(modes.length-1));
try {
mode=modes[modeNumber];
}
catch(ArrayIndexOutOfBoundsException e) {
throw new VorbisFormatException("Reference to invalid mode in audio packet.");
}
mapping=mappings[mode.getMapping()];
final int[] magnitudes=mapping.getMagnitudes();
final int[] angles=mapping.getAngles();
blockFlag=mode.getBlockFlag();
final int blockSize0=iHeader.getBlockSize0();
final int blockSize1=iHeader.getBlockSize1();
n=blockFlag?blockSize1:blockSize0;
if(blockFlag) {
previousWindowFlag=source.getBit();
nextWindowFlag=source.getBit();
}
windowCenter=n/2;
if(blockFlag && !previousWindowFlag) {
leftWindowStart=n/4-blockSize0/4;
leftWindowEnd=n/4+blockSize0/4;
leftN=blockSize0/2;
}
else {
leftWindowStart=0;
leftWindowEnd=n/2;
leftN=windowCenter;
}
if(blockFlag && !nextWindowFlag) {
rightWindowStart=n*3/4-blockSize0/4;
rightWindowEnd=n*3/4+blockSize0/4;
rightN=blockSize0/2;
}
else {
rightWindowStart=windowCenter;
rightWindowEnd=n;
rightN=n/2;
}
window=getComputedWindow();//new double[n];
channelFloors=new Floor[channels];
noResidues=new boolean[channels];
pcm=new float[channels][n];
pcmInt=new int[channels][n];
boolean allFloorsEmpty=true;
for(int i=0; i<channels; i++) {
int submapNumber=mapping.getMux()[i];
int floorNumber=mapping.getSubmapFloors()[submapNumber];
Floor decodedFloor=sHeader.getFloors()[floorNumber].decodeFloor(vorbis, source);
channelFloors[i]=decodedFloor;
noResidues[i]=decodedFloor==null;
if(decodedFloor!=null) {
allFloorsEmpty=false;
}
}
if(allFloorsEmpty) {
return;
}
for(int i=0; i<magnitudes.length; i++) {
if(!noResidues[magnitudes[i]] ||
!noResidues[angles[i]]) {
noResidues[magnitudes[i]]=false;
noResidues[angles[i]]=false;
}
}
Residue[] decodedResidues=new Residue[mapping.getSubmaps()];
for(int i=0; i<mapping.getSubmaps(); i++) {
int ch=0;
boolean[] doNotDecodeFlags=new boolean[channels];
for(int j=0; j<channels; j++) {
if(mapping.getMux()[j]==i) {
doNotDecodeFlags[ch++]=noResidues[j];
}
}
int residueNumber=mapping.getSubmapResidues()[i];
Residue residue=residues[residueNumber];
residue.decodeResidue(vorbis, source, mode, ch, doNotDecodeFlags, pcm);
}
for(int i=mapping.getCouplingSteps()-1; i>=0; i--) {
double newA=0, newM=0;
final float[] magnitudeVector=pcm[magnitudes[i]];
final float[] angleVector=pcm[angles[i]];
for(int j=0; j<magnitudeVector.length; j++) {
float a=angleVector[j];
float m=magnitudeVector[j];
if(a>0) {
//magnitudeVector[j]=m;
angleVector[j]=m>0?m-a:m+a;
}
else {
magnitudeVector[j]=m>0?m+a:m-a;
angleVector[j]=m;
}
}
}
for(int i=0; i<channels; i++) {
if(channelFloors[i]!=null) {
channelFloors[i].computeFloor(pcm[i]);
}
}
// perform an inverse mdct to all channels
for(int i=0; i<channels; i++) {
MdctFloat mdct=blockFlag?iHeader.getMdct1():iHeader.getMdct0();
mdct.imdct(pcm[i], window, pcmInt[i]);
}
}
private float[] getComputedWindow() {
int ix=(blockFlag?4:0)+(previousWindowFlag?2:0)+(nextWindowFlag?1:0);
float[] w=windows[ix];
if(w==null) {
w=new float[n];
for(int i=0;i<leftN;i++){
float x=(float)((i+.5)/leftN*Math.PI/2.);
x=(float)Math.sin(x);
x*=x;
x*=(float)Math.PI/2.;
x=(float)Math.sin(x);
w[i+leftWindowStart]=x;
}
for(int i=leftWindowEnd; i<rightWindowStart; w[i++]=1.0f);
for(int i=0;i<rightN;i++){
float x=(float)((rightN-i-.5)/rightN*Math.PI/2.);
x=(float)Math.sin(x);
x*=x;
x*=(float)Math.PI/2.;
x=(float)Math.sin(x);
w[i+rightWindowStart]=x;
}
windows[ix]=w;
}
return w;
}
protected int getNumberOfSamples() {
return rightWindowStart-leftWindowStart;
}
protected int getPcm(final AudioPacket previousPacket, final int[][] buffer) {
int channels=pcm.length;
int val;
// copy left window flank and mix with right window flank from
// the previous audio packet
for(int i=0; i<channels; i++) {
int j1=0, j2=previousPacket.rightWindowStart;
final int[] ppcm=previousPacket.pcmInt[i];
final int[] tpcm=pcmInt[i];
final int[] target=buffer[i];
for(int j=leftWindowStart; j<leftWindowEnd; j++) {
val=ppcm[j2++]+tpcm[j];
if(val>32767) val=32767;
if(val<-32768) val=-32768;
target[j1++]=val;
}
}
// use System.arraycopy to copy the middle part (if any)
// of the window
if(leftWindowEnd+1<rightWindowStart) {
for(int i=0; i<channels; i++) {
System.arraycopy(pcmInt[i], leftWindowEnd, buffer[i], leftWindowEnd-leftWindowStart, rightWindowStart-leftWindowEnd);
}
}
return rightWindowStart-leftWindowStart;
}
protected void getPcm(final AudioPacket previousPacket, final byte[] buffer) {
int channels=pcm.length;
int val;
// copy left window flank and mix with right window flank from
// the previous audio packet
for(int i=0; i<channels; i++) {
int ix=0, j2=previousPacket.rightWindowStart;
final int[] ppcm=previousPacket.pcmInt[i];
final int[] tpcm=pcmInt[i];
for(int j=leftWindowStart; j<leftWindowEnd; j++) {
val=ppcm[j2++]+tpcm[j];
if(val>32767) val=32767;
if(val<-32768) val=-32768;
buffer[ix+(i*2)+1]=(byte)(val&0xff);
buffer[ix+(i*2)]=(byte)((val>>8)&0xff);
ix+=channels*2;
}
ix=(leftWindowEnd-leftWindowStart)*channels*2;
for(int j=leftWindowEnd; j<rightWindowStart; j++) {
val=tpcm[j];
if(val>32767) val=32767;
if(val<-32768) val=-32768;
buffer[ix+(i*2)+1]=(byte)(val&0xff);
buffer[ix+(i*2)]=(byte)((val>>8)&0xff);
ix+=channels*2;
}
}
}
protected float[] getWindow() {
return window;
}
protected int getLeftWindowStart() {
return leftWindowStart;
}
protected int getLeftWindowEnd() {
return leftWindowEnd;
}
protected int getRightWindowStart() {
return rightWindowStart;
}
protected int getRightWindowEnd() {
return rightWindowEnd;
}
public int[][] getPcm() {
return pcmInt;
}
public float[][] getFreqencyDomain() {
return pcm;
}
}