1 /* 2 * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import javax.sound.sampled.AudioFormat; 25 import javax.sound.sampled.AudioSystem; 26 import javax.sound.sampled.DataLine; 27 import javax.sound.sampled.LineUnavailableException; 28 import javax.sound.sampled.SourceDataLine; 29 import javax.sound.sampled.TargetDataLine; 30 31 /* 32 * @test 33 * @bug 6372428 34 * @summary playback and capture doesn't interrupt after terminating thread that 35 * calls start() 36 * @run main bug6372428 37 * @key headful 38 */ 39 public class bug6372428 { 40 public bug6372428() { 41 } 42 43 public static void main(final String[] args) { 44 bug6372428 pThis = new bug6372428(); 45 boolean failed1 = false; 46 boolean failed2 = false; 47 log(""); 48 log("****************************************************************"); 49 log("*** Playback Test"); 50 log("****************************************************************"); 51 log(""); 52 try { 53 pThis.testPlayback(); 54 } catch (IllegalArgumentException | LineUnavailableException e) { 55 System.out.println("Playback test is not applicable. Skipped"); 56 } catch (Exception ex) { 57 ex.printStackTrace(); 58 failed1 = true; 59 } 60 log(""); 61 log(""); 62 log("****************************************************************"); 63 log("*** Capture Test"); 64 log("****************************************************************"); 65 log(""); 66 try { 67 pThis.testRecord(); 68 } catch (IllegalArgumentException | LineUnavailableException e) { 69 System.out.println("Record test is not applicable. Skipped"); 70 } catch (Exception ex) { 71 ex.printStackTrace(); 72 failed2 = true; 73 } 74 log(""); 75 log(""); 76 log("****************************************************************"); 77 if (failed1 || failed2) { 78 String s = ""; 79 if (failed1 && failed2) 80 s = "playback and capture"; 81 else if (failed1) 82 s = "playback only"; 83 else 84 s = "capture only"; 85 throw new RuntimeException("Test FAILED (" + s + ")"); 86 } 87 log("*** All tests passed successfully."); 88 } 89 90 final static int DATA_LENGTH = 15; // in seconds 91 final static int PLAYTHREAD_DELAY = 5; // in seconds 92 93 // playback test classes/routines 94 95 class PlayThread extends Thread { 96 SourceDataLine line; 97 public PlayThread(SourceDataLine line) { 98 this.line = line; 99 this.setDaemon(true); 100 } 101 102 public void run() { 103 log("PlayThread: starting..."); 104 line.start(); 105 log("PlayThread: delaying " + (PLAYTHREAD_DELAY * 1000) + "ms..."); 106 delay(PLAYTHREAD_DELAY * 1000); 107 log("PlayThread: exiting..."); 108 } 109 } 110 111 class WriteThread extends Thread { 112 SourceDataLine line; 113 byte[] data; 114 volatile int remaining; 115 volatile boolean stopRequested = false; 116 public WriteThread(SourceDataLine line, byte[] data) { 117 this.line = line; 118 this.data = data; 119 remaining = data.length; 120 this.setDaemon(true); 121 } 122 123 public void run() { 124 while (remaining > 0 && !stopRequested) { 125 int avail = line.available(); 126 if (avail > 0) { 127 if (avail > remaining) 128 avail = remaining; 129 int written = line.write(data, data.length - remaining, avail); 130 remaining -= written; 131 log("WriteThread: " + written + " bytes written"); 132 } else { 133 delay(100); 134 } 135 } 136 if (remaining == 0) { 137 log("WriteThread: all data has been written, draining"); 138 line.drain(); 139 } else { 140 log("WriteThread: stop requested"); 141 } 142 log("WriteThread: stopping"); 143 line.stop(); 144 log("WriteThread: exiting"); 145 } 146 147 public boolean isCompleted() { 148 return (remaining <= 0); 149 } 150 151 public void requestStop() { 152 stopRequested = true; 153 } 154 } 155 156 void testPlayback() throws LineUnavailableException { 157 // prepare audio data 158 AudioFormat format = new AudioFormat(22050, 8, 1, false, false); 159 byte[] soundData = new byte[(int) (format.getFrameRate() * format.getFrameSize() * DATA_LENGTH)]; 160 161 // create & open source data line 162 //SourceDataLine line = AudioSystem.getSourceDataLine(format); 163 DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); 164 SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info); 165 166 line.open(format); 167 168 // start write data thread 169 WriteThread p1 = new WriteThread(line, soundData); 170 p1.start(); 171 172 // start line 173 PlayThread p2 = new PlayThread(line); 174 p2.start(); 175 176 // monitor line 177 long lineTime1 = line.getMicrosecondPosition() / 1000; 178 long realTime1 = currentTimeMillis(); 179 while (true) { 180 delay(500); 181 if (!line.isActive()) { 182 log("audio data played completely"); 183 break; 184 } 185 long lineTime2 = line.getMicrosecondPosition() / 1000; 186 long realTime2 = currentTimeMillis(); 187 long dLineTime = lineTime2 - lineTime1; 188 long dRealTime = realTime2 - realTime1; 189 log("line pos: " + lineTime2 + "ms" + ", thread is " + (p2.isAlive() ? "alive" : "DIED")); 190 if (dLineTime < 0) { 191 throw new RuntimeException("ERROR: line position have decreased from " + lineTime1 + " to " + lineTime2); 192 } 193 if (dRealTime < 450) { 194 // delay() has been interrupted? 195 continue; 196 } 197 lineTime1 = lineTime2; 198 realTime1 = realTime2; 199 } 200 } 201 202 203 // recording test classes/routines 204 205 class RecordThread extends Thread { 206 TargetDataLine line; 207 public RecordThread(TargetDataLine line) { 208 this.line = line; 209 this.setDaemon(true); 210 } 211 212 public void run() { 213 log("RecordThread: starting..."); 214 line.start(); 215 log("RecordThread: delaying " + (PLAYTHREAD_DELAY * 1000) + "ms..."); 216 delay(PLAYTHREAD_DELAY * 1000); 217 log("RecordThread: exiting..."); 218 } 219 } 220 221 class ReadThread extends Thread { 222 TargetDataLine line; 223 byte[] data; 224 volatile int remaining; 225 public ReadThread(TargetDataLine line, byte[] data) { 226 this.line = line; 227 this.data = data; 228 remaining = data.length; 229 this.setDaemon(true); 230 } 231 232 public void run() { 233 log("ReadThread: buffer size is " + data.length + " bytes"); 234 delay(200); 235 while ((remaining > 0) && line.isOpen()) { 236 int avail = line.available(); 237 if (avail > 0) { 238 if (avail > remaining) 239 avail = remaining; 240 int read = line.read(data, data.length - remaining, avail); 241 remaining -= read; 242 log("ReadThread: " + read + " bytes read"); 243 } else { 244 delay(100); 245 } 246 if (remaining <= 0) { 247 log("ReadThread: record buffer is full, exiting"); 248 break; 249 } 250 } 251 if (remaining > 0) { 252 log("ReadThread: line has been stopped, exiting"); 253 } 254 } 255 256 public int getCount() { 257 return data.length - remaining; 258 } 259 public boolean isCompleted() { 260 return (remaining <= 0); 261 } 262 } 263 264 void testRecord() throws LineUnavailableException { 265 // prepare audio data 266 AudioFormat format = new AudioFormat(22050, 8, 1, false, false); 267 268 // create & open target data line 269 //TargetDataLine line = AudioSystem.getTargetDataLine(format); 270 DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); 271 TargetDataLine line = (TargetDataLine)AudioSystem.getLine(info); 272 273 line.open(format); 274 275 // start read data thread 276 byte[] data = new byte[(int) (format.getFrameRate() * format.getFrameSize() * DATA_LENGTH)]; 277 ReadThread p1 = new ReadThread(line, data); 278 p1.start(); 279 280 // start line 281 //new RecordThread(line).start(); 282 RecordThread p2 = new RecordThread(line); 283 p2.start(); 284 285 // monitor line 286 long endTime = currentTimeMillis() + DATA_LENGTH * 1000; 287 288 long realTime1 = currentTimeMillis(); 289 long lineTime1 = line.getMicrosecondPosition() / 1000; 290 291 while (realTime1 < endTime && !p1.isCompleted()) { 292 delay(100); 293 long lineTime2 = line.getMicrosecondPosition() / 1000; 294 long realTime2 = currentTimeMillis(); 295 long dLineTime = lineTime2 - lineTime1; 296 long dRealTime = realTime2 - realTime1; 297 log("line pos: " + lineTime2 + "ms" + ", thread is " + (p2.isAlive() ? "alive" : "DIED")); 298 if (dLineTime < 0) { 299 line.stop(); 300 line.close(); 301 throw new RuntimeException("ERROR: line position have decreased from " + lineTime1 + " to " + lineTime2); 302 } 303 if (dRealTime < 450) { 304 // delay() has been interrupted? 305 continue; 306 } 307 lineTime1 = lineTime2; 308 realTime1 = realTime2; 309 } 310 log("stopping line..."); 311 line.stop(); 312 line.close(); 313 314 /* 315 log(""); 316 log(""); 317 log(""); 318 log("recording completed, delaying 5 sec"); 319 log("recorded " + p1.getCount() + " bytes, " + DATA_LENGTH + " seconds: " + (p1.getCount() * 8 / DATA_LENGTH) + " bit/sec"); 320 log(""); 321 log(""); 322 log(""); 323 delay(5000); 324 log("starting playing..."); 325 playRecorded(format, data); 326 */ 327 } 328 329 void playRecorded(AudioFormat format, byte[] data) throws Exception { 330 //SourceDataLine line = AudioSystem.getSourceDataLine(format); 331 DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); 332 SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info); 333 334 line.open(); 335 line.start(); 336 337 int remaining = data.length; 338 while (remaining > 0) { 339 int avail = line.available(); 340 if (avail > 0) { 341 if (avail > remaining) 342 avail = remaining; 343 int written = line.write(data, data.length - remaining, avail); 344 remaining -= written; 345 log("Playing: " + written + " bytes written"); 346 } else { 347 delay(100); 348 } 349 } 350 351 line.drain(); 352 line.stop(); 353 } 354 355 // helper routines 356 static long startTime = currentTimeMillis(); 357 static long currentTimeMillis() { 358 //return System.nanoTime() / 1000000L; 359 return System.currentTimeMillis(); 360 } 361 static void log(String s) { 362 long time = currentTimeMillis() - startTime; 363 long ms = time % 1000; 364 time /= 1000; 365 long sec = time % 60; 366 time /= 60; 367 long min = time % 60; 368 time /= 60; 369 System.out.println("" 370 + (time < 10 ? "0" : "") + time 371 + ":" + (min < 10 ? "0" : "") + min 372 + ":" + (sec < 10 ? "0" : "") + sec 373 + "." + (ms < 10 ? "00" : (ms < 100 ? "0" : "")) + ms 374 + " (" + Thread.currentThread().getName() + ") " + s); 375 } 376 static void delay(int millis) { 377 try { 378 Thread.sleep(millis); 379 } catch (InterruptedException e) {} 380 } 381 }