view overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/SoftSynthesizer.java @ 845:57bbbc3db355

Fix some Gervill nits (correct Soundbank, IOExceptions on URL, wrong name). 2008-05-01 Mark Wielaard <mwielaard@redhat.com> * overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/ DLSSoundbankReader.java (getSoundbank(URL)): Don't throw IOException. (getSoundbank(File)): Allow soundbanks not ending in .dls. * overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/ PATSoundbankReader.java (getSoundbank(URL)): Don't throw IOException. * overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/ SF2SoundbankReader.java (getSoundbank(URL)): Likewise. (getSoundbank(File)): Allow soundbanks not ending in .sf2. * overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/ SoftSynthesizer.java (unloadAllInstruments): Check if Soundbank supported. (unloadInstruments): Likewise.
author Mark Wielaard <mark@klomp.org>
date Thu, 01 May 2008 10:57:58 +0200
parents bcba163568ac
children a1832ea486e9
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(soundbank == null || !isSoundbankSupported(soundbank))
			throw new IllegalArgumentException("Unsupported 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(soundbank == null || !isSoundbankSupported(soundbank))
			throw new IllegalArgumentException("Unsupported soundbank: " + soundbank);

		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");
	}

}