Save This Page
Home » openjdk-7 » com.sun.media » sound » [javadoc | source]
    1   /*
    2    * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package com.sun.media.sound;
   27   
   28   import java.util.Vector;
   29   
   30   import javax.sound.sampled.AudioFormat;
   31   import javax.sound.sampled.AudioSystem;
   32   import javax.sound.sampled.Control;
   33   import javax.sound.sampled.DataLine;
   34   import javax.sound.sampled.LineEvent;
   35   import javax.sound.sampled.LineUnavailableException;
   36   import javax.sound.sampled.Mixer;
   37   
   38   
   39   /**
   40    * AbstractDataLine
   41    *
   42    * @author Kara Kytle
   43    */
   44   abstract class AbstractDataLine extends AbstractLine implements DataLine {
   45   
   46       // DEFAULTS
   47   
   48       // default format
   49       protected /*final*/ AudioFormat defaultFormat;
   50   
   51       // default buffer size in bytes
   52       protected /*final*/ int defaultBufferSize;
   53   
   54       // the lock for synchronization
   55       protected Object lock = new Object();
   56   
   57       // STATE
   58   
   59       // current format
   60       protected AudioFormat format;
   61   
   62       // current buffer size in bytes
   63       protected int bufferSize;
   64   
   65       protected boolean running = false;
   66       private boolean started = false;
   67       private boolean active = false;
   68   
   69   
   70       /**
   71        * Constructs a new AbstractLine.
   72        */
   73       protected AbstractDataLine(DataLine.Info info, AbstractMixer mixer, Control[] controls) {
   74           this(info, mixer, controls, null, AudioSystem.NOT_SPECIFIED);
   75       }
   76   
   77       /**
   78        * Constructs a new AbstractLine.
   79        */
   80       protected AbstractDataLine(DataLine.Info info, AbstractMixer mixer, Control[] controls, AudioFormat format, int bufferSize) {
   81   
   82           super(info, mixer, controls);
   83   
   84           // record the default values
   85           if (format != null) {
   86               defaultFormat = format;
   87           } else {
   88               // default CD-quality
   89               defaultFormat = new AudioFormat(44100.0f, 16, 2, true, Platform.isBigEndian());
   90           }
   91           if (bufferSize > 0) {
   92               defaultBufferSize = bufferSize;
   93           } else {
   94               // 0.5 seconds buffer
   95               defaultBufferSize = ((int) (defaultFormat.getFrameRate() / 2)) * defaultFormat.getFrameSize();
   96           }
   97   
   98           // set the initial values to the defaults
   99           this.format = defaultFormat;
  100           this.bufferSize = defaultBufferSize;
  101       }
  102   
  103   
  104       // DATA LINE METHODS
  105   
  106       public void open(AudioFormat format, int bufferSize) throws LineUnavailableException {
  107           //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
  108           synchronized (mixer) {
  109               if (Printer.trace) Printer.trace("> AbstractDataLine.open(format, bufferSize) (class: "+getClass().getName());
  110   
  111               // if the line is not currently open, try to open it with this format and buffer size
  112               if (!isOpen()) {
  113                   // make sure that the format is specified correctly
  114                   // $$fb part of fix for 4679187: Clip.open() throws unexpected Exceptions
  115                   Toolkit.isFullySpecifiedAudioFormat(format);
  116   
  117                   if (Printer.debug) Printer.debug("  need to open the mixer...");
  118                   // reserve mixer resources for this line
  119                   //mixer.open(this, format, bufferSize);
  120                   mixer.open(this);
  121   
  122                   try {
  123                       // open the data line.  may throw LineUnavailableException.
  124                       implOpen(format, bufferSize);
  125   
  126                       // if we succeeded, set the open state to true and send events
  127                       setOpen(true);
  128   
  129                   } catch (LineUnavailableException e) {
  130                       // release mixer resources for this line and then throw the exception
  131                       mixer.close(this);
  132                       throw e;
  133                   }
  134               } else {
  135                   if (Printer.debug) Printer.debug("  dataline already open");
  136   
  137                   // if the line is already open and the requested format differs from the
  138                   // current settings, throw an IllegalStateException
  139                   //$$fb 2002-04-02: fix for 4661602: Buffersize is checked when re-opening line
  140                   if (!format.matches(getFormat())) {
  141                       throw new IllegalStateException("Line is already open with format " + getFormat() +
  142                                                       " and bufferSize " + getBufferSize());
  143                   }
  144                   //$$fb 2002-07-26: allow changing the buffersize of already open lines
  145                   if (bufferSize > 0) {
  146                       setBufferSize(bufferSize);
  147                   }
  148               }
  149   
  150               if (Printer.trace) Printer.trace("< AbstractDataLine.open(format, bufferSize) completed");
  151           }
  152       }
  153   
  154   
  155       public void open(AudioFormat format) throws LineUnavailableException {
  156           open(format, AudioSystem.NOT_SPECIFIED);
  157       }
  158   
  159   
  160       /**
  161        * This implementation always returns 0.
  162        */
  163       public int available() {
  164           return 0;
  165       }
  166   
  167   
  168       /**
  169        * This implementation does nothing.
  170        */
  171       public void drain() {
  172           if (Printer.trace) Printer.trace("AbstractDataLine: drain");
  173       }
  174   
  175   
  176       /**
  177        * This implementation does nothing.
  178        */
  179       public void flush() {
  180           if (Printer.trace) Printer.trace("AbstractDataLine: flush");
  181       }
  182   
  183   
  184       public void start() {
  185           //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
  186           synchronized(mixer) {
  187               if (Printer.trace) Printer.trace("> "+getClass().getName()+".start() - AbstractDataLine");
  188   
  189               // $$kk: 06.06.99: if not open, this doesn't work....???
  190               if (isOpen()) {
  191   
  192                   if (!isStartedRunning()) {
  193                       mixer.start(this);
  194                       implStart();
  195                       running = true;
  196                   }
  197               }
  198           }
  199   
  200           synchronized(lock) {
  201               lock.notifyAll();
  202           }
  203   
  204           if (Printer.trace) Printer.trace("< "+getClass().getName()+".start() - AbstractDataLine");
  205       }
  206   
  207   
  208       public void stop() {
  209   
  210           //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
  211           synchronized(mixer) {
  212               if (Printer.trace) Printer.trace("> "+getClass().getName()+".stop() - AbstractDataLine");
  213   
  214               // $$kk: 06.06.99: if not open, this doesn't work.
  215               if (isOpen()) {
  216   
  217                   if (isStartedRunning()) {
  218   
  219                       implStop();
  220                       mixer.stop(this);
  221   
  222                       running = false;
  223   
  224                       // $$kk: 11.10.99: this is not exactly correct, but will probably work
  225                       if (started && (!isActive())) {
  226                           setStarted(false);
  227                       }
  228                   }
  229               }
  230           }
  231   
  232           synchronized(lock) {
  233               lock.notifyAll();
  234           }
  235   
  236           if (Printer.trace) Printer.trace("< "+getClass().getName()+".stop() - AbstractDataLine");
  237       }
  238   
  239       // $$jb: 12.10.99: The official API for this is isRunning().
  240       // Per the denied RFE 4297981,
  241       // the change to isStarted() is technically an unapproved API change.
  242       // The 'started' variable is false when playback of data stops.
  243       // It is changed throughout the implementation with setStarted().
  244       // This state is what should be returned by isRunning() in the API.
  245       // Note that the 'running' variable is true between calls to
  246       // start() and stop().  This state is accessed now through the
  247       // isStartedRunning() method, defined below.  I have not changed
  248       // the variable names at this point, since 'running' is accessed
  249       // in MixerSourceLine and MixerClip, and I want to touch as little
  250       // code as possible to change isStarted() back to isRunning().
  251   
  252       public boolean isRunning() {
  253           return started;
  254       }
  255   
  256       public boolean isActive() {
  257           return active;
  258       }
  259   
  260   
  261       public long getMicrosecondPosition() {
  262   
  263           long microseconds = getLongFramePosition();
  264           if (microseconds != AudioSystem.NOT_SPECIFIED) {
  265               microseconds = Toolkit.frames2micros(getFormat(), microseconds);
  266           }
  267           return microseconds;
  268       }
  269   
  270   
  271       public AudioFormat getFormat() {
  272           return format;
  273       }
  274   
  275   
  276       public int getBufferSize() {
  277           return bufferSize;
  278       }
  279   
  280       /**
  281        * This implementation does NOT change the buffer size
  282        */
  283       public int setBufferSize(int newSize) {
  284           return getBufferSize();
  285       }
  286   
  287       /**
  288        * This implementation returns AudioSystem.NOT_SPECIFIED.
  289        */
  290       public float getLevel() {
  291           return (float)AudioSystem.NOT_SPECIFIED;
  292       }
  293   
  294   
  295       // HELPER METHODS
  296   
  297       /**
  298        * running is true after start is called and before stop is called,
  299        * regardless of whether data is actually being presented.
  300        */
  301       // $$jb: 12.10.99: calling this method isRunning() conflicts with
  302       // the official API that was once called isStarted().  Since we
  303       // use this method throughout the implementation, I am renaming
  304       // it to isStartedRunning().  This is part of backing out the
  305       // change denied in RFE 4297981.
  306   
  307       protected boolean isStartedRunning() {
  308           return running;
  309       }
  310   
  311       /**
  312        * This method sets the active state and generates
  313        * events if it changes.
  314        */
  315       protected void setActive(boolean active) {
  316   
  317           if (Printer.trace) Printer.trace("> AbstractDataLine: setActive(" + active + ")");
  318   
  319           //boolean sendEvents = false;
  320           //long position = getLongFramePosition();
  321   
  322           synchronized (this) {
  323   
  324               //if (Printer.debug) Printer.debug("    AbstractDataLine: setActive: this.active: " + this.active);
  325               //if (Printer.debug) Printer.debug("                                 active: " + active);
  326   
  327               if (this.active != active) {
  328                   this.active = active;
  329                   //sendEvents = true;
  330               }
  331           }
  332   
  333           //if (Printer.debug) Printer.debug("                                 this.active: " + this.active);
  334           //if (Printer.debug) Printer.debug("                                 sendEvents: " + sendEvents);
  335   
  336   
  337           // $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;
  338           // putting them in is technically an API change.
  339           // do not generate ACTIVE / INACTIVE events for now
  340           // if (sendEvents) {
  341           //
  342           //      if (active) {
  343           //              sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));
  344           //      } else {
  345           //              sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));
  346           //      }
  347           //}
  348       }
  349   
  350       /**
  351        * This method sets the started state and generates
  352        * events if it changes.
  353        */
  354       protected void setStarted(boolean started) {
  355   
  356           if (Printer.trace) Printer.trace("> AbstractDataLine: setStarted(" + started + ")");
  357   
  358           boolean sendEvents = false;
  359           long position = getLongFramePosition();
  360   
  361           synchronized (this) {
  362   
  363               //if (Printer.debug) Printer.debug("    AbstractDataLine: setStarted: this.started: " + this.started);
  364               //if (Printer.debug) Printer.debug("                                  started: " + started);
  365   
  366               if (this.started != started) {
  367                   this.started = started;
  368                   sendEvents = true;
  369               }
  370           }
  371   
  372           //if (Printer.debug) Printer.debug("                                  this.started: " + this.started);
  373           //if (Printer.debug) Printer.debug("                                  sendEvents: " + sendEvents);
  374   
  375           if (sendEvents) {
  376   
  377               if (started) {
  378                   sendEvents(new LineEvent(this, LineEvent.Type.START, position));
  379               } else {
  380                   sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));
  381               }
  382           }
  383           if (Printer.trace) Printer.trace("< AbstractDataLine: setStarted completed");
  384       }
  385   
  386   
  387       /**
  388        * This method generates a STOP event and sets the started state to false.
  389        * It is here for historic reasons when an EOM event existed.
  390        */
  391       protected void setEOM() {
  392   
  393           if (Printer.trace) Printer.trace("> AbstractDataLine: setEOM()");
  394           //$$fb 2002-04-21: sometimes, 2 STOP events are generated.
  395           // better use setStarted() to send STOP event.
  396           setStarted(false);
  397           if (Printer.trace) Printer.trace("< AbstractDataLine: setEOM() completed");
  398       }
  399   
  400   
  401   
  402   
  403       // OVERRIDES OF ABSTRACT LINE METHODS
  404   
  405       /**
  406        * Try to open the line with the current format and buffer size values.
  407        * If the line is not open, these will be the defaults.  If the
  408        * line is open, this should return quietly because the values
  409        * requested will match the current ones.
  410        */
  411       public void open() throws LineUnavailableException {
  412   
  413           if (Printer.trace) Printer.trace("> "+getClass().getName()+".open() - AbstractDataLine");
  414   
  415           // this may throw a LineUnavailableException.
  416           open(format, bufferSize);
  417           if (Printer.trace) Printer.trace("< "+getClass().getName()+".open() - AbstractDataLine");
  418       }
  419   
  420   
  421       /**
  422        * This should also stop the line.  The closed line should not be running or active.
  423        * After we close the line, we reset the format and buffer size to the defaults.
  424        */
  425       public void close() {
  426           //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
  427           synchronized (mixer) {
  428               if (Printer.trace) Printer.trace("> "+getClass().getName()+".close() - in AbstractDataLine.");
  429   
  430               if (isOpen()) {
  431   
  432                   // stop
  433                   stop();
  434   
  435                   // set the open state to false and send events
  436                   setOpen(false);
  437   
  438                   // close resources for this line
  439                   implClose();
  440   
  441                   // release mixer resources for this line
  442                   mixer.close(this);
  443   
  444                   // reset format and buffer size to the defaults
  445                   format = defaultFormat;
  446                   bufferSize = defaultBufferSize;
  447               }
  448           }
  449           if (Printer.trace) Printer.trace("< "+getClass().getName()+".close() - in AbstractDataLine");
  450       }
  451   
  452   
  453       // IMPLEMENTATIONS OF ABSTRACT LINE ABSTRACE METHODS
  454   
  455   
  456       // ABSTRACT METHODS
  457   
  458       abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;
  459       abstract void implClose();
  460   
  461       abstract void implStart();
  462       abstract void implStop();
  463   }

Save This Page
Home » openjdk-7 » com.sun.media » sound » [javadoc | source]