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.util.Random;
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 4828556
38 * @summary stopping and starting sampled audio plays small chunk in infinite
39 *          loop
40 */
41public class StopStart implements Runnable {
42
43    static int sampleRate = 8000;
44    static double frequency = 2000.0;
45    static double RAD = 2.0 * Math.PI;
46    static Random random = new Random();
47
48    static byte[] audioData = new byte[sampleRate/2];
49    static SourceDataLine source;
50
51    static boolean terminated = false;
52
53    static int buffersWritten = 0;
54    static long bytesWritten = 0;
55    static int buffersWrittenAfter5Seconds;
56
57    static AudioInputStream ais = null;
58    static AudioFormat audioFormat;
59    static String filename;
60
61    static int executedTests=0;
62    static int successfulTests = 0;
63
64    public static void constructAIS() throws Exception {
65        ais = AudioSystem.getAudioInputStream(new File(filename));
66    }
67
68    public static void doStartStopTest1() throws Exception {
69        System.out.println("TEST 1: play for 3 seconds, stop/start/stop/start/play for 3 seconds...");
70        source.start();
71        Thread.sleep(100);
72        bytesWritten = 0;
73        System.out.println("Waiting for 3 seconds...");
74        Thread.sleep(3000);
75        buffersWrittenAfter5Seconds = buffersWritten;
76        System.out.println("Buffers Written: "+buffersWritten);
77        System.out.println("stop()->start()->stop()->start()");
78        source.stop();
79        //System.out.println("start()");
80        source.start();
81        //System.out.println("stop()2 ----------------------------------------------------------");
82        source.stop();
83        //System.out.println("start()");
84        source.start();
85        System.out.println("Buffers Written: "+buffersWritten);
86        System.out.println("Waiting for 3 seconds...");
87        Thread.sleep(3000);
88        System.out.println("Buffers Written: "+buffersWritten);
89        if (buffersWritten >= ((buffersWrittenAfter5Seconds * 2) - ((buffersWrittenAfter5Seconds / 4)))) {
90            successfulTests++;
91        }
92    }
93
94    private static int nextWaitTime() {
95        int waitTime = random.nextInt(25);
96        waitTime*=waitTime;
97        if (waitTime<20) waitTime = 0;
98        return waitTime;
99    }
100
101
102    public static void doStartStopTest2() throws Exception {
103        System.out.println("TEST 2: start and stop 100 times with random wait in between");
104        int max=100;
105        for (int i=0; i<max; i++) {
106            System.out.println("Round "+i);
107            System.out.println("Start....");
108            source.start();
109            int waitTime = nextWaitTime();
110            System.out.println("Waiting for "+waitTime+"ms...");
111            if (waitTime>0) {
112                Thread.sleep(waitTime);
113            }
114            System.out.println("stop()");
115            source.stop();
116            waitTime = nextWaitTime();
117            System.out.println("Waiting for "+waitTime+"ms...");
118            if (waitTime>0) {
119                Thread.sleep(waitTime);
120            }
121        }
122    }
123
124    public static void doStartStopTest3() throws Exception {
125        System.out.println("TEST 3: start and stop 100 times with random wait only every 10 rounds ");
126        int max=100;
127        for (int i=0; i<max; i++) {
128            System.out.println("Round "+i);
129            System.out.println("Start....");
130            source.start();
131            if (i % 10 == 9) {
132                int waitTime = nextWaitTime();
133                System.out.println("Waiting for "+waitTime+"ms...");
134                if (waitTime>0) {
135                    Thread.sleep(waitTime);
136                }
137            }
138            System.out.println("stop()");
139            source.stop();
140            if (i % 13 == 12) {
141                int waitTime = nextWaitTime();
142                System.out.println("Waiting for "+waitTime+"ms...");
143                if (waitTime>0) {
144                    Thread.sleep(waitTime);
145                }
146            }
147        }
148    }
149
150    public static void runTest(int testNum) {
151        terminated = false;
152        Thread thread = null;
153        buffersWrittenAfter5Seconds = 0;
154        // make the tests reproduceable by always seeding with same value
155        random.setSeed(1);
156        try {
157            executedTests++;
158            thread = new Thread(new StopStart());
159            thread.start();
160            switch (testNum) {
161            case 1: doStartStopTest1(); break;
162            case 2: doStartStopTest2(); break;
163            case 3: doStartStopTest3(); break;
164            }
165        } catch (Exception e) {
166            e.printStackTrace();
167        }
168        source.stop();
169        source.close();
170        if (thread!=null) {
171            terminated = true;
172            System.out.println("Waiting for thread to die...");
173            try {
174                thread.join();
175            } catch (InterruptedException ie) {
176                ie.printStackTrace();
177            }
178        }
179    }
180
181    public static void main(String[] args) throws Exception {
182        filename = null;
183        if (args.length>0) {
184            File f = new File(args[0]);
185            if (f.exists()) {
186                filename = args[0];
187                System.out.println("Opening "+filename);
188                constructAIS();
189                audioFormat = ais.getFormat();
190            }
191        }
192        if (filename == null) {
193            audioFormat = new AudioFormat((float)sampleRate, 8, 1, true, true);
194            for (int i=0; i<audioData.length; i++) {
195                audioData[i] = (byte)(Math.sin(RAD*frequency/sampleRate*i)*127.0);
196            }
197        }
198        long startTime = System.currentTimeMillis();
199        Mixer.Info[] mixers = AudioSystem.getMixerInfo();
200        for (int i=0; i<mixers.length; i++) {
201            try {
202                Mixer mixer = AudioSystem.getMixer(mixers[i]);
203                DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
204                String mixerName = mixer.getMixerInfo().getName();
205                try {
206                    source = (SourceDataLine) mixer.getLine(info);
207                    source.open(audioFormat);
208                } catch (IllegalArgumentException iae) {
209                    System.out.println("Mixer "+mixerName+" does not provide a SourceDataLine.");
210                    continue;
211                } catch (LineUnavailableException lue) {
212                    System.out.println("Mixer "+mixerName+": no lines available.");
213                    continue;
214                }
215                System.out.println("***** Testing on Mixer "+mixerName+":");
216                //runTest(2);
217                //runTest(3);
218                runTest(1);
219            } catch (Exception e) {
220                e.printStackTrace();
221            }
222        }
223        if (mixers.length==0) {
224            System.out.println("No mixers available!");
225        } else {
226            long duration = System.currentTimeMillis() - startTime;
227
228            System.out.println("Test took "+(duration/1000)+"s and "+(duration % 1000)+"ms.");
229        }
230
231        System.out.println("Exiting main()");
232        if (executedTests>0) {
233            if (successfulTests == 0) {
234                if (args.length == 0) {
235                    throw new Exception("Test FAILED");
236                }
237                System.out.println("test FAILED.");
238            } else {
239                System.out.println("test successful.");
240            }
241        } else {
242            System.out.println("Could not execute any tests - are soundcards correctly installed?");
243            System.out.println("Test NOT FAILED.");
244        }
245    }
246
247    public void run() {
248        int len = audioData.length;
249        int offset = len;
250        System.out.println("Thread: started. Beginning audio i/o");
251        while (!terminated) {
252            try {
253                //if (!source.isActive()) {
254                //      Thread.sleep(50);
255                //}
256                if (offset >= len) {
257                    offset = 0;
258                    if (ais!=null) {
259                        do {
260                            len = ais.read(audioData, 0, audioData.length);
261                            if (len < 0) {
262                                constructAIS();
263                            }
264                        } while (len < 0);
265                    }
266                }
267                int toWrite = len - offset;
268                int written = source.write(audioData, offset, toWrite);
269                offset+=written;
270                bytesWritten += written;
271                buffersWritten = (int) (bytesWritten / audioData.length);
272            } catch (Exception e) {
273                e.printStackTrace();
274                terminated = true;
275            }
276        }
277        System.out.println("Thread: closing line");
278        source.stop();
279        source.close();
280        System.out.println("Thread finished");
281    }
282}
283