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

/* Načte klip, který pak lze spustit, pozastavit a cyklicky přehrávat.

   Objekt implementující rozhraní SoundsWatcher může být informován
   o tom, že se klip přehrává cyklicky nebo že bylo přehrávání zastaveno.

   Změny 9. Srpna 2004
     - uzavřen vstupní proud v metodě loadClip()

   Změny 15. Září 2004
     - bug: pokud má WAV-soubor méně než 1 sekundu, pak se nic nepřehraje
            (viz http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5085008).
     Řešení: všechny zvuky budou > 1 sekunda.
     - přidáno volání clip.setFramePosition(0) do metody update()
     - přidáno volání metody checkDuration() a proměnná df typu DecimalFormat
*/

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


public class ClipInfo implements LineListener
{
  private final static String SOUND_DIR = "Sounds/";

  private String name, filename;
  private Clip clip = null;
  private boolean isLooping = false;
  private SoundsWatcher watcher = null;
  private DecimalFormat df; 


  public ClipInfo(String nm, String fnm)
  { name = nm;
    filename = SOUND_DIR + fnm;
    df = new DecimalFormat("0.#");  // 1 dm

    loadClip(filename);
  } // konec konstruktoru: ClipInfo()


  private void loadClip(String fnm)
  {
    try {
      // spojí zvukový proud se souborem obsahujícím zvukový klip
      AudioInputStream stream = AudioSystem.getAudioInputStream(
                          getClass().getResource(fnm) );

      AudioFormat format = stream.getFormat();

      // 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;
      }

      DataLine.Info info = new DataLine.Info(Clip.class, format);

      // ujisti se, že zvukový systém podporuje datové linky
      if (!AudioSystem.isLineSupported(info)) {
        System.out.println("Nepodporovaný formát souboru: " + fnm);
        return;
      }

      // získej zdroj linky klipu
      clip = (Clip) AudioSystem.getLine(info);

      // naslouchej událostem klipu
      clip.addLineListener(this);

      clip.open(stream);    // otevři zvukový soubor jako klip
      stream.close(); // se vstupním proudem jsme skončili
     
      checkDuration();
    } // konec bloku: try

    catch (UnsupportedAudioFileException audioException) {
      System.out.println("Unsupported audio file: " + fnm);
    }
    catch (LineUnavailableException noLineException) {
      System.out.println("Zvuková linka není k dispozici : " + fnm);
    }
    catch (IOException ioException) {
      System.out.println("Nemohu přečíst soubor: " + fnm);
    }
    catch (Exception e) {
      System.out.println("Problém s " + fnm);
    }
  } // konec metody: loadClip()


  private void checkDuration()
  {
    // délka klipu (v sekundách)
	double duration = clip.getMicrosecondLength()/1000000.0;  // nové
    if (duration <= 1.0) {
      System.out.println("WARNING. Duration <= 1 sekunda : " + df.format(duration) + " sekund");
      System.out.println("         The clip in " + filename + 
                         " may not play in J2SE 1.5 -- make it longer");
    }
    else
      System.out.println(filename + ": Duration: " + df.format(duration) + " sekund");
  }  // konec metody: checkDuration()



  public void update(LineEvent lineEvent)
  /* Volá se, když linka klipu detekuje událost otevření, uzavření spuštění 
     či zastavení. Upozorní hlídače (pokud existuje).
  */
  {
    // když se zastaví přehrávání klipu / dohraje do konce
    if (lineEvent.getType() == LineEvent.Type.STOP) {
      // System.out.println("update() STOP for " + name);
      clip.stop();
      clip.setFramePosition(0);  // NEW
      if (!isLooping) {  // nepřehrává se cyklicky
        if (watcher != null)
          watcher.atSequenceEnd(name, SoundsWatcher.STOPPED);
      }
      else {      // jinak hraj znovu
        clip.start();
        if (watcher != null)
          watcher.atSequenceEnd(name, SoundsWatcher.REPLAYED);
      }
    }
  } // konec metody: update()


  public void close()
  { if (clip != null) {
      clip.stop();
      clip.close();
    }
  }


  public void play(boolean toLoop)
  { if (clip != null) {
      isLooping = toLoop;
      clip.start(); // spusť přehrávání
    }
  }

  public void stop()
  // zastav klip a vrať jej na začátek
  { if (clip != null) {
      isLooping = false;
      clip.stop();
      clip.setFramePosition(0);
    }
  }

  public void pause()
  // zastav klip a ponech jej na aktuální pozici
  { if (clip != null)
      clip.stop();
  }

  public void resume()
  { if (clip != null)
      clip.start();
  }


  public void setWatcher(SoundsWatcher sw)
  {  watcher = sw;  }


  // -------------- další metody pro přístup k atributům -------------------

  public String getName()
  {  return name;  }

}  // konec třídy: ClipInfo
