Path: blob/master/src/java.desktop/share/classes/com/sun/media/sound/AbstractDataLine.java
66646 views
/*1* Copyright (c) 1999, 2021, 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();296297this.active = active;298299// $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;300// putting them in is technically an API change.301// do not generate ACTIVE / INACTIVE events for now302// if (sendEvents) {303//304// if (active) {305// sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));306// } else {307// sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));308// }309//}310}311312/**313* This method sets the started state and generates314* events if it changes.315*/316final void setStarted(boolean started) {317boolean sendEvents = false;318long position = getLongFramePosition();319320if (this.started != started) {321this.started = started;322sendEvents = true;323}324325if (sendEvents) {326327if (started) {328sendEvents(new LineEvent(this, LineEvent.Type.START, position));329} else {330sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));331}332}333}334335/**336* This method generates a STOP event and sets the started state to false.337* It is here for historic reasons when an EOM event existed.338*/339final void setEOM() {340//$$fb 2002-04-21: sometimes, 2 STOP events are generated.341// better use setStarted() to send STOP event.342setStarted(false);343}344345// OVERRIDES OF ABSTRACT LINE METHODS346347/**348* Try to open the line with the current format and buffer size values.349* If the line is not open, these will be the defaults. If the350* line is open, this should return quietly because the values351* requested will match the current ones.352*/353@Override354public final void open() throws LineUnavailableException {355// this may throw a LineUnavailableException.356open(format, bufferSize);357}358359/**360* This should also stop the line. The closed line should not be running or active.361* After we close the line, we reset the format and buffer size to the defaults.362*/363@Override364public final void close() {365//$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !366synchronized (mixer) {367if (isOpen()) {368369// stop370stop();371372// set the open state to false and send events373setOpen(false);374375// close resources for this line376implClose();377378// release mixer resources for this line379mixer.close(this);380381// reset format and buffer size to the defaults382format = defaultFormat;383bufferSize = defaultBufferSize;384}385}386}387388abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;389abstract void implClose();390391abstract void implStart();392abstract void implStop();393}394395396