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 javax.sound.midi.MidiDevice;
25import javax.sound.midi.MidiSystem;
26import javax.sound.midi.Sequencer;
27import javax.sound.midi.Synthesizer;
28
29/**
30 * @test
31 * @bug 4903786
32 * @summary MIDI OUT does not implement getMicrosecondPosition() consistently
33 */
34public class MidiOutGetMicrosecondPositionBug {
35    static int successfulTests = 0;
36
37    private static void testDevice(MidiDevice device) throws Exception {
38        boolean timestampsAvailable = false;
39        boolean timestampPrecisionOk = false;
40        try {
41            // expected behaviour if not opened?
42            device.open();
43            /* First, we're testing if timestamps are provided at all.
44               Returning -1 (unsupported), while allowed by the API
45               specification, is not sufficient to pass this test. */
46            long timestamp = device.getMicrosecondPosition();
47            timestampsAvailable = (timestamp != -1);
48
49            /* Then, we're testing the precision. Note that the system time
50               is measured in milliseconds, while the device time is measured
51               in microseconds. */
52
53            long systemTime1 = System.currentTimeMillis();
54            long deviceTime1 = device.getMicrosecondPosition();
55            // rest for 5 seconds
56            Thread.sleep(5000);
57            long systemTime2 = System.currentTimeMillis();
58            long deviceTime2 = device.getMicrosecondPosition();
59
60            // now both period measurements are calculated in milliseconds.
61            long systemDuration = systemTime2 - systemTime1;
62            long deviceDuration = (deviceTime2 - deviceTime1) / 1000;
63            long delta = Math.abs(systemDuration - deviceDuration);
64            // a deviation of 0.5 seconds (= 500 ms) is allowed.
65            timestampPrecisionOk = (delta <= 500);
66        } catch (Throwable t) {
67            System.out.println("  - Caught exception. Not failed.");
68            System.out.println("  - " + t.toString());
69            return;
70        } finally {
71            device.close();
72        }
73        if (! timestampsAvailable) {
74            throw new Exception("timestamps are not supported");
75        }
76        if (! timestampPrecisionOk) {
77            throw new Exception("device timer not precise enough");
78        }
79        successfulTests++;
80    }
81
82    private static void doAll() throws Exception {
83        MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
84        for (int i=0; i < infos.length; i++) {
85            MidiDevice device = MidiSystem.getMidiDevice(infos[i]);
86            if ((! (device instanceof Sequencer)) &&
87                (! (device instanceof Synthesizer)) &&
88                (device.getMaxReceivers() > 0 || device.getMaxReceivers() == -1)) {
89
90                System.out.println("--------------");
91                System.out.println("Testing MIDI device: " + infos[i]);
92                testDevice(device);
93            }
94            if (infos.length==0) {
95                System.out.println("No MIDI devices available!");
96            }
97        }
98    }
99
100    public static void main(String[] args) throws Exception {
101        if (!isMidiInstalled()) {
102            return;
103        }
104        doAll();
105        if (successfulTests==0) {
106            System.out.println("Could not execute any of the tests. Test NOT failed.");
107        } else {
108            System.out.println("Test PASSED.");
109        }
110    }
111
112    /**
113     * Returns true if at least one MIDI (port) device is correctly installed on
114     * the system.
115     */
116    public static boolean isMidiInstalled() {
117        boolean result = false;
118        MidiDevice.Info[] devices = MidiSystem.getMidiDeviceInfo();
119        for (int i = 0; i < devices.length; i++) {
120            try {
121                MidiDevice device = MidiSystem.getMidiDevice(devices[i]);
122                result = ! (device instanceof Sequencer) && ! (device instanceof Synthesizer);
123            } catch (Exception e1) {
124                System.err.println(e1);
125            }
126            if (result)
127                break;
128        }
129        if (!result) {
130            System.err.println("Soundcard does not exist or sound drivers not installed!");
131            System.err.println("This test requires sound drivers for execution.");
132        }
133        return result;
134    }
135}
136