6833357: Improve time-stamp support in Gervill to reduce jitter
Reviewed-by: amenkov
This commit is contained in:
parent
ab33367c0a
commit
dca3f935d4
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 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 javax.sound.midi.MidiDevice;
|
||||||
|
import javax.sound.midi.Receiver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Receiver with reference to it's MidiDevice object.
|
||||||
|
*
|
||||||
|
* @author Karl Helgason
|
||||||
|
*/
|
||||||
|
public interface MidiDeviceReceiver extends Receiver {
|
||||||
|
|
||||||
|
/** Obtains the MidiDevice object associated with this Receiver.
|
||||||
|
*/
|
||||||
|
public MidiDevice getMidiDevice();
|
||||||
|
|
||||||
|
}
|
@ -48,6 +48,30 @@ public class SoftAudioBuffer {
|
|||||||
converter = AudioFloatConverter.getConverter(format);
|
converter = AudioFloatConverter.getConverter(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void swap(SoftAudioBuffer swap)
|
||||||
|
{
|
||||||
|
int bak_size = size;
|
||||||
|
float[] bak_buffer = buffer;
|
||||||
|
boolean bak_empty = empty;
|
||||||
|
AudioFormat bak_format = format;
|
||||||
|
AudioFloatConverter bak_converter = converter;
|
||||||
|
byte[] bak_converter_buffer = converter_buffer;
|
||||||
|
|
||||||
|
size = swap.size;
|
||||||
|
buffer = swap.buffer;
|
||||||
|
empty = swap.empty;
|
||||||
|
format = swap.format;
|
||||||
|
converter = swap.converter;
|
||||||
|
converter_buffer = swap.converter_buffer;
|
||||||
|
|
||||||
|
swap.size = bak_size;
|
||||||
|
swap.buffer = bak_buffer;
|
||||||
|
swap.empty = bak_empty;
|
||||||
|
swap.format = bak_format;
|
||||||
|
swap.converter = bak_converter;
|
||||||
|
swap.converter_buffer = bak_converter_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
public AudioFormat getFormat() {
|
public AudioFormat getFormat() {
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
@ -328,7 +328,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void initVoice(SoftVoice voice, SoftPerformer p, int voiceID,
|
protected void initVoice(SoftVoice voice, SoftPerformer p, int voiceID,
|
||||||
int noteNumber, int velocity, ModelConnectionBlock[] connectionBlocks,
|
int noteNumber, int velocity, int delay, ModelConnectionBlock[] connectionBlocks,
|
||||||
ModelChannelMixer channelmixer, boolean releaseTriggered) {
|
ModelChannelMixer channelmixer, boolean releaseTriggered) {
|
||||||
if (voice.active) {
|
if (voice.active) {
|
||||||
// Voice is active , we must steal the voice
|
// Voice is active , we must steal the voice
|
||||||
@ -363,7 +363,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
|
|||||||
voice.objects.put("midi_cc", co_midi_cc);
|
voice.objects.put("midi_cc", co_midi_cc);
|
||||||
voice.objects.put("midi_rpn", co_midi_rpn);
|
voice.objects.put("midi_rpn", co_midi_rpn);
|
||||||
voice.objects.put("midi_nrpn", co_midi_nrpn);
|
voice.objects.put("midi_nrpn", co_midi_nrpn);
|
||||||
voice.noteOn(noteNumber, velocity);
|
voice.noteOn(noteNumber, velocity, delay);
|
||||||
voice.setMute(mute);
|
voice.setMute(mute);
|
||||||
voice.setSoloMute(solomute);
|
voice.setSoloMute(solomute);
|
||||||
if (releaseTriggered)
|
if (releaseTriggered)
|
||||||
@ -399,14 +399,21 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void noteOn(int noteNumber, int velocity) {
|
public void noteOn(int noteNumber, int velocity) {
|
||||||
|
noteOn(noteNumber, velocity, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A special noteOn with delay parameter, which is used to
|
||||||
|
* start note within control buffers.
|
||||||
|
*/
|
||||||
|
protected void noteOn(int noteNumber, int velocity, int delay) {
|
||||||
noteNumber = restrict7Bit(noteNumber);
|
noteNumber = restrict7Bit(noteNumber);
|
||||||
velocity = restrict7Bit(velocity);
|
velocity = restrict7Bit(velocity);
|
||||||
noteOn_internal(noteNumber, velocity);
|
noteOn_internal(noteNumber, velocity, delay);
|
||||||
if (current_mixer != null)
|
if (current_mixer != null)
|
||||||
current_mixer.noteOn(noteNumber, velocity);
|
current_mixer.noteOn(noteNumber, velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void noteOn_internal(int noteNumber, int velocity) {
|
private void noteOn_internal(int noteNumber, int velocity, int delay) {
|
||||||
|
|
||||||
if (velocity == 0) {
|
if (velocity == 0) {
|
||||||
noteOff_internal(noteNumber, 64);
|
noteOff_internal(noteNumber, 64);
|
||||||
@ -490,6 +497,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
|
|||||||
int tunedKey = (int)(Math.round(tuning.getTuning()[noteNumber]/100.0));
|
int tunedKey = (int)(Math.round(tuning.getTuning()[noteNumber]/100.0));
|
||||||
play_noteNumber = noteNumber;
|
play_noteNumber = noteNumber;
|
||||||
play_velocity = velocity;
|
play_velocity = velocity;
|
||||||
|
play_delay = delay;
|
||||||
play_releasetriggered = false;
|
play_releasetriggered = false;
|
||||||
lastVelocity[noteNumber] = velocity;
|
lastVelocity[noteNumber] = velocity;
|
||||||
current_director.noteOn(tunedKey, velocity);
|
current_director.noteOn(tunedKey, velocity);
|
||||||
@ -594,6 +602,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
|
|||||||
play_noteNumber = noteNumber;
|
play_noteNumber = noteNumber;
|
||||||
play_velocity = lastVelocity[noteNumber];
|
play_velocity = lastVelocity[noteNumber];
|
||||||
play_releasetriggered = true;
|
play_releasetriggered = true;
|
||||||
|
play_delay = 0;
|
||||||
current_director.noteOff(tunedKey, velocity);
|
current_director.noteOff(tunedKey, velocity);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -604,12 +613,14 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
|
|||||||
private int voiceNo = 0;
|
private int voiceNo = 0;
|
||||||
private int play_noteNumber = 0;
|
private int play_noteNumber = 0;
|
||||||
private int play_velocity = 0;
|
private int play_velocity = 0;
|
||||||
|
private int play_delay = 0;
|
||||||
private boolean play_releasetriggered = false;
|
private boolean play_releasetriggered = false;
|
||||||
|
|
||||||
public void play(int performerIndex, ModelConnectionBlock[] connectionBlocks) {
|
public void play(int performerIndex, ModelConnectionBlock[] connectionBlocks) {
|
||||||
|
|
||||||
int noteNumber = play_noteNumber;
|
int noteNumber = play_noteNumber;
|
||||||
int velocity = play_velocity;
|
int velocity = play_velocity;
|
||||||
|
int delay = play_delay;
|
||||||
boolean releasetriggered = play_releasetriggered;
|
boolean releasetriggered = play_releasetriggered;
|
||||||
|
|
||||||
SoftPerformer p = current_instrument.getPerformers()[performerIndex];
|
SoftPerformer p = current_instrument.getPerformers()[performerIndex];
|
||||||
@ -633,7 +644,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
|
|||||||
if (voiceNo == -1)
|
if (voiceNo == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, velocity,
|
initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, velocity, delay,
|
||||||
connectionBlocks, current_mixer, releasetriggered);
|
connectionBlocks, current_mixer, releasetriggered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ public class SoftLimiter implements SoftAudioProcessor {
|
|||||||
if (silentcounter > 60) {
|
if (silentcounter > 60) {
|
||||||
if (!mix) {
|
if (!mix) {
|
||||||
bufferLout.clear();
|
bufferLout.clear();
|
||||||
bufferRout.clear();
|
if (bufferRout != null) bufferRout.clear();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ package com.sun.media.sound;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -46,28 +45,37 @@ import javax.sound.sampled.AudioSystem;
|
|||||||
*/
|
*/
|
||||||
public class SoftMainMixer {
|
public 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_LEFT = 0;
|
||||||
public final static int CHANNEL_RIGHT = 1;
|
public final static int CHANNEL_RIGHT = 1;
|
||||||
public final static int CHANNEL_MONO = 2;
|
public final static int CHANNEL_MONO = 2;
|
||||||
public final static int CHANNEL_EFFECT1 = 3;
|
public final static int CHANNEL_DELAY_LEFT = 3;
|
||||||
public final static int CHANNEL_EFFECT2 = 4;
|
public final static int CHANNEL_DELAY_RIGHT = 4;
|
||||||
public final static int CHANNEL_EFFECT3 = 5;
|
public final static int CHANNEL_DELAY_MONO = 5;
|
||||||
public final static int CHANNEL_EFFECT4 = 6;
|
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_LEFT_DRY = 10;
|
||||||
public final static int CHANNEL_RIGHT_DRY = 11;
|
public final static int CHANNEL_RIGHT_DRY = 11;
|
||||||
public final static int CHANNEL_SCRATCH1 = 12;
|
public final static int CHANNEL_SCRATCH1 = 12;
|
||||||
public final static int CHANNEL_SCRATCH2 = 13;
|
public final static int CHANNEL_SCRATCH2 = 13;
|
||||||
public final static int CHANNEL_CHANNELMIXER_LEFT = 14;
|
|
||||||
public final static int CHANNEL_CHANNELMIXER_RIGHT = 15;
|
|
||||||
public final static int CHANNEL_CHANNELMIXER_MONO = 16;
|
|
||||||
protected boolean active_sensing_on = false;
|
protected boolean active_sensing_on = false;
|
||||||
private long msec_last_activity = -1;
|
private long msec_last_activity = -1;
|
||||||
private boolean pusher_silent = false;
|
private boolean pusher_silent = false;
|
||||||
private int pusher_silent_count = 0;
|
private int pusher_silent_count = 0;
|
||||||
private long msec_pos = 0;
|
private long sample_pos = 0;
|
||||||
protected boolean readfully = true;
|
protected boolean readfully = true;
|
||||||
private Object control_mutex;
|
private Object control_mutex;
|
||||||
private SoftSynthesizer synth;
|
private SoftSynthesizer synth;
|
||||||
|
private float samplerate = 44100;
|
||||||
private int nrofchannels = 2;
|
private int nrofchannels = 2;
|
||||||
private SoftVoice[] voicestatus = null;
|
private SoftVoice[] voicestatus = null;
|
||||||
private SoftAudioBuffer[] buffers;
|
private SoftAudioBuffer[] buffers;
|
||||||
@ -75,7 +83,10 @@ public class SoftMainMixer {
|
|||||||
private SoftAudioProcessor chorus;
|
private SoftAudioProcessor chorus;
|
||||||
private SoftAudioProcessor agc;
|
private SoftAudioProcessor agc;
|
||||||
private long msec_buffer_len = 0;
|
private long msec_buffer_len = 0;
|
||||||
|
private int buffer_len = 0;
|
||||||
protected TreeMap<Long, Object> midimessages = new TreeMap<Long, Object>();
|
protected 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_left = 1.0;
|
||||||
double last_volume_right = 1.0;
|
double last_volume_right = 1.0;
|
||||||
private double[] co_master_balance = new double[1];
|
private double[] co_master_balance = new double[1];
|
||||||
@ -83,9 +94,9 @@ public class SoftMainMixer {
|
|||||||
private double[] co_master_coarse_tuning = new double[1];
|
private double[] co_master_coarse_tuning = new double[1];
|
||||||
private double[] co_master_fine_tuning = new double[1];
|
private double[] co_master_fine_tuning = new double[1];
|
||||||
private AudioInputStream ais;
|
private AudioInputStream ais;
|
||||||
private Set<ModelChannelMixer> registeredMixers = null;
|
private Set<SoftChannelMixerContainer> registeredMixers = null;
|
||||||
private Set<ModelChannelMixer> stoppedMixers = null;
|
private Set<ModelChannelMixer> stoppedMixers = null;
|
||||||
private ModelChannelMixer[] cur_registeredMixers = null;
|
private SoftChannelMixerContainer[] cur_registeredMixers = null;
|
||||||
protected SoftControl co_master = new SoftControl() {
|
protected SoftControl co_master = new SoftControl() {
|
||||||
|
|
||||||
double[] balance = co_master_balance;
|
double[] balance = co_master_balance;
|
||||||
@ -413,26 +424,68 @@ public class SoftMainMixer {
|
|||||||
Iterator<Entry<Long, Object>> iter = midimessages.entrySet().iterator();
|
Iterator<Entry<Long, Object>> iter = midimessages.entrySet().iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
Entry<Long, Object> entry = iter.next();
|
Entry<Long, Object> entry = iter.next();
|
||||||
if (entry.getKey() > (timeStamp + 100))
|
if (entry.getKey() >= (timeStamp + msec_buffer_len))
|
||||||
return;
|
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());
|
processMessage(entry.getValue());
|
||||||
iter.remove();
|
iter.remove();
|
||||||
}
|
}
|
||||||
|
delay_midievent = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processAudioBuffers() {
|
protected 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++) {
|
for (int i = 0; i < buffers.length; i++) {
|
||||||
buffers[i].clear();
|
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_left;
|
||||||
double volume_right;
|
double volume_right;
|
||||||
|
|
||||||
ModelChannelMixer[] act_registeredMixers;
|
SoftChannelMixerContainer[] act_registeredMixers;
|
||||||
|
|
||||||
// perform control logic
|
// perform control logic
|
||||||
synchronized (control_mutex) {
|
synchronized (control_mutex) {
|
||||||
|
|
||||||
|
long msec_pos = (long)(sample_pos * (1000000.0 / samplerate));
|
||||||
|
|
||||||
processMessages(msec_pos);
|
processMessages(msec_pos);
|
||||||
|
|
||||||
if (active_sensing_on) {
|
if (active_sensing_on) {
|
||||||
@ -450,7 +503,7 @@ public class SoftMainMixer {
|
|||||||
for (int i = 0; i < voicestatus.length; i++)
|
for (int i = 0; i < voicestatus.length; i++)
|
||||||
if (voicestatus[i].active)
|
if (voicestatus[i].active)
|
||||||
voicestatus[i].processControlLogic();
|
voicestatus[i].processControlLogic();
|
||||||
msec_pos += msec_buffer_len;
|
sample_pos += buffer_len;
|
||||||
|
|
||||||
double volume = co_master_volume[0];
|
double volume = co_master_volume[0];
|
||||||
volume_left = volume;
|
volume_left = volume;
|
||||||
@ -469,7 +522,7 @@ public class SoftMainMixer {
|
|||||||
if (cur_registeredMixers == null) {
|
if (cur_registeredMixers == null) {
|
||||||
if (registeredMixers != null) {
|
if (registeredMixers != null) {
|
||||||
cur_registeredMixers =
|
cur_registeredMixers =
|
||||||
new ModelChannelMixer[registeredMixers.size()];
|
new SoftChannelMixerContainer[registeredMixers.size()];
|
||||||
registeredMixers.toArray(cur_registeredMixers);
|
registeredMixers.toArray(cur_registeredMixers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,44 +536,61 @@ public class SoftMainMixer {
|
|||||||
|
|
||||||
if (act_registeredMixers != null) {
|
if (act_registeredMixers != null) {
|
||||||
|
|
||||||
// Reroute default left,right output
|
// Make backup of left,right,mono channels
|
||||||
// to channelmixer left,right input/output
|
|
||||||
SoftAudioBuffer leftbak = buffers[CHANNEL_LEFT];
|
SoftAudioBuffer leftbak = buffers[CHANNEL_LEFT];
|
||||||
SoftAudioBuffer rightbak = buffers[CHANNEL_RIGHT];
|
SoftAudioBuffer rightbak = buffers[CHANNEL_RIGHT];
|
||||||
SoftAudioBuffer monobak = buffers[CHANNEL_MONO];
|
SoftAudioBuffer monobak = buffers[CHANNEL_MONO];
|
||||||
buffers[CHANNEL_LEFT] = buffers[CHANNEL_CHANNELMIXER_LEFT];
|
SoftAudioBuffer delayleftbak = buffers[CHANNEL_DELAY_LEFT];
|
||||||
buffers[CHANNEL_RIGHT] = buffers[CHANNEL_CHANNELMIXER_RIGHT];
|
SoftAudioBuffer delayrightbak = buffers[CHANNEL_DELAY_RIGHT];
|
||||||
buffers[CHANNEL_MONO] = buffers[CHANNEL_CHANNELMIXER_MONO];
|
SoftAudioBuffer delaymonobak = buffers[CHANNEL_DELAY_MONO];
|
||||||
|
|
||||||
int bufferlen = buffers[CHANNEL_LEFT].getSize();
|
int bufferlen = buffers[CHANNEL_LEFT].getSize();
|
||||||
|
|
||||||
float[][] cbuffer = new float[nrofchannels][];
|
float[][] cbuffer = new float[nrofchannels][];
|
||||||
cbuffer[0] = buffers[CHANNEL_LEFT].array();
|
|
||||||
if (nrofchannels != 1)
|
|
||||||
cbuffer[1] = buffers[CHANNEL_RIGHT].array();
|
|
||||||
|
|
||||||
float[][] obuffer = new float[nrofchannels][];
|
float[][] obuffer = new float[nrofchannels][];
|
||||||
obuffer[0] = leftbak.array();
|
obuffer[0] = leftbak.array();
|
||||||
if (nrofchannels != 1)
|
if (nrofchannels != 1)
|
||||||
obuffer[1] = rightbak.array();
|
obuffer[1] = rightbak.array();
|
||||||
|
|
||||||
for (ModelChannelMixer cmixer : act_registeredMixers) {
|
for (SoftChannelMixerContainer cmixer : act_registeredMixers) {
|
||||||
for (int i = 0; i < cbuffer.length; i++)
|
|
||||||
Arrays.fill(cbuffer[i], 0);
|
// 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();
|
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;
|
boolean hasactivevoices = false;
|
||||||
for (int i = 0; i < voicestatus.length; i++)
|
for (int i = 0; i < voicestatus.length; i++)
|
||||||
if (voicestatus[i].active)
|
if (voicestatus[i].active)
|
||||||
if (voicestatus[i].channelmixer == cmixer) {
|
if (voicestatus[i].channelmixer == cmixer.mixer) {
|
||||||
voicestatus[i].processAudioLogic(buffers);
|
voicestatus[i].processAudioLogic(buffers);
|
||||||
hasactivevoices = true;
|
hasactivevoices = true;
|
||||||
}
|
}
|
||||||
if (!cmixer.process(cbuffer, 0, bufferlen)) {
|
|
||||||
synchronized (control_mutex) {
|
|
||||||
registeredMixers.remove(cmixer);
|
|
||||||
cur_registeredMixers = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!buffers[CHANNEL_MONO].isSilent())
|
if(!buffers[CHANNEL_MONO].isSilent())
|
||||||
{
|
{
|
||||||
@ -542,6 +612,13 @@ public class SoftMainMixer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cmixer.mixer.process(cbuffer, 0, bufferlen)) {
|
||||||
|
synchronized (control_mutex) {
|
||||||
|
registeredMixers.remove(cmixer);
|
||||||
|
cur_registeredMixers = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < cbuffer.length; i++) {
|
for (int i = 0; i < cbuffer.length; i++) {
|
||||||
float[] cbuff = cbuffer[i];
|
float[] cbuff = cbuffer[i];
|
||||||
float[] obuff = obuffer[i];
|
float[] obuff = obuffer[i];
|
||||||
@ -554,7 +631,7 @@ public class SoftMainMixer {
|
|||||||
if (stoppedMixers != null) {
|
if (stoppedMixers != null) {
|
||||||
if (stoppedMixers.contains(cmixer)) {
|
if (stoppedMixers.contains(cmixer)) {
|
||||||
stoppedMixers.remove(cmixer);
|
stoppedMixers.remove(cmixer);
|
||||||
cmixer.stop();
|
cmixer.mixer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -565,6 +642,9 @@ public class SoftMainMixer {
|
|||||||
buffers[CHANNEL_LEFT] = leftbak;
|
buffers[CHANNEL_LEFT] = leftbak;
|
||||||
buffers[CHANNEL_RIGHT] = rightbak;
|
buffers[CHANNEL_RIGHT] = rightbak;
|
||||||
buffers[CHANNEL_MONO] = monobak;
|
buffers[CHANNEL_MONO] = monobak;
|
||||||
|
buffers[CHANNEL_DELAY_LEFT] = delayleftbak;
|
||||||
|
buffers[CHANNEL_DELAY_RIGHT] = delayrightbak;
|
||||||
|
buffers[CHANNEL_DELAY_MONO] = delaymonobak;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -650,14 +730,23 @@ public class SoftMainMixer {
|
|||||||
if(buffers[CHANNEL_LEFT].isSilent()
|
if(buffers[CHANNEL_LEFT].isSilent()
|
||||||
&& buffers[CHANNEL_RIGHT].isSilent())
|
&& buffers[CHANNEL_RIGHT].isSilent())
|
||||||
{
|
{
|
||||||
pusher_silent_count++;
|
|
||||||
if(pusher_silent_count > 5)
|
int midimessages_size;
|
||||||
|
synchronized (control_mutex) {
|
||||||
|
midimessages_size = midimessages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(midimessages_size == 0)
|
||||||
{
|
{
|
||||||
pusher_silent_count = 0;
|
pusher_silent_count++;
|
||||||
synchronized (control_mutex) {
|
if(pusher_silent_count > 5)
|
||||||
pusher_silent = true;
|
{
|
||||||
if(synth.weakstream != null)
|
pusher_silent_count = 0;
|
||||||
synth.weakstream.setInputStream(null);
|
synchronized (control_mutex) {
|
||||||
|
pusher_silent = true;
|
||||||
|
if(synth.weakstream != null)
|
||||||
|
synth.weakstream.setInputStream(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -672,13 +761,18 @@ public class SoftMainMixer {
|
|||||||
// Must only we called within control_mutex synchronization
|
// Must only we called within control_mutex synchronization
|
||||||
public void activity()
|
public void activity()
|
||||||
{
|
{
|
||||||
msec_last_activity = msec_pos;
|
long silent_samples = 0;
|
||||||
if(pusher_silent)
|
if(pusher_silent)
|
||||||
{
|
{
|
||||||
pusher_silent = false;
|
pusher_silent = false;
|
||||||
if(synth.weakstream != null)
|
if(synth.weakstream != null)
|
||||||
|
{
|
||||||
synth.weakstream.setInputStream(ais);
|
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) {
|
public void stopMixer(ModelChannelMixer mixer) {
|
||||||
@ -689,15 +783,22 @@ public class SoftMainMixer {
|
|||||||
|
|
||||||
public void registerMixer(ModelChannelMixer mixer) {
|
public void registerMixer(ModelChannelMixer mixer) {
|
||||||
if (registeredMixers == null)
|
if (registeredMixers == null)
|
||||||
registeredMixers = new HashSet<ModelChannelMixer>();
|
registeredMixers = new HashSet<SoftChannelMixerContainer>();
|
||||||
registeredMixers.add(mixer);
|
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;
|
cur_registeredMixers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoftMainMixer(SoftSynthesizer synth) {
|
public SoftMainMixer(SoftSynthesizer synth) {
|
||||||
this.synth = synth;
|
this.synth = synth;
|
||||||
|
|
||||||
msec_pos = 0;
|
sample_pos = 0;
|
||||||
|
|
||||||
co_master_balance[0] = 0.5;
|
co_master_balance[0] = 0.5;
|
||||||
co_master_volume[0] = 1;
|
co_master_volume[0] = 1;
|
||||||
@ -705,14 +806,18 @@ public class SoftMainMixer {
|
|||||||
co_master_fine_tuning[0] = 0.5;
|
co_master_fine_tuning[0] = 0.5;
|
||||||
|
|
||||||
msec_buffer_len = (long) (1000000.0 / synth.getControlRate());
|
msec_buffer_len = (long) (1000000.0 / synth.getControlRate());
|
||||||
|
samplerate = synth.getFormat().getSampleRate();
|
||||||
nrofchannels = synth.getFormat().getChannels();
|
nrofchannels = synth.getFormat().getChannels();
|
||||||
|
|
||||||
int buffersize = (int) (synth.getFormat().getSampleRate()
|
int buffersize = (int) (synth.getFormat().getSampleRate()
|
||||||
/ synth.getControlRate());
|
/ synth.getControlRate());
|
||||||
|
|
||||||
|
buffer_len = buffersize;
|
||||||
|
|
||||||
|
max_delay_midievent = buffersize;
|
||||||
|
|
||||||
control_mutex = synth.control_mutex;
|
control_mutex = synth.control_mutex;
|
||||||
buffers = new SoftAudioBuffer[17];
|
buffers = new SoftAudioBuffer[14];
|
||||||
for (int i = 0; i < buffers.length; i++) {
|
for (int i = 0; i < buffers.length; i++) {
|
||||||
buffers[i] = new SoftAudioBuffer(buffersize, synth.getFormat());
|
buffers[i] = new SoftAudioBuffer(buffersize, synth.getFormat());
|
||||||
}
|
}
|
||||||
@ -994,7 +1099,10 @@ public class SoftMainMixer {
|
|||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case ShortMessage.NOTE_ON:
|
case ShortMessage.NOTE_ON:
|
||||||
softchannel.noteOn(data1, data2);
|
if(delay_midievent != 0)
|
||||||
|
softchannel.noteOn(data1, data2, delay_midievent);
|
||||||
|
else
|
||||||
|
softchannel.noteOn(data1, data2);
|
||||||
break;
|
break;
|
||||||
case ShortMessage.NOTE_OFF:
|
case ShortMessage.NOTE_OFF:
|
||||||
softchannel.noteOff(data1, data2);
|
softchannel.noteOff(data1, data2);
|
||||||
@ -1021,7 +1129,15 @@ public class SoftMainMixer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getMicrosecondPosition() {
|
public long getMicrosecondPosition() {
|
||||||
return msec_pos;
|
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() {
|
public void close() {
|
||||||
|
@ -26,8 +26,8 @@ package com.sun.media.sound;
|
|||||||
|
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import javax.sound.midi.MidiDevice;
|
||||||
import javax.sound.midi.MidiMessage;
|
import javax.sound.midi.MidiMessage;
|
||||||
import javax.sound.midi.Receiver;
|
|
||||||
import javax.sound.midi.ShortMessage;
|
import javax.sound.midi.ShortMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,7 +35,7 @@ import javax.sound.midi.ShortMessage;
|
|||||||
*
|
*
|
||||||
* @author Karl Helgason
|
* @author Karl Helgason
|
||||||
*/
|
*/
|
||||||
public class SoftReceiver implements Receiver {
|
public class SoftReceiver implements MidiDeviceReceiver {
|
||||||
|
|
||||||
protected boolean open = true;
|
protected boolean open = true;
|
||||||
private Object control_mutex;
|
private Object control_mutex;
|
||||||
@ -51,6 +51,10 @@ public class SoftReceiver implements Receiver {
|
|||||||
this.midimessages = mainmixer.midimessages;
|
this.midimessages = mainmixer.midimessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MidiDevice getMidiDevice() {
|
||||||
|
return synth;
|
||||||
|
}
|
||||||
|
|
||||||
public void send(MidiMessage message, long timeStamp) {
|
public void send(MidiMessage message, long timeStamp) {
|
||||||
|
|
||||||
synchronized (control_mutex) {
|
synchronized (control_mutex) {
|
||||||
@ -60,6 +64,7 @@ public class SoftReceiver implements Receiver {
|
|||||||
|
|
||||||
if (timeStamp != -1) {
|
if (timeStamp != -1) {
|
||||||
synchronized (control_mutex) {
|
synchronized (control_mutex) {
|
||||||
|
mainmixer.activity();
|
||||||
while (midimessages.get(timeStamp) != null)
|
while (midimessages.get(timeStamp) != null)
|
||||||
timeStamp++;
|
timeStamp++;
|
||||||
if (message instanceof ShortMessage
|
if (message instanceof ShortMessage
|
||||||
|
@ -66,6 +66,8 @@ public class SoftSynthesizer implements AudioSynthesizer,
|
|||||||
public SoftAudioPusher pusher = null;
|
public SoftAudioPusher pusher = null;
|
||||||
public AudioInputStream jitter_stream = null;
|
public AudioInputStream jitter_stream = null;
|
||||||
public SourceDataLine sourceDataLine = null;
|
public SourceDataLine sourceDataLine = null;
|
||||||
|
public volatile long silent_samples = 0;
|
||||||
|
private int framesize = 0;
|
||||||
private WeakReference<AudioInputStream> weak_stream_link;
|
private WeakReference<AudioInputStream> weak_stream_link;
|
||||||
private AudioFloatConverter converter;
|
private AudioFloatConverter converter;
|
||||||
private float[] silentbuffer = null;
|
private float[] silentbuffer = null;
|
||||||
@ -101,6 +103,8 @@ public class SoftSynthesizer implements AudioSynthesizer,
|
|||||||
silentbuffer = new float[flen];
|
silentbuffer = new float[flen];
|
||||||
converter.toByteArray(silentbuffer, flen, b, off);
|
converter.toByteArray(silentbuffer, flen, b, off);
|
||||||
|
|
||||||
|
silent_samples += (long)((len / framesize));
|
||||||
|
|
||||||
if(pusher != null)
|
if(pusher != null)
|
||||||
if(weak_stream_link.get() == null)
|
if(weak_stream_link.get() == null)
|
||||||
{
|
{
|
||||||
@ -136,6 +140,7 @@ public class SoftSynthesizer implements AudioSynthesizer,
|
|||||||
weak_stream_link = new WeakReference<AudioInputStream>(stream);
|
weak_stream_link = new WeakReference<AudioInputStream>(stream);
|
||||||
converter = AudioFloatConverter.getConverter(stream.getFormat());
|
converter = AudioFloatConverter.getConverter(stream.getFormat());
|
||||||
samplesize = stream.getFormat().getFrameSize() / stream.getFormat().getChannels();
|
samplesize = stream.getFormat().getFrameSize() / stream.getFormat().getChannels();
|
||||||
|
framesize = stream.getFormat().getFrameSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioInputStream getAudioInputStream()
|
public AudioInputStream getAudioInputStream()
|
||||||
|
@ -43,6 +43,7 @@ public class SoftVoice extends VoiceStatus {
|
|||||||
private int noteOn_noteNumber = 0;
|
private int noteOn_noteNumber = 0;
|
||||||
private int noteOn_velocity = 0;
|
private int noteOn_velocity = 0;
|
||||||
private int noteOff_velocity = 0;
|
private int noteOff_velocity = 0;
|
||||||
|
private int delay = 0;
|
||||||
protected ModelChannelMixer channelmixer = null;
|
protected ModelChannelMixer channelmixer = null;
|
||||||
protected double tunedKey = 0;
|
protected double tunedKey = 0;
|
||||||
protected SoftTuning tuning = null;
|
protected SoftTuning tuning = null;
|
||||||
@ -294,7 +295,7 @@ public class SoftVoice extends VoiceStatus {
|
|||||||
tunedKey = tuning.getTuning(noteNumber) / 100.0;
|
tunedKey = tuning.getTuning(noteNumber) / 100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void noteOn(int noteNumber, int velocity) {
|
protected void noteOn(int noteNumber, int velocity, int delay) {
|
||||||
|
|
||||||
sustain = false;
|
sustain = false;
|
||||||
sostenuto = false;
|
sostenuto = false;
|
||||||
@ -308,6 +309,7 @@ public class SoftVoice extends VoiceStatus {
|
|||||||
|
|
||||||
noteOn_noteNumber = noteNumber;
|
noteOn_noteNumber = noteNumber;
|
||||||
noteOn_velocity = velocity;
|
noteOn_velocity = velocity;
|
||||||
|
this.delay = delay;
|
||||||
|
|
||||||
lastMuteValue = 0;
|
lastMuteValue = 0;
|
||||||
lastSoloMuteValue = 0;
|
lastSoloMuteValue = 0;
|
||||||
@ -562,7 +564,7 @@ public class SoftVoice extends VoiceStatus {
|
|||||||
|
|
||||||
if (stealer_channel != null) {
|
if (stealer_channel != null) {
|
||||||
stealer_channel.initVoice(this, stealer_performer,
|
stealer_channel.initVoice(this, stealer_performer,
|
||||||
stealer_voiceID, stealer_noteNumber, stealer_velocity,
|
stealer_voiceID, stealer_noteNumber, stealer_velocity, 0,
|
||||||
stealer_extendedConnectionBlocks, stealer_channelmixer,
|
stealer_extendedConnectionBlocks, stealer_channelmixer,
|
||||||
stealer_releaseTriggered);
|
stealer_releaseTriggered);
|
||||||
stealer_releaseTriggered = false;
|
stealer_releaseTriggered = false;
|
||||||
@ -733,23 +735,55 @@ public class SoftVoice extends VoiceStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void mixAudioStream(SoftAudioBuffer in, SoftAudioBuffer out,
|
protected void mixAudioStream(SoftAudioBuffer in, SoftAudioBuffer out,
|
||||||
|
SoftAudioBuffer dout,
|
||||||
float amp_from, float amp_to) {
|
float amp_from, float amp_to) {
|
||||||
int bufferlen = in.getSize();
|
int bufferlen = in.getSize();
|
||||||
if (amp_from < 0.000000001 && amp_to < 0.000000001)
|
if (amp_from < 0.000000001 && amp_to < 0.000000001)
|
||||||
return;
|
return;
|
||||||
if (amp_from == amp_to) {
|
if(dout != null && delay != 0)
|
||||||
float[] fout = out.array();
|
{
|
||||||
float[] fin = in.array();
|
if (amp_from == amp_to) {
|
||||||
for (int i = 0; i < bufferlen; i++)
|
float[] fout = out.array();
|
||||||
fout[i] += fin[i] * amp_to;
|
float[] fin = in.array();
|
||||||
} else {
|
int j = 0;
|
||||||
float amp = amp_from;
|
for (int i = delay; i < bufferlen; i++)
|
||||||
float amp_delta = (amp_to - amp_from) / bufferlen;
|
fout[i] += fin[j++] * amp_to;
|
||||||
float[] fout = out.array();
|
fout = dout.array();
|
||||||
float[] fin = in.array();
|
for (int i = 0; i < delay; i++)
|
||||||
for (int i = 0; i < bufferlen; i++) {
|
fout[i] += fin[j++] * amp_to;
|
||||||
amp += amp_delta;
|
} else {
|
||||||
fout[i] += fin[i] * amp;
|
float amp = amp_from;
|
||||||
|
float amp_delta = (amp_to - amp_from) / bufferlen;
|
||||||
|
float[] fout = out.array();
|
||||||
|
float[] fin = in.array();
|
||||||
|
int j = 0;
|
||||||
|
for (int i = delay; i < bufferlen; i++) {
|
||||||
|
amp += amp_delta;
|
||||||
|
fout[i] += fin[j++] * amp;
|
||||||
|
}
|
||||||
|
fout = dout.array();
|
||||||
|
for (int i = 0; i < delay; i++) {
|
||||||
|
amp += amp_delta;
|
||||||
|
fout[i] += fin[j++] * amp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (amp_from == amp_to) {
|
||||||
|
float[] fout = out.array();
|
||||||
|
float[] fin = in.array();
|
||||||
|
for (int i = 0; i < bufferlen; i++)
|
||||||
|
fout[i] += fin[i] * amp_to;
|
||||||
|
} else {
|
||||||
|
float amp = amp_from;
|
||||||
|
float amp_delta = (amp_to - amp_from) / bufferlen;
|
||||||
|
float[] fout = out.array();
|
||||||
|
float[] fin = in.array();
|
||||||
|
for (int i = 0; i < bufferlen; i++) {
|
||||||
|
amp += amp_delta;
|
||||||
|
fout[i] += fin[i] * amp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,6 +819,13 @@ public class SoftVoice extends VoiceStatus {
|
|||||||
SoftAudioBuffer mono = buffer[SoftMainMixer.CHANNEL_MONO];
|
SoftAudioBuffer mono = buffer[SoftMainMixer.CHANNEL_MONO];
|
||||||
SoftAudioBuffer eff1 = buffer[SoftMainMixer.CHANNEL_EFFECT1];
|
SoftAudioBuffer eff1 = buffer[SoftMainMixer.CHANNEL_EFFECT1];
|
||||||
SoftAudioBuffer eff2 = buffer[SoftMainMixer.CHANNEL_EFFECT2];
|
SoftAudioBuffer eff2 = buffer[SoftMainMixer.CHANNEL_EFFECT2];
|
||||||
|
|
||||||
|
SoftAudioBuffer dleft = buffer[SoftMainMixer.CHANNEL_DELAY_LEFT];
|
||||||
|
SoftAudioBuffer dright = buffer[SoftMainMixer.CHANNEL_DELAY_RIGHT];
|
||||||
|
SoftAudioBuffer dmono = buffer[SoftMainMixer.CHANNEL_DELAY_MONO];
|
||||||
|
SoftAudioBuffer deff1 = buffer[SoftMainMixer.CHANNEL_DELAY_EFFECT1];
|
||||||
|
SoftAudioBuffer deff2 = buffer[SoftMainMixer.CHANNEL_DELAY_EFFECT2];
|
||||||
|
|
||||||
SoftAudioBuffer leftdry = buffer[SoftMainMixer.CHANNEL_LEFT_DRY];
|
SoftAudioBuffer leftdry = buffer[SoftMainMixer.CHANNEL_LEFT_DRY];
|
||||||
SoftAudioBuffer rightdry = buffer[SoftMainMixer.CHANNEL_RIGHT_DRY];
|
SoftAudioBuffer rightdry = buffer[SoftMainMixer.CHANNEL_RIGHT_DRY];
|
||||||
|
|
||||||
@ -799,42 +840,42 @@ public class SoftVoice extends VoiceStatus {
|
|||||||
|
|
||||||
if (nrofchannels == 1) {
|
if (nrofchannels == 1) {
|
||||||
out_mixer_left = (out_mixer_left + out_mixer_right) / 2;
|
out_mixer_left = (out_mixer_left + out_mixer_right) / 2;
|
||||||
mixAudioStream(leftdry, left, last_out_mixer_left, out_mixer_left);
|
mixAudioStream(leftdry, left, dleft, last_out_mixer_left, out_mixer_left);
|
||||||
if (rightdry != null)
|
if (rightdry != null)
|
||||||
mixAudioStream(rightdry, left, last_out_mixer_left,
|
mixAudioStream(rightdry, left, dleft, last_out_mixer_left,
|
||||||
out_mixer_left);
|
out_mixer_left);
|
||||||
} else {
|
} else {
|
||||||
if(rightdry == null &&
|
if(rightdry == null &&
|
||||||
last_out_mixer_left == last_out_mixer_right &&
|
last_out_mixer_left == last_out_mixer_right &&
|
||||||
out_mixer_left == out_mixer_right)
|
out_mixer_left == out_mixer_right)
|
||||||
{
|
{
|
||||||
mixAudioStream(leftdry, mono, last_out_mixer_left, out_mixer_left);
|
mixAudioStream(leftdry, mono, dmono, last_out_mixer_left, out_mixer_left);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mixAudioStream(leftdry, left, last_out_mixer_left, out_mixer_left);
|
mixAudioStream(leftdry, left, dleft, last_out_mixer_left, out_mixer_left);
|
||||||
if (rightdry != null)
|
if (rightdry != null)
|
||||||
mixAudioStream(rightdry, right, last_out_mixer_right,
|
mixAudioStream(rightdry, right, dright, last_out_mixer_right,
|
||||||
out_mixer_right);
|
out_mixer_right);
|
||||||
else
|
else
|
||||||
mixAudioStream(leftdry, right, last_out_mixer_right,
|
mixAudioStream(leftdry, right, dright, last_out_mixer_right,
|
||||||
out_mixer_right);
|
out_mixer_right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rightdry == null) {
|
if (rightdry == null) {
|
||||||
mixAudioStream(leftdry, eff1, last_out_mixer_effect1,
|
mixAudioStream(leftdry, eff1, deff1, last_out_mixer_effect1,
|
||||||
out_mixer_effect1);
|
out_mixer_effect1);
|
||||||
mixAudioStream(leftdry, eff2, last_out_mixer_effect2,
|
mixAudioStream(leftdry, eff2, deff2, last_out_mixer_effect2,
|
||||||
out_mixer_effect2);
|
out_mixer_effect2);
|
||||||
} else {
|
} else {
|
||||||
mixAudioStream(leftdry, eff1, last_out_mixer_effect1 * 0.5f,
|
mixAudioStream(leftdry, eff1, deff1, last_out_mixer_effect1 * 0.5f,
|
||||||
out_mixer_effect1 * 0.5f);
|
out_mixer_effect1 * 0.5f);
|
||||||
mixAudioStream(leftdry, eff2, last_out_mixer_effect2 * 0.5f,
|
mixAudioStream(leftdry, eff2, deff2, last_out_mixer_effect2 * 0.5f,
|
||||||
out_mixer_effect2 * 0.5f);
|
out_mixer_effect2 * 0.5f);
|
||||||
mixAudioStream(rightdry, eff1, last_out_mixer_effect1 * 0.5f,
|
mixAudioStream(rightdry, eff1, deff1, last_out_mixer_effect1 * 0.5f,
|
||||||
out_mixer_effect1 * 0.5f);
|
out_mixer_effect1 * 0.5f);
|
||||||
mixAudioStream(rightdry, eff2, last_out_mixer_effect2 * 0.5f,
|
mixAudioStream(rightdry, eff2, deff2, last_out_mixer_effect2 * 0.5f,
|
||||||
out_mixer_effect2 * 0.5f);
|
out_mixer_effect2 * 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @test
|
||||||
|
@summary Test SoftReceiver getMidiDevice method */
|
||||||
|
|
||||||
|
import javax.sound.midi.Receiver;
|
||||||
|
|
||||||
|
import com.sun.media.sound.AudioSynthesizer;
|
||||||
|
import com.sun.media.sound.SoftReceiver;
|
||||||
|
import com.sun.media.sound.SoftSynthesizer;
|
||||||
|
|
||||||
|
public class GetMidiDevice {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
AudioSynthesizer synth = new SoftSynthesizer();
|
||||||
|
synth.openStream(null, null);
|
||||||
|
Receiver recv = synth.getReceiver();
|
||||||
|
if (((SoftReceiver) recv).getMidiDevice() != synth) {
|
||||||
|
throw new Exception("SoftReceiver.getMidiDevice() doesn't return "
|
||||||
|
+ "instance of the synthesizer");
|
||||||
|
}
|
||||||
|
synth.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @test
|
||||||
|
@summary Test rendering when using precise timestamps */
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.sound.midi.MidiChannel;
|
||||||
|
import javax.sound.midi.Receiver;
|
||||||
|
import javax.sound.midi.ShortMessage;
|
||||||
|
import javax.sound.midi.Soundbank;
|
||||||
|
import javax.sound.sampled.AudioFormat;
|
||||||
|
import javax.sound.sampled.AudioInputStream;
|
||||||
|
|
||||||
|
import com.sun.media.sound.AudioFloatConverter;
|
||||||
|
import com.sun.media.sound.AudioSynthesizer;
|
||||||
|
import com.sun.media.sound.ModelAbstractChannelMixer;
|
||||||
|
import com.sun.media.sound.ModelChannelMixer;
|
||||||
|
import com.sun.media.sound.SF2Instrument;
|
||||||
|
import com.sun.media.sound.SF2InstrumentRegion;
|
||||||
|
import com.sun.media.sound.SF2Layer;
|
||||||
|
import com.sun.media.sound.SF2LayerRegion;
|
||||||
|
import com.sun.media.sound.SF2Sample;
|
||||||
|
import com.sun.media.sound.SF2Soundbank;
|
||||||
|
import com.sun.media.sound.SimpleInstrument;
|
||||||
|
import com.sun.media.sound.SimpleSoundbank;
|
||||||
|
import com.sun.media.sound.SoftSynthesizer;
|
||||||
|
|
||||||
|
public class TestPreciseTimestampRendering {
|
||||||
|
|
||||||
|
public static AudioFormat format = new AudioFormat(44100, 16, 1, true,
|
||||||
|
false);
|
||||||
|
|
||||||
|
public static SF2Soundbank createTestSoundbank() {
|
||||||
|
// Create impulse instrument
|
||||||
|
// used to measure timing of note-on playback
|
||||||
|
SF2Soundbank soundbank = new SF2Soundbank();
|
||||||
|
float[] data = new float[100];
|
||||||
|
Arrays.fill(data, 0);
|
||||||
|
data[0] = 1.0f;
|
||||||
|
byte[] bdata = new byte[data.length * format.getFrameSize()];
|
||||||
|
AudioFloatConverter.getConverter(format).toByteArray(data, bdata);
|
||||||
|
|
||||||
|
SF2Sample sample = new SF2Sample(soundbank);
|
||||||
|
sample.setName("Test Sample");
|
||||||
|
sample.setData(bdata);
|
||||||
|
sample.setSampleRate((long) format.getSampleRate());
|
||||||
|
sample.setOriginalPitch(69);
|
||||||
|
soundbank.addResource(sample);
|
||||||
|
|
||||||
|
SF2Layer layer = new SF2Layer(soundbank);
|
||||||
|
layer.setName("Test Layer");
|
||||||
|
soundbank.addResource(layer);
|
||||||
|
SF2LayerRegion region = new SF2LayerRegion();
|
||||||
|
region.setSample(sample);
|
||||||
|
layer.getRegions().add(region);
|
||||||
|
|
||||||
|
SF2Instrument ins = new SF2Instrument(soundbank);
|
||||||
|
ins.setName("Test Instrument");
|
||||||
|
soundbank.addInstrument(ins);
|
||||||
|
SF2InstrumentRegion insregion = new SF2InstrumentRegion();
|
||||||
|
insregion.setLayer(layer);
|
||||||
|
ins.getRegions().add(insregion);
|
||||||
|
|
||||||
|
return soundbank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Soundbank createTestSoundbankWithChannelMixer() {
|
||||||
|
SF2Soundbank soundbank = createTestSoundbank();
|
||||||
|
|
||||||
|
SimpleSoundbank simplesoundbank = new SimpleSoundbank();
|
||||||
|
SimpleInstrument simpleinstrument = new SimpleInstrument() {
|
||||||
|
|
||||||
|
public ModelChannelMixer getChannelMixer(MidiChannel channel,
|
||||||
|
AudioFormat format) {
|
||||||
|
return new ModelAbstractChannelMixer() {
|
||||||
|
boolean active = true;
|
||||||
|
|
||||||
|
public boolean process(float[][] buffer, int offset, int len) {
|
||||||
|
for (int i = 0; i < buffer.length; i++) {
|
||||||
|
float[] cbuffer = buffer[i];
|
||||||
|
for (int j = 0; j < cbuffer.length; j++) {
|
||||||
|
cbuffer[j] = -cbuffer[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
active = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
simpleinstrument.add(soundbank.getInstruments()[0]);
|
||||||
|
simplesoundbank.addInstrument(simpleinstrument);
|
||||||
|
|
||||||
|
return simplesoundbank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
test(createTestSoundbank());
|
||||||
|
test(createTestSoundbankWithChannelMixer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test(Soundbank soundbank) throws Exception {
|
||||||
|
|
||||||
|
// Create instance of synthesizer using the testing soundbank above
|
||||||
|
AudioSynthesizer synth = new SoftSynthesizer();
|
||||||
|
AudioInputStream stream = synth.openStream(format, null);
|
||||||
|
synth.unloadAllInstruments(synth.getDefaultSoundbank());
|
||||||
|
synth.loadAllInstruments(soundbank);
|
||||||
|
Receiver recv = synth.getReceiver();
|
||||||
|
|
||||||
|
// Set volume to max and turn reverb off
|
||||||
|
ShortMessage reverb_off = new ShortMessage();
|
||||||
|
reverb_off.setMessage(ShortMessage.CONTROL_CHANGE, 91, 0);
|
||||||
|
recv.send(reverb_off, -1);
|
||||||
|
ShortMessage full_volume = new ShortMessage();
|
||||||
|
full_volume.setMessage(ShortMessage.CONTROL_CHANGE, 7, 127);
|
||||||
|
recv.send(full_volume, -1);
|
||||||
|
|
||||||
|
Random random = new Random(3485934583945l);
|
||||||
|
|
||||||
|
// Create random timestamps
|
||||||
|
long[] test_timestamps = new long[30];
|
||||||
|
for (int i = 1; i < test_timestamps.length; i++) {
|
||||||
|
test_timestamps[i] = i * 44100
|
||||||
|
+ (int) (random.nextDouble() * 22050.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send midi note on message to synthesizer
|
||||||
|
for (int i = 0; i < test_timestamps.length; i++) {
|
||||||
|
ShortMessage midi_on = new ShortMessage();
|
||||||
|
midi_on.setMessage(ShortMessage.NOTE_ON, 69, 127);
|
||||||
|
recv.send(midi_on,
|
||||||
|
(long) ((test_timestamps[i] / 44100.0) * 1000000.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure timing from rendered audio
|
||||||
|
float[] fbuffer = new float[100];
|
||||||
|
byte[] buffer = new byte[fbuffer.length * format.getFrameSize()];
|
||||||
|
long firsts = -1;
|
||||||
|
int counter = 0;
|
||||||
|
long s = 0;
|
||||||
|
long max_jitter = 0;
|
||||||
|
outerloop: for (int k = 0; k < 10000000; k++) {
|
||||||
|
stream.read(buffer);
|
||||||
|
AudioFloatConverter.getConverter(format).toFloatArray(buffer,
|
||||||
|
fbuffer);
|
||||||
|
for (int i = 0; i < fbuffer.length; i++) {
|
||||||
|
if (fbuffer[i] != 0) {
|
||||||
|
if (firsts == -1)
|
||||||
|
firsts = s;
|
||||||
|
|
||||||
|
long measure_time = (s - firsts);
|
||||||
|
long predicted_time = test_timestamps[counter];
|
||||||
|
|
||||||
|
long jitter = Math.abs(measure_time - predicted_time);
|
||||||
|
|
||||||
|
if (jitter > 10)
|
||||||
|
max_jitter = jitter;
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
if (counter == test_timestamps.length)
|
||||||
|
break outerloop;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synth.close();
|
||||||
|
|
||||||
|
if (counter == 0)
|
||||||
|
throw new Exception("Nothing was measured!");
|
||||||
|
|
||||||
|
if (max_jitter != 0) {
|
||||||
|
throw new Exception("Jitter has occurred! "
|
||||||
|
+ "(max jitter = " + max_jitter + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user