// Path: src/com/nixus/raop/speaker/Speaker.java
// public interface Speaker extends Service {
// 
//     /**
//      * Return the number of ms delay for this speaker, between a packet
//      * being written and it being heard
//      */
//     public int getDelay();
// 
//     /**
//      * Return the size of the speaker buffer in bytes.
//      */
//     public int getBufferSize();
// 
//     /**
//      * Return a nice name for the Speaker for display to the user
//      */
//     public String getDisplayName();
// 
//     /**
//      * Return a unique ID for the speaker
//      */
//     int getUniqueId();
// 
//     /**
//      * Returns true if this speaker can have its gain adjusted
//      */
//     public boolean hasGain();
// 
//     /**
//      * Set the gain of this speaker. A value of NaN means "no audio".
//      * If this method fails, set the Error.
//      */
//     public void setGain(float gain);
// 
//     /**
//      * Set the player for the speaker. This method should
//      * call {@link Player#removeSpeaker} on the old player
//      * and {@link Player#addSpeaker} on the new one, and
//      * anyone moving speakers between players should call
//      * this method rather than those methods directly.
//      */
//     public void setPlayer(Player player);
// 
//     /**
//      * Get the Player currently assigned to this speaker
//      */
//     public Player getPlayer();
// 
//     /**
//      * If this speaker has failed for some reason, return the Exception.
//      * Otherwise return null.
//      */
//     public Exception getError();
// 
//     /**
//      * Get the volume adjustment that applies to this speaker (+ve or -ve, 0
//      * for no adjustment
//      */
//     public int getVolumeAdjustment();
// 
//     /**
//      * Set the volume adjustment that applies to this speaker, which should be
//      * be added to the gain for this speaker.
//      */
//     public void setVolumeAdjustment(int volumeadjust);
// 
//     /**
//      * Open the Speaker and start it. Reset any error that may have been set.
//      */
//     public void open(float sampleRate, int sampleSizeInBits, int channels, boolean signed, boolean bigEndian);
// 
//     /**
//      * Write data to the speaker. If it fails, set the error
//      */
//     public void write(byte[] buf, int off, int len);
// 
//     /**
//      * Immediately stop the speaker and discard any cached data written to it
//      * If it fails, set the error.
//      */
//     public void flush();
// 
//     /**
//      * Block until the data already written to the speaker completes
//      * If it fails, set the error.
//      */
//     public void drain();
// 
//     /**
//      * Close the speaker, immediately stopping it and discarding any data.
//      * Does not throw exception or fail.
//      */
//     public void close();
// 
//     /**
//      * True after open has been called, false after close has been called
//      */
//     public boolean isOpen();
// 
// }


import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.nixus.raop.speaker.Speaker;
package com.nixus.raop.player;



/**
 * A Wrapper around multiple Speaker objects. Done this way
 * so a Player can play to multiple unsynchronzed speakers at once,
 * not sure if that's useful
 *
 * These methods are called from player thread:
 *    close drain flush write isOpen open hasSpeakers
 * Other methods are called from other threads. If they need to interact they must
 * do so carefully and synchronize on this(for mods involving no change to bitrate)
 * or tracklock(for mods involving bitrate)
 */
class MultiLine {
    
//    org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger("multiline");
    private static class SpeakerState {
        long tail = Long.MIN_VALUE;
        int bytedelay;
    }

    private Object tracklock = new Object();    // Just for synchronizing on
    private volatile int volume, msdelay;
    private volatile float trackgain;
    private PlayerImpl player;
    private byte[] queue = new byte[704000];    // ~4 secs
    private volatile long head, tail;

// Path: src/com/nixus/raop/speaker/Speaker.java
// public interface Speaker extends Service {
// 
//     /**
//      * Return the number of ms delay for this speaker, between a packet
//      * being written and it being heard
//      */
//     public int getDelay();
// 
//     /**
//      * Return the size of the speaker buffer in bytes.
//      */
//     public int getBufferSize();
// 
//     /**
//      * Return a nice name for the Speaker for display to the user
//      */
//     public String getDisplayName();
// 
//     /**
//      * Return a unique ID for the speaker
//      */
//     int getUniqueId();
// 
//     /**
//      * Returns true if this speaker can have its gain adjusted
//      */
//     public boolean hasGain();
// 
//     /**
//      * Set the gain of this speaker. A value of NaN means "no audio".
//      * If this method fails, set the Error.
//      */
//     public void setGain(float gain);
// 
//     /**
//      * Set the player for the speaker. This method should
//      * call {@link Player#removeSpeaker} on the old player
//      * and {@link Player#addSpeaker} on the new one, and
//      * anyone moving speakers between players should call
//      * this method rather than those methods directly.
//      */
//     public void setPlayer(Player player);
// 
//     /**
//      * Get the Player currently assigned to this speaker
//      */
//     public Player getPlayer();
// 
//     /**
//      * If this speaker has failed for some reason, return the Exception.
//      * Otherwise return null.
//      */
//     public Exception getError();
// 
//     /**
//      * Get the volume adjustment that applies to this speaker (+ve or -ve, 0
//      * for no adjustment
//      */
//     public int getVolumeAdjustment();
// 
//     /**
//      * Set the volume adjustment that applies to this speaker, which should be
//      * be added to the gain for this speaker.
//      */
//     public void setVolumeAdjustment(int volumeadjust);
// 
//     /**
//      * Open the Speaker and start it. Reset any error that may have been set.
//      */
//     public void open(float sampleRate, int sampleSizeInBits, int channels, boolean signed, boolean bigEndian);
// 
//     /**
//      * Write data to the speaker. If it fails, set the error
//      */
//     public void write(byte[] buf, int off, int len);
// 
//     /**
//      * Immediately stop the speaker and discard any cached data written to it
//      * If it fails, set the error.
//      */
//     public void flush();
// 
//     /**
//      * Block until the data already written to the speaker completes
//      * If it fails, set the error.
//      */
//     public void drain();
// 
//     /**
//      * Close the speaker, immediately stopping it and discarding any data.
//      * Does not throw exception or fail.
//      */
//     public void close();
// 
//     /**
//      * True after open has been called, false after close has been called
//      */
//     public boolean isOpen();
// 
// }


// Path: src/com/nixus/raop/player/MultiLine.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.nixus.raop.speaker.Speaker;

package com.nixus.raop.player;



/**
 * A Wrapper around multiple Speaker objects. Done this way
 * so a Player can play to multiple unsynchronzed speakers at once,
 * not sure if that's useful
 *
 * These methods are called from player thread:
 *    close drain flush write isOpen open hasSpeakers
 * Other methods are called from other threads. If they need to interact they must
 * do so carefully and synchronize on this(for mods involving no change to bitrate)
 * or tracklock(for mods involving bitrate)
 */
class MultiLine {
    
//    org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger("multiline");
    private static class SpeakerState {
        long tail = Long.MIN_VALUE;
        int bytedelay;
    }

    private Object tracklock = new Object();    // Just for synchronizing on
    private volatile int volume, msdelay;
    private volatile float trackgain;
    private PlayerImpl player;
    private byte[] queue = new byte[704000];    // ~4 secs
    private volatile long head, tail;

    private Map<Speaker,SpeakerState> speakers = new ConcurrentHashMap<Speaker,SpeakerState>();