/*
This file is part of jpcsp.
Jpcsp 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.
Jpcsp 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 Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.media.codec.atrac3plus;
import static jpcsp.media.codec.util.CodecUtils.writeOutput;
import jpcsp.media.codec.ICodec;
import jpcsp.media.codec.util.BitReader;
import jpcsp.media.codec.util.FFT;
import org.apache.log4j.Logger;
/*
* Based on the FFmpeg version from Maxim Poliakovski.
* All credits go to him.
*/
public class Atrac3plusDecoder implements ICodec {
public static Logger log = Logger.getLogger("atrac3plus");
public static final int AT3P_ERROR = -1;
public static final int CH_UNIT_MONO = 0; ///< unit containing one coded channel
public static final int CH_UNIT_STEREO = 1; ///< unit containing two jointly-coded channels
public static final int CH_UNIT_EXTENSION = 2; ///< unit containing extension information
public static final int CH_UNIT_TERMINATOR = 3; ///< unit sequence terminator
public static final int ATRAC3P_POWER_COMP_OFF = 15; ///< disable power compensation
public static final int ATRAC3P_SUBBANDS = 16; ///< number of PQF subbands
public static final int ATRAC3P_SUBBAND_SAMPLES = 128; ///< number of samples per subband
public static final int ATRAC3P_FRAME_SAMPLES = ATRAC3P_SUBBANDS * ATRAC3P_SUBBAND_SAMPLES;
public static final int ATRAC3P_PQF_FIR_LEN = 12; ///< length of the prototype FIR of the PQF
private Context ctx;
@Override
public int init(int bytesPerFrame, int channels, int outputChannels, int codingMode) {
ChannelUnit.init();
ctx = new Context();
ctx.outputChannels = outputChannels;
ctx.dsp = new Atrac3plusDsp();
for (int i = 0; i < ctx.numChannelBlocks; i++) {
ctx.channelUnits[i] = new ChannelUnit();
ctx.channelUnits[i].setDsp(ctx.dsp);
}
// initialize IPQF
ctx.ipqfDctCtx = new FFT();
ctx.ipqfDctCtx.mdctInit(5, true, 31.0 / 32768.9);
ctx.mdctCtx = new FFT();
ctx.dsp.initImdct(ctx.mdctCtx);
Atrac3plusDsp.initWaveSynth();
ctx.gaincCtx = new Atrac();
ctx.gaincCtx.initGainCompensation(6, 2);
return 0;
}
@Override
public int decode(int inputAddr, int inputLength, int outputAddr) {
int ret;
if (ctx == null) {
return AT3P_ERROR;
}
if (inputLength < 0) {
return AT3P_ERROR;
}
if (inputLength == 0) {
return 0;
}
ctx.br = new BitReader(inputAddr, inputLength);
if (ctx.br.readBool()) {
log.error(String.format("Invalid start bit"));
return AT3P_ERROR;
}
int chBlock = 0;
int channelsToProcess = 0;
while (ctx.br.getBitsLeft() >= 2) {
int chUnitId = ctx.br.read(2);
if (chUnitId == CH_UNIT_TERMINATOR) {
break;
}
if (chUnitId == CH_UNIT_EXTENSION) {
log.warn(String.format("Non implemented channel unit extension"));
return AT3P_ERROR;
}
if (chBlock >= ctx.channelUnits.length) {
log.error(String.format("Too many channel blocks"));
return AT3P_ERROR;
}
ctx.channelUnits[chBlock].setBitReader(ctx.br);
ctx.channelUnits[chBlock].ctx.unitType = chUnitId;
channelsToProcess = chUnitId + 1;
ctx.channelUnits[chBlock].setNumChannels(channelsToProcess);
ret = ctx.channelUnits[chBlock].decode();
if (ret < 0) {
return ret;
}
ctx.channelUnits[chBlock].decodeResidualSpectrum(ctx.samples);
ctx.channelUnits[chBlock].reconstructFrame(ctx);
writeOutput(ctx.outpBuf, outputAddr, ATRAC3P_FRAME_SAMPLES, channelsToProcess, ctx.outputChannels);
chBlock++;
}
if (log.isDebugEnabled()) {
log.debug(String.format("Bytes read 0x%X", ctx.br.getBytesRead()));
}
return ctx.br.getBytesRead();
}
@Override
public int getNumberOfSamples() {
return ATRAC3P_FRAME_SAMPLES;
}
}