Mercurial > hg > release > icedtea6-1.2
view overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/SoftSynthesizer.java @ 843:bcba163568ac
Integrate Gervill.
2008-04-30 Mark Wielaard <mark@klomp.org>
* Makefile.am (ICEDTEA_PATCHES): Add patches/icedtea-gervill.patch.
* Makefile.in: Regenerated.
* patches/icedtea-gervill.patch: New patch.
* overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/*:
New Gervill files.
author | Mark Wielaard <mark@klomp.org> |
---|---|
date | Wed, 30 Apr 2008 22:09:08 +0200 |
parents | |
children | 57bbbc3db355 |
line wrap: on
line source
/* * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.sun.media.sound; import java.io.File; import java.io.IOException; import java.security.AccessControlException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.sound.midi.Instrument; import javax.sound.midi.MidiChannel; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Patch; import javax.sound.midi.Receiver; import javax.sound.midi.Soundbank; import javax.sound.midi.Transmitter; import javax.sound.midi.VoiceStatus; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; /** * * The software synthesizer class. * * @version %I%, %E% * @author Karl Helgason */ public class SoftSynthesizer implements AudioSynthesizer, ReferenceCountingDevice { private static class Info extends MidiDevice.Info { public Info() { super(INFO_NAME, INFO_VENDOR, INFO_DESCRIPTION, INFO_VERSION); } } protected static final String INFO_NAME = "Gervill"; protected static final String INFO_VENDOR = "OpenJDK Proposal"; protected static final String INFO_DESCRIPTION = "Software MIDI Synthesizer"; protected static final String INFO_VERSION = "0.9"; protected static MidiDevice.Info info = new Info(); private static Soundbank defaultSoundBank = null; protected Object control_mutex = this; protected int voiceIDCounter = 0; protected int voice_allocation_mode = 0; // 0 (default) // 1 (DLS Voice Allocation) protected boolean reverb_on = true; protected boolean chorus_on = true; protected boolean agc_on = true; protected SoftChannel[] channels; protected SoftChannelProxy[] external_channels = null; private boolean largemode = false; private int gmmode = 0; // 0:GM Mode off (default) // 1:GM Level 1, 2:GM Level 2 private int deviceid = 0; private AudioFormat format = new AudioFormat(44100, 16, 2, true, false); private SourceDataLine sourceDataLine = null; private SoftAudioPusher pusher = null; private AudioInputStream pusher_stream = null; private float controlrate = 147f; private boolean open = false; private boolean implicitOpen = false; private SoftResampler resampler = new SoftLinearResampler(); private int number_of_midi_channels = 16; private int maxpoly = 64; private long latency = 200000; // 200 msec private boolean jitter_correction = false; private SoftMainMixer mainmixer; private SoftVoice[] voices; private Map<String, SoftTuning> tunings = new HashMap<String, SoftTuning>(); private Map<String, SoftInstrument> inslist = new HashMap<String, SoftInstrument>(); private Map<String, ModelInstrument> availlist = new HashMap<String, ModelInstrument>(); private Map<String, ModelInstrument> loadedlist = new HashMap<String, ModelInstrument>(); private ArrayList<Receiver> recvslist = new ArrayList<Receiver>(); private void getBuffers(ModelInstrument instrument, List<ModelByteBuffer>buffers) { for(ModelPerformer performer : instrument.getPerformers()) { if(performer.getOscillators() != null) for(ModelOscillator osc : performer.getOscillators()) if(osc instanceof ModelByteBufferWavetable) { ModelByteBufferWavetable w = (ModelByteBufferWavetable)osc; ModelByteBuffer buff = w.getBuffer(); if(buff != null) buffers.add(buff); buff = w.get8BitExtensionBuffer(); if(buff != null) buffers.add(buff); } } } private boolean loadSamples(List<ModelInstrument> instruments) { if(largemode) return true; List<ModelByteBuffer>buffers = new ArrayList<ModelByteBuffer>(); for(ModelInstrument instrument : instruments) getBuffers(instrument, buffers); try { ModelByteBuffer.loadAll(buffers); } catch (IOException e) { return false; } return true; } private boolean loadInstruments(List<ModelInstrument> instruments) { if(!isOpen()) return false; if(!loadSamples(instruments)) return false; synchronized (control_mutex) { if (channels != null) for (SoftChannel c : channels) c.current_instrument = null; for (Instrument instrument : instruments) { String pat = patchToString(instrument.getPatch()); availlist.remove(pat); SoftInstrument softins = new SoftInstrument((ModelInstrument) instrument); inslist.put(pat, softins); loadedlist.put(pat, (ModelInstrument) instrument); } } return true; } private void processPropertyInfo(Map<String, Object> info) { AudioSynthesizerPropertyInfo[] items = getPropertyInfo(info); String resamplerType = (String)items[0].value; if(resamplerType.equalsIgnoreCase("point")) this.resampler = new SoftPointResampler(); if(resamplerType.equalsIgnoreCase("linear")) this.resampler = new SoftLinearResampler2(); if(resamplerType.equalsIgnoreCase("linear1")) this.resampler = new SoftLinearResampler(); if(resamplerType.equalsIgnoreCase("linear2")) this.resampler = new SoftLinearResampler2(); if(resamplerType.equalsIgnoreCase("cubic")) this.resampler = new SoftCubicResampler(); if(resamplerType.equalsIgnoreCase("lanczos")) this.resampler = new SoftLanczosResampler(); if(resamplerType.equalsIgnoreCase("sinc")) this.resampler = new SoftSincResampler(); setFormat((AudioFormat)items[2].value); controlrate = (Float)items[1].value; latency = (Long)items[3].value; deviceid = (Integer)items[4].value; maxpoly = (Integer)items[5].value; reverb_on = (Boolean)items[6].value; chorus_on = (Boolean)items[7].value; agc_on = (Boolean)items[8].value; largemode = (Boolean)items[9].value; number_of_midi_channels = (Integer)items[10].value; jitter_correction = (Boolean)items[11].value; } private String patchToString(Patch patch) { if (patch instanceof ModelPatch && ((ModelPatch) patch).isPercussion()) return "p." + patch.getProgram() + "." + patch.getBank(); else return patch.getProgram() + "." + patch.getBank(); } private void setFormat(AudioFormat format) { if (format.getChannels() > 2) throw new IllegalArgumentException( "Only mono and stereo audio supported."); if (AudioFloatConverter.getConverter(format) == null) throw new IllegalArgumentException("Audio format not supported."); this.format = format; } protected void removeReceiver(Receiver recv) { boolean perform_close = false; synchronized (control_mutex) { if(recvslist.remove(recv)) if(implicitOpen && recvslist.isEmpty()) { perform_close = true; } } if(perform_close) close(); } protected SoftMainMixer getMainMixer() { if (!isOpen()) return null; return mainmixer; } protected SoftInstrument findInstrument(int program, int bank, int channel) { // Add support for GM2 banks 0x78 and 0x79 // as specified in DLS 2.2 in Section 1.4.6 // which allows using percussion and melodic instruments // on all channels if (bank >> 7 == 0x78 || bank >> 7 == 0x79) { SoftInstrument current_instrument = inslist.get(program + "." + bank); if (current_instrument != null) return current_instrument; String p_plaf; if (bank >> 7 == 0x78) p_plaf = "p."; else p_plaf = ""; // Instrument not found fallback to MSB:bank, LSB:0 current_instrument = inslist.get(p_plaf + program + "." + ((bank & 128) << 7)); if (current_instrument != null) return current_instrument; // Instrument not found fallback to MSB:0, LSB:bank current_instrument = inslist.get(p_plaf + program + "." + (bank & 128)); if (current_instrument != null) return current_instrument; // Instrument not found fallback to MSB:0, LSB:0 current_instrument = inslist.get(p_plaf + program + ".0"); if (current_instrument != null) return current_instrument; // Instrument not found fallback to MSB:0, LSB:0, program=0 current_instrument = inslist.get(p_plaf + program + "0.0"); if (current_instrument != null) return current_instrument; return null; } // Channel 10 uses percussion instruments String p_plaf; if (channel == 9) p_plaf = "p."; else p_plaf = ""; SoftInstrument current_instrument = inslist.get(p_plaf + program + "." + bank); if (current_instrument != null) return current_instrument; // Instrument not found fallback to MSB:0, LSB:0 current_instrument = inslist.get(p_plaf + program + ".0"); if (current_instrument != null) return current_instrument; // Instrument not found fallback to MSB:0, LSB:0, program=0 current_instrument = inslist.get(p_plaf + "0.0"); if (current_instrument != null) return current_instrument; return null; } protected int getVoiceAllocationMode() { return voice_allocation_mode; } protected int getGeneralMidiMode() { return gmmode; } protected void setGeneralMidiMode(int gmmode) { this.gmmode = gmmode; } protected int getDeviceID() { return deviceid; } protected float getControlRate() { return controlrate; } protected SoftVoice[] getVoices() { return voices; } protected SoftTuning getTuning(Patch patch) { String t_id = patchToString(patch); SoftTuning tuning = tunings.get(t_id); if (tuning == null) { tuning = new SoftTuning(patch); tunings.put(t_id, tuning); } return tuning; } public long getLatency() { synchronized (control_mutex) { return latency; } } public AudioFormat getFormat() { synchronized (control_mutex) { return format; } } public int getMaxPolyphony() { synchronized (control_mutex) { return maxpoly; } } public MidiChannel[] getChannels() { synchronized (control_mutex) { if(external_channels == null) { external_channels = new SoftChannelProxy[16]; for (int i = 0; i < external_channels.length; i++) external_channels[i] = new SoftChannelProxy(); } MidiChannel[] ret; if(isOpen()) ret = new MidiChannel[channels.length]; else ret = new MidiChannel[16]; for (int i = 0; i < ret.length; i++) ret[i] = external_channels[i]; return ret; } } public VoiceStatus[] getVoiceStatus() { if(!isOpen()) { VoiceStatus[] tempVoiceStatusArray = new VoiceStatus[getMaxPolyphony()]; for (int i = 0; i < tempVoiceStatusArray.length; i++) { VoiceStatus b = new VoiceStatus(); b.active = false; b.bank = 0; b.channel = 0; b.note = 0; b.program = 0; b.volume = 0; tempVoiceStatusArray[i] = b; } return tempVoiceStatusArray; } synchronized (control_mutex) { VoiceStatus[] tempVoiceStatusArray = new VoiceStatus[voices.length]; for (int i = 0; i < voices.length; i++) { VoiceStatus a = voices[i]; VoiceStatus b = new VoiceStatus(); b.active = a.active; b.bank = a.bank; b.channel = a.channel; b.note = a.note; b.program = a.program; b.volume = a.volume; tempVoiceStatusArray[i] = b; } return tempVoiceStatusArray; } } public boolean isSoundbankSupported(Soundbank soundbank) { for (Instrument ins : soundbank.getInstruments()) if (!(ins instanceof ModelInstrument)) return false; return true; } public boolean loadInstrument(Instrument instrument) { if (instrument == null || (!(instrument instanceof ModelInstrument))) throw new IllegalArgumentException("Unsupported instrument: " + instrument.toString()); List<ModelInstrument> instruments = new ArrayList<ModelInstrument>(); instruments.add((ModelInstrument)instrument); return loadInstruments(instruments); } public void unloadInstrument(Instrument instrument) { if (instrument == null || (!(instrument instanceof ModelInstrument))) throw new IllegalArgumentException("Unsupported instrument: " + instrument.toString()); if(!isOpen()) return; String pat = patchToString(instrument.getPatch()); synchronized (control_mutex) { for (SoftChannel c : channels) c.current_instrument = null; inslist.remove(pat); loadedlist.remove(pat); availlist.remove(pat); } } public boolean remapInstrument(Instrument from, Instrument to) { if (from == null) throw new NullPointerException(); if (to == null) throw new NullPointerException(); if (!(from instanceof ModelInstrument)) throw new IllegalArgumentException("Unsupported instrument: " + from.toString()); if (!(to instanceof ModelInstrument)) throw new IllegalArgumentException("Unsupported instrument: " + to.toString()); if(!isOpen()) return false; synchronized (control_mutex) { if (!loadedlist.containsValue(to) && !availlist.containsValue(to)) throw new IllegalArgumentException("Instrument to is not loaded."); unloadInstrument(from); ModelMappedInstrument mfrom = new ModelMappedInstrument( (ModelInstrument) to, from.getPatch()); return loadInstrument(mfrom); } } public synchronized Soundbank getDefaultSoundbank() { if (defaultSoundBank == null) { try { File javahome = new File(System.getProperties().getProperty( "java.home")); File libaudio = new File(new File(javahome, "lib"), "audio"); if (libaudio.exists()) { File foundfile = null; File[] files = libaudio.listFiles(); if (files != null) { for (int i = 0; i < files.length; i++) { File file = files[i]; if (file.isFile()) { String lname = file.getName().toLowerCase(); if (lname.endsWith(".sf2") || lname.endsWith(".dls")) { if (foundfile == null || (file.length() > foundfile .length())) foundfile = file; } } } } if (foundfile != null) { try { Soundbank sbk = MidiSystem.getSoundbank(foundfile); defaultSoundBank = sbk; return defaultSoundBank; } catch (Exception e) { e.printStackTrace(); } } } if (System.getProperties().getProperty("os.name").startsWith( "Windows")) { File gm_dls = new File(System.getenv("SystemRoot") + "\\system32\\drivers\\gm.dls"); if (gm_dls.exists()) { try { Soundbank sbk = MidiSystem.getSoundbank(gm_dls); defaultSoundBank = sbk; return defaultSoundBank; } catch (Exception e) { e.printStackTrace(); } } } } catch (AccessControlException e) { } catch (Exception e) { e.printStackTrace(); } try { defaultSoundBank = EmergencySoundbank.createSoundbank(); } catch (Exception e) { e.printStackTrace(); } } return defaultSoundBank; } public Instrument[] getAvailableInstruments() { if(!isOpen()) { Soundbank defsbk = getDefaultSoundbank(); if(defsbk == null) return new Instrument[0]; return defsbk.getInstruments(); } synchronized (control_mutex) { ModelInstrument[] inslist_array = new ModelInstrument[availlist .values().size()]; availlist.values().toArray(inslist_array); Arrays.sort(inslist_array, new ModelInstrumentComparator()); return inslist_array; } } public Instrument[] getLoadedInstruments() { if(!isOpen()) return new Instrument[0]; synchronized (control_mutex) { ModelInstrument[] inslist_array = new ModelInstrument[loadedlist .values().size()]; loadedlist.values().toArray(inslist_array); Arrays.sort(inslist_array, new ModelInstrumentComparator()); return inslist_array; } } public boolean loadAllInstruments(Soundbank soundbank) { List<ModelInstrument> instruments = new ArrayList<ModelInstrument>(); for (Instrument ins : soundbank.getInstruments()) { if (ins == null || !(ins instanceof ModelInstrument)) throw new IllegalArgumentException("Unsupported instrument: " + ins); instruments.add((ModelInstrument)ins); } return loadInstruments(instruments); } public void unloadAllInstruments(Soundbank soundbank) { if(!isOpen()) return; for (Instrument ins : soundbank.getInstruments()) { if (ins instanceof ModelInstrument) { unloadInstrument(ins); } } } public boolean loadInstruments(Soundbank soundbank, Patch[] patchList) { List<ModelInstrument> instruments = new ArrayList<ModelInstrument>(); for (Patch patch : patchList) { Instrument ins = soundbank.getInstrument(patch); if (ins == null || !(ins instanceof ModelInstrument)) throw new IllegalArgumentException("Unsupported instrument: " + ins); instruments.add((ModelInstrument)ins); } return loadInstruments(instruments); } public void unloadInstruments(Soundbank soundbank, Patch[] patchList) { if(!isOpen()) return; for (Patch pat : patchList) { Instrument ins = soundbank.getInstrument(pat); if (ins instanceof ModelInstrument) { unloadInstrument(ins); } } } public MidiDevice.Info getDeviceInfo() { return info; } public AudioSynthesizerPropertyInfo[] getPropertyInfo(Map<String, Object> info) { List<AudioSynthesizerPropertyInfo> list = new ArrayList<AudioSynthesizerPropertyInfo>(); AudioSynthesizerPropertyInfo item; item = new AudioSynthesizerPropertyInfo("interpolation", "linear"); item.choices = new String[] {"linear", "linear1", "linear2", "cubic", "lanczos", "sinc", "point"}; item.description = "Interpolation method"; list.add(item); item = new AudioSynthesizerPropertyInfo("control rate", 147f); item.description = "Control rate"; list.add(item); item = new AudioSynthesizerPropertyInfo("format", new AudioFormat(44100, 16, 2, true, false)); item.description = "Default audio format"; list.add(item); item = new AudioSynthesizerPropertyInfo("latency", 120000L); item.description = "Default latency"; list.add(item); item = new AudioSynthesizerPropertyInfo("device id", 0); item.description = "Device ID for SysEx Messages"; list.add(item); item = new AudioSynthesizerPropertyInfo("max polyphony", 64); item.description = "Maximum polyphony"; list.add(item); item = new AudioSynthesizerPropertyInfo("reverb", true); item.description = "Turn reverb effect on or off"; list.add(item); item = new AudioSynthesizerPropertyInfo("chorus", true); item.description = "Turn chorus effect on or off"; list.add(item); item = new AudioSynthesizerPropertyInfo("auto gain control", true); item.description = "Turn auto gain control on or off"; list.add(item); item = new AudioSynthesizerPropertyInfo("large mode", false); item.description = "Turn large mode on or off."; list.add(item); item = new AudioSynthesizerPropertyInfo("midi channels", 16); item.description = "Number of midi channels."; list.add(item); item = new AudioSynthesizerPropertyInfo("jitter correction", true); item.description = "Turn jitter correction on or off."; list.add(item); AudioSynthesizerPropertyInfo[] items; items = list.toArray(new AudioSynthesizerPropertyInfo[list.size()]); if(info != null) for(AudioSynthesizerPropertyInfo item2 : items) { Object v = info.get(item2.name); Class c = (item2.valueClass); if(v != null) if(c.isInstance(v)) item2.value = v; } return items; } public void open() throws MidiUnavailableException { if(isOpen()) { synchronized (control_mutex) { implicitOpen = false; } return; } open(null, null); } public void open(SourceDataLine line, Map<String, Object> info) throws MidiUnavailableException { if(isOpen()) { synchronized (control_mutex) { implicitOpen = false; } return; } synchronized (control_mutex) { try { if(line != null) setFormat(line.getFormat()); AudioInputStream ais = openStream(getFormat(), info); if(line == null) line = AudioSystem.getSourceDataLine(getFormat()); double latency = this.latency; if(!line.isOpen()) { int bufferSize = getFormat().getFrameSize() * (int)(getFormat().getFrameRate() * (latency / 1000000f)); line.open(getFormat(), bufferSize); // Remember that we opened that line // so we can close again in SoftSynthesizer.close() sourceDataLine = line; } if(!line.isActive()) line.start(); int controlbuffersize = 512; try { controlbuffersize = ais.available(); } catch (IOException e) {} // Tell mixer not fill read buffers fully. // This lowers latency, and tells DataPusher // to read in smaller amounts. //mainmixer.readfully = false; //pusher = new DataPusher(line, ais); int buffersize = line.getBufferSize(); buffersize -= buffersize % controlbuffersize; if(buffersize < 3*controlbuffersize) buffersize = 3*controlbuffersize; if(jitter_correction) { ais = new SoftJitterCorrector(ais, buffersize, controlbuffersize); } pusher = new SoftAudioPusher(line, ais, controlbuffersize); pusher_stream = ais; pusher.start(); } catch(LineUnavailableException e) { if(isOpen()) close(); throw new MidiUnavailableException(e.toString()); } } } public AudioInputStream openStream(AudioFormat targetFormat, Map<String, Object> info) throws MidiUnavailableException { if(isOpen()) throw new MidiUnavailableException("Synthesizer is already open"); synchronized (control_mutex) { open = true; implicitOpen = false; gmmode = 0; voice_allocation_mode = 0; processPropertyInfo(info); if(targetFormat != null) setFormat(targetFormat); Soundbank defbank = getDefaultSoundbank(); if (defbank != null) { loadAllInstruments(defbank); availlist.putAll(loadedlist); loadedlist.clear(); } voices = new SoftVoice[maxpoly]; for (int i = 0; i < maxpoly; i++) voices[i] = new SoftVoice(this); mainmixer = new SoftMainMixer(this); channels = new SoftChannel[number_of_midi_channels]; for (int i = 0; i < channels.length; i++) channels[i] = new SoftChannel(this, i); if(external_channels == null) { // Always create external_channels array // with 16 or more channels // so getChannels works correctly // when the synhtesizer is closed. if(channels.length < 16) external_channels = new SoftChannelProxy[16]; else external_channels = new SoftChannelProxy[channels.length]; for (int i = 0; i < external_channels.length; i++) external_channels[i] = new SoftChannelProxy(); } else { // We must resize external_channels array // but we must also copy the old SoftChannelProxy // into the new one if(channels.length > external_channels.length) { SoftChannelProxy[] new_external_channels = new SoftChannelProxy[channels.length]; for (int i = 0; i < external_channels.length; i++) new_external_channels[i] = external_channels[i]; for (int i = external_channels.length; i < new_external_channels.length; i++) new_external_channels[i] = new SoftChannelProxy(); } } for (int i = 0; i < channels.length; i++) external_channels[i].setChannel(channels[i]); for (SoftVoice voice : getVoices()) voice.resampler = resampler.openStreamer(); for (Receiver recv : getReceivers()) { SoftReceiver srecv = ((SoftReceiver)recv); srecv.open = open; srecv.mainmixer = mainmixer; srecv.midimessages = mainmixer.midimessages; } return mainmixer.getInputStream(); } } public void close() { if(!isOpen()) return; SoftAudioPusher pusher_to_be_closed = null; AudioInputStream pusher_stream_to_be_closed = null; synchronized (control_mutex) { if(pusher != null) { pusher_to_be_closed = pusher; pusher_stream_to_be_closed = pusher_stream; pusher = null; pusher_stream = null; } } if(pusher_to_be_closed != null) { // Pusher must not be closed synchronized against control_mutex // this may result in synchronized conflict between pusher and current thread. pusher_to_be_closed.stop(); try { pusher_stream_to_be_closed.close(); } catch (IOException e) { e.printStackTrace(); } } synchronized (control_mutex) { if(mainmixer != null) mainmixer.close(); open = false; implicitOpen = false; mainmixer = null; voices = null; channels = null; if(external_channels != null) for (int i = 0; i < external_channels.length; i++) external_channels[i].setChannel(null); if(sourceDataLine != null) { sourceDataLine.drain(); sourceDataLine.close(); sourceDataLine = null; } inslist.clear(); availlist.clear(); loadedlist.clear(); tunings.clear(); while(recvslist.size() != 0) recvslist.get(recvslist.size()-1).close(); } } public boolean isOpen() { synchronized (control_mutex) { return open; } } public long getMicrosecondPosition() { if(!isOpen()) return 0; synchronized (control_mutex) { return mainmixer.getMicrosecondPosition(); } } public int getMaxReceivers() { return -1; } public int getMaxTransmitters() { return 0; } public Receiver getReceiver() throws MidiUnavailableException { synchronized (control_mutex) { SoftReceiver receiver = new SoftReceiver(this); receiver.open = open; recvslist.add(receiver); return receiver; } } public List<Receiver> getReceivers() { synchronized (control_mutex) { ArrayList<Receiver> recvs = new ArrayList<Receiver>(); recvs.addAll(recvslist); return recvs; } } public Transmitter getTransmitter() throws MidiUnavailableException { throw new MidiUnavailableException("No transmitter available"); } public List<Transmitter> getTransmitters() { return new ArrayList<Transmitter>(); } public Receiver getReceiverReferenceCounting() throws MidiUnavailableException { if(!isOpen()) { open(); synchronized (control_mutex) { implicitOpen = true; } } return getReceiver(); } public Transmitter getTransmitterReferenceCounting() throws MidiUnavailableException { throw new MidiUnavailableException("No transmitter available"); } }