/*
 * Copyright 2007 Sun Microsystems, Inc.
 *
 * This file is part of jVoiceBridge.
 *
 * jVoiceBridge is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License version 2 as 
 * published by the Free Software Foundation and distributed hereunder 
 * to you.
 *
 * jVoiceBridge 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/>.
 *
 * Sun designates this particular file as subject to the "Classpath"
 * exception as provided by Sun in the License file that accompanied this 
 * code. 
 */

package com.sun.voip;

import org.xiph.speex.*;

public class SpeexDecoder {

    private org.xiph.speex.SpeexDecoder speexDecoder;

    private int sampleRate;
    private int channels;

    private int decodes;
    private long decodeTime;

    private boolean bigEndian = true;

    public SpeexDecoder(int sampleRate, int channels) throws SpeexException {
	this.sampleRate = sampleRate;
	this.channels = channels;

        speexDecoder = new org.xiph.speex.SpeexDecoder();

	int mode = 0;

        String s = " Narrow Band";

        if (sampleRate > 8000) {
            s = " Wide Band";
            mode++;         // wide band
        }

        if (sampleRate > 16000) {
            s = " Ultra-Wide Band";
            mode++;         // ultra wide band
        }

	if (Logger.logLevel >= Logger.LOG_INFO) {
            Logger.println("Initializing Speex Decoder using "
                + sampleRate + "/" + channels);
	}

        if (speexDecoder.init(mode, sampleRate, channels, true) == false) {
            throw new SpeexException(
		"Speex decoder initialization failed!");
        }

        try {
            speexDecoder.setBigEndian(true);
        } catch (Exception e) {
            bigEndian = false;
        }
    }

    /*
     * Decode speex data starting at offset for length bytes.
     * Return the length of the decoded data.
     */
    boolean debug = false;

    public int[] decodeToIntArray(byte[] data, int offset, int length) 
            throws SpeexException {

	byte[] byteData = decodeToByteArray(data, offset, length);

	return AudioConversion.bytesToInts(byteData);
    }

    public byte[] decodeToByteArray(byte[] data, int offset, int length) 
            throws SpeexException {

	if (Logger.logLevel >= Logger.LOG_MOREDETAIL) {
	    Util.dump("decode input:  offset " + offset 
		+ " length " + length, data, 0, offset + length);
 	}

        long start = CurrentTime.getTime();

	try {
            speexDecoder.processData(data, offset, length);
	} catch (java.io.StreamCorruptedException e) {
	    throw new SpeexException(e.getMessage());
	}

        int decodedLength = speexDecoder.getProcessedDataByteSize();

        if (decodedLength <= 0) {
            Logger.println("Speex decode data length is " + decodedLength);
            throw new SpeexException("Decoded length negative");
        }

	byte[] byteData = new byte[decodedLength];

        speexDecoder.getProcessedData(byteData, 0);

	if (bigEndian == false) {
	    /*
	     * The latest version of Speex only understands little endian
	     */
	    for (int i = 0; i < byteData.length; i += 2) {
	        byte b = byteData[i];

	        byteData[i] = byteData[i + 1];
	        byteData[i + 1] = b;
	    }
	}

        decodes++;
        decodeTime += (CurrentTime.getTime() - start);

	return byteData;
    }

    public int getDecodes() {
	return decodes;
    }

    public long getDecodeTime() {
	return decodeTime;
    }

    public void resetStatistics() {
	decodes = 0;
	decodeTime = 0;
    }

}

