/*
 * 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 SpeexEncoder {

    private org.xiph.speex.SpeexEncoder speexEncoder;

    private int sampleRate;
    private int channels;

    private int pcmPacketSize;

    private int encodes;
    private int bytesEncoded = 0;
    private long encodeTime;

    private boolean bigEndian = true;

    public SpeexEncoder(int sampleRate, int channels) 
	    throws SpeexException {

	this.sampleRate = sampleRate;
	this.channels = channels;

        if (sampleRate > 32000) {
            throw new SpeexException(
		"Speex cannot be used with sample rate " + sampleRate); 
        }

	speexEncoder = new org.xiph.speex.SpeexEncoder();

	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 encoder using "
                + sampleRate + "/" + channels + " " + s);
	}

        if (speexEncoder.init(mode, 0, sampleRate, channels) == false) {
            throw new SpeexException(
		"Speex encoder initialization failed!");
	}

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

        speexEncoder.getEncoder().setVbr(true);

        pcmPacketSize = 2 * channels * speexEncoder.getFrameSize();;

	if (Logger.logLevel >= Logger.LOG_INFO) {
            Logger.println("speex frame size "
                + speexEncoder.getFrameSize()
                + " pcmPacketSize " + pcmPacketSize);
	}
    }

    public void setQuality(int quality) {
	speexEncoder.getEncoder().setQuality(quality);
    }

    public void setComplexity(int complexity) {
	// XXX setting complexity to 0 breaks it horribly!
	speexEncoder.getEncoder().setComplexity(complexity);
    }

    public int getPcmPacketSize() {
	return pcmPacketSize;
    }

    /*
     * processes data starting at offset for length bytes.
     * Return the length of the encoded data.
     */
    public int encode(int[] inData, byte[] outData, int outOffset) 
	    throws SpeexException {

	return encode(AudioConversion.intsToBytes(inData), outData, 
	    outOffset);
    }

    public int encode(byte[] inData, byte[] outData, int outOffset)
	    throws SpeexException {

        if (Logger.logLevel == -59) {
	    Logger.logLevel = 3;

	    Util.dump("encode input:  length " 
		+ inData.length, inData, 0, 16);
	}

	long start = CurrentTime.getTime();

	if (bigEndian == false) {
	    /*
	     * The latest version of JSpeex only understands little endian
	     */
	    byte[] data = new byte[inData.length];

            for (int i = 0; i < data.length; i += 2) {
	        data[i] = inData[i + 1];
	        data[i + 1] = inData[i];
	    }

	    inData = data;
	}

	try {
            speexEncoder.processData(inData, 0, inData.length);
	} catch (Exception e) {
	    Logger.println("inData.length " + inData.length
		+ " outData.length " + outData.length
		+ " outOffset " + outOffset);

	    e.printStackTrace();
	    throw new SpeexException("SpeexEncode:  " + e.getMessage());
	}

	int encSize = speexEncoder.getProcessedDataByteSize();

        encodes++;
        encodeTime += (CurrentTime.getTime() - start);
        bytesEncoded += inData.length;

	speexEncoder.getProcessedData(outData, outOffset);

        if (Logger.logLevel >= Logger.LOG_MOREDETAIL) {
	    Util.dump("encodeData output: " + encSize, outData, 0, encSize);
	}

	return encSize;
    }

    public int getEncodes() {
	return encodes;
    }

    public int getBytesEncoded() {
	return bytesEncoded;
    }

    public long getEncodeTime() {
	return encodeTime;
    }

    public void resetStatistics() {
	encodes = 0;
	bytesEncoded = 0;
	encodeTime = 0;
    }

}
