/* * Copyright (C) 2014 James Lawrence. * * This file is part of LibLab. * * LibLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.sqrt.liblab.codec; import com.sqrt.liblab.io.DataSource; import com.sqrt.liblab.io.SeekableByteArrayOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class Vima { public static ByteArrayOutputStream decompress(DataSource source, int len) throws IOException { int numChannels = 1; byte[] sBytes = new byte[2]; short[] sWords = new short[2]; // Left step index hint sBytes[0] = source.get(); if ((sBytes[0] & 0x80) != 0) { // Now in stereo! sBytes[0] = (byte) ~sBytes[0]; numChannels = 2; } // Left PCM hint sWords[0] = source.getShort(); if (numChannels > 1) { sBytes[1] = source.get(); // Right step index hint sWords[1] = source.getShort(); // Right PCM hint } int numSamples = len / (numChannels * 2); SeekableByteArrayOutputStream ibaos = new SeekableByteArrayOutputStream(8192); int bits = source.getShort(); int bitPtr = 0; for (int channel = 0; channel < numChannels; channel++) { ibaos.seek(channel * 2); int stepIndexHint = sBytes[channel]; int pcm = sWords[channel]; for (int sample = 0; sample < numSamples; sample++) { int numBits = imcTable2[stepIndexHint]; bitPtr += numBits; int highBit = 1 << (numBits - 1); int lowBits = highBit - 1; int val = (bits >> (16 - bitPtr)) & (highBit | lowBits); if (bitPtr > 7) { // refresh... bits = ((bits & 0xff) << 8) | source.getUByte(); bitPtr -= 8; } if ((val & highBit) != 0) val ^= highBit; else highBit = 0; if (val == lowBits) { pcm = (short) ((bits << bitPtr) & 0xffffff00); bits = ((bits & 0xff) << 8) | source.getUByte(); pcm |= ((bits >>> (8 - bitPtr)) & 0xff); bits = ((bits & 0xff) << 8) | source.getUByte(); } else { int delta = predict_table[(val << (7 - numBits)) | (stepIndexHint << 6)]; if (val != 0) delta += (imcTable1[stepIndexHint] >>> (numBits - 1)); if (highBit != 0) delta = -delta; pcm += delta; if (pcm < Short.MIN_VALUE) pcm = Short.MIN_VALUE; else if (pcm > Short.MAX_VALUE) pcm = Short.MAX_VALUE; } ibaos.write((pcm >>> 8) & 0xff); ibaos.write(pcm & 0xff); ibaos.skip((numChannels-1)*2); // 1 channel? skip 0 bytes, 2 channels? skip 2 bytes... etc. stepIndexHint += offsets[numBits - 2][val]; if (stepIndexHint < 0) stepIndexHint = 0; else if (stepIndexHint > 88) stepIndexHint = 88; } } return ibaos; } private static final short[] imcTable1 = { 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 }; private static final byte[] imcTable2 = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; private static final byte[] imcOtherTable1 = { -1, 4, -1, 4 }; private static final byte[] imcOtherTable2 = { -1, -1, 2, 6, -1, -1, 2, 6 }; private static final byte[] imcOtherTable3 = { -1, -1, -1, -1, 1, 2, 4, 6, -1, -1, -1, -1, 1, 2, 4, 6 }; private static final byte[] imcOtherTable4 = { -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 2, 2, 4, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 2, 2, 4, 5, 6 }; private static final byte[] imcOtherTable5 = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 5, 5, 6, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 5, 5, 6, 6 }; private static final byte[] imcOtherTable6 = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6 }; private static final byte[][] offsets = { imcOtherTable1, imcOtherTable2, imcOtherTable3, imcOtherTable4, imcOtherTable5, imcOtherTable6 }; private static int[] predict_table = new int[5786]; static { int destTableStartPos, incer; for (destTableStartPos = 0, incer = 0; destTableStartPos < 64; destTableStartPos++, incer++) { int destTablePos, imcTable1Pos; for (imcTable1Pos = 0, destTablePos = destTableStartPos; imcTable1Pos < imcTable1.length; imcTable1Pos++, destTablePos += 64) { int put = 0, count, tableValue; for (count = 32, tableValue = imcTable1[imcTable1Pos]; count != 0; count >>>= 1, tableValue >>>= 1) { if ((incer & count) != 0) { put += tableValue; } } predict_table[destTablePos] = put; } } } }