Save This Page
Home » openjdk-7 » com.sun.media » sound » [javadoc | source]
    1   /*
    2    * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   package com.sun.media.sound;
   26   
   27   import java.util.Arrays;
   28   
   29   /**
   30    * A chorus effect made using LFO and variable delay. One for each channel
   31    * (left,right), with different starting phase for stereo effect.
   32    *
   33    * @author Karl Helgason
   34    */
   35   public class SoftChorus implements SoftAudioProcessor {
   36   
   37       private static class VariableDelay {
   38   
   39           private float[] delaybuffer;
   40           private int rovepos = 0;
   41           private float gain = 1;
   42           private float rgain = 0;
   43           private float delay = 0;
   44           private float lastdelay = 0;
   45           private float feedback = 0;
   46   
   47           public VariableDelay(int maxbuffersize) {
   48               delaybuffer = new float[maxbuffersize];
   49           }
   50   
   51           public void setDelay(float delay) {
   52               this.delay = delay;
   53           }
   54   
   55           public void setFeedBack(float feedback) {
   56               this.feedback = feedback;
   57           }
   58   
   59           public void setGain(float gain) {
   60               this.gain = gain;
   61           }
   62   
   63           public void setReverbSendGain(float rgain) {
   64               this.rgain = rgain;
   65           }
   66   
   67           public void processMix(float[] in, float[] out, float[] rout) {
   68               float gain = this.gain;
   69               float delay = this.delay;
   70               float feedback = this.feedback;
   71   
   72               float[] delaybuffer = this.delaybuffer;
   73               int len = in.length;
   74               float delaydelta = (delay - lastdelay) / len;
   75               int rnlen = delaybuffer.length;
   76               int rovepos = this.rovepos;
   77   
   78               if (rout == null)
   79                   for (int i = 0; i < len; i++) {
   80                       float r = rovepos - (lastdelay + 2) + rnlen;
   81                       int ri = (int) r;
   82                       float s = r - ri;
   83                       float a = delaybuffer[ri % rnlen];
   84                       float b = delaybuffer[(ri + 1) % rnlen];
   85                       float o = a * (1 - s) + b * (s);
   86                       out[i] += o * gain;
   87                       delaybuffer[rovepos] = in[i] + o * feedback;
   88                       rovepos = (rovepos + 1) % rnlen;
   89                       lastdelay += delaydelta;
   90                   }
   91               else
   92                   for (int i = 0; i < len; i++) {
   93                       float r = rovepos - (lastdelay + 2) + rnlen;
   94                       int ri = (int) r;
   95                       float s = r - ri;
   96                       float a = delaybuffer[ri % rnlen];
   97                       float b = delaybuffer[(ri + 1) % rnlen];
   98                       float o = a * (1 - s) + b * (s);
   99                       out[i] += o * gain;
  100                       rout[i] += o * rgain;
  101                       delaybuffer[rovepos] = in[i] + o * feedback;
  102                       rovepos = (rovepos + 1) % rnlen;
  103                       lastdelay += delaydelta;
  104                   }
  105               this.rovepos = rovepos;
  106               lastdelay = delay;
  107           }
  108   
  109           public void processReplace(float[] in, float[] out, float[] rout) {
  110               Arrays.fill(out, 0);
  111               Arrays.fill(rout, 0);
  112               processMix(in, out, rout);
  113           }
  114       }
  115   
  116       private static class LFODelay {
  117   
  118           private double phase = 1;
  119           private double phase_step = 0;
  120           private double depth = 0;
  121           private VariableDelay vdelay;
  122           private double samplerate;
  123           private double controlrate;
  124   
  125           public LFODelay(double samplerate, double controlrate) {
  126               this.samplerate = samplerate;
  127               this.controlrate = controlrate;
  128               // vdelay = new VariableDelay((int)(samplerate*4));
  129               vdelay = new VariableDelay((int) ((this.depth + 10) * 2));
  130   
  131           }
  132   
  133           public void setDepth(double depth) {
  134               this.depth = depth * samplerate;
  135               vdelay = new VariableDelay((int) ((this.depth + 10) * 2));
  136           }
  137   
  138           public void setRate(double rate) {
  139               double g = (Math.PI * 2) * (rate / controlrate);
  140               phase_step = g;
  141           }
  142   
  143           public void setPhase(double phase) {
  144               this.phase = phase;
  145           }
  146   
  147           public void setFeedBack(float feedback) {
  148               vdelay.setFeedBack(feedback);
  149           }
  150   
  151           public void setGain(float gain) {
  152               vdelay.setGain(gain);
  153           }
  154   
  155           public void setReverbSendGain(float rgain) {
  156               vdelay.setReverbSendGain(rgain);
  157           }
  158   
  159           public void processMix(float[] in, float[] out, float[] rout) {
  160               phase += phase_step;
  161               while(phase > (Math.PI * 2)) phase -= (Math.PI * 2);
  162               vdelay.setDelay((float) (depth * 0.5 * (Math.cos(phase) + 2)));
  163               vdelay.processMix(in, out, rout);
  164           }
  165   
  166           public void processReplace(float[] in, float[] out, float[] rout) {
  167               phase += phase_step;
  168               while(phase > (Math.PI * 2)) phase -= (Math.PI * 2);
  169               vdelay.setDelay((float) (depth * 0.5 * (Math.cos(phase) + 2)));
  170               vdelay.processReplace(in, out, rout);
  171   
  172           }
  173       }
  174       private boolean mix = true;
  175       private SoftAudioBuffer inputA;
  176       private SoftAudioBuffer left;
  177       private SoftAudioBuffer right;
  178       private SoftAudioBuffer reverb;
  179       private LFODelay vdelay1L;
  180       private LFODelay vdelay1R;
  181       private float rgain = 0;
  182       private boolean dirty = true;
  183       private double dirty_vdelay1L_rate;
  184       private double dirty_vdelay1R_rate;
  185       private double dirty_vdelay1L_depth;
  186       private double dirty_vdelay1R_depth;
  187       private float dirty_vdelay1L_feedback;
  188       private float dirty_vdelay1R_feedback;
  189       private float dirty_vdelay1L_reverbsendgain;
  190       private float dirty_vdelay1R_reverbsendgain;
  191       private float controlrate;
  192   
  193       public void init(float samplerate, float controlrate) {
  194           this.controlrate = controlrate;
  195           vdelay1L = new LFODelay(samplerate, controlrate);
  196           vdelay1R = new LFODelay(samplerate, controlrate);
  197           vdelay1L.setGain(1.0f); // %
  198           vdelay1R.setGain(1.0f); // %
  199           vdelay1L.setPhase(0.5 * Math.PI);
  200           vdelay1R.setPhase(0);
  201   
  202           globalParameterControlChange(new int[]{0x01 * 128 + 0x02}, 0, 2);
  203       }
  204   
  205       public void globalParameterControlChange(int[] slothpath, long param,
  206               long value) {
  207           if (slothpath.length == 1) {
  208               if (slothpath[0] == 0x01 * 128 + 0x02) {
  209                   if (param == 0) { // Chorus Type
  210                       switch ((int)value) {
  211                       case 0: // Chorus 1 0 (0%) 3 (0.4Hz) 5 (1.9ms) 0 (0%)
  212                           globalParameterControlChange(slothpath, 3, 0);
  213                           globalParameterControlChange(slothpath, 1, 3);
  214                           globalParameterControlChange(slothpath, 2, 5);
  215                           globalParameterControlChange(slothpath, 4, 0);
  216                           break;
  217                       case 1: // Chorus 2 5 (4%) 9 (1.1Hz) 19 (6.3ms) 0 (0%)
  218                           globalParameterControlChange(slothpath, 3, 5);
  219                           globalParameterControlChange(slothpath, 1, 9);
  220                           globalParameterControlChange(slothpath, 2, 19);
  221                           globalParameterControlChange(slothpath, 4, 0);
  222                           break;
  223                       case 2: // Chorus 3 8 (6%) 3 (0.4Hz) 19 (6.3ms) 0 (0%)
  224                           globalParameterControlChange(slothpath, 3, 8);
  225                           globalParameterControlChange(slothpath, 1, 3);
  226                           globalParameterControlChange(slothpath, 2, 19);
  227                           globalParameterControlChange(slothpath, 4, 0);
  228                           break;
  229                       case 3: // Chorus 4 16 (12%) 9 (1.1Hz) 16 (5.3ms) 0 (0%)
  230                           globalParameterControlChange(slothpath, 3, 16);
  231                           globalParameterControlChange(slothpath, 1, 9);
  232                           globalParameterControlChange(slothpath, 2, 16);
  233                           globalParameterControlChange(slothpath, 4, 0);
  234                           break;
  235                       case 4: // FB Chorus 64 (49%) 2 (0.2Hz) 24 (7.8ms) 0 (0%)
  236                           globalParameterControlChange(slothpath, 3, 64);
  237                           globalParameterControlChange(slothpath, 1, 2);
  238                           globalParameterControlChange(slothpath, 2, 24);
  239                           globalParameterControlChange(slothpath, 4, 0);
  240                           break;
  241                       case 5: // Flanger 112 (86%) 1 (0.1Hz) 5 (1.9ms) 0 (0%)
  242                           globalParameterControlChange(slothpath, 3, 112);
  243                           globalParameterControlChange(slothpath, 1, 1);
  244                           globalParameterControlChange(slothpath, 2, 5);
  245                           globalParameterControlChange(slothpath, 4, 0);
  246                           break;
  247                       default:
  248                           break;
  249                       }
  250                   } else if (param == 1) { // Mod Rate
  251                       dirty_vdelay1L_rate = (value * 0.122);
  252                       dirty_vdelay1R_rate = (value * 0.122);
  253                       dirty = true;
  254                   } else if (param == 2) { // Mod Depth
  255                       dirty_vdelay1L_depth = ((value + 1) / 3200.0);
  256                       dirty_vdelay1R_depth = ((value + 1) / 3200.0);
  257                       dirty = true;
  258                   } else if (param == 3) { // Feedback
  259                       dirty_vdelay1L_feedback = (value * 0.00763f);
  260                       dirty_vdelay1R_feedback = (value * 0.00763f);
  261                       dirty = true;
  262                   }
  263                   if (param == 4) { // Send to Reverb
  264                       rgain = value * 0.00787f;
  265                       dirty_vdelay1L_reverbsendgain = (value * 0.00787f);
  266                       dirty_vdelay1R_reverbsendgain = (value * 0.00787f);
  267                       dirty = true;
  268                   }
  269   
  270               }
  271           }
  272       }
  273   
  274       public void processControlLogic() {
  275           if (dirty) {
  276               dirty = false;
  277               vdelay1L.setRate(dirty_vdelay1L_rate);
  278               vdelay1R.setRate(dirty_vdelay1R_rate);
  279               vdelay1L.setDepth(dirty_vdelay1L_depth);
  280               vdelay1R.setDepth(dirty_vdelay1R_depth);
  281               vdelay1L.setFeedBack(dirty_vdelay1L_feedback);
  282               vdelay1R.setFeedBack(dirty_vdelay1R_feedback);
  283               vdelay1L.setReverbSendGain(dirty_vdelay1L_reverbsendgain);
  284               vdelay1R.setReverbSendGain(dirty_vdelay1R_reverbsendgain);
  285           }
  286       }
  287       double silentcounter = 1000;
  288   
  289       public void processAudio() {
  290   
  291           if (inputA.isSilent()) {
  292               silentcounter += 1 / controlrate;
  293   
  294               if (silentcounter > 1) {
  295                   if (!mix) {
  296                       left.clear();
  297                       right.clear();
  298                   }
  299                   return;
  300               }
  301           } else
  302               silentcounter = 0;
  303   
  304           float[] inputA = this.inputA.array();
  305           float[] left = this.left.array();
  306           float[] right = this.right == null ? null : this.right.array();
  307           float[] reverb = rgain != 0 ? this.reverb.array() : null;
  308   
  309           if (mix) {
  310               vdelay1L.processMix(inputA, left, reverb);
  311               if (right != null)
  312                   vdelay1R.processMix(inputA, right, reverb);
  313           } else {
  314               vdelay1L.processReplace(inputA, left, reverb);
  315               if (right != null)
  316                   vdelay1R.processReplace(inputA, right, reverb);
  317           }
  318       }
  319   
  320       public void setInput(int pin, SoftAudioBuffer input) {
  321           if (pin == 0)
  322               inputA = input;
  323       }
  324   
  325       public void setMixMode(boolean mix) {
  326           this.mix = mix;
  327       }
  328   
  329       public void setOutput(int pin, SoftAudioBuffer output) {
  330           if (pin == 0)
  331               left = output;
  332           if (pin == 1)
  333               right = output;
  334           if (pin == 2)
  335               reverb = output;
  336       }
  337   }

Save This Page
Home » openjdk-7 » com.sun.media » sound » [javadoc | source]