1 /*
   2  * Copyright (c) 2003, 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 java.io.ByteArrayInputStream;
  25 
  26 import javax.sound.sampled.AudioFormat;
  27 import javax.sound.sampled.AudioInputStream;
  28 import javax.sound.sampled.AudioSystem;
  29 import javax.sound.sampled.Clip;
  30 import javax.sound.sampled.DataLine;
  31 import javax.sound.sampled.LineUnavailableException;
  32 import javax.sound.sampled.Mixer;
  33 
  34 /**
  35  * @test
  36  * @bug 4946945
  37  * @summary Crash in javasound while running TicTacToe demo applet tiger b26
  38  */
  39 public class ClipFlushCrash {
  40     static int frameCount = 441000; // lets say 10 seconds
  41     static AudioFormat format = new AudioFormat(44100.0f, 16, 2, true, false);
  42     static ByteArrayInputStream bais =
  43       new ByteArrayInputStream(new byte[frameCount * format.getFrameSize()]);
  44 
  45     static int success = 0;
  46 
  47     public static void run(Mixer m) {
  48         Clip clip = null;
  49         try {
  50             if (m == null) {
  51                 out("Using default mixer");
  52                 clip = (Clip) AudioSystem.getClip();
  53             } else {
  54                 out("Using mixer: "+m);
  55                 DataLine.Info info = new DataLine.Info(Clip.class, format, AudioSystem.NOT_SPECIFIED);
  56                 clip = (Clip) m.getLine(info);
  57             }
  58             out(" got clip: "+clip);
  59             if (!clip.getClass().toString().contains("Direct")) {
  60                 out(" no direct audio clip -> do not test.");
  61                 return;
  62             }
  63 
  64             out(" open");
  65             bais.reset();
  66             clip.open(new AudioInputStream(bais, format, frameCount));
  67 
  68             AT at1 = new AT(clip, "flush thread", 123) {
  69                 public void doAction() throws Exception {
  70                         log("flush");
  71                         clip.flush();
  72                 }
  73             };
  74             AT at2 = new AT(clip, "setFramePosition thread", 67) {
  75                 public void doAction() throws Exception {
  76                         int pos = (int) (Math.random() * clip.getFrameLength());
  77                         log("setPosition to frame "+pos);
  78                         clip.setFramePosition(pos);
  79                 }
  80             };
  81             AT at3 = new AT(clip, "start/stop thread", 300) {
  82                 public void doAction() throws Exception {
  83                         if (clip.isRunning()) {
  84                                 log("stop");
  85                                 clip.stop();
  86                         } else {
  87                                 log("start");
  88                                 clip.setFramePosition(0);
  89                                 clip.start();
  90                         }
  91                 }
  92             };
  93             AT at4 = new AT(clip, "open/close thread", 600) {
  94                 public synchronized void doAction() throws Exception {
  95                         log("close");
  96                         clip.close();
  97                         wait(50);
  98                         if (!terminated) {
  99                                 log("open");
 100                                 bais.reset();
 101                                 clip.open(new AudioInputStream(bais, format, frameCount));
 102                         }
 103                 }
 104             };
 105 
 106             out(" clip.start");
 107             clip.start();
 108             out(" for 10 seconds, call start/stop, setFramePosition, and flush from other threads");
 109             at1.start();
 110             at2.start();
 111             at3.start();
 112             at4.start();
 113             try {
 114                 Thread.sleep(10000);
 115             } catch (InterruptedException ie) {}
 116             out(" finished.");
 117                 at1.terminate();
 118                 at2.terminate();
 119                 at3.terminate();
 120                 at4.terminate();
 121             out(" clip.close()");
 122             clip.close();
 123             success++;
 124         } catch (LineUnavailableException luae) {
 125             // line not available, test not failed
 126             System.err.println(luae);
 127         } catch (IllegalArgumentException iae) {
 128             // line not available, test not failed
 129             System.err.println(iae);
 130         } catch (Throwable t) {
 131             t.printStackTrace();
 132         }
 133     }
 134 
 135     public static void main(String[] args) throws Exception     {
 136         if (isSoundcardInstalled()) {
 137                 bais.mark(0);
 138             run(null);
 139             Mixer.Info[] infos = AudioSystem.getMixerInfo();
 140             for (int i = 0; i<infos.length; i++) {
 141                 try {
 142                         Mixer m = AudioSystem.getMixer(infos[i]);
 143                         run(m);
 144                 } catch (Exception e) {
 145                 }
 146             }
 147             if (success > 0) {
 148                 out("No crash -> Test passed");
 149             } else {
 150                 System.err.println("Test could not execute: please install an audio device");
 151             }
 152         }
 153     }
 154 
 155     /**
 156      * Returns true if at least one soundcard is correctly installed
 157      * on the system.
 158      */
 159     public static boolean isSoundcardInstalled() {
 160         boolean result = false;
 161         try {
 162             Mixer.Info[] mixers = AudioSystem.getMixerInfo();
 163             if (mixers.length > 0) {
 164                 result = AudioSystem.getSourceDataLine(null) != null;
 165             }
 166         } catch (Exception e) {
 167             System.err.println("Exception occured: "+e);
 168         }
 169         if (!result) {
 170             System.err.println("Soundcard does not exist or sound drivers not installed!");
 171             System.err.println("This test requires sound drivers for execution.");
 172         }
 173         return result;
 174     }
 175 
 176     public static void out(String s) {
 177         /*long t = System.nanoTime() / 1000000l;
 178         String ts = ""+(t % 1000);
 179         while (ts.length() < 3) ts = "0"+ts;
 180         System.out.println(""+(t/1000)+":"+ts+" "+s);
 181         System.out.flush();*/
 182         System.out.println(s);
 183     }
 184 
 185     private abstract static class AT extends Thread {
 186         protected boolean terminated = false;
 187         protected Clip clip;
 188         private int waitTime;
 189 
 190         public AT(Clip clip, String name, int waitTime) {
 191                 super(name);
 192                 this.clip = clip;
 193                 this.waitTime = waitTime;
 194         }
 195 
 196         public abstract void doAction() throws Exception;
 197 
 198                 public void run() {
 199                         log("start");
 200                         while (!terminated) {
 201                                 try {
 202                                         synchronized(this) {
 203                                             wait(waitTime);
 204                                         }
 205                                         if (!terminated) {
 206                                                 doAction();
 207                                         }
 208                                 } catch(Exception e) {
 209                                         log("exception: "+e);
 210                                 }
 211                         }
 212                         log("exit");
 213                 }
 214 
 215                 public synchronized void terminate() {
 216                         log("terminate");
 217                         terminated = true;
 218                         notifyAll();
 219                 }
 220 
 221         protected void log(String s) {
 222             //out("   "+Thread.currentThread().getId()+" "+getName()+": "+s);
 223             out("   "+getName()+": "+s);
 224         }
 225     }
 226 }