| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
package com.sun.media.sound;  | 
 | 
 | 
 | 
import java.util.Arrays;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public final class SoftReverb implements SoftAudioProcessor { | 
 | 
 | 
 | 
    private final static class Delay { | 
 | 
 | 
 | 
        private float[] delaybuffer;  | 
 | 
        private int rovepos = 0;  | 
 | 
 | 
 | 
        Delay() { | 
 | 
            delaybuffer = null;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void setDelay(int delay) { | 
 | 
            if (delay == 0)  | 
 | 
                delaybuffer = null;  | 
 | 
            else  | 
 | 
                delaybuffer = new float[delay];  | 
 | 
            rovepos = 0;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void processReplace(float[] inout) { | 
 | 
            if (delaybuffer == null)  | 
 | 
                return;  | 
 | 
            int len = inout.length;  | 
 | 
            int rnlen = delaybuffer.length;  | 
 | 
            int rovepos = this.rovepos;  | 
 | 
 | 
 | 
            for (int i = 0; i < len; i++) { | 
 | 
                float x = inout[i];  | 
 | 
                inout[i] = delaybuffer[rovepos];  | 
 | 
                delaybuffer[rovepos] = x;  | 
 | 
                if (++rovepos == rnlen)  | 
 | 
                    rovepos = 0;  | 
 | 
            }  | 
 | 
            this.rovepos = rovepos;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private final static class AllPass { | 
 | 
 | 
 | 
        private final float[] delaybuffer;  | 
 | 
        private final int delaybuffersize;  | 
 | 
        private int rovepos = 0;  | 
 | 
        private float feedback;  | 
 | 
 | 
 | 
        AllPass(int size) { | 
 | 
            delaybuffer = new float[size];  | 
 | 
            delaybuffersize = size;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void setFeedBack(float feedback) { | 
 | 
            this.feedback = feedback;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void processReplace(float inout[]) { | 
 | 
            int len = inout.length;  | 
 | 
            int delaybuffersize = this.delaybuffersize;  | 
 | 
            int rovepos = this.rovepos;  | 
 | 
            for (int i = 0; i < len; i++) { | 
 | 
                float delayout = delaybuffer[rovepos];  | 
 | 
                float input = inout[i];  | 
 | 
                inout[i] = delayout - input;  | 
 | 
                delaybuffer[rovepos] = input + delayout * feedback;  | 
 | 
                if (++rovepos == delaybuffersize)  | 
 | 
                    rovepos = 0;  | 
 | 
            }  | 
 | 
            this.rovepos = rovepos;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void processReplace(float in[], float out[]) { | 
 | 
            int len = in.length;  | 
 | 
            int delaybuffersize = this.delaybuffersize;  | 
 | 
            int rovepos = this.rovepos;  | 
 | 
            for (int i = 0; i < len; i++) { | 
 | 
                float delayout = delaybuffer[rovepos];  | 
 | 
                float input = in[i];  | 
 | 
                out[i] = delayout - input;  | 
 | 
                delaybuffer[rovepos] = input + delayout * feedback;  | 
 | 
                if (++rovepos == delaybuffersize)  | 
 | 
                    rovepos = 0;  | 
 | 
            }  | 
 | 
            this.rovepos = rovepos;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private final static class Comb { | 
 | 
 | 
 | 
        private final float[] delaybuffer;  | 
 | 
        private final int delaybuffersize;  | 
 | 
        private int rovepos = 0;  | 
 | 
        private float feedback;  | 
 | 
        private float filtertemp = 0;  | 
 | 
        private float filtercoeff1 = 0;  | 
 | 
        private float filtercoeff2 = 1;  | 
 | 
 | 
 | 
        Comb(int size) { | 
 | 
            delaybuffer = new float[size];  | 
 | 
            delaybuffersize = size;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void setFeedBack(float feedback) { | 
 | 
            this.feedback = feedback;  | 
 | 
            filtercoeff2 = (1 - filtercoeff1)* feedback;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void processMix(float in[], float out[]) { | 
 | 
            int len = in.length;  | 
 | 
            int delaybuffersize = this.delaybuffersize;  | 
 | 
            int rovepos = this.rovepos;  | 
 | 
            float filtertemp = this.filtertemp;  | 
 | 
            float filtercoeff1 = this.filtercoeff1;  | 
 | 
            float filtercoeff2 = this.filtercoeff2;  | 
 | 
            for (int i = 0; i < len; i++) { | 
 | 
                float delayout = delaybuffer[rovepos];  | 
 | 
                  | 
 | 
                filtertemp = (delayout * filtercoeff2)  | 
 | 
                        + (filtertemp * filtercoeff1);  | 
 | 
                out[i] += delayout;  | 
 | 
                delaybuffer[rovepos] = in[i] + filtertemp;  | 
 | 
                if (++rovepos == delaybuffersize)  | 
 | 
                    rovepos = 0;  | 
 | 
            }  | 
 | 
            this.filtertemp  = filtertemp;  | 
 | 
            this.rovepos = rovepos;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void processReplace(float in[], float out[]) { | 
 | 
            int len = in.length;  | 
 | 
            int delaybuffersize = this.delaybuffersize;  | 
 | 
            int rovepos = this.rovepos;  | 
 | 
            float filtertemp = this.filtertemp;  | 
 | 
            float filtercoeff1 = this.filtercoeff1;  | 
 | 
            float filtercoeff2 = this.filtercoeff2;  | 
 | 
            for (int i = 0; i < len; i++) { | 
 | 
                float delayout = delaybuffer[rovepos];  | 
 | 
                  | 
 | 
                filtertemp = (delayout * filtercoeff2)  | 
 | 
                        + (filtertemp * filtercoeff1);  | 
 | 
                out[i] = delayout;  | 
 | 
                delaybuffer[rovepos] = in[i] + filtertemp;  | 
 | 
                if (++rovepos == delaybuffersize)  | 
 | 
                    rovepos = 0;  | 
 | 
            }  | 
 | 
            this.filtertemp  = filtertemp;  | 
 | 
            this.rovepos = rovepos;  | 
 | 
        }  | 
 | 
 | 
 | 
        public void setDamp(float val) { | 
 | 
            filtercoeff1 = val;  | 
 | 
            filtercoeff2 = (1 - filtercoeff1)* feedback;  | 
 | 
        }  | 
 | 
    }  | 
 | 
    private float roomsize;  | 
 | 
    private float damp;  | 
 | 
    private float gain = 1;  | 
 | 
    private Delay delay;  | 
 | 
    private Comb[] combL;  | 
 | 
    private Comb[] combR;  | 
 | 
    private AllPass[] allpassL;  | 
 | 
    private AllPass[] allpassR;  | 
 | 
    private float[] input;  | 
 | 
    private float[] out;  | 
 | 
    private float[] pre1;  | 
 | 
    private float[] pre2;  | 
 | 
    private float[] pre3;  | 
 | 
    private boolean denormal_flip = false;  | 
 | 
    private boolean mix = true;  | 
 | 
    private SoftAudioBuffer inputA;  | 
 | 
    private SoftAudioBuffer left;  | 
 | 
    private SoftAudioBuffer right;  | 
 | 
    private boolean dirty = true;  | 
 | 
    private float dirty_roomsize;  | 
 | 
    private float dirty_damp;  | 
 | 
    private float dirty_predelay;  | 
 | 
    private float dirty_gain;  | 
 | 
    private float samplerate;  | 
 | 
    private boolean light = true;  | 
 | 
 | 
 | 
    public void init(float samplerate, float controlrate) { | 
 | 
        this.samplerate = samplerate;  | 
 | 
 | 
 | 
        double freqscale = ((double) samplerate) / 44100.0;  | 
 | 
        // freqscale = 1.0/ freqscale;  | 
 | 
 | 
 | 
        int stereospread = 23;  | 
 | 
 | 
 | 
        delay = new Delay();  | 
 | 
 | 
 | 
        combL = new Comb[8];  | 
 | 
        combR = new Comb[8];  | 
 | 
        combL[0] = new Comb((int) (freqscale * (1116)));  | 
 | 
        combR[0] = new Comb((int) (freqscale * (1116 + stereospread)));  | 
 | 
        combL[1] = new Comb((int) (freqscale * (1188)));  | 
 | 
        combR[1] = new Comb((int) (freqscale * (1188 + stereospread)));  | 
 | 
        combL[2] = new Comb((int) (freqscale * (1277)));  | 
 | 
        combR[2] = new Comb((int) (freqscale * (1277 + stereospread)));  | 
 | 
        combL[3] = new Comb((int) (freqscale * (1356)));  | 
 | 
        combR[3] = new Comb((int) (freqscale * (1356 + stereospread)));  | 
 | 
        combL[4] = new Comb((int) (freqscale * (1422)));  | 
 | 
        combR[4] = new Comb((int) (freqscale * (1422 + stereospread)));  | 
 | 
        combL[5] = new Comb((int) (freqscale * (1491)));  | 
 | 
        combR[5] = new Comb((int) (freqscale * (1491 + stereospread)));  | 
 | 
        combL[6] = new Comb((int) (freqscale * (1557)));  | 
 | 
        combR[6] = new Comb((int) (freqscale * (1557 + stereospread)));  | 
 | 
        combL[7] = new Comb((int) (freqscale * (1617)));  | 
 | 
        combR[7] = new Comb((int) (freqscale * (1617 + stereospread)));  | 
 | 
 | 
 | 
        allpassL = new AllPass[4];  | 
 | 
        allpassR = new AllPass[4];  | 
 | 
        allpassL[0] = new AllPass((int) (freqscale * (556)));  | 
 | 
        allpassR[0] = new AllPass((int) (freqscale * (556 + stereospread)));  | 
 | 
        allpassL[1] = new AllPass((int) (freqscale * (441)));  | 
 | 
        allpassR[1] = new AllPass((int) (freqscale * (441 + stereospread)));  | 
 | 
        allpassL[2] = new AllPass((int) (freqscale * (341)));  | 
 | 
        allpassR[2] = new AllPass((int) (freqscale * (341 + stereospread)));  | 
 | 
        allpassL[3] = new AllPass((int) (freqscale * (225)));  | 
 | 
        allpassR[3] = new AllPass((int) (freqscale * (225 + stereospread)));  | 
 | 
 | 
 | 
        for (int i = 0; i < allpassL.length; i++) { | 
 | 
            allpassL[i].setFeedBack(0.5f);  | 
 | 
            allpassR[i].setFeedBack(0.5f);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        globalParameterControlChange(new int[]{0x01 * 128 + 0x01}, 0, 4); | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
    public void setInput(int pin, SoftAudioBuffer input) { | 
 | 
        if (pin == 0)  | 
 | 
            inputA = input;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setOutput(int pin, SoftAudioBuffer output) { | 
 | 
        if (pin == 0)  | 
 | 
            left = output;  | 
 | 
        if (pin == 1)  | 
 | 
            right = output;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setMixMode(boolean mix) { | 
 | 
        this.mix = mix;  | 
 | 
    }  | 
 | 
 | 
 | 
    private boolean silent = true;  | 
 | 
 | 
 | 
    public void processAudio() { | 
 | 
        boolean silent_input = this.inputA.isSilent();  | 
 | 
        if(!silent_input)  | 
 | 
            silent = false;  | 
 | 
        if(silent)  | 
 | 
        { | 
 | 
            if (!mix) { | 
 | 
                left.clear();  | 
 | 
                right.clear();  | 
 | 
            }  | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
        float[] inputA = this.inputA.array();  | 
 | 
        float[] left = this.left.array();  | 
 | 
        float[] right = this.right == null ? null : this.right.array();  | 
 | 
 | 
 | 
        int numsamples = inputA.length;  | 
 | 
        if (input == null || input.length < numsamples)  | 
 | 
            input = new float[numsamples];  | 
 | 
 | 
 | 
        float again = gain * 0.018f / 2;  | 
 | 
 | 
 | 
        denormal_flip = !denormal_flip;  | 
 | 
        if(denormal_flip)  | 
 | 
            for (int i = 0; i < numsamples; i++)  | 
 | 
                input[i] = inputA[i] * again + 1E-20f;  | 
 | 
        else  | 
 | 
            for (int i = 0; i < numsamples; i++)  | 
 | 
                input[i] = inputA[i] * again - 1E-20f;  | 
 | 
 | 
 | 
        delay.processReplace(input);  | 
 | 
 | 
 | 
        if(light && (right != null))  | 
 | 
        { | 
 | 
            if (pre1 == null || pre1.length < numsamples)  | 
 | 
            { | 
 | 
                pre1 = new float[numsamples];  | 
 | 
                pre2 = new float[numsamples];  | 
 | 
                pre3 = new float[numsamples];  | 
 | 
            }  | 
 | 
 | 
 | 
            for (int i = 0; i < allpassL.length; i++)  | 
 | 
                allpassL[i].processReplace(input);  | 
 | 
 | 
 | 
            combL[0].processReplace(input, pre3);  | 
 | 
            combL[1].processReplace(input, pre3);  | 
 | 
 | 
 | 
            combL[2].processReplace(input, pre1);  | 
 | 
            for (int i = 4; i < combL.length-2; i+=2)  | 
 | 
                combL[i].processMix(input, pre1);  | 
 | 
 | 
 | 
            combL[3].processReplace(input, pre2);;  | 
 | 
            for (int i = 5; i < combL.length-2; i+=2)  | 
 | 
                combL[i].processMix(input, pre2);  | 
 | 
 | 
 | 
            if (!mix)  | 
 | 
            { | 
 | 
                Arrays.fill(right, 0);  | 
 | 
                Arrays.fill(left, 0);  | 
 | 
            }  | 
 | 
            for (int i = combR.length-2; i < combR.length; i++)  | 
 | 
                combR[i].processMix(input, right);  | 
 | 
            for (int i = combL.length-2; i < combL.length; i++)  | 
 | 
                combL[i].processMix(input, left);  | 
 | 
 | 
 | 
            for (int i = 0; i < numsamples; i++)  | 
 | 
            { | 
 | 
                float p = pre1[i] - pre2[i];  | 
 | 
                float m = pre3[i];  | 
 | 
                left[i] += m + p;  | 
 | 
                right[i] += m - p;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        else  | 
 | 
        { | 
 | 
            if (out == null || out.length < numsamples)  | 
 | 
                out = new float[numsamples];  | 
 | 
 | 
 | 
            if (right != null) { | 
 | 
                if (!mix)  | 
 | 
                    Arrays.fill(right, 0);  | 
 | 
                allpassR[0].processReplace(input, out);  | 
 | 
                for (int i = 1; i < allpassR.length; i++)  | 
 | 
                    allpassR[i].processReplace(out);  | 
 | 
                for (int i = 0; i < combR.length; i++)  | 
 | 
                    combR[i].processMix(out, right);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (!mix)  | 
 | 
                Arrays.fill(left, 0);  | 
 | 
            allpassL[0].processReplace(input, out);  | 
 | 
            for (int i = 1; i < allpassL.length; i++)  | 
 | 
                allpassL[i].processReplace(out);  | 
 | 
            for (int i = 0; i < combL.length; i++)  | 
 | 
                combL[i].processMix(out, left);  | 
 | 
        }  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
        if (silent_input) { | 
 | 
            silent = true;  | 
 | 
            for (int i = 0; i < numsamples; i++)  | 
 | 
            { | 
 | 
                float v = left[i];  | 
 | 
                if(v > 1E-10 || v < -1E-10)  | 
 | 
                { | 
 | 
                    silent = false;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
    public void globalParameterControlChange(int[] slothpath, long param,  | 
 | 
            long value) { | 
 | 
        if (slothpath.length == 1) { | 
 | 
            if (slothpath[0] == 0x01 * 128 + 0x01) { | 
 | 
 | 
 | 
                if (param == 0) { | 
 | 
                    if (value == 0) { | 
 | 
                        // Small Room A small size room with a length  | 
 | 
                          | 
 | 
                        dirty_roomsize = (1.1f);  | 
 | 
                        dirty_damp = (5000);  | 
 | 
                        dirty_predelay = (0);  | 
 | 
                        dirty_gain = (4);  | 
 | 
                        dirty = true;  | 
 | 
                    }  | 
 | 
                    if (value == 1) { | 
 | 
                        // Medium Room A medium size room with a length  | 
 | 
                          | 
 | 
                        dirty_roomsize = (1.3f);  | 
 | 
                        dirty_damp = (5000);  | 
 | 
                        dirty_predelay = (0);  | 
 | 
                        dirty_gain = (3);  | 
 | 
                        dirty = true;  | 
 | 
                    }  | 
 | 
                    if (value == 2) { | 
 | 
                        // Large Room A large size room suitable for  | 
 | 
                          | 
 | 
                        dirty_roomsize = (1.5f);  | 
 | 
                        dirty_damp = (5000);  | 
 | 
                        dirty_predelay = (0);  | 
 | 
                        dirty_gain = (2);  | 
 | 
                        dirty = true;  | 
 | 
                    }  | 
 | 
                    if (value == 3) { | 
 | 
                          | 
 | 
                        dirty_roomsize = (1.8f);  | 
 | 
                        dirty_damp = (24000);  | 
 | 
                        dirty_predelay = (0.02f);  | 
 | 
                        dirty_gain = (1.5f);  | 
 | 
                        dirty = true;  | 
 | 
                    }  | 
 | 
                    if (value == 4) { | 
 | 
                        // Large Hall A large size concert hall  | 
 | 
                          | 
 | 
                        dirty_roomsize = (1.8f);  | 
 | 
                        dirty_damp = (24000);  | 
 | 
                        dirty_predelay = (0.03f);  | 
 | 
                        dirty_gain = (1.5f);  | 
 | 
                        dirty = true;  | 
 | 
                    }  | 
 | 
                    if (value == 8) { | 
 | 
                          | 
 | 
                        dirty_roomsize = (1.3f);  | 
 | 
                        dirty_damp = (2500);  | 
 | 
                        dirty_predelay = (0);  | 
 | 
                        dirty_gain = (6);  | 
 | 
                        dirty = true;  | 
 | 
                    }  | 
 | 
                } else if (param == 1) { | 
 | 
                    dirty_roomsize = ((float) (Math.exp((value - 40) * 0.025)));  | 
 | 
                    dirty = true;  | 
 | 
                }  | 
 | 
 | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void processControlLogic() { | 
 | 
        if (dirty) { | 
 | 
            dirty = false;  | 
 | 
            setRoomSize(dirty_roomsize);  | 
 | 
            setDamp(dirty_damp);  | 
 | 
            setPreDelay(dirty_predelay);  | 
 | 
            setGain(dirty_gain);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setRoomSize(float value) { | 
 | 
        roomsize = 1 - (0.17f / value);  | 
 | 
 | 
 | 
        for (int i = 0; i < combL.length; i++) { | 
 | 
            combL[i].feedback = roomsize;  | 
 | 
            combR[i].feedback = roomsize;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setPreDelay(float value) { | 
 | 
        delay.setDelay((int)(value * samplerate));  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setGain(float gain) { | 
 | 
        this.gain = gain;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setDamp(float value) { | 
 | 
        double x = (value / samplerate) * (2 * Math.PI);  | 
 | 
        double cx = 2 - Math.cos(x);  | 
 | 
        damp = (float)(cx - Math.sqrt(cx * cx - 1));  | 
 | 
        if (damp > 1)  | 
 | 
            damp = 1;  | 
 | 
        if (damp < 0)  | 
 | 
            damp = 0;  | 
 | 
 | 
 | 
          | 
 | 
        for (int i = 0; i < combL.length; i++) { | 
 | 
            combL[i].setDamp(damp);  | 
 | 
            combR[i].setDamp(damp);  | 
 | 
        }  | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
    public void setLightMode(boolean light)  | 
 | 
    { | 
 | 
        this.light = light;  | 
 | 
    }  | 
 | 
}  | 
 | 
 |