
// Bufferedmlayer.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Načte zvukový soubor jako kousky bajtů do bufferu
   přes objekt typu AudioInputStream, načež je předá
   objektu typu SourceDataLine.

   Tento způsob nevyžaduje, aby byl celý zvukový soubor v paměti
   najednou.

   Kód je ve stylu jazyka C, tedy jako série statických metod
   a statických globálních proměnných. Se třídami/objekty by
   vše fungovalo úplně stejně.

   Změny 16. září
     bug: nepřehraje krátké soubory typu WAV, podobně jako PlayClip.java
     - přidána metoda checkDuration(), která hlásí délku zvukového souboru
 */


import java.io.*;
import javax.sound.sampled.*;


public class Bufferedmlayer  
{
  private static AudioInputStream stream;
  private static AudioFormat format = null;
  private static SourceDataLine line = null;


  public static void main(String[] args) 
  {
    if (args.length != 1) {
      System.out.println("Použití: java Bufferedmlayer <clip file>");
      System.exit(0);
    }

    createInput("Sounds/" + args[0]);
    createOutput();

    int numBytes = (int)(stream.getFrameLength() * format.getFrameSize());
         // použij metodu getFrameLength() z proudu, protože formát
         // verze může vráti -1 (pro formát souboru typu WAV vrací vždy -1)
    System.out.println("Velikost v bajtech: " + numBytes);

    checkDuration();
    play();

    System.exit(0);   // v J2SE 1.4.2 nebo dříve nutné
  } // konec metody: main()


  private static void checkDuration()
  {
    long milliseconds =  (long)((stream.getFrameLength() * 1000) / 
                                stream.getFormat().getFrameRate());
    double duration = milliseconds / 1000.0;
    if (duration <= 1.0) {
      System.out.println("WARNING. Duration <= 1 sekunda : " + duration + " sekund");
      System.out.println("         The sample may not play in J2SE 1.5 -- make it longer");
    }
    else
      System.out.println("Délka: " + duration + " sekund");
  }  // konec metody: checkDuration()


  private static void createInput(String fnm)
  // Připraví vstupní zvukový proud z audio souboru
  {
    try {
      // připoj zvukový proud k vzorkovanému audio-souboru
      stream = AudioSystem.getAudioInputStream( new File(fnm) );
      format = stream.getFormat();
      System.out.println("Audio formát: " + format);

      // převeď data ve formátu ULAW/ALAW do formátu PCM
      if ( (format.getEncoding() == AudioFormat.Encoding.ULAW) ||
           (format.getEncoding() == AudioFormat.Encoding.ALAW) ) {
        AudioFormat newFormat = 
           new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
                                format.getSampleRate(),
                                format.getSampleSizeInBits()*2,
                                format.getChannels(),
                                format.getFrameSize()*2,
                                format.getFrameRate(), true);  // způsob uložení dat: big endian
        // aktualizuj podrobné údaje ohledně datového proudu a formátu
        stream = AudioSystem.getAudioInputStream(newFormat, stream);
        System.out.println("Převedený audio formát" + newFormat);
        format = newFormat;
      }
    }
    catch (UnsupportedAudioFileException e) 
    {  System.out.println( e.getMessage());  
       System.exit(0);
    }
    catch (IOException e) 
    {  System.out.println( e.getMessage());  
       System.exit(0);
    }
  }  // konec metody: createInput()


  private static void createOutput()
  // připraví objekt typu SourceDataLine směřující do JVM-směšovače
  {
    try {
      // shromáždi údaje potřebné k vytvoření linky
      DataLine.Info info =
            new DataLine.Info(SourceDataLine.class, format);
      if (!AudioSystem.isLineSupported(info)) {
        System.out.println("Linka nepodporuje: + format);
        System.exit(0);
      }
      // získej linku požadovaného formátu
      line = (SourceDataLine) AudioSystem.getLine(info);
      line.open(format); 
    }
    catch (Exception e)
    {  System.out.println( e.getMessage());  
       System.exit(0);
    }
  }  // konec metody: createOutput()


  private static void play()
  /* Načte zvukový soubor po kouskách do bufferu a 
     předá je skrz objekt typu SourceDataLine */
  {
    int numRead = 0;
    byte[] buffer = new byte[line.getBufferSize()];

    line.start();
    // načítej a přehrávej dávky zvukových dat
    try {
      int offset;
      while ((numRead = stream.read(buffer, 0, buffer.length)) >= 0) {
        // System.out.println("načteno: " + numRead);
        offset = 0;
        while (offset < numRead)
          offset += line.write(buffer, offset, numRead-offset);
      }
    }
    catch (IOException e) 
    {  System.out.println( e.getMessage()); }

    // posečkej, dokud se nepřehrají všechna data a pak linku uzavři
    // System.out.println("vyčerpaná začátek");
    line.drain();
    // System.out.println("vyčerpaný konec");
    line.stop();
    line.close();
  }  // konec metody: play()

} // konec třídy: Bufferedmlayer