content: /*
   * HalfNES by Andrew Hoffman
   * Licensed under the GNU GPL Version 3. See LICENSE file
   */
  package com.grapeshot.halfnes.audio;
  
  import com.grapeshot.halfnes.NES;
  import com.grapeshot.halfnes.PrefsSingleton;
  import com.grapeshot.halfnes.audio.AudioOutInterface;
  import com.grapeshot.halfnes.mappers.Mapper;
  import javax.sound.sampled.*;
  
  /**
   *
   * @author Andrew
   */
  public class SwingAudioImpl implements AudioOutInterface {
  
      private boolean soundEnable;
      private SourceDataLine sdl;
      private byte[] audiobuf;
      private int bufptr = 0;
      private float outputvol;
  
      public SwingAudioImpl(final NES nes, final int samplerate, Mapper.TVType tvtype) {
          soundEnable = PrefsSingleton.get().getBoolean("soundEnable", true);
          outputvol = (float) (PrefsSingleton.get().getInt("outputvol", 13107) / 16384.);
          double fps;
          switch (tvtype) {
              case NTSC:
              default:
                  fps = 60.;
                  break;
              case PAL:
              case DENDY:
                  fps = 50.;
                  break;
          }
          if (soundEnable) {
              final int samplesperframe = (int) Math.ceil((samplerate * 2) / fps);
              audiobuf = new byte[samplesperframe * 2];
              try {
                  AudioFormat af = new AudioFormat(
                          samplerate,
                          16,//bit
                          2,//channel
                          true,//signed
                          false //little endian
                  //(works everywhere, afaict, but macs need 44100 sample rate)
                  );
                  sdl = AudioSystem.getSourceDataLine(af);
                  sdl.open(af, samplesperframe * 4 * 2 /*ch*/ * 2 /*bytes/sample*/); 
                  //create 4 frame audio buffer
                  sdl.start();
              } catch (LineUnavailableException a) {
                  System.err.println(a);
                  nes.messageBox("Unable to inintialize sound.");
                  soundEnable = false;
              } catch (IllegalArgumentException a) {
                  System.err.println(a);
                  nes.messageBox("Unable to inintialize sound.");
                  soundEnable = false;
              }
          }
      }
  
      @Override
      public final void flushFrame(final boolean waitIfBufferFull) {
          if (soundEnable) {
  
  //            if (sdl.available() == sdl.getBufferSize()) {
  //                System.err.println("Audio is underrun");
  //            }
              if (sdl.available() < bufptr) {
  //                System.err.println("Audio is blocking");
                  if (waitIfBufferFull) {
  
                      //write to audio buffer and don't worry if it blocks
                      sdl.write(audiobuf, 0, bufptr);
                  }
                  //else don't bother to write if the buffer is full
              } else {
                  sdl.write(audiobuf, 0, bufptr);
              }
          }
          bufptr = 0;
  
      }
  
      @Override
      public final void outputSample(int sample) {
          if (soundEnable) {
              sample *= outputvol;
              if (sample < -32768) {
                  sample = -32768;
                  //System.err.println("clip");
              }
              if (sample > 32767) {
                  sample = 32767;
                  //System.err.println("clop");
              }
              //left ch
              int lch = sample;
              audiobuf[bufptr] = (byte) (lch & 0xff);
              audiobuf[bufptr + 1] = (byte) ((lch >> 8) & 0xff);
              //right ch
              int rch = sample;
              audiobuf[bufptr + 2] = (byte) (rch & 0xff);
              audiobuf[bufptr + 3] = (byte) ((rch >> 8) & 0xff);
              bufptr += 4;
          }
      }
  
      @Override
      public void pause() {
          if (soundEnable) {
              sdl.flush();
              sdl.stop();
          }
      }
  
      @Override
      public void resume() {
          if (soundEnable) {
              sdl.start();
          }
      }
  
      @Override
      public final void destroy() {
          if (soundEnable) {
              sdl.stop();
              sdl.close();
          }
      }
  
      public final boolean bufferHasLessThan(final int samples) {
          //returns true if the audio buffer has less than the specified amt of samples remaining in it
          return (sdl == null) ? false : ((sdl.getBufferSize() - sdl.available()) <= samples);
      }
  }
  