1 /*
2  * Copyright (c) 2002, 2019, 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 
26 package com.sun.media.sound;
27 
28 import java.util.Objects;
29 
30 import javax.sound.midi.MidiDevice;
31 import javax.sound.midi.spi.MidiDeviceProvider;
32 
33 /**
34  * Super class for MIDI input or output device provider.
35  *
36  * @author Florian Bomers
37  */
38 public 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 {
Platform.initialize()46         Platform.initialize();
47         enabled = Platform.isMidiIOEnabled();
48         // $$fb number of MIDI devices may change with time
49         // also for memory's sake, do not initialize the arrays here
50     }
51 
readDeviceInfos()52     final synchronized void readDeviceInfos() {
53         Info[] infos = getInfoCache();
54         MidiDevice[] devices = getDeviceCache();
55         if (!enabled) {
56             if (infos == null || infos.length != 0) {
57                 setInfoCache(new Info[0]);
58             }
59             if (devices == null || devices.length != 0) {
60                 setDeviceCache(new MidiDevice[0]);
61             }
62             return;
63         }
64 
65         int oldNumDevices = (infos==null)?-1:infos.length;
66         int newNumDevices = getNumDevices();
67         if (oldNumDevices != newNumDevices) {
68             // initialize the arrays
69             Info[] newInfos = new Info[newNumDevices];
70             MidiDevice[] newDevices = new MidiDevice[newNumDevices];
71 
72             for (int i = 0; i < newNumDevices; i++) {
73                 Info newInfo = createInfo(i);
74 
75                 // in case that we are re-reading devices, try to find
76                 // the previous one and reuse it
77                 if (infos != null) {
78                     for (int ii = 0; ii < infos.length; ii++) {
79                         Info info = infos[ii];
80                         if (info != null && info.equalStrings(newInfo)) {
81                             // new info matches the still existing info. Use old one
82                             newInfos[i] = info;
83                             info.setIndex(i);
84                             infos[ii] = null; // prevent re-use
85                             newDevices[i] = devices[ii];
86                             devices[ii] = null;
87                             break;
88                         }
89                     }
90                 }
91                 if (newInfos[i] == null) {
92                     newInfos[i] = newInfo;
93                 }
94             }
95             // the remaining MidiDevice.Info instances in the infos array
96             // have become obsolete.
97             if (infos != null) {
98                 for (int i = 0; i < infos.length; i++) {
99                     if (infos[i] != null) {
100                         // disable this device info
101                         infos[i].setIndex(-1);
102                     }
103                     // what to do with the MidiDevice instances that are left
104                     // in the devices array ?? Close them ?
105                 }
106             }
107             // commit new list of infos.
108             setInfoCache(newInfos);
109             setDeviceCache(newDevices);
110         }
111     }
112 
113     @Override
getDeviceInfo()114     public final MidiDevice.Info[] getDeviceInfo() {
115         readDeviceInfos();
116         Info[] infos = getInfoCache();
117         MidiDevice.Info[] localArray = new MidiDevice.Info[infos.length];
118         System.arraycopy(infos, 0, localArray, 0, infos.length);
119         return localArray;
120     }
121 
122     @Override
getDevice(final MidiDevice.Info info)123     public final MidiDevice getDevice(final MidiDevice.Info info) {
124         Objects.requireNonNull(info);
125         if (info instanceof Info) {
126             readDeviceInfos();
127             MidiDevice[] devices = getDeviceCache();
128             Info[] infos = getInfoCache();
129             Info thisInfo = (Info) info;
130             int index = thisInfo.getIndex();
131             if (index >= 0 && index < devices.length && infos[index] == info) {
132                 if (devices[index] == null) {
133                     devices[index] = createDevice(thisInfo);
134                 }
135                 if (devices[index] != null) {
136                     return devices[index];
137                 }
138             }
139         }
140         throw MidiUtils.unsupportedDevice(info);
141     }
142 
143     /**
144      * Info class for MidiDevices.  Adds an index value for
145      * making native references to a particular device.
146      */
147     static class Info extends MidiDevice.Info {
148         private int index;
149 
Info(String name, String vendor, String description, String version, int index)150         Info(String name, String vendor, String description, String version, int index) {
151             super(name, vendor, description, version);
152             this.index = index;
153         }
154 
equalStrings(Info info)155         final boolean equalStrings(Info info) {
156             return      (info != null
157                          && getName().equals(info.getName())
158                          && getVendor().equals(info.getVendor())
159                          && getDescription().equals(info.getDescription())
160                          && getVersion().equals(info.getVersion()));
161         }
162 
getIndex()163         final int getIndex() {
164             return index;
165         }
166 
setIndex(int index)167         final void setIndex(int index) {
168             this.index = index;
169         }
170 
171     } // class Info
172 
getNumDevices()173     abstract int getNumDevices();
getDeviceCache()174     abstract MidiDevice[] getDeviceCache();
setDeviceCache(MidiDevice[] devices)175     abstract void setDeviceCache(MidiDevice[] devices);
getInfoCache()176     abstract Info[] getInfoCache();
setInfoCache(Info[] infos)177     abstract void setInfoCache(Info[] infos);
178 
createInfo(int index)179     abstract Info createInfo(int index);
createDevice(Info info)180     abstract MidiDevice createDevice(Info info);
181 }
182