package framework;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.DataLine;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import framework.Sound;
import framework.generators.Generator;

/**
 * Manage a list of Sound objects.
 *
 * This class provides methods to play, pause and mix different sounds.
 */
public class Player {
    public Player() {
        try {
            // 44,100 samples per second, 16-bit audio, mono, signed PCM, little Endian
            AudioFormat format = new AudioFormat(
                (float) Generator.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
            // 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);
        }

        playlist = new HashMap<Sound, Boolean>();
    }

    /**
     * Add the specified Sound to the playlist.
     * @param s the sound to be added
     */
    public void addSound(Sound s) {
        playlist.put(s, true);
    }

    /**
     * Remove the specified Sound from the playlist.
     * @param s the Sound to be removed
     */
    public void removeSound(Sound s) {
        playlist.remove(s);
    }

    /**
     * Toggle a Sound inside the playlist.
     *
     * If its value is true, it is added to the mix.
     * If its value is false, it is muted.
     * @param s the Sound to be toggled
     */
    public void toggleSound(Sound s) {
        if(playlist.get(s) != null)
            playlist.put(s, !playlist.get(s));
    }

    /**
     * Play all the Sound objects which are not muted.
     */
    public void play() {
        ArrayList<Sound> selection = new ArrayList<Sound>();



        line.start();

        double[] data = entry.getKey().getData();
                for(int i = 0; i < data.length; i++) {
                    // Clip
                    if (data[i] < -1.0) data[i] = -1.0;
                    if (data[i] > +1.0) data[i] = +1.0;

                    // Convert to bytes
                    short s = (short) (MAX_16_BIT * data[i]);
                    buffer[bufferSize++] = (byte) s;
                    buffer[bufferSize++] = (byte) (s >> 8);   // little Endian

                    // Send to sound card if buffer is full
                    if (bufferSize >= buffer.length) {
                        line.write(buffer, 0, buffer.length);
                        bufferSize = 0;
                    }
                }

        line.drain();
        line.stop();
    }

    /**
     * Get all the Sound objects that are not muted.
     * @return The list of Sound objects
     */
    public ArrayList<Sound> getPlayableSounds() {
        ArrayList<Sound> selection = new ArrayList<Sound>();
        for(Map.Entry<Sound, Boolean> entry : playlist.entrySet()) {
            if(entry.getValue())
                selection.add(entry.getKey());
        }
        return selection;
    }

    private SourceDataLine              line;
    private HashMap<Sound, Boolean>     playlist;
    private byte[]                      buffer; // Internal buffer
    private int                         bufferSize = 0; // Current number of samples in buffer

    // Constants
    private final int       BYTES_PER_SAMPLE    = 2;
    private final int       BITS_PER_SAMPLE     = 16;
    private final double    MAX_16_BIT          = Short.MAX_VALUE;
    private final int       SAMPLE_BUFFER_SIZE  = 4096;
}
@@ -2241,35 +2241,30 @@
 n = 
-new ArrayList%3C
+getPlayable
 Sound
-%3E
+s
 ();%0A%0A
-%0A%0A
     
@@ -2294,46 +2294,193 @@
     
-double%5B%5D data = entry.getKey().getData
+// Find the maximum duration%0A        double maxDuration = 0;%0A        for(Sound s : selection) %7B%0A            if(s.getDuration() %3E maxDuration)%0A                maxDuration = s.getDuration
 ();%0A
@@ -2479,32 +2479,35 @@
 tion();%0A        
+%7D%0A%0A
         for(int 
@@ -2521,111 +2521,276 @@
 i %3C 
-data.length; i++) %7B%0A                    // Clip%0A                    if (data%5Bi%5D %3C -1.0) data%5Bi%5D = -1.0;
+(int) (maxDuration * Generator.SAMPLE_RATE); i++) %7B%0A            short sh = 0;%0A            for(Sound s : selection) %7B%0A                if(i %3C (int) (s.getDuration() * Generator.SAMPLE_RATE)) %7B%0A                    double%5B%5D data = s.getData();%0A%0A                    // Clip
 %0A   
@@ -2822,11 +2822,11 @@
 %5Bi%5D 
-%3E +
+%3C -
 1.0)
@@ -2840,15 +2840,14 @@
 %5D = 
-+
+-
 1.0;%0A
-%0A
     
@@ -2866,27 +2866,44 @@
     
-// Convert to bytes
+if (data%5Bi%5D %3E +1.0) data%5Bi%5D = +1.0;%0A
 %0A   
@@ -2921,21 +2921,16 @@
       sh
-ort s
  = (shor
@@ -2932,16 +2932,17 @@
 (short) 
+(
 (MAX_16_
@@ -2955,16 +2955,22 @@
 data%5Bi%5D)
+ + sh)
 ;%0A      
@@ -2963,32 +2963,56 @@
  + sh);%0A        
+        %7D%0A            %7D%0A
             buff
@@ -3038,16 +3038,17 @@
 (byte) s
+h
 ;%0A      
@@ -3049,32 +3049,24 @@
             
-     
-   
 buffer%5Bbuffe
@@ -3085,16 +3085,17 @@
 byte) (s
+h
  %3E%3E 8); 
@@ -3126,24 +3126,16 @@
         
-     
-   
 // Send 
@@ -3174,32 +3174,24 @@
             
-     
-   
 if (bufferSi
@@ -3209,32 +3209,24 @@
 r.length) %7B%0A
-        
             
@@ -3279,32 +3279,24 @@
             
-       
- 
 bufferSize =
@@ -3311,34 +3311,18 @@
         
-        %7D%0A        
+%7D%0A
         

Rewrite play function in player. Mixing is now part of it and it works!