Package edu.unc.cs.sportsync.main.sound

Source Code of edu.unc.cs.sportsync.main.sound.SoundCheck

package edu.unc.cs.sportsync.main.sound;

/*************************************************************************************
*
* Author(s) - Michael Barlock, Kartik Sethuraman
*        created: October 9, 2012
*    last modified: November 13, 2012
*
* Function - This class houses all the Java Sound functionality for the application.
*    Responsible for opening input/output lines, setting the volume level, playing
*    test output, outputing delayed audio, caching audio.
*   
* Key algorithms - Methods that use the input/output audio lines are synchronized to
*    ensure mutual exclusion.  A circular buffer is used to cache the input audio.
*
*
*************************************************************************************/
import java.io.BufferedInputStream;
import java.text.DecimalFormat;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;

import edu.unc.cs.sportsync.main.settings.Settings;
import edu.unc.cs.sportsync.main.ui.error.ErrorUtil;

public class SoundCheck extends Thread {
  private final int DELAY_PARAM = 170000;

  private final int BUFFER_SIZE;
  private TargetDataLine inputLine;
  private SourceDataLine outputLine;
  private Mixer inputMixer;
  private Mixer outputMixer;
  private final AudioFormat SOUND_FORMAT;
  private int delayAmount;

  private boolean fullyCached;
  private int bufferCacheCount;
  private int cachingAmount;

  private byte[] myBuffer;
  private byte[] outputBufferQueue;

  private boolean disposed;
  private final Settings settings;

  private double percentLevelVolume;

  private Clip myClip = null;
  private LineListener testAudioListener;

  /*
   * AudioFormat tells the format in which the data is recorded/played
   */
  public SoundCheck(AudioFormat format, int bufferSize, Settings settings) throws LineUnavailableException {
    this.settings = settings;
    /*
     * Get the input/output lines
     */
    BUFFER_SIZE = bufferSize;
    SOUND_FORMAT = format;
    disposed = false;

    openLines();
  }

  public void setTestAudioListener(LineListener tAudioListener) {
    testAudioListener = tAudioListener;
  }

  protected int calculateRMSLevel(byte[] chunk) {
    DecimalFormat df = new DecimalFormat("##");
    float audioVal = 0;
    for (int i = 0; i < chunk.length - 1; i++) {
      audioVal = (float) (((chunk[i + 1] << 8) | (chunk[i] & 0xff)) / 32768.0);
    }

    int percent = Integer.valueOf(df.format(Math.abs(audioVal) * 1000));
    // System.out.println(percent);

    return percent;
  }

  public synchronized void dispose() {
    inputLine.close();
    outputLine.close();
    disposed = true;
  }

  public int getBufferPercentage() {
    double percent = fullyCached ? 100 : ((double) bufferCacheCount / cachingAmount) * 100;
    return (int) percent;
  }

  public int getInputLevel() {
    return calculateRMSLevel(myBuffer);
  }

  public double getOutputLevel() {
    return outputLine.getLevel();
  }

  public synchronized void openLines() throws LineUnavailableException {
    if (inputLine != null && inputLine.isOpen()) {
      inputLine.close();
    }

    if (outputLine != null && outputLine.isOpen()) {
      outputLine.close();
    }
    myClip = null;
    inputLine = null;
    outputLine = null;

    inputMixer = AudioSystem.getMixer(settings.getInputMixer());
    outputMixer = AudioSystem.getMixer(settings.getOutputMixer());

    if (!inputMixer.isOpen()) {
      inputMixer.open();
    }
    if (!outputMixer.isOpen()) {
      outputMixer.open();
    }

    Line.Info[] targetlineinfos = inputMixer.getTargetLineInfo();
    for (int i = 0; i < targetlineinfos.length; i++) {
      if (targetlineinfos[i].getLineClass() == TargetDataLine.class) {
        inputLine = (TargetDataLine) AudioSystem.getLine(targetlineinfos[i]);
        break;
      }
    }

    Line.Info[] sourcelineinfos = outputMixer.getSourceLineInfo();
    for (int i = 0; i < sourcelineinfos.length; i++) {
      if (sourcelineinfos[i].getLineClass() == SourceDataLine.class) {
        outputLine = (SourceDataLine) AudioSystem.getLine(sourcelineinfos[i]);
        break;
      }
    }
    if (inputMixer.isOpen()) {
      inputMixer.close();
    }
    if (outputMixer.isOpen()) {
      outputMixer.close();
    }

    if (inputLine == null || outputLine == null) {
      throw new LineUnavailableException();
    }

    inputLine.open(SOUND_FORMAT, BUFFER_SIZE);
    outputLine.open(SOUND_FORMAT, BUFFER_SIZE);

    inputLine.start();
    outputLine.start();
  }

  public void playTestOutput() {
    if (myClip == null) {
      AudioInputStream testFileInputStream = null;
      Mixer.Info mixer = settings.getOutputMixer();

      try {
        testFileInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(this.getClass().getResourceAsStream("UNCFightSongShort.wav")));
        myClip = AudioSystem.getClip(mixer);
        if (!myClip.isOpen()) {
          myClip.open(testFileInputStream);
        }
      } catch (Exception e) {
        try {
          testFileInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(this.getClass().getResourceAsStream("test.wav")));
          myClip = AudioSystem.getClip(mixer);
          if (!myClip.isOpen()) {
            myClip.open(testFileInputStream);
          }
        } catch (Exception e1) {
          e1.printStackTrace();
          ErrorUtil.openStackTraceDialog("A Fatal Error has occured and the application will need to shut down", e);
          System.exit(1);
        }
      }
      myClip.addLineListener(testAudioListener);
    } else {
      myClip.setFramePosition(0);

    }

    myClip.start();

    if (myClip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
      FloatControl volume = (FloatControl) myClip.getControl(FloatControl.Type.MASTER_GAIN);
      float maximum = volume.getMaximum();
      volume.setValue(maximum);
    }
  }

  private synchronized int read(byte[] buffer, int offset, int length) {
    return inputLine.read(buffer, offset, length);
  }

  public synchronized void resetBuffer() {
    cachingAmount = (int) (Math.ceil((float) DELAY_PARAM / BUFFER_SIZE) * settings.getMaxDelay() + 1);
    outputBufferQueue = new byte[0];
    outputBufferQueue = new byte[cachingAmount * BUFFER_SIZE];
    bufferCacheCount = 0;
    fullyCached = false;
    delayAmount = 0;
  }

  @Override
  public void run() {
    int offset;
    int delayVar;

    myBuffer = new byte[BUFFER_SIZE];
    resetBuffer();

    while (!disposed) {
      // read data from input line
      read(myBuffer, 0, myBuffer.length);

      // System.out.printf("numbytesread = %d\n", k);

      // write to output line
      // outputBufferQueue[count] = myBuffer.clone();
      System.arraycopy(myBuffer, 0, outputBufferQueue, bufferCacheCount * BUFFER_SIZE, BUFFER_SIZE);

      if (bufferCacheCount == cachingAmount - 1) {
        fullyCached = true;
      }

      bufferCacheCount = (bufferCacheCount + 1) % cachingAmount;

      delayVar = (delayAmount * DELAY_PARAM) / 10;

      if (fullyCached || delayVar < (bufferCacheCount - 1) * BUFFER_SIZE) {
        offset = ((bufferCacheCount + cachingAmount - 1) * BUFFER_SIZE - delayVar) % (cachingAmount * BUFFER_SIZE);

        // System.out.printf("count = %d offset = %d cachingAmount = %d delayVar = %d bufferSize = %d outputBufferSize = %d\n",
        // bufferCacheCount, offset, cachingAmount, delayVar,
        // BUFFER_SIZE,
        // outputBufferQueue.length);

        if ((BUFFER_SIZE * cachingAmount - offset) < BUFFER_SIZE) {
          write(outputBufferQueue, offset, (BUFFER_SIZE * cachingAmount - offset));
          write(outputBufferQueue, 0, BUFFER_SIZE - (BUFFER_SIZE * cachingAmount - offset));
        } else {
          write(outputBufferQueue, offset, BUFFER_SIZE);
        }

      }
    }
  }

  public void setDelayAmount(int amount) {
    delayAmount = amount;
  }

  public void setVolume() {
    if (outputLine.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
      FloatControl volume = (FloatControl) outputLine.getControl(FloatControl.Type.MASTER_GAIN);
      float minimum = volume.getMinimum();
      float maximum = volume.getMaximum();
      float currentVolume = (float) (minimum + percentLevelVolume * (maximum - minimum) / 100.0F);
      volume.setValue(currentVolume);
    }

  }

  public void setVolume(double percentLevel) {
    if (outputLine.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
      percentLevelVolume = percentLevel;
      FloatControl volume = (FloatControl) outputLine.getControl(FloatControl.Type.MASTER_GAIN);
      float minimum = volume.getMinimum();
      float maximum = volume.getMaximum();
      float currentVolume = (float) (minimum + percentLevelVolume * (maximum - minimum) / 100.0F);
      volume.setValue(currentVolume);
    }

  }

  @Override
  public void start() {
    super.start();
  }

  public synchronized void stopTestOutput() {
    myClip.stop();
  }

  public void toggleMute() {
    BooleanControl bc = (BooleanControl) outputLine.getControl(BooleanControl.Type.MUTE);
    if (bc != null) {
      bc.setValue(!bc.getValue());
    }
  }

  public void setMute(boolean state) {
    BooleanControl bc = (BooleanControl) outputLine.getControl(BooleanControl.Type.MUTE);
    if (bc != null) {
      bc.setValue(state);
    }
  }

  // possibly synchronized?
  public void write(byte[] buffer, int offset, int length) {
    outputLine.write(buffer, offset, length);
  }
}
TOP

Related Classes of edu.unc.cs.sportsync.main.sound.SoundCheck

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.