view overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/SoftChorus.java @ 861:30c7450d05dc

New Gervill CVS import. Add AudioFloatFormatConverter FormatConversionProvider. 2008-05-09 Mark Wielaard <mwielaard@redhat.com> * overlays/openjdk/jdk/src/share/classes/com/sun/media/sound: Import Gervill fixes from CVS. See CHANGES.txt. Check isConversionSupported() in AudioFloatFormatConverter.getTargetFormats(). * patches/icedtea-gervill.patch: Add AudioFloatFormatConverter to javax.sound.sampled.spi.FormatConversionProvider.
author Mark Wielaard <mark@klomp.org>
date Fri, 09 May 2008 19:04:37 +0200
parents bcba163568ac
children
line wrap: on
line source

/*
 * Copyright 2007 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 java.util.Arrays;

/**
 * A chorus effect made using LFO and variable delay. One for each channel
 * (left,right), with different starting phase for stereo effect.
 * 
 * @version %I%, %E%
 * @author Karl Helgason
 */
public class SoftChorus implements SoftAudioProcessor {

	private class VariableDelay {
		private float[] delaybuffer;

		private int rovepos = 0;

		private volatile float gain = 1;

		private volatile float rgain = 0;

		private volatile float delay = 0;

		private float lastdelay = 0;

		private volatile float feedback = 0;

		public VariableDelay(int maxbuffersize) {
			delaybuffer = new float[maxbuffersize];
		}

		public void setDelay(float delay) {
			this.delay = delay;
		}

		public void setFeedBack(float feedback) {
			this.feedback = feedback;
		}

		public void setGain(float gain) {
			this.gain = gain;
		}

		public void setReverbSendGain(float rgain) {
			this.rgain = rgain;
		}

		public void processMix(float[] in, float[] out, float[] rout) {
			float gain = this.gain;
			float delay = this.delay;
			float feedback = this.feedback;

			float[] delaybuffer = this.delaybuffer;
			int len = in.length;
			float delaydelta = (delay - lastdelay) / len;
			int rnlen = delaybuffer.length;
			int rovepos = this.rovepos;

			if (rout == null)
				for (int i = 0; i < len; i++) {
					float r = rovepos - (lastdelay + 2) + rnlen;
					int ri = (int) r;
					float s = r - ri;
					float a = delaybuffer[ri % rnlen];
					float b = delaybuffer[(ri + 1) % rnlen];
					float o = a * (1 - s) + b * (s);
					out[i] += o * gain;
					delaybuffer[rovepos] = in[i] + o * feedback;
					rovepos = (rovepos + 1) % rnlen;
					lastdelay += delaydelta;
				}
			else
				for (int i = 0; i < len; i++) {
					float r = rovepos - (lastdelay + 2) + rnlen;
					int ri = (int) r;
					float s = r - ri;
					float a = delaybuffer[ri % rnlen];
					float b = delaybuffer[(ri + 1) % rnlen];
					float o = a * (1 - s) + b * (s);
					out[i] += o * gain;
					rout[i] += o * rgain;
					delaybuffer[rovepos] = in[i] + o * feedback;
					rovepos = (rovepos + 1) % rnlen;
					lastdelay += delaydelta;
				}
			this.rovepos = rovepos;
			lastdelay = delay;
		}

		public void processReplace(float[] in, float[] out, float[] rout) {
			Arrays.fill(out, 0);
			Arrays.fill(rout, 0);
			processMix(in, out, rout);
		}

	}

	private class LFODelay {

		private volatile double c_cos_delta;

		private volatile double c_sin_delta;

		private double c_cos = 1;

		private double c_sin = 0;

		private double depth = 0;

		private VariableDelay vdelay;

		private double samplerate;

		private double controlrate;

		public LFODelay(double samplerate, double controlrate) {
			this.samplerate = samplerate;
			this.controlrate = controlrate;
			// vdelay = new VariableDelay((int)(samplerate*4));
			vdelay = new VariableDelay((int) ((this.depth + 10) * 2));

		}

		public void setDepth(double depth) {
			this.depth = depth * samplerate;
			vdelay = new VariableDelay((int) ((this.depth + 10) * 2));
		}

		public void setRate(double rate) {
			double g = (Math.PI * 2) * (rate / controlrate);
			c_cos_delta = Math.cos(g);
			c_sin_delta = Math.sin(g);
		}

		public void setPhase(double phase) {
			c_cos = Math.cos(phase);
			c_sin = Math.sin(phase);
		}

		public void setFeedBack(float feedback) {
			vdelay.setFeedBack(feedback);
		}

		public void setGain(float gain) {
			vdelay.setGain(gain);
		}

		public void setReverbSendGain(float rgain) {
			vdelay.setReverbSendGain(rgain);
		}

		public void processMix(float[] in, float[] out, float[] rout) {
			c_cos = c_cos * c_cos_delta - c_sin * c_sin_delta;
			c_sin = c_cos * c_sin_delta + c_sin * c_cos_delta;
			vdelay.setDelay((float) (depth * 0.5 * (c_cos + 2)));
			vdelay.processMix(in, out, rout);
		}

		public void processReplace(float[] in, float[] out, float[] rout) {
			c_cos = c_cos * c_cos_delta - c_sin * c_sin_delta;
			c_sin = c_cos * c_sin_delta + c_sin * c_cos_delta;
			vdelay.setDelay((float) (depth * 0.5 * (c_cos + 2)));
			vdelay.processReplace(in, out, rout);

		}
	}

	private boolean mix = true;

	private SoftAudioBuffer inputA;

	private SoftAudioBuffer left;

	private SoftAudioBuffer right;

	private SoftAudioBuffer reverb;

	private LFODelay vdelay1L;

	private LFODelay vdelay1R;

	private float rgain = 0;

	private boolean dirty = true;

	private double dirty_vdelay1L_rate;

	private double dirty_vdelay1R_rate;

	private double dirty_vdelay1L_depth;

	private double dirty_vdelay1R_depth;

	private float dirty_vdelay1L_feedback;

	private float dirty_vdelay1R_feedback;

	private float dirty_vdelay1L_reverbsendgain;

	private float dirty_vdelay1R_reverbsendgain;
	
	private float controlrate;

	public void init(float samplerate, float controlrate) {
		this.controlrate = controlrate;
		vdelay1L = new LFODelay(samplerate, controlrate);
		vdelay1R = new LFODelay(samplerate, controlrate);
		vdelay1L.setGain(1.0f); // %
		vdelay1R.setGain(1.0f); // %
		vdelay1L.setPhase(0.5 * Math.PI);
		vdelay1R.setPhase(0);

		globalParameterControlChange(new int[] { 0x01 * 128 + 0x02 }, 0, 2);
	}

	public void globalParameterControlChange(int[] slothpath, long param,
			long value) {
		if (slothpath.length == 1)
			if (slothpath[0] == 0x01 * 128 + 0x02) {

				if (param == 0) // Chorus Type
				{
					switch ((int) value) {
					case 0: // Chorus 1 0 (0%) 3 (0.4Hz) 5 (1.9ms) 0 (0%)
						globalParameterControlChange(slothpath, 3, 0);
						globalParameterControlChange(slothpath, 1, 3);
						globalParameterControlChange(slothpath, 2, 5);
						globalParameterControlChange(slothpath, 4, 0);
						break;
					case 1: // Chorus 2 5 (4%) 9 (1.1Hz) 19 (6.3ms) 0 (0%)
						globalParameterControlChange(slothpath, 3, 5);
						globalParameterControlChange(slothpath, 1, 9);
						globalParameterControlChange(slothpath, 2, 19);
						globalParameterControlChange(slothpath, 4, 0);
						break;
					case 2: // Chorus 3 8 (6%) 3 (0.4Hz) 19 (6.3ms) 0 (0%)
						globalParameterControlChange(slothpath, 3, 8);
						globalParameterControlChange(slothpath, 1, 3);
						globalParameterControlChange(slothpath, 2, 19);
						globalParameterControlChange(slothpath, 4, 0);
						break;
					case 3: // Chorus 4 16 (12%) 9 (1.1Hz) 16 (5.3ms) 0 (0%)
						globalParameterControlChange(slothpath, 3, 16);
						globalParameterControlChange(slothpath, 1, 9);
						globalParameterControlChange(slothpath, 2, 16);
						globalParameterControlChange(slothpath, 4, 0);
						break;
					case 4: // FB Chorus 64 (49%) 2 (0.2Hz) 24 (7.8ms) 0 (0%)
						globalParameterControlChange(slothpath, 3, 64);
						globalParameterControlChange(slothpath, 1, 2);
						globalParameterControlChange(slothpath, 2, 24);
						globalParameterControlChange(slothpath, 4, 0);
						break;
					case 5: // Flanger 112 (86%) 1 (0.1Hz) 5 (1.9ms) 0 (0%)
						globalParameterControlChange(slothpath, 3, 112);
						globalParameterControlChange(slothpath, 1, 1);
						globalParameterControlChange(slothpath, 2, 5);
						globalParameterControlChange(slothpath, 4, 0);
						break;
					default:
						break;
					}
				} else if (param == 1) // Mod Rate
				{
					dirty_vdelay1L_rate = (value * 0.122);
					dirty_vdelay1R_rate = (value * 0.122);
					dirty = true;
				} else if (param == 2) // Mod Depth
				{
					dirty_vdelay1L_depth = ((value + 1) / 3200.0);
					dirty_vdelay1R_depth = ((value + 1) / 3200.0);
					dirty = true;
				} else if (param == 3) // Feedback
				{
					dirty_vdelay1L_feedback = (value * 0.00763f);
					dirty_vdelay1R_feedback = (value * 0.00763f);
					dirty = true;
				}
				if (param == 4) // Send to Reverb
				{
					rgain = value * 0.00787f;
					dirty_vdelay1L_reverbsendgain = (value * 0.00787f);
					dirty_vdelay1R_reverbsendgain = (value * 0.00787f);
					dirty = true;
				}

			}
	}

	public void processControlLogic() {
		if (dirty) {
			dirty = false;
			vdelay1L.setRate(dirty_vdelay1L_rate);
			vdelay1R.setRate(dirty_vdelay1R_rate);
			vdelay1L.setDepth(dirty_vdelay1L_depth);
			vdelay1R.setDepth(dirty_vdelay1R_depth);
			vdelay1L.setFeedBack(dirty_vdelay1L_feedback);
			vdelay1R.setFeedBack(dirty_vdelay1R_feedback);
			vdelay1L.setReverbSendGain(dirty_vdelay1L_reverbsendgain);
			vdelay1R.setReverbSendGain(dirty_vdelay1R_reverbsendgain);
		}
	}

	double silentcounter = 1000;

	public void processAudio() {

		if (inputA.isSilent()) {
			silentcounter += 1 / controlrate;

			if (silentcounter > 1) {
				if (!mix) {
					left.clear();
					right.clear();
				}
				return;
			}
		} else
			silentcounter = 0;

		float[] inputA = this.inputA.array();
		float[] left = this.left.array();
		float[] right = this.right == null ? null : this.right.array();
		float[] reverb = rgain != 0 ? this.reverb.array() : null;

		if (mix) {
			vdelay1L.processMix(inputA, left, reverb);
			if (right != null)
				vdelay1R.processMix(inputA, right, reverb);
		} else {
			vdelay1L.processReplace(inputA, left, reverb);
			if (right != null)
				vdelay1R.processReplace(inputA, right, reverb);
		}
	}

	public void setInput(int pin, SoftAudioBuffer input) {
		if (pin == 0)
			inputA = input;
	}

	public void setMixMode(boolean mix) {
		this.mix = mix;
	}

	public void setOutput(int pin, SoftAudioBuffer output) {
		if (pin == 0)
			left = output;
		if (pin == 1)
			right = output;
		if (pin == 2)
			reverb = output;
	}

}