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     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 }