/*
* Copyright © 2011 Rebecca G. Bettencourt / Kreative Software
* IMA4 Component Copyright © 2014 James Wallace
* <p>
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a>
* <p>
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
* <p>
* Alternatively, the contents of this file may be used under the terms
* of the GNU Lesser General Public License (the "LGPL License"), in which
* case the provisions of LGPL License are applicable instead of those
* above. If you wish to allow use of your version of this file only
* under the terms of the LGPL License and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the LGPL License. If you do not delete
* the provisions above, a recipient may use your version of this file
* under either the MPL or the LGPL License.
* @since KSFL 1.2
* @author Rebecca G. Bettencourt, Kreative Software; James Wallace.
*/
package com.kreative.rsrc.misc;
public class IMA4Decoder {
private static IMA4State ima4state;
private static class IMA4State
{
public int prevpred;
public int predindex;
public byte[] outp;
public byte[] inp;
public IMA4State(byte[] in, byte[] out) {
outp = out;
inp = in;
}
}
public static int indexTable[] = {
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};
public static int stepsizeTable[] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
public static int decode(byte indata[], int inOffset, byte outdata[], int outOffset, int len, IMA4State state, int stride)
{
int inputbuffer = 0;
boolean firstrun = false;
int valpred = state.prevpred;
int index = state.predindex;
int lastIndex = index;
for(; len > 0; len--)
{
int indexnibbles;
if(firstrun)
{
indexnibbles = inputbuffer >> 4 & 0xf;
} else
{
inputbuffer = state.inp[inOffset++];
indexnibbles = inputbuffer & 0xf;
}
firstrun = !firstrun;
index += indexTable[indexnibbles];
if(index < 0)
index = 0;
else
if(index > 88)
index = 88;
int sign = (indexnibbles & 8);
int step = stepsizeTable[lastIndex];
int delta = (indexnibbles & 7);
int diff = (step >> 3);
if((delta & 4) != 0) diff += step;
if((delta & 2) != 0) diff += step >> 1;
if((delta & 1) != 0) diff += step >> 2;
//flip the sign
if(sign != 0)
diff = -diff;
valpred += diff;
if(valpred > 32767)
valpred = 32767;
else
if(valpred < -32768)
valpred = -32768;
lastIndex = index;
state.outp[outOffset++] = (byte)(valpred >> 8);
state.outp[outOffset++] = (byte)valpred;
outOffset += stride;
}
state.prevpred = valpred;
state.predindex = index;
return outOffset;
}
private static byte[] decompressStream4(byte[] in, byte[] out, int channels)
{
ima4state=new IMA4State(in,out);
return decodeIMA4(in, out, in.length,out.length, channels);
}
static byte[] decodeIMA4(byte[] in, byte[] out, int inlen, int outlen,int channels)
{
switch (channels)
{
// 32 bytes IMA4 chunk size (with 2 for prediction)
case 1: //mono
return decodeIMA4mono(in,out,inlen,32);
case 2: //stereo
return decodeIMA4stereo(in,out,inlen,32);
default:
return null;
}
}
private static byte[] decodeIMA4mono(byte[] in, byte[] out, int inlen, int blockSize)
{
int inCount = 0;
int outCount = 0;
// IMA4 mono chunk format is 2 bytes header followed by 32 bytes encoded data
//we need to manipulate
inlen = (inlen / (blockSize+2) ) * (blockSize+2);
while (inCount<inlen) {
int pred = (in[inCount++] << 8);
pred |= (in[inCount++] & 0xff);
int index = pred & 0x7F;
if (index>88)
index=88;
//Gah, signed types!
ima4state.prevpred=pred & 0xFFFFFF80 ;
ima4state.predindex=index;
decode(in,inCount,out,outCount,blockSize<<1,ima4state,0);
inCount += blockSize;
outCount += blockSize<<2;
}
return ima4state.outp;
}
private static byte[] decodeIMA4stereo(byte[] in, byte[] out, int inlen, int blockSize)
{
int inCount = 0;
int outCount = 0;
// IMA4 stereo chunks are interleaved, so we bounce between channels
inlen = (inlen / 2 /(blockSize+2) ) * (blockSize+2)*2;
// IMA4 stereo chunk format is left IMA4 mono chunk followed by right IMA4 mono chunk
while (inCount<inlen)
{
//LEFT
int predL = (in[inCount++] << 8);
predL |= (in[inCount++] & 0xff);
int indexL = predL & 0x7F;
if (indexL>88)
indexL=88;
//Gah, signed types!
ima4state.prevpred=predL & 0xFFFFFF80 ;
ima4state.predindex=indexL;
decode(in,inCount,out,outCount,blockSize<<1,ima4state,2);
inCount += blockSize;
//RIGHT
int prevR = (in[inCount++] << 8);
prevR |= (in[inCount++] & 0xff);
int indexR = prevR & 0x7F;
if (indexR>88)
indexR=88;
//Gah, signed types!
ima4state.prevpred=prevR & 0xFFFFFF80 ;
ima4state.predindex=indexR;
decode(in, inCount, out, outCount+2, blockSize<<1, ima4state, 2);
//loop counters
inCount += blockSize;
outCount += blockSize<<3;
}
return ima4state.outp;
}
public static byte[] decompressIMA4(byte[] compressedData, int channels) {
byte[] decompressedData = new byte[compressedData.length*4];
byte[] ret = decompressStream4(compressedData, decompressedData, channels);
return ret;
}
}