337 lines · 12.5 KB · Java
RawDownload
/* * Copyright (c) 2007, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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. * * @author Karl Helgason */public class SoftChorus implements SoftAudioProcessor {     private static class VariableDelay {         private float[] delaybuffer;        private int rovepos = 0;        private float gain = 1;        private float rgain = 0;        private float delay = 0;        private float lastdelay = 0;        private 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 static class LFODelay {         private double phase = 1;        private double phase_step = 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);            phase_step = g;        }         public void setPhase(double phase) {            this.phase = 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) {            phase += phase_step;            while(phase > (Math.PI * 2)) phase -= (Math.PI * 2);            vdelay.setDelay((float) (depth * 0.5 * (Math.cos(phase) + 2)));            vdelay.processMix(in, out, rout);        }         public void processReplace(float[] in, float[] out, float[] rout) {            phase += phase_step;            while(phase > (Math.PI * 2)) phase -= (Math.PI * 2);            vdelay.setDelay((float) (depth * 0.5 * (Math.cos(phase) + 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;    }}