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.File;
  25 import java.io.IOException;
  26 
  27 import javax.sound.sampled.AudioFormat;
  28 import javax.sound.sampled.AudioInputStream;
  29 import javax.sound.sampled.AudioSystem;
  30 import javax.sound.sampled.DataLine;
  31 import javax.sound.sampled.LineUnavailableException;
  32 import javax.sound.sampled.Mixer;
  33 import javax.sound.sampled.SourceDataLine;
  34 
  35 /**
  36  * @test
  37  * @bug 4834461
  38  * @summary Applet hang when you load it during sound card is in use
  39  * @run main/manual PlaySine
  40  */
  41 public class PlaySine {
  42 
  43     static int sampleRate = 8000;
  44     static double frequency = 2000.0;
  45     static double RAD = 2.0 * Math.PI;
  46 
  47     static byte[] audioData = new byte[sampleRate/2];
  48     static SourceDataLine source;
  49     static Mixer mixer = null;
  50 
  51     static AudioInputStream ais = null;
  52     static AudioFormat audioFormat;
  53     static String filename;
  54 
  55     public static void constructAIS() {
  56         try {
  57             ais = AudioSystem.getAudioInputStream(new File(filename));
  58         } catch (Exception e) {
  59             println("ERROR: could not open "+filename+": "+e.getMessage());
  60         }
  61     }
  62 
  63     public static void print(String s) {
  64         System.out.print(s);
  65     }
  66     public static void println(String s) {
  67         System.out.println(s);
  68     }
  69 
  70     public static void key() {
  71         println("");
  72         print("Press ENTER to continue...");
  73         try {
  74             System.in.read();
  75         } catch (IOException ioe) {
  76         }
  77     }
  78 
  79     static int audioLen = -1;
  80     static int audioOffset = -1;
  81 
  82     public static void writeData() {
  83         if (audioLen == -1) {
  84             audioLen = audioData.length;
  85         }
  86         if (audioOffset < 0) {
  87             audioOffset = audioLen;
  88         }
  89         try {
  90             if (audioOffset >= audioLen) {
  91                 audioOffset = 0;
  92                 if (ais!=null) {
  93                     do {
  94                         audioLen = ais.read(audioData, 0, audioData.length);
  95                         if (audioLen < 0) {
  96                             constructAIS();
  97                         }
  98                     } while (audioLen < 0);
  99                 }
 100             }
 101             int toWrite = audioLen - audioOffset;
 102             int written = source.write(audioData, audioOffset, toWrite);
 103             audioOffset+=written;
 104         } catch (Exception e) {
 105             e.printStackTrace();
 106         }
 107     }
 108 
 109 
 110     public static int play(boolean shouldPlay) {
 111         int res = 0;
 112         DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
 113         try {
 114             println("Getting line from mixer...");
 115             source = (SourceDataLine) mixer.getLine(info);
 116             println("Opening line...");
 117             println("  -- if the program is hanging here, kill the process that has blocks the audio device now.");
 118             source.open(audioFormat);
 119             println("Starting line...");
 120             source.start();
 121             println("Writing audio data for 1 second...");
 122             long startTime = System.currentTimeMillis();
 123             while (System.currentTimeMillis() - startTime < 1000) {
 124                 writeData();
 125                 Thread.sleep(100);
 126             }
 127             res = 1;
 128         } catch (IllegalArgumentException iae) {
 129             println("IllegalArgumentException: "+iae.getMessage());
 130             println("Sound device cannot handle this audio format.");
 131             println("ERROR: Test environment not correctly set up.");
 132             if (source!=null) {
 133                 source.close();
 134             }
 135             return 3;
 136         } catch (LineUnavailableException lue) {
 137             println("LineUnavailableException: "+lue.getMessage());
 138             if (shouldPlay) {
 139                 println("ERROR: the line should be available now!.");
 140                 println("       Verify that you killed the other audio process.");
 141             } else {
 142                 println("Correct behavior! the bug is fixed.");
 143             }
 144             res = 2;
 145         } catch (Exception e) {
 146             println("Unexpected Exception: "+e.toString());
 147         }
 148         if (source != null) {
 149             println("Draining...");
 150             try {
 151                 source.drain();
 152             } catch (NullPointerException npe) {
 153                 println("(NullPointerException: bug fixed in J2SE 1.4.2");
 154             }
 155             println("Stopping...");
 156             source.stop();
 157             println("Closing...");
 158             source.close();
 159             source = null;
 160         }
 161         return res;
 162     }
 163 
 164     public static void main(String[] args) throws Exception {
 165         println("This is an interactive test. You can run it with a filename as argument");
 166         println("It is only meant to be run on linux, with the (old) OSS kernel drivers (/dev/dsp)");
 167         println("This test should not be run on systems with ALSA installed, or kernel 2.6 or higher.");
 168         println("");
 169         println("The test verifies that Java Sound fails correctly if another process is blocking");
 170         println("the audio device.");
 171         println("");
 172         println("Checking sanity...");
 173         Mixer.Info[] mixers=null;
 174 
 175         mixers = AudioSystem.getMixerInfo();
 176         for (int i=0; i<mixers.length; i++) {
 177             try {
 178                 Mixer thisMixer = AudioSystem.getMixer(mixers[i]);
 179                 String mixerName = thisMixer.getMixerInfo().getName();
 180                 if (mixerName.indexOf("Java Sound")>=0
 181                     && mixerName.indexOf("Engine")>=0) {
 182                     mixer = thisMixer;
 183                     break;
 184                 }
 185             } catch (Exception e) {
 186                 e.printStackTrace();
 187             }
 188         }
 189         if (mixer == null) {
 190             if (mixers.length==0) {
 191                 System.out.println("ERROR: No mixers available!");
 192             } else {
 193                 println("ERROR: Java Sound Engine could not be found.");
 194             }
 195             println("Cannot run this test.");
 196             return;
 197         }
 198         println("  ...using mixer "+mixer.getMixerInfo());
 199 
 200         String osname = System.getProperty("os.name");
 201         if ((osname == null) || (osname.toLowerCase().indexOf("linux")<0)) {
 202             println("ERROR: not running on linux (you are running on "+osname+")");
 203             return;
 204         }
 205         println("  ...running on "+osname);
 206         println("  ...sanity test OK.");
 207 
 208         filename = null;
 209         if (args.length>0) {
 210             File f = new File(args[0]);
 211             if (f.exists()) {
 212                 filename = args[0];
 213                 println("Opening "+filename);
 214                 constructAIS();
 215                 if (ais!=null) {
 216                     audioFormat = ais.getFormat();
 217                 }
 218             }
 219         }
 220         if (ais == null) {
 221             println("Using self-generated sine wave for playback");
 222             audioFormat = new AudioFormat((float)sampleRate, 8, 1, true, true);
 223             for (int i=0; i<audioData.length; i++) {
 224                 audioData[i] = (byte)(Math.sin(RAD*frequency/sampleRate*i)*127.0);
 225             }
 226         }
 227 
 228         println("");
 229         println("Now, on a second console, run the following command:");
 230         println("    cat - < /dev/zero > /dev/dsp");
 231         key();
 232         println("After you press ENTER now, the mixer will be opened.");
 233         println("There are 3 possible cases that can occur:");
 234         println("1) you'll hear a sine wave");
 235         println("   -> you are running with mixing OSS drivers. ");
 236         println("      Some soundcards only provide mixing OSS drivers.");
 237         println("      Test environment not valid. ");
 238         println("      Repeat on another machine where you can reproduce the bug first.");
 239         println("2) this program stops doing anything after 'Opening line...'");
 240         println("   -> this is the bug.");
 241         println("      Kill the command on the other console with Ctrl-C, this program");
 242         println("      should continue working then.");
 243         println("3) this program reports a LineUnavailableException");
 244         println("   -> bug is fixed.");
 245         println("      OR you run with non-blocking OSS drivers.");
 246         println("      make sure that you can reproduce this bug first with e.g. J2SE 1.4.1");
 247         key();
 248         int playedFirst = play(false);
 249         int playedSecond = 0;
 250 
 251         if (playedFirst == 2) {
 252             println("");
 253             println("Now kill the other process with Ctrl-C.");
 254             println("After that, this program should be able to play ");
 255             println("the sine wave without problems.");
 256             key();
 257             playedSecond = play(true);
 258         }
 259         println("");
 260         if (playedFirst == 1) {
 261             println("Test FAILED.");
 262         }
 263         else if (playedFirst == 2 && playedSecond == 1) {
 264             println("Test SUCCESSFUL");
 265         } else {
 266             println("Test not failed (but not successful either...).");
 267         }
 268     }
 269 }