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.ByteArrayInputStream;
25
26import javax.sound.sampled.AudioFormat;
27import javax.sound.sampled.AudioInputStream;
28import javax.sound.sampled.AudioSystem;
29import javax.sound.sampled.Clip;
30import javax.sound.sampled.DataLine;
31import javax.sound.sampled.LineUnavailableException;
32import javax.sound.sampled.Mixer;
33
34/**
35 * @test
36 * @bug 4946945
37 * @summary Crash in javasound while running TicTacToe demo applet tiger b26
38 */
39public 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}
227