jdk/test/javax/sound/sampled/DirectAudio/bug6372428.java
changeset 41905 e8e5df013c6e
child 44656 72decb1e4935
equal deleted inserted replaced
41904:524d908e49ea 41905:e8e5df013c6e
       
     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 }