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 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 { 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 56 final synchronized void readDeviceInfos() { 57 Info[] infos = getInfoCache(); 58 MidiDevice[] devices = getDeviceCache(); 59 if (!enabled) { 60 if (infos == null || infos.length != 0) { 61 setInfoCache(new Info[0]); 62 } 63 if (devices == null || devices.length != 0) { 64 setDeviceCache(new MidiDevice[0]); 65 } 66 return; 67 } 68 69 int oldNumDevices = (infos==null)?-1:infos.length; 70 int newNumDevices = getNumDevices(); 71 if (oldNumDevices != newNumDevices) { 72 if (Printer.trace) Printer.trace(getClass().toString() 73 +": readDeviceInfos: old numDevices: "+oldNumDevices 74 +" newNumDevices: "+ newNumDevices); 75 76 // initialize the arrays 77 Info[] newInfos = new Info[newNumDevices]; 78 MidiDevice[] newDevices = new MidiDevice[newNumDevices]; 79 80 for (int i = 0; i < newNumDevices; i++) { 81 Info newInfo = createInfo(i); 82 83 // in case that we are re-reading devices, try to find 84 // the previous one and reuse it 85 if (infos != null) { 86 for (int ii = 0; ii < infos.length; ii++) { 87 Info info = infos[ii]; 88 if (info != null && info.equalStrings(newInfo)) { 89 // new info matches the still existing info. Use old one 90 newInfos[i] = info; 91 info.setIndex(i); 92 infos[ii] = null; // prevent re-use 93 newDevices[i] = devices[ii]; 94 devices[ii] = null; 95 break; 96 } 97 } 98 } 99 if (newInfos[i] == null) { 100 newInfos[i] = newInfo; 101 } 102 } 103 // the remaining MidiDevice.Info instances in the infos array 104 // have become obsolete. 105 if (infos != null) { 106 for (int i = 0; i < infos.length; i++) { 107 if (infos[i] != null) { 108 // disable this device info 109 infos[i].setIndex(-1); 110 } 111 // what to do with the MidiDevice instances that are left 112 // in the devices array ?? Close them ? 113 } 114 } 115 // commit new list of infos. 116 setInfoCache(newInfos); 117 setDeviceCache(newDevices); 118 } 119 } 120 121 @Override 122 public final MidiDevice.Info[] getDeviceInfo() { 123 readDeviceInfos(); 124 Info[] infos = getInfoCache(); 125 MidiDevice.Info[] localArray = new MidiDevice.Info[infos.length]; 126 System.arraycopy(infos, 0, localArray, 0, infos.length); 127 return localArray; 128 } 129 130 @Override 131 public final MidiDevice getDevice(final MidiDevice.Info info) { 132 Objects.requireNonNull(info); 133 if (info instanceof Info) { 134 readDeviceInfos(); 135 MidiDevice[] devices = getDeviceCache(); 136 Info[] infos = getInfoCache(); 137 Info thisInfo = (Info) info; 138 int index = thisInfo.getIndex(); 139 if (index >= 0 && index < devices.length && infos[index] == info) { 140 if (devices[index] == null) { 141 devices[index] = createDevice(thisInfo); 142 } 143 if (devices[index] != null) { 144 return devices[index]; 145 } 146 } 147 } 148 throw MidiUtils.unsupportedDevice(info); 149 } 150 151 152 // INNER CLASSES 153 154 155 /** 156 * Info class for MidiDevices. Adds an index value for 157 * making native references to a particular device. 158 */ 159 static class Info extends MidiDevice.Info { 160 private int index; 161 162 Info(String name, String vendor, String description, String version, int index) { 163 super(name, vendor, description, version); 164 this.index = index; 165 } 166 167 final boolean equalStrings(Info info) { 168 return (info != null 169 && getName().equals(info.getName()) 170 && getVendor().equals(info.getVendor()) 171 && getDescription().equals(info.getDescription()) 172 && getVersion().equals(info.getVersion())); 173 } 174 175 final int getIndex() { 176 return index; 177 } 178 179 final void setIndex(int index) { 180 this.index = index; 181 } 182 183 } // class Info 184 185 186 // ABSTRACT METHODS 187 188 abstract int getNumDevices(); 189 abstract MidiDevice[] getDeviceCache(); 190 abstract void setDeviceCache(MidiDevice[] devices); 191 abstract Info[] getInfoCache(); 192 abstract void setInfoCache(Info[] infos); 193 194 abstract Info createInfo(int index); 195 abstract MidiDevice createDevice(Info info); 196 }