|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final class SoftMainMixer { |
|
|
|
// A private class thats contains a ModelChannelMixer and it's private buffers. |
|
|
|
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(); |
|
|
|
|
|
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: |
|
subid2 = data[4] & 0xFF; |
|
switch (subid2) { |
|
case 0x01: |
|
{ |
|
|
|
SoftTuning tuning = synth.getTuning(new Patch(0, |
|
data[5] & 0xFF)); |
|
tuning.load(data); |
|
break; |
|
} |
|
case 0x04: |
|
case 0x05: |
|
case 0x06: |
|
case 0x07: |
|
|
|
{ |
|
|
|
SoftTuning tuning = synth.getTuning(new Patch( |
|
data[5] & 0xFF, data[6] & 0xFF)); |
|
tuning.load(data); |
|
break; |
|
} |
|
case 0x08: |
|
|
|
case 0x09: |
|
|
|
{ |
|
|
|
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: |
|
subid2 = data[4] & 0xFF; |
|
switch (subid2) { |
|
case 0x01: |
|
synth.setGeneralMidiMode(1); |
|
reset(); |
|
break; |
|
case 0x02: |
|
synth.setGeneralMidiMode(0); |
|
reset(); |
|
break; |
|
case 0x03: |
|
synth.setGeneralMidiMode(2); |
|
reset(); |
|
break; |
|
default: |
|
break; |
|
} |
|
break; |
|
case 0x0A: |
|
subid2 = data[4] & 0xFF; |
|
switch (subid2) { |
|
case 0x01: |
|
if (synth.getGeneralMidiMode() == 0) |
|
synth.setGeneralMidiMode(1); |
|
synth.voice_allocation_mode = 1; |
|
reset(); |
|
break; |
|
case 0x02: |
|
synth.setGeneralMidiMode(0); |
|
synth.voice_allocation_mode = 0; |
|
reset(); |
|
break; |
|
case 0x03: |
|
synth.voice_allocation_mode = 0; |
|
break; |
|
case 0x04: |
|
synth.voice_allocation_mode = 1; |
|
break; |
|
default: |
|
break; |
|
} |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
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: |
|
|
|
subid2 = data[4] & 0xFF; |
|
switch (subid2) { |
|
case 0x01: |
|
case 0x02: |
|
case 0x03: |
|
case 0x04: |
|
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: |
|
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: |
|
subid2 = data[4] & 0xFF; |
|
switch (subid2) { |
|
case 0x02: |
|
{ |
|
|
|
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: |
|
|
|
{ |
|
|
|
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: |
|
|
|
case 0x09: |
|
|
|
{ |
|
|
|
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: |
|
subid2 = data[4] & 0xFF; |
|
switch (subid2) { |
|
case 0x01: |
|
{ |
|
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: |
|
{ |
|
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: |
|
{ |
|
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: |
|
{ |
|
subid2 = data[4] & 0xFF; |
|
switch (subid2) { |
|
case 0x01: |
|
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; |
|
|
|
|
|
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 |
|
|
|
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) { |
|
|
|
|
|
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 |
|
|
|
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]; |
|
} |
|
} |
|
} |
|
|
|
|
|
if (synth.chorus_on) |
|
chorus.processAudio(); |
|
|
|
if (synth.reverb_on) |
|
reverb.processAudio(); |
|
|
|
if (nrofchannels == 1) |
|
volume_left = (volume_left + volume_right) / 2; |
|
|
|
|
|
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(); |
|
|
|
} |
|
|
|
|
|
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() { |
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
globalParameterControlChange( |
|
new int[]{0x01 * 128 + 0x01}, new long[]{0}, new long[]{4}); |
|
|
|
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() { |
|
} |
|
} |