6833357: Improve time-stamp support in Gervill to reduce jitter
Reviewed-by: amenkov
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/media/sound/MidiDeviceReceiver.java Fri Nov 27 17:13:02 2009 +0300
@@ -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();
+
+}
--- a/jdk/src/share/classes/com/sun/media/sound/SoftAudioBuffer.java Fri Nov 06 19:48:56 2009 +0300
+++ b/jdk/src/share/classes/com/sun/media/sound/SoftAudioBuffer.java Fri Nov 27 17:13:02 2009 +0300
@@ -48,6 +48,30 @@
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;
}
--- a/jdk/src/share/classes/com/sun/media/sound/SoftChannel.java Fri Nov 06 19:48:56 2009 +0300
+++ b/jdk/src/share/classes/com/sun/media/sound/SoftChannel.java Fri Nov 27 17:13:02 2009 +0300
@@ -328,7 +328,7 @@
}
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 @@
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 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 @@
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 @@
play_noteNumber = noteNumber;
play_velocity = lastVelocity[noteNumber];
play_releasetriggered = true;
+ play_delay = 0;
current_director.noteOff(tunedKey, velocity);
}
@@ -604,12 +613,14 @@
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 @@
if (voiceNo == -1)
return;
- initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, velocity,
+ initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, velocity, delay,
connectionBlocks, current_mixer, releasetriggered);
}
--- a/jdk/src/share/classes/com/sun/media/sound/SoftLimiter.java Fri Nov 06 19:48:56 2009 +0300
+++ b/jdk/src/share/classes/com/sun/media/sound/SoftLimiter.java Fri Nov 27 17:13:02 2009 +0300
@@ -79,7 +79,7 @@
if (silentcounter > 60) {
if (!mix) {
bufferLout.clear();
- bufferRout.clear();
+ if (bufferRout != null) bufferRout.clear();
}
return;
}
--- a/jdk/src/share/classes/com/sun/media/sound/SoftMainMixer.java Fri Nov 06 19:48:56 2009 +0300
+++ b/jdk/src/share/classes/com/sun/media/sound/SoftMainMixer.java Fri Nov 27 17:13:02 2009 +0300
@@ -26,7 +26,6 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
if (stoppedMixers != null) {
if (stoppedMixers.contains(cmixer)) {
stoppedMixers.remove(cmixer);
- cmixer.stop();
+ cmixer.mixer.stop();
}
}
}
@@ -565,6 +642,9 @@
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 @@
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 @@
// 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 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 @@
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 @@
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 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() {
--- a/jdk/src/share/classes/com/sun/media/sound/SoftReceiver.java Fri Nov 06 19:48:56 2009 +0300
+++ b/jdk/src/share/classes/com/sun/media/sound/SoftReceiver.java Fri Nov 27 17:13:02 2009 +0300
@@ -26,8 +26,8 @@
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 @@
*
* @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 @@
this.midimessages = mainmixer.midimessages;
}
+ public MidiDevice getMidiDevice() {
+ return synth;
+ }
+
public void send(MidiMessage message, long timeStamp) {
synchronized (control_mutex) {
@@ -60,6 +64,7 @@
if (timeStamp != -1) {
synchronized (control_mutex) {
+ mainmixer.activity();
while (midimessages.get(timeStamp) != null)
timeStamp++;
if (message instanceof ShortMessage
--- a/jdk/src/share/classes/com/sun/media/sound/SoftSynthesizer.java Fri Nov 06 19:48:56 2009 +0300
+++ b/jdk/src/share/classes/com/sun/media/sound/SoftSynthesizer.java Fri Nov 27 17:13:02 2009 +0300
@@ -66,6 +66,8 @@
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 @@
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 @@
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()
--- a/jdk/src/share/classes/com/sun/media/sound/SoftVoice.java Fri Nov 06 19:48:56 2009 +0300
+++ b/jdk/src/share/classes/com/sun/media/sound/SoftVoice.java Fri Nov 27 17:13:02 2009 +0300
@@ -43,6 +43,7 @@
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 @@
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 @@
noteOn_noteNumber = noteNumber;
noteOn_velocity = velocity;
+ this.delay = delay;
lastMuteValue = 0;
lastSoloMuteValue = 0;
@@ -562,7 +564,7 @@
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 @@
}
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 @@
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 @@
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);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/Gervill/SoftReceiver/GetMidiDevice.java Fri Nov 27 17:13:02 2009 +0300
@@ -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();
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/Gervill/SoftSynthesizer/TestPreciseTimestampRendering.java Fri Nov 27 17:13:02 2009 +0300
@@ -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 + ")");
+ }
+
+ }
+
+}