title: Multiple sounds on Java
body: <p>I am trying to play 2 sounds (e.g. 220Hz and 440Hz) at the same time in Java.</p>
  
  <p>I managed to play one sound using <a href="http://introcs.cs.princeton.edu/java/stdlib/StdAudio.java.html" rel="nofollow">StdAudio</a>.
  Later on, I made it not static, and removed some methods that are irrelevant for me.</p>
  
  <p>What I don't know is how to play 2 sounds at the same time.
  I tried to do that with thread but they aren't always synchronized.</p>
  
  <p>Below is my modified version of StdAudio, and here is the example of how I tried to use threads.</p>
  
  <p>program.java</p>
  
  <pre><code>public class program {
  
      public static void main(String[] args) {
          Thread t1 = new Thread(new soundThread(220));
          t1.start();
          Thread t2 = new Thread(new soundThread(440));
          t2.start();
  
          t1.notify();
          t2.notify();
      }
  
  }
  </code></pre>
  
  <p>soundThread.java</p>
  
  <pre><code>public class soundThread implements Runnable {
      private int fq;
  
      public soundThread(int fq) {
          this.fq = fq;
      }
  
      public void run() {
          StdAudio s = new StdAudio();
          double[] note = s.note(fq, 2, 1);
          try {
              this.wait();
          } catch (Exception e) {
          }
  
          s.play(note);
  
          s.close();
      }
  
  }
  </code></pre>
  
  <p>StdAudio.java</p>
  
  <pre><code>/*************************************************************************
   *  Compilation:  javac this.java
   *  Execution:    java StdAudio
   *  
   *  Simple library for reading, writing, and manipulating .wav files.
  
   *
   *  Limitations
   *  -----------
   *    - Does not seem to work properly when reading .wav files from a .jar file.
   *    - Assumes the audio is monaural, with sampling rate of 44,100.
   *
   *************************************************************************/
  
  import javax.sound.sampled.*;
  
  /**
   * &lt;i&gt;Standard audio&lt;/i&gt;. This class provides a basic capability for creating,
   * reading, and saving audio.
   * &lt;p&gt;
   * The audio format uses a sampling rate of 44,100 (CD quality audio), 16-bit,
   * monaural.
   * 
   * &lt;p&gt;
   * For additional documentation, see &lt;a
   * href="http://introcs.cs.princeton.edu/15inout"&gt;Section 1.5&lt;/a&gt; of
   * &lt;i&gt;Introduction to Programming in Java: An Interdisciplinary Approach&lt;/i&gt; by
   * Robert Sedgewick and Kevin Wayne.
   */
  public final class StdAudio {
  
      /**
       * The sample rate - 44,100 Hz for CD quality audio.
       */
      public final int SAMPLE_RATE = 44100;
  
      private final int BYTES_PER_SAMPLE = 2; // 16-bit audio
      private final int BITS_PER_SAMPLE = 16; // 16-bit audio
      private final double MAX_16_BIT = Short.MAX_VALUE; // 32,767
      private final int SAMPLE_BUFFER_SIZE = 4096;
  
      private SourceDataLine line; // to play the sound
      private byte[] buffer; // our internal buffer
      private int bufferSize = 0; // number of samples currently in internal
                                  // buffer
  
      // initializer
      {
          init();
      }
  
      // open up an audio stream
      private void init() {
          try {
              // 44,100 samples per second, 16-bit audio, mono, signed PCM, little
              // Endian
              AudioFormat format = new AudioFormat((float) SAMPLE_RATE,
                      BITS_PER_SAMPLE, 1, true, false);
              DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
  
              line = (SourceDataLine) AudioSystem.getLine(info);
              line.open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);
  
              // the internal buffer is a fraction of the actual buffer size, this
              // choice is arbitrary
              // it gets divided because we can't expect the buffered data to line
              // up exactly with when
              // the sound card decides to push out its samples.
              buffer = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE / 3];
          } catch (Exception e) {
              System.out.println(e.getMessage());
              System.exit(1);
          }
  
          // no sound gets made before this call
          line.start();
      }
  
      /**
       * Close standard audio.
       */
      public void close() {
          line.drain();
          line.stop();
      }
  
      /**
       * Write one sample (between -1.0 and +1.0) to standard audio. If the sample
       * is outside the range, it will be clipped.
       */
      public void play(double in) {
  
          // clip if outside [-1, +1]
          if (in &lt; -1.0)
              in = -1.0;
          if (in &gt; +1.0)
              in = +1.0;
  
          // convert to bytes
          short s = (short) (MAX_16_BIT * in);
          buffer[bufferSize++] = (byte) s;
          buffer[bufferSize++] = (byte) (s &gt;&gt; 8); // little Endian
  
          // send to sound card if buffer is full
          if (bufferSize &gt;= buffer.length) {
              line.write(buffer, 0, buffer.length);
              bufferSize = 0;
          }
      }
  
      /**
       * Write an array of samples (between -1.0 and +1.0) to standard audio. If a
       * sample is outside the range, it will be clipped.
       */
      public void play(double[] input) {
          for (int i = 0; i &lt; input.length; i++) {
              play(input[i]);
          }
      }
  
      /**
       * Create a note (sine wave) of the given frequency (Hz), for the given
       * duration (seconds) scaled to the given volume (amplitude).
       */
      public double[] note(double hz, double duration, double amplitude) {
          int N = (int) (this.SAMPLE_RATE * duration);
          double[] a = new double[N + 1];
          for (int i = 0; i &lt;= N; i++)
              a[i] = amplitude
                      * Math.sin(2 * Math.PI * i * hz / this.SAMPLE_RATE);
          return a;
      }
  
  }
  </code></pre>
  
  <p>Thanks in advance,
  Shay Ben Moshe</p>
  
  <p><strong>EDIT:</strong>
  The solution was writing this method:</p>
  
  <pre><code>public double[] multipleNotes(double[] hzs, double duration,
          double amplitude) {
      amplitude = amplitude / hzs.length;
      int N = (int) (SAMPLE_RATE * duration);
      double[] a = new double[N + 1];
      for (int i = 0; i &lt;= N; i++) {
          a[i] = 0;
          for (int j = 0; j &lt; hzs.length; j++)
              a[i] += amplitude
                      * Math.sin(2 * Math.PI * i * hzs[j] / SAMPLE_RATE);
      }
      return a;
  }
  </code></pre>
  
  <p><strong>EDIT2:</strong>
  Even better solution for me (O(1) memory):</p>
  
  <pre><code>public void multiplePlay(double[] hzs, double duration, double amplitude) {
      amplitude = amplitude / hzs.length;
      int N = (int) (SAMPLE_RATE * duration);
      double sum;
      for (int i = 0; i &lt;= N; i++) {
          sum = 0;
          for (int j = 0; j &lt; hzs.length; j++)
              sum += amplitude
                      * Math.sin(2 * Math.PI * i * hzs[j] / SAMPLE_RATE);
          this.play(sum);
      }
  }
  </code></pre>