< prev index next >

src/java.desktop/share/classes/com/sun/media/sound/AbstractDataLine.java

Print this page




  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 javax.sound.sampled.AudioFormat;
  29 import javax.sound.sampled.AudioSystem;
  30 import javax.sound.sampled.Control;
  31 import javax.sound.sampled.DataLine;
  32 import javax.sound.sampled.LineEvent;
  33 import javax.sound.sampled.LineUnavailableException;
  34 
  35 
  36 /**
  37  * AbstractDataLine
  38  *
  39  * @author Kara Kytle
  40  */
  41 abstract class AbstractDataLine extends AbstractLine implements DataLine {
  42 
  43     // DEFAULTS
  44 
  45     // default format
  46     private final AudioFormat defaultFormat;
  47 
  48     // default buffer size in bytes
  49     private final int defaultBufferSize;
  50 
  51     // the lock for synchronization
  52     protected final Object lock = new Object();
  53 
  54     // STATE
  55 


 130             } else {
 131                 if (Printer.debug) Printer.debug("  dataline already open");
 132 
 133                 // if the line is already open and the requested format differs from the
 134                 // current settings, throw an IllegalStateException
 135                 //$$fb 2002-04-02: fix for 4661602: Buffersize is checked when re-opening line
 136                 if (!format.matches(getFormat())) {
 137                     throw new IllegalStateException("Line is already open with format " + getFormat() +
 138                                                     " and bufferSize " + getBufferSize());
 139                 }
 140                 //$$fb 2002-07-26: allow changing the buffersize of already open lines
 141                 if (bufferSize > 0) {
 142                     setBufferSize(bufferSize);
 143                 }
 144             }
 145 
 146             if (Printer.trace) Printer.trace("< AbstractDataLine.open(format, bufferSize) completed");
 147         }
 148     }
 149 
 150 
 151     public final void open(AudioFormat format) throws LineUnavailableException {
 152         open(format, AudioSystem.NOT_SPECIFIED);
 153     }
 154 
 155 
 156     /**
 157      * This implementation always returns 0.
 158      */

 159     public int available() {
 160         return 0;
 161     }
 162 
 163 
 164     /**
 165      * This implementation does nothing.
 166      */

 167     public void drain() {
 168         if (Printer.trace) Printer.trace("AbstractDataLine: drain");
 169     }
 170 
 171 
 172     /**
 173      * This implementation does nothing.
 174      */

 175     public void flush() {
 176         if (Printer.trace) Printer.trace("AbstractDataLine: flush");
 177     }
 178 
 179 
 180     public final void start() {
 181         //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
 182         synchronized(mixer) {
 183             if (Printer.trace) Printer.trace("> "+getClass().getName()+".start() - AbstractDataLine");
 184 
 185             // $$kk: 06.06.99: if not open, this doesn't work....???
 186             if (isOpen()) {
 187 
 188                 if (!isStartedRunning()) {
 189                     mixer.start(this);
 190                     implStart();
 191                     running = true;
 192                 }
 193             }
 194         }
 195 
 196         synchronized(lock) {
 197             lock.notifyAll();
 198         }
 199 
 200         if (Printer.trace) Printer.trace("< "+getClass().getName()+".start() - AbstractDataLine");
 201     }
 202 
 203 
 204     public final void stop() {
 205 
 206         //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
 207         synchronized(mixer) {
 208             if (Printer.trace) Printer.trace("> "+getClass().getName()+".stop() - AbstractDataLine");
 209 
 210             // $$kk: 06.06.99: if not open, this doesn't work.
 211             if (isOpen()) {
 212 
 213                 if (isStartedRunning()) {
 214 
 215                     implStop();
 216                     mixer.stop(this);
 217 
 218                     running = false;
 219 
 220                     // $$kk: 11.10.99: this is not exactly correct, but will probably work
 221                     if (started && (!isActive())) {
 222                         setStarted(false);
 223                     }


 228         synchronized(lock) {
 229             lock.notifyAll();
 230         }
 231 
 232         if (Printer.trace) Printer.trace("< "+getClass().getName()+".stop() - AbstractDataLine");
 233     }
 234 
 235     // $$jb: 12.10.99: The official API for this is isRunning().
 236     // Per the denied RFE 4297981,
 237     // the change to isStarted() is technically an unapproved API change.
 238     // The 'started' variable is false when playback of data stops.
 239     // It is changed throughout the implementation with setStarted().
 240     // This state is what should be returned by isRunning() in the API.
 241     // Note that the 'running' variable is true between calls to
 242     // start() and stop().  This state is accessed now through the
 243     // isStartedRunning() method, defined below.  I have not changed
 244     // the variable names at this point, since 'running' is accessed
 245     // in MixerSourceLine and MixerClip, and I want to touch as little
 246     // code as possible to change isStarted() back to isRunning().
 247 

 248     public final boolean isRunning() {
 249         return started;
 250     }
 251 

 252     public final boolean isActive() {
 253         return active;
 254     }
 255 
 256 
 257     public final long getMicrosecondPosition() {
 258 
 259         long microseconds = getLongFramePosition();
 260         if (microseconds != AudioSystem.NOT_SPECIFIED) {
 261             microseconds = Toolkit.frames2micros(getFormat(), microseconds);
 262         }
 263         return microseconds;
 264     }
 265 
 266 
 267     public final AudioFormat getFormat() {
 268         return format;
 269     }
 270 
 271 
 272     public final int getBufferSize() {
 273         return bufferSize;
 274     }
 275 
 276     /**
 277      * This implementation does NOT change the buffer size
 278      */
 279     public final int setBufferSize(int newSize) {
 280         return getBufferSize();
 281     }
 282 
 283     /**
 284      * This implementation returns AudioSystem.NOT_SPECIFIED.
 285      */

 286     public final float getLevel() {
 287         return (float)AudioSystem.NOT_SPECIFIED;
 288     }
 289 
 290 
 291     // HELPER METHODS
 292 
 293     /**
 294      * running is true after start is called and before stop is called,
 295      * regardless of whether data is actually being presented.
 296      */
 297     // $$jb: 12.10.99: calling this method isRunning() conflicts with
 298     // the official API that was once called isStarted().  Since we
 299     // use this method throughout the implementation, I am renaming
 300     // it to isStartedRunning().  This is part of backing out the
 301     // change denied in RFE 4297981.
 302 
 303     final boolean isStartedRunning() {
 304         return running;
 305     }
 306 
 307     /**
 308      * This method sets the active state and generates
 309      * events if it changes.
 310      */
 311     final void setActive(boolean active) {
 312 
 313         if (Printer.trace) Printer.trace("> AbstractDataLine: setActive(" + active + ")");
 314 
 315         //boolean sendEvents = false;
 316         //long position = getLongFramePosition();
 317 
 318         synchronized (this) {
 319 
 320             //if (Printer.debug) Printer.debug("    AbstractDataLine: setActive: this.active: " + this.active);
 321             //if (Printer.debug) Printer.debug("                                 active: " + active);
 322 
 323             if (this.active != active) {
 324                 this.active = active;
 325                 //sendEvents = true;
 326             }
 327         }
 328 
 329         //if (Printer.debug) Printer.debug("                                 this.active: " + this.active);
 330         //if (Printer.debug) Printer.debug("                                 sendEvents: " + sendEvents);
 331 
 332 
 333         // $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;
 334         // putting them in is technically an API change.
 335         // do not generate ACTIVE / INACTIVE events for now
 336         // if (sendEvents) {
 337         //
 338         //      if (active) {
 339         //              sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));
 340         //      } else {
 341         //              sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));
 342         //      }
 343         //}
 344     }
 345 
 346     /**
 347      * This method sets the started state and generates
 348      * events if it changes.
 349      */
 350     final void setStarted(boolean started) {
 351 
 352         if (Printer.trace) Printer.trace("> AbstractDataLine: setStarted(" + started + ")");
 353 
 354         boolean sendEvents = false;
 355         long position = getLongFramePosition();
 356 
 357         synchronized (this) {
 358 
 359             //if (Printer.debug) Printer.debug("    AbstractDataLine: setStarted: this.started: " + this.started);
 360             //if (Printer.debug) Printer.debug("                                  started: " + started);
 361 
 362             if (this.started != started) {
 363                 this.started = started;
 364                 sendEvents = true;
 365             }
 366         }
 367 
 368         //if (Printer.debug) Printer.debug("                                  this.started: " + this.started);
 369         //if (Printer.debug) Printer.debug("                                  sendEvents: " + sendEvents);
 370 
 371         if (sendEvents) {
 372 
 373             if (started) {
 374                 sendEvents(new LineEvent(this, LineEvent.Type.START, position));
 375             } else {
 376                 sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));
 377             }
 378         }
 379         if (Printer.trace) Printer.trace("< AbstractDataLine: setStarted completed");
 380     }
 381 
 382 
 383     /**
 384      * This method generates a STOP event and sets the started state to false.
 385      * It is here for historic reasons when an EOM event existed.
 386      */
 387     final void setEOM() {
 388 
 389         if (Printer.trace) Printer.trace("> AbstractDataLine: setEOM()");
 390         //$$fb 2002-04-21: sometimes, 2 STOP events are generated.
 391         // better use setStarted() to send STOP event.
 392         setStarted(false);
 393         if (Printer.trace) Printer.trace("< AbstractDataLine: setEOM() completed");
 394     }
 395 
 396 
 397 
 398 
 399     // OVERRIDES OF ABSTRACT LINE METHODS
 400 
 401     /**
 402      * Try to open the line with the current format and buffer size values.
 403      * If the line is not open, these will be the defaults.  If the
 404      * line is open, this should return quietly because the values
 405      * requested will match the current ones.
 406      */

 407     public final void open() throws LineUnavailableException {
 408 
 409         if (Printer.trace) Printer.trace("> "+getClass().getName()+".open() - AbstractDataLine");
 410 
 411         // this may throw a LineUnavailableException.
 412         open(format, bufferSize);
 413         if (Printer.trace) Printer.trace("< "+getClass().getName()+".open() - AbstractDataLine");
 414     }
 415 
 416 
 417     /**
 418      * This should also stop the line.  The closed line should not be running or active.
 419      * After we close the line, we reset the format and buffer size to the defaults.
 420      */

 421     public final void close() {
 422         //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
 423         synchronized (mixer) {
 424             if (Printer.trace) Printer.trace("> "+getClass().getName()+".close() - in AbstractDataLine.");
 425 
 426             if (isOpen()) {
 427 
 428                 // stop
 429                 stop();
 430 
 431                 // set the open state to false and send events
 432                 setOpen(false);
 433 
 434                 // close resources for this line
 435                 implClose();
 436 
 437                 // release mixer resources for this line
 438                 mixer.close(this);
 439 
 440                 // reset format and buffer size to the defaults
 441                 format = defaultFormat;
 442                 bufferSize = defaultBufferSize;
 443             }
 444         }
 445         if (Printer.trace) Printer.trace("< "+getClass().getName()+".close() - in AbstractDataLine");
 446     }
 447 
 448 
 449     // IMPLEMENTATIONS OF ABSTRACT LINE ABSTRACE METHODS
 450 
 451 
 452     // ABSTRACT METHODS
 453 
 454     abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;
 455     abstract void implClose();
 456 
 457     abstract void implStart();
 458     abstract void implStop();
 459 }


  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 javax.sound.sampled.AudioFormat;
  29 import javax.sound.sampled.AudioSystem;
  30 import javax.sound.sampled.Control;
  31 import javax.sound.sampled.DataLine;
  32 import javax.sound.sampled.LineEvent;
  33 import javax.sound.sampled.LineUnavailableException;
  34 

  35 /**
  36  * AbstractDataLine
  37  *
  38  * @author Kara Kytle
  39  */
  40 abstract class AbstractDataLine extends AbstractLine implements DataLine {
  41 
  42     // DEFAULTS
  43 
  44     // default format
  45     private final AudioFormat defaultFormat;
  46 
  47     // default buffer size in bytes
  48     private final int defaultBufferSize;
  49 
  50     // the lock for synchronization
  51     protected final Object lock = new Object();
  52 
  53     // STATE
  54 


 129             } else {
 130                 if (Printer.debug) Printer.debug("  dataline already open");
 131 
 132                 // if the line is already open and the requested format differs from the
 133                 // current settings, throw an IllegalStateException
 134                 //$$fb 2002-04-02: fix for 4661602: Buffersize is checked when re-opening line
 135                 if (!format.matches(getFormat())) {
 136                     throw new IllegalStateException("Line is already open with format " + getFormat() +
 137                                                     " and bufferSize " + getBufferSize());
 138                 }
 139                 //$$fb 2002-07-26: allow changing the buffersize of already open lines
 140                 if (bufferSize > 0) {
 141                     setBufferSize(bufferSize);
 142                 }
 143             }
 144 
 145             if (Printer.trace) Printer.trace("< AbstractDataLine.open(format, bufferSize) completed");
 146         }
 147     }
 148 

 149     public final void open(AudioFormat format) throws LineUnavailableException {
 150         open(format, AudioSystem.NOT_SPECIFIED);
 151     }
 152 

 153     /**
 154      * This implementation always returns 0.
 155      */
 156     @Override
 157     public int available() {
 158         return 0;
 159     }
 160 

 161     /**
 162      * This implementation does nothing.
 163      */
 164     @Override
 165     public void drain() {
 166         if (Printer.trace) Printer.trace("AbstractDataLine: drain");
 167     }
 168 

 169     /**
 170      * This implementation does nothing.
 171      */
 172     @Override
 173     public void flush() {
 174         if (Printer.trace) Printer.trace("AbstractDataLine: flush");
 175     }
 176 
 177     @Override
 178     public final void start() {
 179         //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
 180         synchronized(mixer) {
 181             if (Printer.trace) Printer.trace("> "+getClass().getName()+".start() - AbstractDataLine");
 182 
 183             // $$kk: 06.06.99: if not open, this doesn't work....???
 184             if (isOpen()) {
 185 
 186                 if (!isStartedRunning()) {
 187                     mixer.start(this);
 188                     implStart();
 189                     running = true;
 190                 }
 191             }
 192         }
 193 
 194         synchronized(lock) {
 195             lock.notifyAll();
 196         }
 197 
 198         if (Printer.trace) Printer.trace("< "+getClass().getName()+".start() - AbstractDataLine");
 199     }
 200 
 201     @Override
 202     public final void stop() {
 203 
 204         //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
 205         synchronized(mixer) {
 206             if (Printer.trace) Printer.trace("> "+getClass().getName()+".stop() - AbstractDataLine");
 207 
 208             // $$kk: 06.06.99: if not open, this doesn't work.
 209             if (isOpen()) {
 210 
 211                 if (isStartedRunning()) {
 212 
 213                     implStop();
 214                     mixer.stop(this);
 215 
 216                     running = false;
 217 
 218                     // $$kk: 11.10.99: this is not exactly correct, but will probably work
 219                     if (started && (!isActive())) {
 220                         setStarted(false);
 221                     }


 226         synchronized(lock) {
 227             lock.notifyAll();
 228         }
 229 
 230         if (Printer.trace) Printer.trace("< "+getClass().getName()+".stop() - AbstractDataLine");
 231     }
 232 
 233     // $$jb: 12.10.99: The official API for this is isRunning().
 234     // Per the denied RFE 4297981,
 235     // the change to isStarted() is technically an unapproved API change.
 236     // The 'started' variable is false when playback of data stops.
 237     // It is changed throughout the implementation with setStarted().
 238     // This state is what should be returned by isRunning() in the API.
 239     // Note that the 'running' variable is true between calls to
 240     // start() and stop().  This state is accessed now through the
 241     // isStartedRunning() method, defined below.  I have not changed
 242     // the variable names at this point, since 'running' is accessed
 243     // in MixerSourceLine and MixerClip, and I want to touch as little
 244     // code as possible to change isStarted() back to isRunning().
 245 
 246     @Override
 247     public final boolean isRunning() {
 248         return started;
 249     }
 250 
 251     @Override
 252     public final boolean isActive() {
 253         return active;
 254     }
 255 
 256     @Override
 257     public final long getMicrosecondPosition() {
 258 
 259         long microseconds = getLongFramePosition();
 260         if (microseconds != AudioSystem.NOT_SPECIFIED) {
 261             microseconds = Toolkit.frames2micros(getFormat(), microseconds);
 262         }
 263         return microseconds;
 264     }
 265 
 266     @Override
 267     public final AudioFormat getFormat() {
 268         return format;
 269     }
 270 
 271     @Override
 272     public final int getBufferSize() {
 273         return bufferSize;
 274     }
 275 
 276     /**
 277      * This implementation does NOT change the buffer size
 278      */
 279     public final int setBufferSize(int newSize) {
 280         return getBufferSize();
 281     }
 282 
 283     /**
 284      * This implementation returns AudioSystem.NOT_SPECIFIED.
 285      */
 286     @Override
 287     public final float getLevel() {
 288         return (float)AudioSystem.NOT_SPECIFIED;
 289     }
 290 

 291     // HELPER METHODS
 292 
 293     /**
 294      * running is true after start is called and before stop is called,
 295      * regardless of whether data is actually being presented.
 296      */
 297     // $$jb: 12.10.99: calling this method isRunning() conflicts with
 298     // the official API that was once called isStarted().  Since we
 299     // use this method throughout the implementation, I am renaming
 300     // it to isStartedRunning().  This is part of backing out the
 301     // change denied in RFE 4297981.
 302 
 303     final boolean isStartedRunning() {
 304         return running;
 305     }
 306 
 307     /**
 308      * This method sets the active state and generates
 309      * events if it changes.
 310      */
 311     final void setActive(boolean active) {
 312 
 313         if (Printer.trace) Printer.trace("> AbstractDataLine: setActive(" + active + ")");
 314 
 315         //boolean sendEvents = false;
 316         //long position = getLongFramePosition();
 317 
 318         synchronized (this) {
 319 



 320             if (this.active != active) {
 321                 this.active = active;
 322                 //sendEvents = true;
 323             }
 324         }
 325 




 326         // $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;
 327         // putting them in is technically an API change.
 328         // do not generate ACTIVE / INACTIVE events for now
 329         // if (sendEvents) {
 330         //
 331         //      if (active) {
 332         //              sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));
 333         //      } else {
 334         //              sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));
 335         //      }
 336         //}
 337     }
 338 
 339     /**
 340      * This method sets the started state and generates
 341      * events if it changes.
 342      */
 343     final void setStarted(boolean started) {
 344 
 345         if (Printer.trace) Printer.trace("> AbstractDataLine: setStarted(" + started + ")");
 346 
 347         boolean sendEvents = false;
 348         long position = getLongFramePosition();
 349 
 350         synchronized (this) {
 351 



 352             if (this.started != started) {
 353                 this.started = started;
 354                 sendEvents = true;
 355             }
 356         }
 357 



 358         if (sendEvents) {
 359 
 360             if (started) {
 361                 sendEvents(new LineEvent(this, LineEvent.Type.START, position));
 362             } else {
 363                 sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));
 364             }
 365         }
 366         if (Printer.trace) Printer.trace("< AbstractDataLine: setStarted completed");
 367     }
 368 

 369     /**
 370      * This method generates a STOP event and sets the started state to false.
 371      * It is here for historic reasons when an EOM event existed.
 372      */
 373     final void setEOM() {
 374 
 375         if (Printer.trace) Printer.trace("> AbstractDataLine: setEOM()");
 376         //$$fb 2002-04-21: sometimes, 2 STOP events are generated.
 377         // better use setStarted() to send STOP event.
 378         setStarted(false);
 379         if (Printer.trace) Printer.trace("< AbstractDataLine: setEOM() completed");
 380     }
 381 



 382     // OVERRIDES OF ABSTRACT LINE METHODS
 383 
 384     /**
 385      * Try to open the line with the current format and buffer size values.
 386      * If the line is not open, these will be the defaults.  If the
 387      * line is open, this should return quietly because the values
 388      * requested will match the current ones.
 389      */
 390     @Override
 391     public final void open() throws LineUnavailableException {
 392 
 393         if (Printer.trace) Printer.trace("> "+getClass().getName()+".open() - AbstractDataLine");
 394 
 395         // this may throw a LineUnavailableException.
 396         open(format, bufferSize);
 397         if (Printer.trace) Printer.trace("< "+getClass().getName()+".open() - AbstractDataLine");
 398     }
 399 

 400     /**
 401      * This should also stop the line.  The closed line should not be running or active.
 402      * After we close the line, we reset the format and buffer size to the defaults.
 403      */
 404     @Override
 405     public final void close() {
 406         //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
 407         synchronized (mixer) {
 408             if (Printer.trace) Printer.trace("> "+getClass().getName()+".close() - in AbstractDataLine.");
 409 
 410             if (isOpen()) {
 411 
 412                 // stop
 413                 stop();
 414 
 415                 // set the open state to false and send events
 416                 setOpen(false);
 417 
 418                 // close resources for this line
 419                 implClose();
 420 
 421                 // release mixer resources for this line
 422                 mixer.close(this);
 423 
 424                 // reset format and buffer size to the defaults
 425                 format = defaultFormat;
 426                 bufferSize = defaultBufferSize;
 427             }
 428         }
 429         if (Printer.trace) Printer.trace("< "+getClass().getName()+".close() - in AbstractDataLine");
 430     }
 431 






 432     abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;
 433     abstract void implClose();
 434 
 435     abstract void implStart();
 436     abstract void implStop();
 437 }
< prev index next >