1/*
2 * Copyright (c) 2002, 2015, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.media.sound;
27
28import java.util.Objects;
29
30import javax.sound.midi.MidiDevice;
31import javax.sound.midi.spi.MidiDeviceProvider;
32
33/**
34 * Super class for MIDI input or output device provider.
35 *
36 * @author Florian Bomers
37 */
38public abstract class AbstractMidiDeviceProvider extends MidiDeviceProvider {
39
40    private static final boolean enabled;
41
42    /**
43     * Create objects representing all MIDI output devices on the system.
44     */
45    static {
46        if (Printer.trace) Printer.trace("AbstractMidiDeviceProvider: static");
47        Platform.initialize();
48        enabled = Platform.isMidiIOEnabled();
49        if (Printer.trace) Printer.trace("AbstractMidiDeviceProvider: enabled: " + enabled);
50
51        // $$fb number of MIDI devices may change with time
52        // also for memory's sake, do not initialize the arrays here
53    }
54
55    final synchronized void readDeviceInfos() {
56        Info[] infos = getInfoCache();
57        MidiDevice[] devices = getDeviceCache();
58        if (!enabled) {
59            if (infos == null || infos.length != 0) {
60                setInfoCache(new Info[0]);
61            }
62            if (devices == null || devices.length != 0) {
63                setDeviceCache(new MidiDevice[0]);
64            }
65            return;
66        }
67
68        int oldNumDevices = (infos==null)?-1:infos.length;
69        int newNumDevices = getNumDevices();
70        if (oldNumDevices != newNumDevices) {
71            if (Printer.trace) Printer.trace(getClass().toString()
72                                             +": readDeviceInfos: old numDevices: "+oldNumDevices
73                                             +"  newNumDevices: "+ newNumDevices);
74
75            // initialize the arrays
76            Info[] newInfos = new Info[newNumDevices];
77            MidiDevice[] newDevices = new MidiDevice[newNumDevices];
78
79            for (int i = 0; i < newNumDevices; i++) {
80                Info newInfo = createInfo(i);
81
82                // in case that we are re-reading devices, try to find
83                // the previous one and reuse it
84                if (infos != null) {
85                    for (int ii = 0; ii < infos.length; ii++) {
86                        Info info = infos[ii];
87                        if (info != null && info.equalStrings(newInfo)) {
88                            // new info matches the still existing info. Use old one
89                            newInfos[i] = info;
90                            info.setIndex(i);
91                            infos[ii] = null; // prevent re-use
92                            newDevices[i] = devices[ii];
93                            devices[ii] = null;
94                            break;
95                        }
96                    }
97                }
98                if (newInfos[i] == null) {
99                    newInfos[i] = newInfo;
100                }
101            }
102            // the remaining MidiDevice.Info instances in the infos array
103            // have become obsolete.
104            if (infos != null) {
105                for (int i = 0; i < infos.length; i++) {
106                    if (infos[i] != null) {
107                        // disable this device info
108                        infos[i].setIndex(-1);
109                    }
110                    // what to do with the MidiDevice instances that are left
111                    // in the devices array ?? Close them ?
112                }
113            }
114            // commit new list of infos.
115            setInfoCache(newInfos);
116            setDeviceCache(newDevices);
117        }
118    }
119
120    @Override
121    public final MidiDevice.Info[] getDeviceInfo() {
122        readDeviceInfos();
123        Info[] infos = getInfoCache();
124        MidiDevice.Info[] localArray = new MidiDevice.Info[infos.length];
125        System.arraycopy(infos, 0, localArray, 0, infos.length);
126        return localArray;
127    }
128
129    @Override
130    public final MidiDevice getDevice(final MidiDevice.Info info) {
131        Objects.requireNonNull(info);
132        if (info instanceof Info) {
133            readDeviceInfos();
134            MidiDevice[] devices = getDeviceCache();
135            Info[] infos = getInfoCache();
136            Info thisInfo = (Info) info;
137            int index = thisInfo.getIndex();
138            if (index >= 0 && index < devices.length && infos[index] == info) {
139                if (devices[index] == null) {
140                    devices[index] = createDevice(thisInfo);
141                }
142                if (devices[index] != null) {
143                    return devices[index];
144                }
145            }
146        }
147        throw MidiUtils.unsupportedDevice(info);
148    }
149
150    /**
151     * Info class for MidiDevices.  Adds an index value for
152     * making native references to a particular device.
153     */
154    static class Info extends MidiDevice.Info {
155        private int index;
156
157        Info(String name, String vendor, String description, String version, int index) {
158            super(name, vendor, description, version);
159            this.index = index;
160        }
161
162        final boolean equalStrings(Info info) {
163            return      (info != null
164                         && getName().equals(info.getName())
165                         && getVendor().equals(info.getVendor())
166                         && getDescription().equals(info.getDescription())
167                         && getVersion().equals(info.getVersion()));
168        }
169
170        final int getIndex() {
171            return index;
172        }
173
174        final void setIndex(int index) {
175            this.index = index;
176        }
177
178    } // class Info
179
180    abstract int getNumDevices();
181    abstract MidiDevice[] getDeviceCache();
182    abstract void setDeviceCache(MidiDevice[] devices);
183    abstract Info[] getInfoCache();
184    abstract void setInfoCache(Info[] infos);
185
186    abstract Info createInfo(int index);
187    abstract MidiDevice createDevice(Info info);
188}
189