/*
 * Copyright (c) 2007, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.media.sound;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import javax.sound.midi.MidiMessage;
import javax.sound.midi.Patch;
import javax.sound.midi.ShortMessage;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

Software synthesizer main audio mixer.
Author:Karl Helgason
/** * Software synthesizer main audio mixer. * * @author Karl Helgason */
public final class SoftMainMixer { // A private class thats contains a ModelChannelMixer and it's private buffers. // This becomes necessary when we want to have separate delay buffers for each channel mixer. private class SoftChannelMixerContainer { ModelChannelMixer mixer; SoftAudioBuffer[] buffers; } public final static int CHANNEL_LEFT = 0; public final static int CHANNEL_RIGHT = 1; public final static int CHANNEL_MONO = 2; public final static int CHANNEL_DELAY_LEFT = 3; public final static int CHANNEL_DELAY_RIGHT = 4; public final static int CHANNEL_DELAY_MONO = 5; public final static int CHANNEL_EFFECT1 = 6; public final static int CHANNEL_EFFECT2 = 7; public final static int CHANNEL_DELAY_EFFECT1 = 8; public final static int CHANNEL_DELAY_EFFECT2 = 9; public final static int CHANNEL_LEFT_DRY = 10; public final static int CHANNEL_RIGHT_DRY = 11; public final static int CHANNEL_SCRATCH1 = 12; public final static int CHANNEL_SCRATCH2 = 13; boolean active_sensing_on = false; private long msec_last_activity = -1; private boolean pusher_silent = false; private int pusher_silent_count = 0; private long sample_pos = 0; boolean readfully = true; private final Object control_mutex; private SoftSynthesizer synth; private float samplerate = 44100; private int nrofchannels = 2; private SoftVoice[] voicestatus = null; private SoftAudioBuffer[] buffers; private SoftReverb reverb; private SoftAudioProcessor chorus; private SoftAudioProcessor agc; private long msec_buffer_len = 0; private int buffer_len = 0; TreeMap<Long, Object> midimessages = new TreeMap<Long, Object>(); private int delay_midievent = 0; private int max_delay_midievent = 0; double last_volume_left = 1.0; double last_volume_right = 1.0; private double[] co_master_balance = new double[1]; private double[] co_master_volume = new double[1]; private double[] co_master_coarse_tuning = new double[1]; private double[] co_master_fine_tuning = new double[1]; private AudioInputStream ais; private Set<SoftChannelMixerContainer> registeredMixers = null; private Set<ModelChannelMixer> stoppedMixers = null; private SoftChannelMixerContainer[] cur_registeredMixers = null; SoftControl co_master = new SoftControl() { double[] balance = co_master_balance; double[] volume = co_master_volume; double[] coarse_tuning = co_master_coarse_tuning; double[] fine_tuning = co_master_fine_tuning; public double[] get(int instance, String name) { if (name == null) return null; if (name.equals("balance")) return balance; if (name.equals("volume")) return volume; if (name.equals("coarse_tuning")) return coarse_tuning; if (name.equals("fine_tuning")) return fine_tuning; return null; } }; private void processSystemExclusiveMessage(byte[] data) { synchronized (synth.control_mutex) { activity(); // Universal Non-Real-Time SysEx if ((data[1] & 0xFF) == 0x7E) { int deviceID = data[2] & 0xFF; if (deviceID == 0x7F || deviceID == synth.getDeviceID()) { int subid1 = data[3] & 0xFF; int subid2; switch (subid1) { case 0x08: // MIDI Tuning Standard subid2 = data[4] & 0xFF; switch (subid2) { case 0x01: // BULK TUNING DUMP { // http://www.midi.org/about-midi/tuning.shtml SoftTuning tuning = synth.getTuning(new Patch(0, data[5] & 0xFF)); tuning.load(data); break; } case 0x04: // KEY-BASED TUNING DUMP case 0x05: // SCALE/OCTAVE TUNING DUMP, 1 byte format case 0x06: // SCALE/OCTAVE TUNING DUMP, 2 byte format case 0x07: // SINGLE NOTE TUNING CHANGE (NON REAL-TIME) // (BANK) { // http://www.midi.org/about-midi/tuning_extens.shtml SoftTuning tuning = synth.getTuning(new Patch( data[5] & 0xFF, data[6] & 0xFF)); tuning.load(data); break; } case 0x08: // scale/octave tuning 1-byte form (Non // Real-Time) case 0x09: // scale/octave tuning 2-byte form (Non // Real-Time) { // http://www.midi.org/about-midi/tuning-scale.shtml SoftTuning tuning = new SoftTuning(data); int channelmask = (data[5] & 0xFF) * 16384 + (data[6] & 0xFF) * 128 + (data[7] & 0xFF); SoftChannel[] channels = synth.channels; for (int i = 0; i < channels.length; i++) if ((channelmask & (1 << i)) != 0) channels[i].tuning = tuning; break; } default: break; } break; case 0x09: // General Midi Message subid2 = data[4] & 0xFF; switch (subid2) { case 0x01: // General Midi 1 On synth.setGeneralMidiMode(1); reset(); break; case 0x02: // General Midi Off synth.setGeneralMidiMode(0); reset(); break; case 0x03: // General MidI Level 2 On synth.setGeneralMidiMode(2); reset(); break; default: break; } break; case 0x0A: // DLS Message subid2 = data[4] & 0xFF; switch (subid2) { case 0x01: // DLS On if (synth.getGeneralMidiMode() == 0) synth.setGeneralMidiMode(1); synth.voice_allocation_mode = 1; reset(); break; case 0x02: // DLS Off synth.setGeneralMidiMode(0); synth.voice_allocation_mode = 0; reset(); break; case 0x03: // DLS Static Voice Allocation Off synth.voice_allocation_mode = 0; break; case 0x04: // DLS Static Voice Allocation On synth.voice_allocation_mode = 1; break; default: break; } break; default: break; } } } // Universal Real-Time SysEx if ((data[1] & 0xFF) == 0x7F) { int deviceID = data[2] & 0xFF; if (deviceID == 0x7F || deviceID == synth.getDeviceID()) { int subid1 = data[3] & 0xFF; int subid2; switch (subid1) { case 0x04: // Device Control subid2 = data[4] & 0xFF; switch (subid2) { case 0x01: // Master Volume case 0x02: // Master Balane case 0x03: // Master fine tuning case 0x04: // Master coarse tuning int val = (data[5] & 0x7F) + ((data[6] & 0x7F) * 128); if (subid2 == 0x01) setVolume(val); else if (subid2 == 0x02) setBalance(val); else if (subid2 == 0x03) setFineTuning(val); else if (subid2 == 0x04) setCoarseTuning(val); break; case 0x05: // Global Parameter Control int ix = 5; int slotPathLen = (data[ix++] & 0xFF); int paramWidth = (data[ix++] & 0xFF); int valueWidth = (data[ix++] & 0xFF); int[] slotPath = new int[slotPathLen]; for (int i = 0; i < slotPathLen; i++) { int msb = (data[ix++] & 0xFF); int lsb = (data[ix++] & 0xFF); slotPath[i] = msb * 128 + lsb; } int paramCount = (data.length - 1 - ix) / (paramWidth + valueWidth); long[] params = new long[paramCount]; long[] values = new long[paramCount]; for (int i = 0; i < paramCount; i++) { values[i] = 0; for (int j = 0; j < paramWidth; j++) params[i] = params[i] * 128 + (data[ix++] & 0xFF); for (int j = 0; j < valueWidth; j++) values[i] = values[i] * 128 + (data[ix++] & 0xFF); } globalParameterControlChange(slotPath, params, values); break; default: break; } break; case 0x08: // MIDI Tuning Standard subid2 = data[4] & 0xFF; switch (subid2) { case 0x02: // SINGLE NOTE TUNING CHANGE (REAL-TIME) { // http://www.midi.org/about-midi/tuning.shtml SoftTuning tuning = synth.getTuning(new Patch(0, data[5] & 0xFF)); tuning.load(data); SoftVoice[] voices = synth.getVoices(); for (int i = 0; i < voices.length; i++) if (voices[i].active) if (voices[i].tuning == tuning) voices[i].updateTuning(tuning); break; } case 0x07: // SINGLE NOTE TUNING CHANGE (REAL-TIME) // (BANK) { // http://www.midi.org/about-midi/tuning_extens.shtml SoftTuning tuning = synth.getTuning(new Patch( data[5] & 0xFF, data[6] & 0xFF)); tuning.load(data); SoftVoice[] voices = synth.getVoices(); for (int i = 0; i < voices.length; i++) if (voices[i].active) if (voices[i].tuning == tuning) voices[i].updateTuning(tuning); break; } case 0x08: // scale/octave tuning 1-byte form //(Real-Time) case 0x09: // scale/octave tuning 2-byte form // (Real-Time) { // http://www.midi.org/about-midi/tuning-scale.shtml SoftTuning tuning = new SoftTuning(data); int channelmask = (data[5] & 0xFF) * 16384 + (data[6] & 0xFF) * 128 + (data[7] & 0xFF); SoftChannel[] channels = synth.channels; for (int i = 0; i < channels.length; i++) if ((channelmask & (1 << i)) != 0) channels[i].tuning = tuning; SoftVoice[] voices = synth.getVoices(); for (int i = 0; i < voices.length; i++) if (voices[i].active) if ((channelmask & (1 << (voices[i].channel))) != 0) voices[i].updateTuning(tuning); break; } default: break; } break; case 0x09: // Control Destination Settings subid2 = data[4] & 0xFF; switch (subid2) { case 0x01: // Channel Pressure { int[] destinations = new int[(data.length - 7) / 2]; int[] ranges = new int[(data.length - 7) / 2]; int ix = 0; for (int j = 6; j < data.length - 1; j += 2) { destinations[ix] = data[j] & 0xFF; ranges[ix] = data[j + 1] & 0xFF; ix++; } int channel = data[5] & 0xFF; SoftChannel softchannel = synth.channels[channel]; softchannel.mapChannelPressureToDestination( destinations, ranges); break; } case 0x02: // Poly Pressure { int[] destinations = new int[(data.length - 7) / 2]; int[] ranges = new int[(data.length - 7) / 2]; int ix = 0; for (int j = 6; j < data.length - 1; j += 2) { destinations[ix] = data[j] & 0xFF; ranges[ix] = data[j + 1] & 0xFF; ix++; } int channel = data[5] & 0xFF; SoftChannel softchannel = synth.channels[channel]; softchannel.mapPolyPressureToDestination( destinations, ranges); break; } case 0x03: // Control Change { int[] destinations = new int[(data.length - 7) / 2]; int[] ranges = new int[(data.length - 7) / 2]; int ix = 0; for (int j = 7; j < data.length - 1; j += 2) { destinations[ix] = data[j] & 0xFF; ranges[ix] = data[j + 1] & 0xFF; ix++; } int channel = data[5] & 0xFF; SoftChannel softchannel = synth.channels[channel]; int control = data[6] & 0xFF; softchannel.mapControlToDestination(control, destinations, ranges); break; } default: break; } break; case 0x0A: // Key Based Instrument Control { subid2 = data[4] & 0xFF; switch (subid2) { case 0x01: // Basic Message int channel = data[5] & 0xFF; int keynumber = data[6] & 0xFF; SoftChannel softchannel = synth.channels[channel]; for (int j = 7; j < data.length - 1; j += 2) { int controlnumber = data[j] & 0xFF; int controlvalue = data[j + 1] & 0xFF; softchannel.controlChangePerNote(keynumber, controlnumber, controlvalue); } break; default: break; } break; } default: break; } } } } } private void processMessages(long timeStamp) { Iterator<Entry<Long, Object>> iter = midimessages.entrySet().iterator(); while (iter.hasNext()) { Entry<Long, Object> entry = iter.next(); if (entry.getKey() >= (timeStamp + msec_buffer_len)) return; long msec_delay = entry.getKey() - timeStamp; delay_midievent = (int)(msec_delay * (samplerate / 1000000.0) + 0.5); if(delay_midievent > max_delay_midievent) delay_midievent = max_delay_midievent; if(delay_midievent < 0) delay_midievent = 0; processMessage(entry.getValue()); iter.remove(); } delay_midievent = 0; } void processAudioBuffers() { if(synth.weakstream != null && synth.weakstream.silent_samples != 0) { sample_pos += synth.weakstream.silent_samples; synth.weakstream.silent_samples = 0; } for (int i = 0; i < buffers.length; i++) { if(i != CHANNEL_DELAY_LEFT && i != CHANNEL_DELAY_RIGHT && i != CHANNEL_DELAY_MONO && i != CHANNEL_DELAY_EFFECT1 && i != CHANNEL_DELAY_EFFECT2) buffers[i].clear(); } if(!buffers[CHANNEL_DELAY_LEFT].isSilent()) { buffers[CHANNEL_LEFT].swap(buffers[CHANNEL_DELAY_LEFT]); } if(!buffers[CHANNEL_DELAY_RIGHT].isSilent()) { buffers[CHANNEL_RIGHT].swap(buffers[CHANNEL_DELAY_RIGHT]); } if(!buffers[CHANNEL_DELAY_MONO].isSilent()) { buffers[CHANNEL_MONO].swap(buffers[CHANNEL_DELAY_MONO]); } if(!buffers[CHANNEL_DELAY_EFFECT1].isSilent()) { buffers[CHANNEL_EFFECT1].swap(buffers[CHANNEL_DELAY_EFFECT1]); } if(!buffers[CHANNEL_DELAY_EFFECT2].isSilent()) { buffers[CHANNEL_EFFECT2].swap(buffers[CHANNEL_DELAY_EFFECT2]); } double volume_left; double volume_right; SoftChannelMixerContainer[] act_registeredMixers; // perform control logic synchronized (control_mutex) { long msec_pos = (long)(sample_pos * (1000000.0 / samplerate)); processMessages(msec_pos); if (active_sensing_on) { // Active Sensing // if no message occurs for max 1000 ms // then do AllSoundOff on all channels if ((msec_pos - msec_last_activity) > 1000000) { active_sensing_on = false; for (SoftChannel c : synth.channels) c.allSoundOff(); } } for (int i = 0; i < voicestatus.length; i++) if (voicestatus[i].active) voicestatus[i].processControlLogic(); sample_pos += buffer_len; double volume = co_master_volume[0]; volume_left = volume; volume_right = volume; double balance = co_master_balance[0]; if (balance > 0.5) volume_left *= (1 - balance) * 2; else volume_right *= balance * 2; chorus.processControlLogic(); reverb.processControlLogic(); agc.processControlLogic(); if (cur_registeredMixers == null) { if (registeredMixers != null) { cur_registeredMixers = new SoftChannelMixerContainer[registeredMixers.size()]; registeredMixers.toArray(cur_registeredMixers); } } act_registeredMixers = cur_registeredMixers; if (act_registeredMixers != null) if (act_registeredMixers.length == 0) act_registeredMixers = null; } if (act_registeredMixers != null) { // Make backup of left,right,mono channels SoftAudioBuffer leftbak = buffers[CHANNEL_LEFT]; SoftAudioBuffer rightbak = buffers[CHANNEL_RIGHT]; SoftAudioBuffer monobak = buffers[CHANNEL_MONO]; SoftAudioBuffer delayleftbak = buffers[CHANNEL_DELAY_LEFT]; SoftAudioBuffer delayrightbak = buffers[CHANNEL_DELAY_RIGHT]; SoftAudioBuffer delaymonobak = buffers[CHANNEL_DELAY_MONO]; int bufferlen = buffers[CHANNEL_LEFT].getSize(); float[][] cbuffer = new float[nrofchannels][]; float[][] obuffer = new float[nrofchannels][]; obuffer[0] = leftbak.array(); if (nrofchannels != 1) obuffer[1] = rightbak.array(); for (SoftChannelMixerContainer cmixer : act_registeredMixers) { // Reroute default left,right output // to channelmixer left,right input/output buffers[CHANNEL_LEFT] = cmixer.buffers[CHANNEL_LEFT]; buffers[CHANNEL_RIGHT] = cmixer.buffers[CHANNEL_RIGHT]; buffers[CHANNEL_MONO] = cmixer.buffers[CHANNEL_MONO]; buffers[CHANNEL_DELAY_LEFT] = cmixer.buffers[CHANNEL_DELAY_LEFT]; buffers[CHANNEL_DELAY_RIGHT] = cmixer.buffers[CHANNEL_DELAY_RIGHT]; buffers[CHANNEL_DELAY_MONO] = cmixer.buffers[CHANNEL_DELAY_MONO]; buffers[CHANNEL_LEFT].clear(); buffers[CHANNEL_RIGHT].clear(); buffers[CHANNEL_MONO].clear(); if(!buffers[CHANNEL_DELAY_LEFT].isSilent()) { buffers[CHANNEL_LEFT].swap(buffers[CHANNEL_DELAY_LEFT]); } if(!buffers[CHANNEL_DELAY_RIGHT].isSilent()) { buffers[CHANNEL_RIGHT].swap(buffers[CHANNEL_DELAY_RIGHT]); } if(!buffers[CHANNEL_DELAY_MONO].isSilent()) { buffers[CHANNEL_MONO].swap(buffers[CHANNEL_DELAY_MONO]); } cbuffer[0] = buffers[CHANNEL_LEFT].array(); if (nrofchannels != 1) cbuffer[1] = buffers[CHANNEL_RIGHT].array(); boolean hasactivevoices = false; for (int i = 0; i < voicestatus.length; i++) if (voicestatus[i].active) if (voicestatus[i].channelmixer == cmixer.mixer) { voicestatus[i].processAudioLogic(buffers); hasactivevoices = true; } if(!buffers[CHANNEL_MONO].isSilent()) { float[] mono = buffers[CHANNEL_MONO].array(); float[] left = buffers[CHANNEL_LEFT].array(); if (nrofchannels != 1) { float[] right = buffers[CHANNEL_RIGHT].array(); for (int i = 0; i < bufferlen; i++) { float v = mono[i]; left[i] += v; right[i] += v; } } else { for (int i = 0; i < bufferlen; i++) { left[i] += mono[i]; } } } if (!cmixer.mixer.process(cbuffer, 0, bufferlen)) { synchronized (control_mutex) { registeredMixers.remove(cmixer); cur_registeredMixers = null; } } for (int i = 0; i < cbuffer.length; i++) { float[] cbuff = cbuffer[i]; float[] obuff = obuffer[i]; for (int j = 0; j < bufferlen; j++) obuff[j] += cbuff[j]; } if (!hasactivevoices) { synchronized (control_mutex) { if (stoppedMixers != null) { if (stoppedMixers.contains(cmixer)) { stoppedMixers.remove(cmixer); cmixer.mixer.stop(); } } } } } buffers[CHANNEL_LEFT] = leftbak; buffers[CHANNEL_RIGHT] = rightbak; buffers[CHANNEL_MONO] = monobak; buffers[CHANNEL_DELAY_LEFT] = delayleftbak; buffers[CHANNEL_DELAY_RIGHT] = delayrightbak; buffers[CHANNEL_DELAY_MONO] = delaymonobak; } for (int i = 0; i < voicestatus.length; i++) if (voicestatus[i].active) if (voicestatus[i].channelmixer == null) voicestatus[i].processAudioLogic(buffers); if(!buffers[CHANNEL_MONO].isSilent()) { float[] mono = buffers[CHANNEL_MONO].array(); float[] left = buffers[CHANNEL_LEFT].array(); int bufferlen = buffers[CHANNEL_LEFT].getSize(); if (nrofchannels != 1) { float[] right = buffers[CHANNEL_RIGHT].array(); for (int i = 0; i < bufferlen; i++) { float v = mono[i]; left[i] += v; right[i] += v; } } else { for (int i = 0; i < bufferlen; i++) { left[i] += mono[i]; } } } // Run effects if (synth.chorus_on) chorus.processAudio(); if (synth.reverb_on) reverb.processAudio(); if (nrofchannels == 1) volume_left = (volume_left + volume_right) / 2; // Set Volume / Balance if (last_volume_left != volume_left || last_volume_right != volume_right) { float[] left = buffers[CHANNEL_LEFT].array(); float[] right = buffers[CHANNEL_RIGHT].array(); int bufferlen = buffers[CHANNEL_LEFT].getSize(); float amp; float amp_delta; amp = (float)(last_volume_left * last_volume_left); amp_delta = (float)((volume_left * volume_left - amp) / bufferlen); for (int i = 0; i < bufferlen; i++) { amp += amp_delta; left[i] *= amp; } if (nrofchannels != 1) { amp = (float)(last_volume_right * last_volume_right); amp_delta = (float)((volume_right*volume_right - amp) / bufferlen); for (int i = 0; i < bufferlen; i++) { amp += amp_delta; right[i] *= volume_right; } } last_volume_left = volume_left; last_volume_right = volume_right; } else { if (volume_left != 1.0 || volume_right != 1.0) { float[] left = buffers[CHANNEL_LEFT].array(); float[] right = buffers[CHANNEL_RIGHT].array(); int bufferlen = buffers[CHANNEL_LEFT].getSize(); float amp; amp = (float) (volume_left * volume_left); for (int i = 0; i < bufferlen; i++) left[i] *= amp; if (nrofchannels != 1) { amp = (float)(volume_right * volume_right); for (int i = 0; i < bufferlen; i++) right[i] *= amp; } } } if(buffers[CHANNEL_LEFT].isSilent() && buffers[CHANNEL_RIGHT].isSilent()) { int midimessages_size; synchronized (control_mutex) { midimessages_size = midimessages.size(); } if(midimessages_size == 0) { pusher_silent_count++; if(pusher_silent_count > 5) { pusher_silent_count = 0; synchronized (control_mutex) { pusher_silent = true; if(synth.weakstream != null) synth.weakstream.setInputStream(null); } } } } else pusher_silent_count = 0; if (synth.agc_on) agc.processAudio(); } // Must only we called within control_mutex synchronization public void activity() { long silent_samples = 0; if(pusher_silent) { pusher_silent = false; if(synth.weakstream != null) { synth.weakstream.setInputStream(ais); silent_samples = synth.weakstream.silent_samples;; } } msec_last_activity = (long)((sample_pos + silent_samples) * (1000000.0 / samplerate)); } public void stopMixer(ModelChannelMixer mixer) { if (stoppedMixers == null) stoppedMixers = new HashSet<ModelChannelMixer>(); stoppedMixers.add(mixer); } public void registerMixer(ModelChannelMixer mixer) { if (registeredMixers == null) registeredMixers = new HashSet<SoftChannelMixerContainer>(); SoftChannelMixerContainer mixercontainer = new SoftChannelMixerContainer(); mixercontainer.buffers = new SoftAudioBuffer[6]; for (int i = 0; i < mixercontainer.buffers.length; i++) { mixercontainer.buffers[i] = new SoftAudioBuffer(buffer_len, synth.getFormat()); } mixercontainer.mixer = mixer; registeredMixers.add(mixercontainer); cur_registeredMixers = null; } public SoftMainMixer(SoftSynthesizer synth) { this.synth = synth; sample_pos = 0; co_master_balance[0] = 0.5; co_master_volume[0] = 1; co_master_coarse_tuning[0] = 0.5; co_master_fine_tuning[0] = 0.5; msec_buffer_len = (long) (1000000.0 / synth.getControlRate()); samplerate = synth.getFormat().getSampleRate(); nrofchannels = synth.getFormat().getChannels(); int buffersize = (int) (synth.getFormat().getSampleRate() / synth.getControlRate()); buffer_len = buffersize; max_delay_midievent = buffersize; control_mutex = synth.control_mutex; buffers = new SoftAudioBuffer[14]; for (int i = 0; i < buffers.length; i++) { buffers[i] = new SoftAudioBuffer(buffersize, synth.getFormat()); } voicestatus = synth.getVoices(); reverb = new SoftReverb(); chorus = new SoftChorus(); agc = new SoftLimiter(); float samplerate = synth.getFormat().getSampleRate(); float controlrate = synth.getControlRate(); reverb.init(samplerate, controlrate); chorus.init(samplerate, controlrate); agc.init(samplerate, controlrate); reverb.setLightMode(synth.reverb_light); reverb.setMixMode(true); chorus.setMixMode(true); agc.setMixMode(false); chorus.setInput(0, buffers[CHANNEL_EFFECT2]); chorus.setOutput(0, buffers[CHANNEL_LEFT]); if (nrofchannels != 1) chorus.setOutput(1, buffers[CHANNEL_RIGHT]); chorus.setOutput(2, buffers[CHANNEL_EFFECT1]); reverb.setInput(0, buffers[CHANNEL_EFFECT1]); reverb.setOutput(0, buffers[CHANNEL_LEFT]); if (nrofchannels != 1) reverb.setOutput(1, buffers[CHANNEL_RIGHT]); agc.setInput(0, buffers[CHANNEL_LEFT]); if (nrofchannels != 1) agc.setInput(1, buffers[CHANNEL_RIGHT]); agc.setOutput(0, buffers[CHANNEL_LEFT]); if (nrofchannels != 1) agc.setOutput(1, buffers[CHANNEL_RIGHT]); InputStream in = new InputStream() { private final SoftAudioBuffer[] buffers = SoftMainMixer.this.buffers; private final int nrofchannels = SoftMainMixer.this.synth.getFormat().getChannels(); private final int buffersize = buffers[0].getSize(); private final byte[] bbuffer = new byte[buffersize * (SoftMainMixer.this.synth.getFormat() .getSampleSizeInBits() / 8) * nrofchannels]; private int bbuffer_pos = 0; private final byte[] single = new byte[1]; public void fillBuffer() { /* boolean pusher_silent2; synchronized (control_mutex) { pusher_silent2 = pusher_silent; } if(!pusher_silent2)*/ processAudioBuffers(); for (int i = 0; i < nrofchannels; i++) buffers[i].get(bbuffer, i); bbuffer_pos = 0; } public int read(byte[] b, int off, int len) { int bbuffer_len = bbuffer.length; int offlen = off + len; int orgoff = off; byte[] bbuffer = this.bbuffer; while (off < offlen) { if (available() == 0) fillBuffer(); else { int bbuffer_pos = this.bbuffer_pos; while (off < offlen && bbuffer_pos < bbuffer_len) b[off++] = bbuffer[bbuffer_pos++]; this.bbuffer_pos = bbuffer_pos; if (!readfully) return off - orgoff; } } return len; } public int read() throws IOException { int ret = read(single); if (ret == -1) return -1; return single[0] & 0xFF; } public int available() { return bbuffer.length - bbuffer_pos; } public void close() { SoftMainMixer.this.synth.close(); } }; ais = new AudioInputStream(in, synth.getFormat(), AudioSystem.NOT_SPECIFIED); } public AudioInputStream getInputStream() { return ais; } public void reset() { SoftChannel[] channels = synth.channels; for (int i = 0; i < channels.length; i++) { channels[i].allSoundOff(); channels[i].resetAllControllers(true); if (synth.getGeneralMidiMode() == 2) { if (i == 9) channels[i].programChange(0, 0x78 * 128); else channels[i].programChange(0, 0x79 * 128); } else channels[i].programChange(0, 0); } setVolume(0x7F * 128 + 0x7F); setBalance(0x40 * 128 + 0x00); setCoarseTuning(0x40 * 128 + 0x00); setFineTuning(0x40 * 128 + 0x00); // Reset Reverb globalParameterControlChange( new int[]{0x01 * 128 + 0x01}, new long[]{0}, new long[]{4}); // Reset Chorus globalParameterControlChange( new int[]{0x01 * 128 + 0x02}, new long[]{0}, new long[]{2}); } public void setVolume(int value) { synchronized (control_mutex) { co_master_volume[0] = value / 16384.0; } } public void setBalance(int value) { synchronized (control_mutex) { co_master_balance[0] = value / 16384.0; } } public void setFineTuning(int value) { synchronized (control_mutex) { co_master_fine_tuning[0] = value / 16384.0; } } public void setCoarseTuning(int value) { synchronized (control_mutex) { co_master_coarse_tuning[0] = value / 16384.0; } } public int getVolume() { synchronized (control_mutex) { return (int) (co_master_volume[0] * 16384.0); } } public int getBalance() { synchronized (control_mutex) { return (int) (co_master_balance[0] * 16384.0); } } public int getFineTuning() { synchronized (control_mutex) { return (int) (co_master_fine_tuning[0] * 16384.0); } } public int getCoarseTuning() { synchronized (control_mutex) { return (int) (co_master_coarse_tuning[0] * 16384.0); } } public void globalParameterControlChange(int[] slothpath, long[] params, long[] paramsvalue) { if (slothpath.length == 0) return; synchronized (control_mutex) { // slothpath: 01xx are reserved only for GM2 if (slothpath[0] == 0x01 * 128 + 0x01) { for (int i = 0; i < paramsvalue.length; i++) { reverb.globalParameterControlChange(slothpath, params[i], paramsvalue[i]); } } if (slothpath[0] == 0x01 * 128 + 0x02) { for (int i = 0; i < paramsvalue.length; i++) { chorus.globalParameterControlChange(slothpath, params[i], paramsvalue[i]); } } } } public void processMessage(Object object) { if (object instanceof byte[]) processMessage((byte[]) object); if (object instanceof MidiMessage) processMessage((MidiMessage)object); } public void processMessage(MidiMessage message) { if (message instanceof ShortMessage) { ShortMessage sms = (ShortMessage)message; processMessage(sms.getChannel(), sms.getCommand(), sms.getData1(), sms.getData2()); return; } processMessage(message.getMessage()); } public void processMessage(byte[] data) { int status = 0; if (data.length > 0) status = data[0] & 0xFF; if (status == 0xF0) { processSystemExclusiveMessage(data); return; } int cmd = (status & 0xF0); int ch = (status & 0x0F); int data1; int data2; if (data.length > 1) data1 = data[1] & 0xFF; else data1 = 0; if (data.length > 2) data2 = data[2] & 0xFF; else data2 = 0; processMessage(ch, cmd, data1, data2); } public void processMessage(int ch, int cmd, int data1, int data2) { synchronized (synth.control_mutex) { activity(); } if (cmd == 0xF0) { int status = cmd | ch; switch (status) { case ShortMessage.ACTIVE_SENSING: synchronized (synth.control_mutex) { active_sensing_on = true; } break; default: break; } return; } SoftChannel[] channels = synth.channels; if (ch >= channels.length) return; SoftChannel softchannel = channels[ch]; switch (cmd) { case ShortMessage.NOTE_ON: if(delay_midievent != 0) softchannel.noteOn(data1, data2, delay_midievent); else softchannel.noteOn(data1, data2); break; case ShortMessage.NOTE_OFF: softchannel.noteOff(data1, data2); break; case ShortMessage.POLY_PRESSURE: softchannel.setPolyPressure(data1, data2); break; case ShortMessage.CONTROL_CHANGE: softchannel.controlChange(data1, data2); break; case ShortMessage.PROGRAM_CHANGE: softchannel.programChange(data1); break; case ShortMessage.CHANNEL_PRESSURE: softchannel.setChannelPressure(data1); break; case ShortMessage.PITCH_BEND: softchannel.setPitchBend(data1 + data2 * 128); break; default: break; } } public long getMicrosecondPosition() { if(pusher_silent) { if(synth.weakstream != null) { return (long)((sample_pos + synth.weakstream.silent_samples) * (1000000.0 / samplerate)); } } return (long)(sample_pos * (1000000.0 / samplerate)); } public void close() { } }