6833357: Improve time-stamp support in Gervill to reduce jitter

Reviewed-by: amenkov
This commit is contained in:
Karl Helgason 2009-11-27 17:13:02 +03:00
parent ab33367c0a
commit dca3f935d4
10 changed files with 585 additions and 86 deletions

View File

@ -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();
}

View File

@ -48,6 +48,30 @@ public class SoftAudioBuffer {
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() {
return format;
}

View File

@ -328,7 +328,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
}
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) {
if (voice.active) {
// 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_rpn", co_midi_rpn);
voice.objects.put("midi_nrpn", co_midi_nrpn);
voice.noteOn(noteNumber, velocity);
voice.noteOn(noteNumber, velocity, delay);
voice.setMute(mute);
voice.setSoloMute(solomute);
if (releaseTriggered)
@ -399,14 +399,21 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
}
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);
velocity = restrict7Bit(velocity);
noteOn_internal(noteNumber, velocity);
noteOn_internal(noteNumber, velocity, delay);
if (current_mixer != null)
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) {
noteOff_internal(noteNumber, 64);
@ -490,6 +497,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
int tunedKey = (int)(Math.round(tuning.getTuning()[noteNumber]/100.0));
play_noteNumber = noteNumber;
play_velocity = velocity;
play_delay = delay;
play_releasetriggered = false;
lastVelocity[noteNumber] = velocity;
current_director.noteOn(tunedKey, velocity);
@ -594,6 +602,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
play_noteNumber = noteNumber;
play_velocity = lastVelocity[noteNumber];
play_releasetriggered = true;
play_delay = 0;
current_director.noteOff(tunedKey, velocity);
}
@ -604,12 +613,14 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
private int voiceNo = 0;
private int play_noteNumber = 0;
private int play_velocity = 0;
private int play_delay = 0;
private boolean play_releasetriggered = false;
public void play(int performerIndex, ModelConnectionBlock[] connectionBlocks) {
int noteNumber = play_noteNumber;
int velocity = play_velocity;
int delay = play_delay;
boolean releasetriggered = play_releasetriggered;
SoftPerformer p = current_instrument.getPerformers()[performerIndex];
@ -633,7 +644,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer {
if (voiceNo == -1)
return;
initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, velocity,
initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, velocity, delay,
connectionBlocks, current_mixer, releasetriggered);
}

View File

@ -79,7 +79,7 @@ public class SoftLimiter implements SoftAudioProcessor {
if (silentcounter > 60) {
if (!mix) {
bufferLout.clear();
bufferRout.clear();
if (bufferRout != null) bufferRout.clear();
}
return;
}

View File

@ -26,7 +26,6 @@ package com.sun.media.sound;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@ -46,28 +45,37 @@ import javax.sound.sampled.AudioSystem;
*/
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_RIGHT = 1;
public final static int CHANNEL_MONO = 2;
public final static int CHANNEL_EFFECT1 = 3;
public final static int CHANNEL_EFFECT2 = 4;
public final static int CHANNEL_EFFECT3 = 5;
public final static int CHANNEL_EFFECT4 = 6;
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;
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;
private long msec_last_activity = -1;
private boolean pusher_silent = false;
private int pusher_silent_count = 0;
private long msec_pos = 0;
private long sample_pos = 0;
protected boolean readfully = true;
private Object control_mutex;
private SoftSynthesizer synth;
private float samplerate = 44100;
private int nrofchannels = 2;
private SoftVoice[] voicestatus = null;
private SoftAudioBuffer[] buffers;
@ -75,7 +83,10 @@ public class SoftMainMixer {
private SoftAudioProcessor chorus;
private SoftAudioProcessor agc;
private long msec_buffer_len = 0;
private int buffer_len = 0;
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_right = 1.0;
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_fine_tuning = new double[1];
private AudioInputStream ais;
private Set<ModelChannelMixer> registeredMixers = null;
private Set<SoftChannelMixerContainer> registeredMixers = null;
private Set<ModelChannelMixer> stoppedMixers = null;
private ModelChannelMixer[] cur_registeredMixers = null;
private SoftChannelMixerContainer[] cur_registeredMixers = null;
protected SoftControl co_master = new SoftControl() {
double[] balance = co_master_balance;
@ -413,26 +424,68 @@ public class SoftMainMixer {
Iterator<Entry<Long, Object>> iter = midimessages.entrySet().iterator();
while (iter.hasNext()) {
Entry<Long, Object> entry = iter.next();
if (entry.getKey() > (timeStamp + 100))
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;
}
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++) {
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_right;
ModelChannelMixer[] act_registeredMixers;
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) {
@ -450,7 +503,7 @@ public class SoftMainMixer {
for (int i = 0; i < voicestatus.length; i++)
if (voicestatus[i].active)
voicestatus[i].processControlLogic();
msec_pos += msec_buffer_len;
sample_pos += buffer_len;
double volume = co_master_volume[0];
volume_left = volume;
@ -469,7 +522,7 @@ public class SoftMainMixer {
if (cur_registeredMixers == null) {
if (registeredMixers != null) {
cur_registeredMixers =
new ModelChannelMixer[registeredMixers.size()];
new SoftChannelMixerContainer[registeredMixers.size()];
registeredMixers.toArray(cur_registeredMixers);
}
}
@ -483,44 +536,61 @@ public class SoftMainMixer {
if (act_registeredMixers != null) {
// Reroute default left,right output
// to channelmixer left,right input/output
// Make backup of left,right,mono channels
SoftAudioBuffer leftbak = buffers[CHANNEL_LEFT];
SoftAudioBuffer rightbak = buffers[CHANNEL_RIGHT];
SoftAudioBuffer monobak = buffers[CHANNEL_MONO];
buffers[CHANNEL_LEFT] = buffers[CHANNEL_CHANNELMIXER_LEFT];
buffers[CHANNEL_RIGHT] = buffers[CHANNEL_CHANNELMIXER_RIGHT];
buffers[CHANNEL_MONO] = buffers[CHANNEL_CHANNELMIXER_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][];
cbuffer[0] = buffers[CHANNEL_LEFT].array();
if (nrofchannels != 1)
cbuffer[1] = buffers[CHANNEL_RIGHT].array();
float[][] obuffer = new float[nrofchannels][];
obuffer[0] = leftbak.array();
if (nrofchannels != 1)
obuffer[1] = rightbak.array();
for (ModelChannelMixer cmixer : act_registeredMixers) {
for (int i = 0; i < cbuffer.length; i++)
Arrays.fill(cbuffer[i], 0);
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) {
if (voicestatus[i].channelmixer == cmixer.mixer) {
voicestatus[i].processAudioLogic(buffers);
hasactivevoices = true;
}
if (!cmixer.process(cbuffer, 0, bufferlen)) {
synchronized (control_mutex) {
registeredMixers.remove(cmixer);
cur_registeredMixers = null;
}
}
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++) {
float[] cbuff = cbuffer[i];
float[] obuff = obuffer[i];
@ -554,7 +631,7 @@ public class SoftMainMixer {
if (stoppedMixers != null) {
if (stoppedMixers.contains(cmixer)) {
stoppedMixers.remove(cmixer);
cmixer.stop();
cmixer.mixer.stop();
}
}
}
@ -565,6 +642,9 @@ public class SoftMainMixer {
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;
}
@ -650,14 +730,23 @@ public class SoftMainMixer {
if(buffers[CHANNEL_LEFT].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;
synchronized (control_mutex) {
pusher_silent = true;
if(synth.weakstream != null)
synth.weakstream.setInputStream(null);
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);
}
}
}
}
@ -672,13 +761,18 @@ public class SoftMainMixer {
// Must only we called within control_mutex synchronization
public void activity()
{
msec_last_activity = msec_pos;
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) {
@ -689,15 +783,22 @@ public class SoftMainMixer {
public void registerMixer(ModelChannelMixer mixer) {
if (registeredMixers == null)
registeredMixers = new HashSet<ModelChannelMixer>();
registeredMixers.add(mixer);
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;
msec_pos = 0;
sample_pos = 0;
co_master_balance[0] = 0.5;
co_master_volume[0] = 1;
@ -705,14 +806,18 @@ public class SoftMainMixer {
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[17];
buffers = new SoftAudioBuffer[14];
for (int i = 0; i < buffers.length; i++) {
buffers[i] = new SoftAudioBuffer(buffersize, synth.getFormat());
}
@ -994,7 +1099,10 @@ public class SoftMainMixer {
switch (cmd) {
case ShortMessage.NOTE_ON:
softchannel.noteOn(data1, data2);
if(delay_midievent != 0)
softchannel.noteOn(data1, data2, delay_midievent);
else
softchannel.noteOn(data1, data2);
break;
case ShortMessage.NOTE_OFF:
softchannel.noteOff(data1, data2);
@ -1021,7 +1129,15 @@ public class SoftMainMixer {
}
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() {

View File

@ -26,8 +26,8 @@ package com.sun.media.sound;
import java.util.TreeMap;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
/**
@ -35,7 +35,7 @@ import javax.sound.midi.ShortMessage;
*
* @author Karl Helgason
*/
public class SoftReceiver implements Receiver {
public class SoftReceiver implements MidiDeviceReceiver {
protected boolean open = true;
private Object control_mutex;
@ -51,6 +51,10 @@ public class SoftReceiver implements Receiver {
this.midimessages = mainmixer.midimessages;
}
public MidiDevice getMidiDevice() {
return synth;
}
public void send(MidiMessage message, long timeStamp) {
synchronized (control_mutex) {
@ -60,6 +64,7 @@ public class SoftReceiver implements Receiver {
if (timeStamp != -1) {
synchronized (control_mutex) {
mainmixer.activity();
while (midimessages.get(timeStamp) != null)
timeStamp++;
if (message instanceof ShortMessage

View File

@ -66,6 +66,8 @@ public class SoftSynthesizer implements AudioSynthesizer,
public SoftAudioPusher pusher = null;
public AudioInputStream jitter_stream = null;
public SourceDataLine sourceDataLine = null;
public volatile long silent_samples = 0;
private int framesize = 0;
private WeakReference<AudioInputStream> weak_stream_link;
private AudioFloatConverter converter;
private float[] silentbuffer = null;
@ -101,6 +103,8 @@ public class SoftSynthesizer implements AudioSynthesizer,
silentbuffer = new float[flen];
converter.toByteArray(silentbuffer, flen, b, off);
silent_samples += (long)((len / framesize));
if(pusher != null)
if(weak_stream_link.get() == null)
{
@ -136,6 +140,7 @@ public class SoftSynthesizer implements AudioSynthesizer,
weak_stream_link = new WeakReference<AudioInputStream>(stream);
converter = AudioFloatConverter.getConverter(stream.getFormat());
samplesize = stream.getFormat().getFrameSize() / stream.getFormat().getChannels();
framesize = stream.getFormat().getFrameSize();
}
public AudioInputStream getAudioInputStream()

View File

@ -43,6 +43,7 @@ public class SoftVoice extends VoiceStatus {
private int noteOn_noteNumber = 0;
private int noteOn_velocity = 0;
private int noteOff_velocity = 0;
private int delay = 0;
protected ModelChannelMixer channelmixer = null;
protected double tunedKey = 0;
protected SoftTuning tuning = null;
@ -294,7 +295,7 @@ public class SoftVoice extends VoiceStatus {
tunedKey = tuning.getTuning(noteNumber) / 100.0;
}
protected void noteOn(int noteNumber, int velocity) {
protected void noteOn(int noteNumber, int velocity, int delay) {
sustain = false;
sostenuto = false;
@ -308,6 +309,7 @@ public class SoftVoice extends VoiceStatus {
noteOn_noteNumber = noteNumber;
noteOn_velocity = velocity;
this.delay = delay;
lastMuteValue = 0;
lastSoloMuteValue = 0;
@ -562,7 +564,7 @@ public class SoftVoice extends VoiceStatus {
if (stealer_channel != null) {
stealer_channel.initVoice(this, stealer_performer,
stealer_voiceID, stealer_noteNumber, stealer_velocity,
stealer_voiceID, stealer_noteNumber, stealer_velocity, 0,
stealer_extendedConnectionBlocks, stealer_channelmixer,
stealer_releaseTriggered);
stealer_releaseTriggered = false;
@ -733,23 +735,55 @@ public class SoftVoice extends VoiceStatus {
}
protected void mixAudioStream(SoftAudioBuffer in, SoftAudioBuffer out,
SoftAudioBuffer dout,
float amp_from, float amp_to) {
int bufferlen = in.getSize();
if (amp_from < 0.000000001 && amp_to < 0.000000001)
return;
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;
if(dout != null && delay != 0)
{
if (amp_from == amp_to) {
float[] fout = out.array();
float[] fin = in.array();
int j = 0;
for (int i = delay; i < bufferlen; i++)
fout[i] += fin[j++] * amp_to;
fout = dout.array();
for (int i = 0; i < delay; i++)
fout[i] += fin[j++] * amp_to;
} else {
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 eff1 = buffer[SoftMainMixer.CHANNEL_EFFECT1];
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 rightdry = buffer[SoftMainMixer.CHANNEL_RIGHT_DRY];
@ -799,42 +840,42 @@ public class SoftVoice extends VoiceStatus {
if (nrofchannels == 1) {
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)
mixAudioStream(rightdry, left, last_out_mixer_left,
mixAudioStream(rightdry, left, dleft, last_out_mixer_left,
out_mixer_left);
} else {
if(rightdry == null &&
last_out_mixer_left == last_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
{
mixAudioStream(leftdry, left, last_out_mixer_left, out_mixer_left);
mixAudioStream(leftdry, left, dleft, last_out_mixer_left, out_mixer_left);
if (rightdry != null)
mixAudioStream(rightdry, right, last_out_mixer_right,
mixAudioStream(rightdry, right, dright, last_out_mixer_right,
out_mixer_right);
else
mixAudioStream(leftdry, right, last_out_mixer_right,
mixAudioStream(leftdry, right, dright, last_out_mixer_right,
out_mixer_right);
}
}
if (rightdry == null) {
mixAudioStream(leftdry, eff1, last_out_mixer_effect1,
mixAudioStream(leftdry, eff1, deff1, last_out_mixer_effect1,
out_mixer_effect1);
mixAudioStream(leftdry, eff2, last_out_mixer_effect2,
mixAudioStream(leftdry, eff2, deff2, last_out_mixer_effect2,
out_mixer_effect2);
} 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);
mixAudioStream(leftdry, eff2, last_out_mixer_effect2 * 0.5f,
mixAudioStream(leftdry, eff2, deff2, last_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);
mixAudioStream(rightdry, eff2, last_out_mixer_effect2 * 0.5f,
mixAudioStream(rightdry, eff2, deff2, last_out_mixer_effect2 * 0.5f,
out_mixer_effect2 * 0.5f);
}

View File

@ -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();
}
}

View File

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