| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.media.sound;  | 
 | 
 | 
 | 
import javax.sound.sampled.AudioFormat;  | 
 | 
import javax.sound.sampled.AudioSystem;  | 
 | 
import javax.sound.sampled.Control;  | 
 | 
import javax.sound.sampled.DataLine;  | 
 | 
import javax.sound.sampled.LineEvent;  | 
 | 
import javax.sound.sampled.LineUnavailableException;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
abstract class AbstractDataLine extends AbstractLine implements DataLine { | 
 | 
 | 
 | 
    // DEFAULTS  | 
 | 
 | 
 | 
      | 
 | 
    private final AudioFormat defaultFormat;  | 
 | 
 | 
 | 
      | 
 | 
    private final int defaultBufferSize;  | 
 | 
 | 
 | 
      | 
 | 
    protected final Object lock = new Object();  | 
 | 
 | 
 | 
    // STATE  | 
 | 
 | 
 | 
      | 
 | 
    protected AudioFormat format;  | 
 | 
 | 
 | 
      | 
 | 
    protected int bufferSize;  | 
 | 
 | 
 | 
    private volatile boolean running;  | 
 | 
    private volatile boolean started;  | 
 | 
    private volatile boolean active;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    protected AbstractDataLine(DataLine.Info info, AbstractMixer mixer, Control[] controls) { | 
 | 
        this(info, mixer, controls, null, AudioSystem.NOT_SPECIFIED);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    protected AbstractDataLine(DataLine.Info info, AbstractMixer mixer, Control[] controls, AudioFormat format, int bufferSize) { | 
 | 
 | 
 | 
        super(info, mixer, controls);  | 
 | 
 | 
 | 
          | 
 | 
        if (format != null) { | 
 | 
            defaultFormat = format;  | 
 | 
        } else { | 
 | 
              | 
 | 
            defaultFormat = new AudioFormat(44100.0f, 16, 2, true, Platform.isBigEndian());  | 
 | 
        }  | 
 | 
        if (bufferSize > 0) { | 
 | 
            defaultBufferSize = bufferSize;  | 
 | 
        } else { | 
 | 
              | 
 | 
            defaultBufferSize = ((int) (defaultFormat.getFrameRate() / 2)) * defaultFormat.getFrameSize();  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        this.format = defaultFormat;  | 
 | 
        this.bufferSize = defaultBufferSize;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    // DATA LINE METHODS  | 
 | 
 | 
 | 
    public final void open(AudioFormat format, int bufferSize) throws LineUnavailableException { | 
 | 
          | 
 | 
        synchronized (mixer) { | 
 | 
            if (Printer.trace) Printer.trace("> AbstractDataLine.open(format, bufferSize) (class: "+getClass().getName()); | 
 | 
 | 
 | 
              | 
 | 
            if (!isOpen()) { | 
 | 
                // make sure that the format is specified correctly  | 
 | 
                  | 
 | 
                Toolkit.isFullySpecifiedAudioFormat(format);  | 
 | 
 | 
 | 
                if (Printer.debug) Printer.debug("  need to open the mixer..."); | 
 | 
                // reserve mixer resources for this line  | 
 | 
                  | 
 | 
                mixer.open(this);  | 
 | 
 | 
 | 
                try { | 
 | 
                      | 
 | 
                    implOpen(format, bufferSize);  | 
 | 
 | 
 | 
                      | 
 | 
                    setOpen(true);  | 
 | 
 | 
 | 
                } catch (LineUnavailableException e) { | 
 | 
                      | 
 | 
                    mixer.close(this);  | 
 | 
                    throw e;  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                if (Printer.debug) Printer.debug("  dataline already open"); | 
 | 
 | 
 | 
                // if the line is already open and the requested format differs from the  | 
 | 
                // current settings, throw an IllegalStateException  | 
 | 
                  | 
 | 
                if (!format.matches(getFormat())) { | 
 | 
                    throw new IllegalStateException("Line is already open with format " + getFormat() + | 
 | 
                                                    " and bufferSize " + getBufferSize());  | 
 | 
                }  | 
 | 
                  | 
 | 
                if (bufferSize > 0) { | 
 | 
                    setBufferSize(bufferSize);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (Printer.trace) Printer.trace("< AbstractDataLine.open(format, bufferSize) completed"); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    public final void open(AudioFormat format) throws LineUnavailableException { | 
 | 
        open(format, AudioSystem.NOT_SPECIFIED);  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public int available() { | 
 | 
        return 0;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public void drain() { | 
 | 
        if (Printer.trace) Printer.trace("AbstractDataLine: drain"); | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public void flush() { | 
 | 
        if (Printer.trace) Printer.trace("AbstractDataLine: flush"); | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    public final void start() { | 
 | 
          | 
 | 
        synchronized(mixer) { | 
 | 
            if (Printer.trace) Printer.trace("> "+getClass().getName()+".start() - AbstractDataLine"); | 
 | 
 | 
 | 
              | 
 | 
            if (isOpen()) { | 
 | 
 | 
 | 
                if (!isStartedRunning()) { | 
 | 
                    mixer.start(this);  | 
 | 
                    implStart();  | 
 | 
                    running = true;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        synchronized(lock) { | 
 | 
            lock.notifyAll();  | 
 | 
        }  | 
 | 
 | 
 | 
        if (Printer.trace) Printer.trace("< "+getClass().getName()+".start() - AbstractDataLine"); | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    public final void stop() { | 
 | 
 | 
 | 
          | 
 | 
        synchronized(mixer) { | 
 | 
            if (Printer.trace) Printer.trace("> "+getClass().getName()+".stop() - AbstractDataLine"); | 
 | 
 | 
 | 
              | 
 | 
            if (isOpen()) { | 
 | 
 | 
 | 
                if (isStartedRunning()) { | 
 | 
 | 
 | 
                    implStop();  | 
 | 
                    mixer.stop(this);  | 
 | 
 | 
 | 
                    running = false;  | 
 | 
 | 
 | 
                      | 
 | 
                    if (started && (!isActive())) { | 
 | 
                        setStarted(false);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        synchronized(lock) { | 
 | 
            lock.notifyAll();  | 
 | 
        }  | 
 | 
 | 
 | 
        if (Printer.trace) Printer.trace("< "+getClass().getName()+".stop() - AbstractDataLine"); | 
 | 
    }  | 
 | 
 | 
 | 
    // $$jb: 12.10.99: The official API for this is isRunning().  | 
 | 
    // Per the denied RFE 4297981,  | 
 | 
    // the change to isStarted() is technically an unapproved API change.  | 
 | 
    // The 'started' variable is false when playback of data stops.  | 
 | 
    // It is changed throughout the implementation with setStarted().  | 
 | 
    // This state is what should be returned by isRunning() in the API.  | 
 | 
    // Note that the 'running' variable is true between calls to  | 
 | 
    // start() and stop().  This state is accessed now through the  | 
 | 
    // isStartedRunning() method, defined below.  I have not changed  | 
 | 
    // the variable names at this point, since 'running' is accessed  | 
 | 
    // in MixerSourceLine and MixerClip, and I want to touch as little  | 
 | 
    // code as possible to change isStarted() back to isRunning().  | 
 | 
 | 
 | 
    public final boolean isRunning() { | 
 | 
        return started;  | 
 | 
    }  | 
 | 
 | 
 | 
    public final boolean isActive() { | 
 | 
        return active;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    public final long getMicrosecondPosition() { | 
 | 
 | 
 | 
        long microseconds = getLongFramePosition();  | 
 | 
        if (microseconds != AudioSystem.NOT_SPECIFIED) { | 
 | 
            microseconds = Toolkit.frames2micros(getFormat(), microseconds);  | 
 | 
        }  | 
 | 
        return microseconds;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    public final AudioFormat getFormat() { | 
 | 
        return format;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    public final int getBufferSize() { | 
 | 
        return bufferSize;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public final int setBufferSize(int newSize) { | 
 | 
        return getBufferSize();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public final float getLevel() { | 
 | 
        return (float)AudioSystem.NOT_SPECIFIED;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    // HELPER METHODS  | 
 | 
 | 
 | 
    /**  | 
 | 
     * running is true after start is called and before stop is called,  | 
 | 
     * regardless of whether data is actually being presented.  | 
 | 
     */  | 
 | 
    // $$jb: 12.10.99: calling this method isRunning() conflicts with  | 
 | 
    // the official API that was once called isStarted().  Since we  | 
 | 
    // use this method throughout the implementation, I am renaming  | 
 | 
    // it to isStartedRunning().  This is part of backing out the  | 
 | 
    // change denied in RFE 4297981.  | 
 | 
 | 
 | 
    final boolean isStartedRunning() { | 
 | 
        return running;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    final void setActive(boolean active) { | 
 | 
 | 
 | 
        if (Printer.trace) Printer.trace("> AbstractDataLine: setActive(" + active + ")"); | 
 | 
 | 
 | 
        //boolean sendEvents = false;  | 
 | 
        //long position = getLongFramePosition();  | 
 | 
 | 
 | 
        synchronized (this) { | 
 | 
 | 
 | 
            //if (Printer.debug) Printer.debug("    AbstractDataLine: setActive: this.active: " + this.active); | 
 | 
            //if (Printer.debug) Printer.debug("                                 active: " + active); | 
 | 
 | 
 | 
            if (this.active != active) { | 
 | 
                this.active = active;  | 
 | 
                //sendEvents = true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        //if (Printer.debug) Printer.debug("                                 this.active: " + this.active); | 
 | 
        //if (Printer.debug) Printer.debug("                                 sendEvents: " + sendEvents); | 
 | 
 | 
 | 
 | 
 | 
        // $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;  | 
 | 
        // putting them in is technically an API change.  | 
 | 
        // do not generate ACTIVE / INACTIVE events for now  | 
 | 
        // if (sendEvents) { | 
 | 
        //  | 
 | 
        //      if (active) { | 
 | 
        //              sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));  | 
 | 
        //      } else { | 
 | 
        //              sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));  | 
 | 
        //      }  | 
 | 
        //}  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    final void setStarted(boolean started) { | 
 | 
 | 
 | 
        if (Printer.trace) Printer.trace("> AbstractDataLine: setStarted(" + started + ")"); | 
 | 
 | 
 | 
        boolean sendEvents = false;  | 
 | 
        long position = getLongFramePosition();  | 
 | 
 | 
 | 
        synchronized (this) { | 
 | 
 | 
 | 
            //if (Printer.debug) Printer.debug("    AbstractDataLine: setStarted: this.started: " + this.started); | 
 | 
            //if (Printer.debug) Printer.debug("                                  started: " + started); | 
 | 
 | 
 | 
            if (this.started != started) { | 
 | 
                this.started = started;  | 
 | 
                sendEvents = true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        //if (Printer.debug) Printer.debug("                                  this.started: " + this.started); | 
 | 
        //if (Printer.debug) Printer.debug("                                  sendEvents: " + sendEvents); | 
 | 
 | 
 | 
        if (sendEvents) { | 
 | 
 | 
 | 
            if (started) { | 
 | 
                sendEvents(new LineEvent(this, LineEvent.Type.START, position));  | 
 | 
            } else { | 
 | 
                sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (Printer.trace) Printer.trace("< AbstractDataLine: setStarted completed"); | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    final void setEOM() { | 
 | 
 | 
 | 
        if (Printer.trace) Printer.trace("> AbstractDataLine: setEOM()"); | 
 | 
        //$$fb 2002-04-21: sometimes, 2 STOP events are generated.  | 
 | 
          | 
 | 
        setStarted(false);  | 
 | 
        if (Printer.trace) Printer.trace("< AbstractDataLine: setEOM() completed"); | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
    // OVERRIDES OF ABSTRACT LINE METHODS  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final void open() throws LineUnavailableException { | 
 | 
 | 
 | 
        if (Printer.trace) Printer.trace("> "+getClass().getName()+".open() - AbstractDataLine"); | 
 | 
 | 
 | 
          | 
 | 
        open(format, bufferSize);  | 
 | 
        if (Printer.trace) Printer.trace("< "+getClass().getName()+".open() - AbstractDataLine"); | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public final void close() { | 
 | 
          | 
 | 
        synchronized (mixer) { | 
 | 
            if (Printer.trace) Printer.trace("> "+getClass().getName()+".close() - in AbstractDataLine."); | 
 | 
 | 
 | 
            if (isOpen()) { | 
 | 
 | 
 | 
                  | 
 | 
                stop();  | 
 | 
 | 
 | 
                  | 
 | 
                setOpen(false);  | 
 | 
 | 
 | 
                  | 
 | 
                implClose();  | 
 | 
 | 
 | 
                  | 
 | 
                mixer.close(this);  | 
 | 
 | 
 | 
                  | 
 | 
                format = defaultFormat;  | 
 | 
                bufferSize = defaultBufferSize;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (Printer.trace) Printer.trace("< "+getClass().getName()+".close() - in AbstractDataLine"); | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    // IMPLEMENTATIONS OF ABSTRACT LINE ABSTRACE METHODS  | 
 | 
 | 
 | 
 | 
 | 
    // ABSTRACT METHODS  | 
 | 
 | 
 | 
    abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;  | 
 | 
    abstract void implClose();  | 
 | 
 | 
 | 
    abstract void implStart();  | 
 | 
    abstract void implStop();  | 
 | 
}  |