package sound;

import ip.gui.frames.OpenFrame;
import sun.audio.AudioData;
import sun.audio.AudioDataStream;
import sun.audio.AudioPlayer;
import sun.audio.AudioStream;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;

import futils.Futil;

public class UlawCodec {

  private byte ulawData[];
  private double doubleArray[];


  private AudioDataStream audioDataStream = null;

//private  AudioStream audioStream = null;



  private final int samplingRate = 8000;



  // exp_lut - a lookup table for quick natural logarithm

  // transformation

  private final static int

      exp_lut[] = {0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,

                   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, 5, 5,

                   5, 5, 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, 6,

                   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,

                   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,

                   6, 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, 7,

                   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,

                   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,

                   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,

                   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,

                   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,

                   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,

                   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};





// ---------------------------------------------------------------

// Perform construction with given UlawData[];

// ---------------------------------------------------------------



  public UlawCodec(byte ulawArrayOfByte[]) {

    ulawData = ulawArrayOfByte;

  }





// ---------------------------------------------------------------

// Perform construction by opening a file

// name, then calling UlawCodec();

// public UlawCodec();

// ---------------------------------------------------------------

  public void readAUFile(String fileName) {
    // force recomputation
    // of the doubleArray, as it is invalid
    doubleArray = null;
    try {

      AudioStream audioStream =
          new AudioStream(new
              FileInputStream(fileName));

      // AudioStream constructor expects data stream from AU file as input

      ulawData = new byte[audioStream.getLength()];

      audioStream.read(
          ulawData, 0, audioStream.getLength());

    } catch (Exception e) {
      e.printStackTrace();
    }

  }

  public UlawCodec() {
    String fileName =
        Futil.getReadFileName(
            "select an au file");
    readAUFile(fileName);
  }

  public UlawCodec(String _fn) {
    String fileName = _fn;
    readAUFile(fileName);
  }

  public UlawCodec(URL _url) {
    try {
      readAU(_url);
    } catch (Exception e) {
    }
    ;
  }




// ---------------------------------------------------------------

//

// public UlawCodec(short linearData[]);

//

// This constructor can be used to produce u-law encoded ip.audio

// samples from an linerly encoded array of 16 bit signed integers

//

// ---------------------------------------------------------------

  public UlawCodec(short linearArrayOfShort[]) {

    ulawData = new byte[linearArrayOfShort.length];

    int max = 0;

    int sample, sign, exponent, mantissa;

    // now we need to find the sample of maximal amplitude

    // then we can scale the data in such a way that the biggest

    // sample will be 0x7FFF (max signed short)

    for (int i = 0; i < linearArrayOfShort.length; i++)

      if (Math.abs(linearArrayOfShort[i]) > max)

        max = Math.abs(linearArrayOfShort[i]);


    float factor = (float) 0x7FFF / max;

    for (int i = 0; i < linearArrayOfShort.length; i++) {

      sample = (int) (linearArrayOfShort[i] * factor);

      // lets calculate sign of the sample and its absolute value

      sign = (sample >> 8) & 0x80;

      if (sign != 0) sample = -sample;

      // adding bias

      sample = sample + 0x84;

      // now the tricky part: quick logarithmic transform, so that smaller samples

      // will be larger and larger samples smaller

      exponent = exp_lut[(sample >> 7) & 0xFF];

      mantissa = (sample >> (exponent + 3)) & 0x0F;

      // lets pack it into one byte

      ulawData[i] = (byte) ~(sign | (exponent << 4) | mantissa);

    }

  }



// ---------------------------------------------------------------

//

// public UlawCodec(double linearData[]);

//

// This constructor can be used to produce u-law encoded ip.audio

// samples from an linerly encoded array of 64 bit signed signed

// floating point numbers ranged from -1 to 1

//

// ---------------------------------------------------------------

  public UlawCodec(double linearArrayOfDouble[]) {

    int sample, sign, exponent, mantissa;

    ulawData = new byte[linearArrayOfDouble.length];

    // this metod is quite similar to UlawCodec(short linearData[])

    // with exception that no scaling is done, the input is assumed

    // be in the appropriate range. What if it is not? ... crush and burn

    for (int i = 0; i < linearArrayOfDouble.length; i++) {

      sample = (int) (linearArrayOfDouble[i] * 0x7FFF);

      sign = (sample >> 8) & 0x80;

      if (sign != 0) sample = -sample;

      sample = sample + 0x84;

      exponent = exp_lut[(sample >> 7) & 0xFF];

      mantissa = (sample >> (exponent + 3)) & 0x0F;

      ulawData[i] = (byte) ~(sign | (exponent << 4) | mantissa);

    }

  }

  public void readAU(URL url)
      throws IOException {
    InputStream is =
        getInputStream(url);
    readAU(is);
  }

  public InputStream getInputStream(URL url) {
    InputStream is = null;
    try {
      url.openConnection();
      is = url.openStream();
    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return is;
  }

  public void readAU(InputStream is)
      throws IOException {
    AudioStream audioStream =
        new AudioStream(is);
    ulawData = new byte[audioStream.getLength()];

    audioStream.read(
        ulawData, 0, audioStream.getLength());
  }


  public void readAUFile() {
    String fileName =
        Futil.getReadFileName("Select an au file");
    readAUFile(fileName);
  }


  public void writeAUFile() {

    String fileName =
        Futil.getReadFileName("select an au file");

    writeAUFile(fileName);

  }



// ---------------------------------------------------------------

//

// public void writeAUFile(String name);

//

// This method saves the ip.audio data stored in this object into

// an AU file.

//

// ---------------------------------------------------------------

  public void writeAUFile(String fileName) {

    try {

      DataOutputStream os = new

          DataOutputStream(new FileOutputStream(fileName));

      // not too much magic about it, just ".snd" ASCII string represented as

      //integer

      os.writeInt(0x2E736E64); // magic

      os.writeInt(0x00000020); // offset of the data

      os.writeInt(ulawData.length); // data size

      // good old 8 bit per sample u-law encoded data format (code = 1)

      os.writeInt(0x00000001); // format code

      os.writeInt(0x00001F40); // sampling rate

      os.writeInt(0x00000001); // channel count

      os.writeInt(0x00000000); // reserved

      os.writeInt(0x00000000); // reserved

      os.write(ulawData, 0, ulawData.length);

    } catch (Exception e) {
      System.out.println(e);
    }

  }


  public void run() {
    try {
      stop();
      AudioData audioData = new AudioData(ulawData);
      audioDataStream = new AudioDataStream(audioData);
      AudioPlayer.player.start(audioDataStream);
      Thread.sleep(ulawData.length / 8 + 100);
    } catch (Exception e) {
    }

  }

  public void play() {
    stop();
    AudioData audioData =
        new AudioData(ulawData);
    audioDataStream =
        new AudioDataStream(audioData);
    AudioPlayer.player.start(
        audioDataStream);
  }

  private boolean isPlaying() {
    return audioDataStream != null;
  }

  public void stop() {
    if (isPlaying()) {
      AudioPlayer.player.stop(audioDataStream);
      audioDataStream = null;
    }
  }

  public static void playFromFile() {
    UlawCodec ulc = new UlawCodec();
    ulc.play();
    //ulc.writeAUFile();
  }

  public static void play(URL url) {
    UlawCodec ulc = new UlawCodec(url);
    ulc.play();
  }

  public static void main(String args[]) {
    playFromFile();
  }

  public byte[] getUlawData() {
    return ulawData;
  }

  public void setUlawData(byte ulawArrayOfByte[]) {
    ulawData = ulawArrayOfByte;
    // force recomputation of the doubleArray
    doubleArray = null;
  }

  public double[] getDoubleArray() {

    if (doubleArray == null)

      doubleArray = computeDoubleArray();

    return doubleArray;

  }

  // out = decodeUlaw(in)

  private double[] computeDoubleArray() {

    double[] out = new double[ulawData.length];

    for (int i = 0; i < ulawData.length; i++) {

      out[i] = Ulaw.ulawToDouble(ulawData[i]);

    }

    return out;

  }


  public int getLength() {

    return ulawData.length;

  }


  public double getDuration() {

    // Assume 8,000 samples per second

    // then

    // number_samples / 8,000 = number of seconds.

    return ulawData.length / samplingRate;

  }

  public void reverseUlaw() {

    int l = ulawData.length;

    int i;

    byte tmp;


    for (i = 0; i < l / 2; i++) {

      tmp = ulawData[i];

      ulawData[i] = ulawData[l - i - 1];

      ulawData[l - i - 1] = tmp;

    }

    doubleArray = null;

  }
}