Path: blob/master/src/java.desktop/share/classes/com/sun/media/sound/DataPusher.java
41161 views
/*1* Copyright (c) 2002, 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 java.util.Arrays;2829import javax.sound.sampled.AudioFormat;30import javax.sound.sampled.AudioInputStream;31import javax.sound.sampled.SourceDataLine;3233/**34* Class to write an AudioInputStream to a SourceDataLine.35* Was previously an inner class in various classes like JavaSoundAudioClip36* and sun.audio.AudioDevice.37* It auto-opens and closes the SourceDataLine.38*39* @author Kara Kytle40* @author Florian Bomers41*/4243public final class DataPusher implements Runnable {4445private static final int AUTO_CLOSE_TIME = 5000;4647private final SourceDataLine source;48private final AudioFormat format;4950// stream as source data51private final AudioInputStream ais;5253// byte array as source data54private final byte[] audioData;55private final int audioDataByteLength;56private int pos;57private int newPos = -1;58private boolean looping;5960private Thread pushThread = null;61private int wantedState;62private int threadState;6364private final int STATE_NONE = 0;65private final int STATE_PLAYING = 1;66private final int STATE_WAITING = 2;67private final int STATE_STOPPING = 3;68private final int STATE_STOPPED = 4;69private final int BUFFER_SIZE = 16384;7071public DataPusher(SourceDataLine sourceLine, AudioFormat format, byte[] audioData, int byteLength) {72this(sourceLine, format, null, audioData, byteLength);73}7475public DataPusher(SourceDataLine sourceLine, AudioInputStream ais) {76this(sourceLine, ais.getFormat(), ais, null, 0);77}7879private DataPusher(final SourceDataLine source, final AudioFormat format,80final AudioInputStream ais, final byte[] audioData,81final int audioDataByteLength) {82this.source = source;83this.format = format;84this.ais = ais;85this.audioDataByteLength = audioDataByteLength;86this.audioData = audioData == null ? null : Arrays.copyOf(audioData,87audioData.length);88}8990public synchronized void start() {91start(false);92}9394public synchronized void start(boolean loop) {95try {96if (threadState == STATE_STOPPING) {97// wait that the thread has finished stopping98stop();99}100looping = loop;101newPos = 0;102wantedState = STATE_PLAYING;103if (!source.isOpen()) {104source.open(format);105}106source.flush();107source.start();108if (pushThread == null) {109pushThread = JSSecurityManager.createThread(this,110null, // name111false, // daemon112-1, // priority113true); // doStart114}115notifyAll();116} catch (Exception e) {117if (Printer.err) e.printStackTrace();118}119}120121public synchronized void stop() {122if (threadState == STATE_STOPPING123|| threadState == STATE_STOPPED124|| pushThread == null) {125return;126}127wantedState = STATE_WAITING;128if (source != null) {129source.flush();130}131notifyAll();132int maxWaitCount = 50; // 5 seconds133while ((maxWaitCount-- >= 0) && (threadState == STATE_PLAYING)) {134try {135wait(100);136} catch (InterruptedException e) { }137}138}139140synchronized void close() {141if (source != null) {142source.close();143}144}145146/**147* Write data to the source data line.148*/149@Override150public void run() {151byte[] buffer = null;152boolean useStream = (ais != null);153if (useStream) {154buffer = new byte[BUFFER_SIZE];155} else {156buffer = audioData;157}158while (wantedState != STATE_STOPPING) {159//try {160if (wantedState == STATE_WAITING) {161// wait for 5 seconds - maybe the clip is to be played again162try {163synchronized(this) {164threadState = STATE_WAITING;165wantedState = STATE_STOPPING;166wait(AUTO_CLOSE_TIME);167}168} catch (InterruptedException ie) {}169continue;170}171if (newPos >= 0) {172pos = newPos;173newPos = -1;174}175threadState = STATE_PLAYING;176int toWrite = BUFFER_SIZE;177if (useStream) {178try {179pos = 0; // always write from beginning of buffer180// don't use read(byte[]), because some streams181// may not override that method182toWrite = ais.read(buffer, 0, buffer.length);183} catch (java.io.IOException ioe) {184// end of stream185toWrite = -1;186}187} else {188if (toWrite > audioDataByteLength - pos) {189toWrite = audioDataByteLength - pos;190}191if (toWrite == 0) {192toWrite = -1; // end of "stream"193}194}195if (toWrite < 0) {196if (!useStream && looping) {197pos = 0;198continue;199}200wantedState = STATE_WAITING;201source.drain();202continue;203}204int bytesWritten = source.write(buffer, pos, toWrite);205pos += bytesWritten;206}207threadState = STATE_STOPPING;208source.flush();209source.stop();210source.flush();211source.close();212threadState = STATE_STOPPED;213synchronized (this) {214pushThread = null;215notifyAll();216}217}218} // class DataPusher219220221