Path: blob/master/src/java.desktop/share/classes/com/sun/media/sound/AbstractDataLine.java
41161 views
/*1* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.media.sound;2627import javax.sound.sampled.AudioFormat;28import javax.sound.sampled.AudioSystem;29import javax.sound.sampled.Control;30import javax.sound.sampled.DataLine;31import javax.sound.sampled.LineEvent;32import javax.sound.sampled.LineUnavailableException;3334/**35* AbstractDataLine36*37* @author Kara Kytle38*/39abstract class AbstractDataLine extends AbstractLine implements DataLine {4041// DEFAULTS4243// default format44private final AudioFormat defaultFormat;4546// default buffer size in bytes47private final int defaultBufferSize;4849// the lock for synchronization50protected final Object lock = new Object();5152// STATE5354// current format55protected AudioFormat format;5657// current buffer size in bytes58protected int bufferSize;5960private volatile boolean running;61private volatile boolean started;62private volatile boolean active;6364/**65* Constructs a new AbstractLine.66*/67protected AbstractDataLine(DataLine.Info info, AbstractMixer mixer, Control[] controls) {68this(info, mixer, controls, null, AudioSystem.NOT_SPECIFIED);69}7071/**72* Constructs a new AbstractLine.73*/74protected AbstractDataLine(DataLine.Info info, AbstractMixer mixer, Control[] controls, AudioFormat format, int bufferSize) {7576super(info, mixer, controls);7778// record the default values79if (format != null) {80defaultFormat = format;81} else {82// default CD-quality83defaultFormat = new AudioFormat(44100.0f, 16, 2, true, Platform.isBigEndian());84}85if (bufferSize > 0) {86defaultBufferSize = bufferSize;87} else {88// 0.5 seconds buffer89defaultBufferSize = ((int) (defaultFormat.getFrameRate() / 2)) * defaultFormat.getFrameSize();90}9192// set the initial values to the defaults93this.format = defaultFormat;94this.bufferSize = defaultBufferSize;95}969798// DATA LINE METHODS99100public final void open(AudioFormat format, int bufferSize) throws LineUnavailableException {101//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !102synchronized (mixer) {103// if the line is not currently open, try to open it with this format and buffer size104if (!isOpen()) {105// make sure that the format is specified correctly106// $$fb part of fix for 4679187: Clip.open() throws unexpected Exceptions107Toolkit.isFullySpecifiedAudioFormat(format);108// reserve mixer resources for this line109//mixer.open(this, format, bufferSize);110mixer.open(this);111112try {113// open the data line. may throw LineUnavailableException.114implOpen(format, bufferSize);115116// if we succeeded, set the open state to true and send events117setOpen(true);118119} catch (LineUnavailableException e) {120// release mixer resources for this line and then throw the exception121mixer.close(this);122throw e;123}124} else {125// if the line is already open and the requested format differs from the126// current settings, throw an IllegalStateException127//$$fb 2002-04-02: fix for 4661602: Buffersize is checked when re-opening line128if (!format.matches(getFormat())) {129throw new IllegalStateException("Line is already open with format " + getFormat() +130" and bufferSize " + getBufferSize());131}132//$$fb 2002-07-26: allow changing the buffersize of already open lines133if (bufferSize > 0) {134setBufferSize(bufferSize);135}136}137}138}139140public final void open(AudioFormat format) throws LineUnavailableException {141open(format, AudioSystem.NOT_SPECIFIED);142}143144/**145* This implementation always returns 0.146*/147@Override148public int available() {149return 0;150}151152/**153* This implementation does nothing.154*/155@Override156public void drain() {157}158159/**160* This implementation does nothing.161*/162@Override163public void flush() {164}165166@Override167public final void start() {168//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !169synchronized(mixer) {170171// $$kk: 06.06.99: if not open, this doesn't work....???172if (isOpen()) {173174if (!isStartedRunning()) {175mixer.start(this);176implStart();177running = true;178}179}180}181182synchronized(lock) {183lock.notifyAll();184}185}186187@Override188public final void stop() {189190//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !191synchronized(mixer) {192// $$kk: 06.06.99: if not open, this doesn't work.193if (isOpen()) {194195if (isStartedRunning()) {196197implStop();198mixer.stop(this);199200running = false;201202// $$kk: 11.10.99: this is not exactly correct, but will probably work203if (started && (!isActive())) {204setStarted(false);205}206}207}208}209210synchronized(lock) {211lock.notifyAll();212}213}214215// $$jb: 12.10.99: The official API for this is isRunning().216// Per the denied RFE 4297981,217// the change to isStarted() is technically an unapproved API change.218// The 'started' variable is false when playback of data stops.219// It is changed throughout the implementation with setStarted().220// This state is what should be returned by isRunning() in the API.221// Note that the 'running' variable is true between calls to222// start() and stop(). This state is accessed now through the223// isStartedRunning() method, defined below. I have not changed224// the variable names at this point, since 'running' is accessed225// in MixerSourceLine and MixerClip, and I want to touch as little226// code as possible to change isStarted() back to isRunning().227228@Override229public final boolean isRunning() {230return started;231}232233@Override234public final boolean isActive() {235return active;236}237238@Override239public final long getMicrosecondPosition() {240241long microseconds = getLongFramePosition();242if (microseconds != AudioSystem.NOT_SPECIFIED) {243microseconds = Toolkit.frames2micros(getFormat(), microseconds);244}245return microseconds;246}247248@Override249public final AudioFormat getFormat() {250return format;251}252253@Override254public final int getBufferSize() {255return bufferSize;256}257258/**259* This implementation does NOT change the buffer size260*/261public final int setBufferSize(int newSize) {262return getBufferSize();263}264265/**266* This implementation returns AudioSystem.NOT_SPECIFIED.267*/268@Override269public final float getLevel() {270return (float)AudioSystem.NOT_SPECIFIED;271}272273// HELPER METHODS274275/**276* running is true after start is called and before stop is called,277* regardless of whether data is actually being presented.278*/279// $$jb: 12.10.99: calling this method isRunning() conflicts with280// the official API that was once called isStarted(). Since we281// use this method throughout the implementation, I am renaming282// it to isStartedRunning(). This is part of backing out the283// change denied in RFE 4297981.284285final boolean isStartedRunning() {286return running;287}288289/**290* This method sets the active state and generates291* events if it changes.292*/293final void setActive(boolean active) {294//boolean sendEvents = false;295//long position = getLongFramePosition();296297synchronized (this) {298299if (this.active != active) {300this.active = active;301//sendEvents = true;302}303}304305// $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;306// putting them in is technically an API change.307// do not generate ACTIVE / INACTIVE events for now308// if (sendEvents) {309//310// if (active) {311// sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));312// } else {313// sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));314// }315//}316}317318/**319* This method sets the started state and generates320* events if it changes.321*/322final void setStarted(boolean started) {323boolean sendEvents = false;324long position = getLongFramePosition();325326synchronized (this) {327328if (this.started != started) {329this.started = started;330sendEvents = true;331}332}333334if (sendEvents) {335336if (started) {337sendEvents(new LineEvent(this, LineEvent.Type.START, position));338} else {339sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));340}341}342}343344/**345* This method generates a STOP event and sets the started state to false.346* It is here for historic reasons when an EOM event existed.347*/348final void setEOM() {349//$$fb 2002-04-21: sometimes, 2 STOP events are generated.350// better use setStarted() to send STOP event.351setStarted(false);352}353354// OVERRIDES OF ABSTRACT LINE METHODS355356/**357* Try to open the line with the current format and buffer size values.358* If the line is not open, these will be the defaults. If the359* line is open, this should return quietly because the values360* requested will match the current ones.361*/362@Override363public final void open() throws LineUnavailableException {364// this may throw a LineUnavailableException.365open(format, bufferSize);366}367368/**369* This should also stop the line. The closed line should not be running or active.370* After we close the line, we reset the format and buffer size to the defaults.371*/372@Override373public final void close() {374//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !375synchronized (mixer) {376if (isOpen()) {377378// stop379stop();380381// set the open state to false and send events382setOpen(false);383384// close resources for this line385implClose();386387// release mixer resources for this line388mixer.close(this);389390// reset format and buffer size to the defaults391format = defaultFormat;392bufferSize = defaultBufferSize;393}394}395}396397abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;398abstract void implClose();399400abstract void implStart();401abstract void implStop();402}403404405