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
24import java.io.File;
25import java.io.IOException;
26
27import javax.sound.sampled.AudioFormat;
28import javax.sound.sampled.AudioInputStream;
29import javax.sound.sampled.AudioSystem;
30import javax.sound.sampled.DataLine;
31import javax.sound.sampled.LineUnavailableException;
32import javax.sound.sampled.Mixer;
33import 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 */
41public 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}
270