/*
  * based on www.jsresources.org's AudioDataBuffer
  *
  * ------------------------------
  * AudioDataBuffer.java: 
  * Copyright (c) 2003 by Matthias Pfisterer
  * All rights reserved.
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  *
  * - Redistributions of source code must retain the above copyright notice,
  *   this list of conditions and the following disclaimer.
  * - Redistributions in binary form must reproduce the above copyright
  *   notice, this list of conditions and the following disclaimer in the
  *   documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  * ------------------------------
  */

 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;

 import javax.sound.sampled.AudioFileFormat;
 import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;

 public class WaveToPlateDataWave {
   private static final boolean   DEBUG = false;
   private static final int       BUFFER_LENGTH = 1024;

   static double biasVoltage;

   public static void main(String[] args)
     throws Exception
   {
     if (args.length != 3)
       {
         printUsageAndExit();
       }
     File sourceFile = new File(args[0]);
     File targetFile = new File(args[1]);

     biasVoltage = Double.parseDouble(args[2]);

     /* Get the type of the source file. We need this information
        later to write the audio data to a file of the same type.
     */
     AudioFileFormat fileFormat = AudioSystem.getAudioFileFormat(sourceFile);
     AudioFileFormat.Type targetFileType = fileFormat.getType();
     AudioFormat audioFormat = fileFormat.getFormat();

     /* Read the audio data into a memory buffer.
      */
     AudioInputStream inputAIS = AudioSystem.getAudioInputStream(sourceFile);
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     int nBufferSize = BUFFER_LENGTH * audioFormat.getFrameSize();
     byte[]       abBuffer = new byte[nBufferSize];
     while (true)
       {
         if (DEBUG) { out("trying to read (bytes): " + abBuffer.length); }
         int      nBytesRead = inputAIS.read(abBuffer);
         if (DEBUG) { out("read (bytes): " + nBytesRead); }
         if (nBytesRead == -1)
           {
             break;
           }
         baos.write(abBuffer, 0, nBytesRead);
       }

     /* Here's the byte array everybody wants.
      */
     byte[] abAudioData = baos.toByteArray();

     // transform
     {
       AudioFormat format = audioFormat;
       byte[] audioBytes = abAudioData;
       int numChannels = format.getChannels();
       if (numChannels < 2) {
         System.out.println("-- audio source is not stereo, exit! " + audioFormat);
         System.exit(1);
       }

       if (format.getSampleSizeInBits() != 16) {
         System.out.println("-- audioFormat size is not 16, exit! " + audioFormat);
         System.exit(1);
       }

       boolean bigEndian  = format.isBigEndian();

       for (int idx = 0; idx < audioBytes.length;idx+=4) {
         int ch1Val = 0;
         int ch2Val = 0;
         int MSB = 0;
         int LSB = 0;
         if (bigEndian) {
           MSB = (int) audioBytes[idx];
           LSB = (int) audioBytes[idx+1];
           ch1Val= MSB << 8 | (255 & LSB);
           MSB = (int) audioBytes[idx+2];
           LSB = (int) audioBytes[idx+3];
           ch2Val= MSB << 8 | (255 & LSB);
         } else {
           LSB = (int) audioBytes[idx];
           MSB = (int) audioBytes[idx+1];
           ch1Val= MSB << 8 | (255 & LSB);
           LSB = (int) audioBytes[idx+2];
           MSB = (int) audioBytes[idx+3];
           ch2Val= MSB << 8 | (255 & LSB);
         }

         // transform 
         if (ch1Val < 0) ch2Val = 0;
         else {
           double effV = biasVoltage + ((double)ch1Val / 100.0);
           if (effV < 0.0) ch2Val = 0;
           else
             ch2Val = (int) (Math.pow(effV, 1.5) * 9.0);
         }

         if (DEBUG && idx < 1000) {
           System.out.println("-- ch1Val: " + ch1Val + " ch2Val: " + ch2Val);
         }

         if (bigEndian) {
           audioBytes[idx+2] = (byte) (ch2Val >> 8);
           audioBytes[idx+3] = (byte) (ch2Val & 255);
         } else {
           audioBytes[idx+2] = (byte) (ch2Val & 255);
           audioBytes[idx+3] = (byte) (ch2Val >> 8);
         }

       }

     }

     /* And now, write it to a file again.
      */
     ByteArrayInputStream bais = new ByteArrayInputStream(abAudioData);
     AudioInputStream outputAIS = new AudioInputStream(
                                                       bais, audioFormat,
                                                       abAudioData.length / audioFormat.getFrameSize());
     int  nWrittenBytes = AudioSystem.write(outputAIS,
                                           targetFileType,
                                           targetFile);
     if (DEBUG) { out("Written bytes: " + nWrittenBytes); }
   }

   private static void printUsageAndExit()
   {
     out("WaveToPlateDataWave: usage:");
     out("\tjava WaveToPlateDataWave <sourcefile> <targetfile> <biasVoltage>");
     System.exit(0);
   }

   private static void out(String strMessage)
   {
     System.out.println(strMessage);
   }
 }