--- /dev/null 2016-10-24 21:41:13.000000000 +0300 +++ new/test/javax/sound/sampled/DirectAudio/bug6372428.java 2016-10-24 21:41:13.000000000 +0300 @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.SourceDataLine; +import javax.sound.sampled.TargetDataLine; + +/* + * @test + * @bug 6372428 + * @summary playback and capture doesn't interrupt after terminating thread that + * calls start() + * @run main bug6372428 + * @key headful + */ +public class bug6372428 { + public bug6372428() { + } + + public static void main(final String[] args) { + bug6372428 pThis = new bug6372428(); + boolean failed1 = false; + boolean failed2 = false; + log(""); + log("****************************************************************"); + log("*** Playback Test"); + log("****************************************************************"); + log(""); + try { + pThis.testPlayback(); + } catch (IllegalArgumentException | LineUnavailableException e) { + System.out.println("Playback test is not applicable. Skipped"); + } catch (Exception ex) { + ex.printStackTrace(); + failed1 = true; + } + log(""); + log(""); + log("****************************************************************"); + log("*** Capture Test"); + log("****************************************************************"); + log(""); + try { + pThis.testRecord(); + } catch (IllegalArgumentException | LineUnavailableException e) { + System.out.println("Record test is not applicable. Skipped"); + } catch (Exception ex) { + ex.printStackTrace(); + failed2 = true; + } + log(""); + log(""); + log("****************************************************************"); + if (failed1 || failed2) { + String s = ""; + if (failed1 && failed2) + s = "playback and capture"; + else if (failed1) + s = "playback only"; + else + s = "capture only"; + throw new RuntimeException("Test FAILED (" + s + ")"); + } + log("*** All tests passed successfully."); + } + + final static int DATA_LENGTH = 15; // in seconds + final static int PLAYTHREAD_DELAY = 5; // in seconds + + // playback test classes/routines + + class PlayThread extends Thread { + SourceDataLine line; + public PlayThread(SourceDataLine line) { + this.line = line; + this.setDaemon(true); + } + + public void run() { + log("PlayThread: starting..."); + line.start(); + log("PlayThread: delaying " + (PLAYTHREAD_DELAY * 1000) + "ms..."); + delay(PLAYTHREAD_DELAY * 1000); + log("PlayThread: exiting..."); + } + } + + class WriteThread extends Thread { + SourceDataLine line; + byte[] data; + volatile int remaining; + volatile boolean stopRequested = false; + public WriteThread(SourceDataLine line, byte[] data) { + this.line = line; + this.data = data; + remaining = data.length; + this.setDaemon(true); + } + + public void run() { + while (remaining > 0 && !stopRequested) { + int avail = line.available(); + if (avail > 0) { + if (avail > remaining) + avail = remaining; + int written = line.write(data, data.length - remaining, avail); + remaining -= written; + log("WriteThread: " + written + " bytes written"); + } else { + delay(100); + } + } + if (remaining == 0) { + log("WriteThread: all data has been written, draining"); + line.drain(); + } else { + log("WriteThread: stop requested"); + } + log("WriteThread: stopping"); + line.stop(); + log("WriteThread: exiting"); + } + + public boolean isCompleted() { + return (remaining <= 0); + } + + public void requestStop() { + stopRequested = true; + } + } + + void testPlayback() throws LineUnavailableException { + // prepare audio data + AudioFormat format = new AudioFormat(22050, 8, 1, false, false); + byte[] soundData = new byte[(int) (format.getFrameRate() * format.getFrameSize() * DATA_LENGTH)]; + + // create & open source data line + //SourceDataLine line = AudioSystem.getSourceDataLine(format); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); + SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info); + + line.open(format); + + // start write data thread + WriteThread p1 = new WriteThread(line, soundData); + p1.start(); + + // start line + PlayThread p2 = new PlayThread(line); + p2.start(); + + // monitor line + long lineTime1 = line.getMicrosecondPosition() / 1000; + long realTime1 = currentTimeMillis(); + while (true) { + delay(500); + if (!line.isActive()) { + log("audio data played completely"); + break; + } + long lineTime2 = line.getMicrosecondPosition() / 1000; + long realTime2 = currentTimeMillis(); + long dLineTime = lineTime2 - lineTime1; + long dRealTime = realTime2 - realTime1; + log("line pos: " + lineTime2 + "ms" + ", thread is " + (p2.isAlive() ? "alive" : "DIED")); + if (dLineTime < 0) { + throw new RuntimeException("ERROR: line position have decreased from " + lineTime1 + " to " + lineTime2); + } + if (dRealTime < 450) { + // delay() has been interrupted? + continue; + } + lineTime1 = lineTime2; + realTime1 = realTime2; + } + } + + + // recording test classes/routines + + class RecordThread extends Thread { + TargetDataLine line; + public RecordThread(TargetDataLine line) { + this.line = line; + this.setDaemon(true); + } + + public void run() { + log("RecordThread: starting..."); + line.start(); + log("RecordThread: delaying " + (PLAYTHREAD_DELAY * 1000) + "ms..."); + delay(PLAYTHREAD_DELAY * 1000); + log("RecordThread: exiting..."); + } + } + + class ReadThread extends Thread { + TargetDataLine line; + byte[] data; + volatile int remaining; + public ReadThread(TargetDataLine line, byte[] data) { + this.line = line; + this.data = data; + remaining = data.length; + this.setDaemon(true); + } + + public void run() { + log("ReadThread: buffer size is " + data.length + " bytes"); + delay(200); + while ((remaining > 0) && line.isOpen()) { + int avail = line.available(); + if (avail > 0) { + if (avail > remaining) + avail = remaining; + int read = line.read(data, data.length - remaining, avail); + remaining -= read; + log("ReadThread: " + read + " bytes read"); + } else { + delay(100); + } + if (remaining <= 0) { + log("ReadThread: record buffer is full, exiting"); + break; + } + } + if (remaining > 0) { + log("ReadThread: line has been stopped, exiting"); + } + } + + public int getCount() { + return data.length - remaining; + } + public boolean isCompleted() { + return (remaining <= 0); + } + } + + void testRecord() throws LineUnavailableException { + // prepare audio data + AudioFormat format = new AudioFormat(22050, 8, 1, false, false); + + // create & open target data line + //TargetDataLine line = AudioSystem.getTargetDataLine(format); + DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); + TargetDataLine line = (TargetDataLine)AudioSystem.getLine(info); + + line.open(format); + + // start read data thread + byte[] data = new byte[(int) (format.getFrameRate() * format.getFrameSize() * DATA_LENGTH)]; + ReadThread p1 = new ReadThread(line, data); + p1.start(); + + // start line + //new RecordThread(line).start(); + RecordThread p2 = new RecordThread(line); + p2.start(); + + // monitor line + long endTime = currentTimeMillis() + DATA_LENGTH * 1000; + + long realTime1 = currentTimeMillis(); + long lineTime1 = line.getMicrosecondPosition() / 1000; + + while (realTime1 < endTime && !p1.isCompleted()) { + delay(100); + long lineTime2 = line.getMicrosecondPosition() / 1000; + long realTime2 = currentTimeMillis(); + long dLineTime = lineTime2 - lineTime1; + long dRealTime = realTime2 - realTime1; + log("line pos: " + lineTime2 + "ms" + ", thread is " + (p2.isAlive() ? "alive" : "DIED")); + if (dLineTime < 0) { + line.stop(); + line.close(); + throw new RuntimeException("ERROR: line position have decreased from " + lineTime1 + " to " + lineTime2); + } + if (dRealTime < 450) { + // delay() has been interrupted? + continue; + } + lineTime1 = lineTime2; + realTime1 = realTime2; + } + log("stopping line..."); + line.stop(); + line.close(); + + /* + log(""); + log(""); + log(""); + log("recording completed, delaying 5 sec"); + log("recorded " + p1.getCount() + " bytes, " + DATA_LENGTH + " seconds: " + (p1.getCount() * 8 / DATA_LENGTH) + " bit/sec"); + log(""); + log(""); + log(""); + delay(5000); + log("starting playing..."); + playRecorded(format, data); + */ + } + + void playRecorded(AudioFormat format, byte[] data) throws Exception { + //SourceDataLine line = AudioSystem.getSourceDataLine(format); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); + SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info); + + line.open(); + line.start(); + + int remaining = data.length; + while (remaining > 0) { + int avail = line.available(); + if (avail > 0) { + if (avail > remaining) + avail = remaining; + int written = line.write(data, data.length - remaining, avail); + remaining -= written; + log("Playing: " + written + " bytes written"); + } else { + delay(100); + } + } + + line.drain(); + line.stop(); + } + + // helper routines + static long startTime = currentTimeMillis(); + static long currentTimeMillis() { + //return System.nanoTime() / 1000000L; + return System.currentTimeMillis(); + } + static void log(String s) { + long time = currentTimeMillis() - startTime; + long ms = time % 1000; + time /= 1000; + long sec = time % 60; + time /= 60; + long min = time % 60; + time /= 60; + System.out.println("" + + (time < 10 ? "0" : "") + time + + ":" + (min < 10 ? "0" : "") + min + + ":" + (sec < 10 ? "0" : "") + sec + + "." + (ms < 10 ? "00" : (ms < 100 ? "0" : "")) + ms + + " (" + Thread.currentThread().getName() + ") " + s); + } + static void delay(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) {} + } +}