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    * Reverb effect based on allpass/comb filters. First audio is send to 8
   31    * parelled comb filters and then mixed together and then finally send thru 3
   32    * different allpass filters.
   33    *
   34    * @author Karl Helgason
   35    */
   36   public class SoftReverb implements SoftAudioProcessor {
   37   
   38       private final static class Delay {
   39   
   40           private float[] delaybuffer;
   41           private int rovepos = 0;
   42   
   43           public Delay() {
   44               delaybuffer = null;
   45           }
   46   
   47           public void setDelay(int delay) {
   48               if (delay == 0)
   49                   delaybuffer = null;
   50               else
   51                   delaybuffer = new float[delay];
   52               rovepos = 0;
   53           }
   54   
   55           public void processReplace(float[] inout) {
   56               if (delaybuffer == null)
   57                   return;
   58               int len = inout.length;
   59               int rnlen = delaybuffer.length;
   60               int rovepos = this.rovepos;
   61   
   62               for (int i = 0; i < len; i++) {
   63                   float x = inout[i];
   64                   inout[i] = delaybuffer[rovepos];
   65                   delaybuffer[rovepos] = x;
   66                   if (++rovepos == rnlen)
   67                       rovepos = 0;
   68               }
   69               this.rovepos = rovepos;
   70           }
   71       }
   72   
   73       private final static class AllPass {
   74   
   75           private final float[] delaybuffer;
   76           private final int delaybuffersize;
   77           private int rovepos = 0;
   78           private float feedback;
   79   
   80           public AllPass(int size) {
   81               delaybuffer = new float[size];
   82               delaybuffersize = size;
   83           }
   84   
   85           public void setFeedBack(float feedback) {
   86               this.feedback = feedback;
   87           }
   88   
   89           public void processReplace(float inout[]) {
   90               int len = inout.length;
   91               int delaybuffersize = this.delaybuffersize;
   92               int rovepos = this.rovepos;
   93               for (int i = 0; i < len; i++) {
   94                   float delayout = delaybuffer[rovepos];
   95                   float input = inout[i];
   96                   inout[i] = delayout - input;
   97                   delaybuffer[rovepos] = input + delayout * feedback;
   98                   if (++rovepos == delaybuffersize)
   99                       rovepos = 0;
  100               }
  101               this.rovepos = rovepos;
  102           }
  103   
  104           public void processReplace(float in[], float out[]) {
  105               int len = in.length;
  106               int delaybuffersize = this.delaybuffersize;
  107               int rovepos = this.rovepos;
  108               for (int i = 0; i < len; i++) {
  109                   float delayout = delaybuffer[rovepos];
  110                   float input = in[i];
  111                   out[i] = delayout - input;
  112                   delaybuffer[rovepos] = input + delayout * feedback;
  113                   if (++rovepos == delaybuffersize)
  114                       rovepos = 0;
  115               }
  116               this.rovepos = rovepos;
  117           }
  118       }
  119   
  120       private final static class Comb {
  121   
  122           private final float[] delaybuffer;
  123           private final int delaybuffersize;
  124           private int rovepos = 0;
  125           private float feedback;
  126           private float filtertemp = 0;
  127           private float filtercoeff1 = 0;
  128           private float filtercoeff2 = 1;
  129   
  130           public Comb(int size) {
  131               delaybuffer = new float[size];
  132               delaybuffersize = size;
  133           }
  134   
  135           public void setFeedBack(float feedback) {
  136               this.feedback = feedback;
  137               filtercoeff2 = (1 - filtercoeff1)* feedback;
  138           }
  139   
  140           public void processMix(float in[], float out[]) {
  141               int len = in.length;
  142               int delaybuffersize = this.delaybuffersize;
  143               int rovepos = this.rovepos;
  144               float filtertemp = this.filtertemp;
  145               float filtercoeff1 = this.filtercoeff1;
  146               float filtercoeff2 = this.filtercoeff2;
  147               for (int i = 0; i < len; i++) {
  148                   float delayout = delaybuffer[rovepos];
  149                   // One Pole Lowpass Filter
  150                   filtertemp = (delayout * filtercoeff2)
  151                           + (filtertemp * filtercoeff1);
  152                   out[i] += delayout;
  153                   delaybuffer[rovepos] = in[i] + filtertemp;
  154                   if (++rovepos == delaybuffersize)
  155                       rovepos = 0;
  156               }
  157               this.filtertemp  = filtertemp;
  158               this.rovepos = rovepos;
  159           }
  160   
  161           public void processReplace(float in[], float out[]) {
  162               int len = in.length;
  163               int delaybuffersize = this.delaybuffersize;
  164               int rovepos = this.rovepos;
  165               float filtertemp = this.filtertemp;
  166               float filtercoeff1 = this.filtercoeff1;
  167               float filtercoeff2 = this.filtercoeff2;
  168               for (int i = 0; i < len; i++) {
  169                   float delayout = delaybuffer[rovepos];
  170                   // One Pole Lowpass Filter
  171                   filtertemp = (delayout * filtercoeff2)
  172                           + (filtertemp * filtercoeff1);
  173                   out[i] = delayout;
  174                   delaybuffer[rovepos] = in[i] + filtertemp;
  175                   if (++rovepos == delaybuffersize)
  176                       rovepos = 0;
  177               }
  178               this.filtertemp  = filtertemp;
  179               this.rovepos = rovepos;
  180           }
  181   
  182           public void setDamp(float val) {
  183               filtercoeff1 = val;
  184               filtercoeff2 = (1 - filtercoeff1)* feedback;
  185           }
  186       }
  187       private float roomsize;
  188       private float damp;
  189       private float gain = 1;
  190       private Delay delay;
  191       private Comb[] combL;
  192       private Comb[] combR;
  193       private AllPass[] allpassL;
  194       private AllPass[] allpassR;
  195       private float[] input;
  196       private float[] out;
  197       private float[] pre1;
  198       private float[] pre2;
  199       private float[] pre3;
  200       private boolean denormal_flip = false;
  201       private boolean mix = true;
  202       private SoftAudioBuffer inputA;
  203       private SoftAudioBuffer left;
  204       private SoftAudioBuffer right;
  205       private boolean dirty = true;
  206       private float dirty_roomsize;
  207       private float dirty_damp;
  208       private float dirty_predelay;
  209       private float dirty_gain;
  210       private float samplerate;
  211       private boolean light = true;
  212   
  213       public void init(float samplerate, float controlrate) {
  214           this.samplerate = samplerate;
  215   
  216           double freqscale = ((double) samplerate) / 44100.0;
  217           // freqscale = 1.0/ freqscale;
  218   
  219           int stereospread = 23;
  220   
  221           delay = new Delay();
  222   
  223           combL = new Comb[8];
  224           combR = new Comb[8];
  225           combL[0] = new Comb((int) (freqscale * (1116)));
  226           combR[0] = new Comb((int) (freqscale * (1116 + stereospread)));
  227           combL[1] = new Comb((int) (freqscale * (1188)));
  228           combR[1] = new Comb((int) (freqscale * (1188 + stereospread)));
  229           combL[2] = new Comb((int) (freqscale * (1277)));
  230           combR[2] = new Comb((int) (freqscale * (1277 + stereospread)));
  231           combL[3] = new Comb((int) (freqscale * (1356)));
  232           combR[3] = new Comb((int) (freqscale * (1356 + stereospread)));
  233           combL[4] = new Comb((int) (freqscale * (1422)));
  234           combR[4] = new Comb((int) (freqscale * (1422 + stereospread)));
  235           combL[5] = new Comb((int) (freqscale * (1491)));
  236           combR[5] = new Comb((int) (freqscale * (1491 + stereospread)));
  237           combL[6] = new Comb((int) (freqscale * (1557)));
  238           combR[6] = new Comb((int) (freqscale * (1557 + stereospread)));
  239           combL[7] = new Comb((int) (freqscale * (1617)));
  240           combR[7] = new Comb((int) (freqscale * (1617 + stereospread)));
  241   
  242           allpassL = new AllPass[4];
  243           allpassR = new AllPass[4];
  244           allpassL[0] = new AllPass((int) (freqscale * (556)));
  245           allpassR[0] = new AllPass((int) (freqscale * (556 + stereospread)));
  246           allpassL[1] = new AllPass((int) (freqscale * (441)));
  247           allpassR[1] = new AllPass((int) (freqscale * (441 + stereospread)));
  248           allpassL[2] = new AllPass((int) (freqscale * (341)));
  249           allpassR[2] = new AllPass((int) (freqscale * (341 + stereospread)));
  250           allpassL[3] = new AllPass((int) (freqscale * (225)));
  251           allpassR[3] = new AllPass((int) (freqscale * (225 + stereospread)));
  252   
  253           for (int i = 0; i < allpassL.length; i++) {
  254               allpassL[i].setFeedBack(0.5f);
  255               allpassR[i].setFeedBack(0.5f);
  256           }
  257   
  258           /* Init other settings */
  259           globalParameterControlChange(new int[]{0x01 * 128 + 0x01}, 0, 4);
  260   
  261       }
  262   
  263       public void setInput(int pin, SoftAudioBuffer input) {
  264           if (pin == 0)
  265               inputA = input;
  266       }
  267   
  268       public void setOutput(int pin, SoftAudioBuffer output) {
  269           if (pin == 0)
  270               left = output;
  271           if (pin == 1)
  272               right = output;
  273       }
  274   
  275       public void setMixMode(boolean mix) {
  276           this.mix = mix;
  277       }
  278   
  279       private boolean silent = true;
  280   
  281       public void processAudio() {
  282           boolean silent_input = this.inputA.isSilent();
  283           if(!silent_input)
  284               silent = false;
  285           if(silent)
  286           {
  287               if (!mix) {
  288                   left.clear();
  289                   right.clear();
  290               }
  291               return;
  292           }
  293   
  294           float[] inputA = this.inputA.array();
  295           float[] left = this.left.array();
  296           float[] right = this.right == null ? null : this.right.array();
  297   
  298           int numsamples = inputA.length;
  299           if (input == null || input.length < numsamples)
  300               input = new float[numsamples];
  301   
  302           float again = gain * 0.018f / 2;
  303   
  304           denormal_flip = !denormal_flip;
  305           if(denormal_flip)
  306               for (int i = 0; i < numsamples; i++)
  307                   input[i] = inputA[i] * again + 1E-20f;
  308           else
  309               for (int i = 0; i < numsamples; i++)
  310                   input[i] = inputA[i] * again - 1E-20f;
  311   
  312           delay.processReplace(input);
  313   
  314           if(light && (right != null))
  315           {
  316               if (pre1 == null || pre1.length < numsamples)
  317               {
  318                   pre1 = new float[numsamples];
  319                   pre2 = new float[numsamples];
  320                   pre3 = new float[numsamples];
  321               }
  322   
  323               for (int i = 0; i < allpassL.length; i++)
  324                   allpassL[i].processReplace(input);
  325   
  326               combL[0].processReplace(input, pre3);
  327               combL[1].processReplace(input, pre3);
  328   
  329               combL[2].processReplace(input, pre1);
  330               for (int i = 4; i < combL.length-2; i+=2)
  331                   combL[i].processMix(input, pre1);
  332   
  333               combL[3].processReplace(input, pre2);;
  334               for (int i = 5; i < combL.length-2; i+=2)
  335                   combL[i].processMix(input, pre2);
  336   
  337               if (!mix)
  338               {
  339                   Arrays.fill(right, 0);
  340                   Arrays.fill(left, 0);
  341               }
  342               for (int i = combR.length-2; i < combR.length; i++)
  343                   combR[i].processMix(input, right);
  344               for (int i = combL.length-2; i < combL.length; i++)
  345                   combL[i].processMix(input, left);
  346   
  347               for (int i = 0; i < numsamples; i++)
  348               {
  349                   float p = pre1[i] - pre2[i];
  350                   float m = pre3[i];
  351                   left[i] += m + p;
  352                   right[i] += m - p;
  353               }
  354           }
  355           else
  356           {
  357               if (out == null || out.length < numsamples)
  358                   out = new float[numsamples];
  359   
  360               if (right != null) {
  361                   if (!mix)
  362                       Arrays.fill(right, 0);
  363                   allpassR[0].processReplace(input, out);
  364                   for (int i = 1; i < allpassR.length; i++)
  365                       allpassR[i].processReplace(out);
  366                   for (int i = 0; i < combR.length; i++)
  367                       combR[i].processMix(out, right);
  368               }
  369   
  370               if (!mix)
  371                   Arrays.fill(left, 0);
  372               allpassL[0].processReplace(input, out);
  373               for (int i = 1; i < allpassL.length; i++)
  374                   allpassL[i].processReplace(out);
  375               for (int i = 0; i < combL.length; i++)
  376                   combL[i].processMix(out, left);
  377           }
  378   
  379   
  380   
  381   
  382   
  383   
  384           if (silent_input) {
  385               silent = true;
  386               for (int i = 0; i < numsamples; i++)
  387               {
  388                   float v = left[i];
  389                   if(v > 1E-10 || v < -1E-10)
  390                   {
  391                       silent = false;
  392                       break;
  393                   }
  394               }
  395           }
  396   
  397       }
  398   
  399       public void globalParameterControlChange(int[] slothpath, long param,
  400               long value) {
  401           if (slothpath.length == 1) {
  402               if (slothpath[0] == 0x01 * 128 + 0x01) {
  403   
  404                   if (param == 0) {
  405                       if (value == 0) {
  406                           // Small Room A small size room with a length
  407                           // of 5m or so.
  408                           dirty_roomsize = (1.1f);
  409                           dirty_damp = (5000);
  410                           dirty_predelay = (0);
  411                           dirty_gain = (4);
  412                           dirty = true;
  413                       }
  414                       if (value == 1) {
  415                           // Medium Room A medium size room with a length
  416                           // of 10m or so.
  417                           dirty_roomsize = (1.3f);
  418                           dirty_damp = (5000);
  419                           dirty_predelay = (0);
  420                           dirty_gain = (3);
  421                           dirty = true;
  422                       }
  423                       if (value == 2) {
  424                           // Large Room A large size room suitable for
  425                           // live performances.
  426                           dirty_roomsize = (1.5f);
  427                           dirty_damp = (5000);
  428                           dirty_predelay = (0);
  429                           dirty_gain = (2);
  430                           dirty = true;
  431                       }
  432                       if (value == 3) {
  433                           // Medium Hall A medium size concert hall.
  434                           dirty_roomsize = (1.8f);
  435                           dirty_damp = (24000);
  436                           dirty_predelay = (0.02f);
  437                           dirty_gain = (1.5f);
  438                           dirty = true;
  439                       }
  440                       if (value == 4) {
  441                           // Large Hall A large size concert hall
  442                           // suitable for a full orchestra.
  443                           dirty_roomsize = (1.8f);
  444                           dirty_damp = (24000);
  445                           dirty_predelay = (0.03f);
  446                           dirty_gain = (1.5f);
  447                           dirty = true;
  448                       }
  449                       if (value == 8) {
  450                           // Plate A plate reverb simulation.
  451                           dirty_roomsize = (1.3f);
  452                           dirty_damp = (2500);
  453                           dirty_predelay = (0);
  454                           dirty_gain = (6);
  455                           dirty = true;
  456                       }
  457                   } else if (param == 1) {
  458                       dirty_roomsize = ((float) (Math.exp((value - 40) * 0.025)));
  459                       dirty = true;
  460                   }
  461   
  462               }
  463           }
  464       }
  465   
  466       public void processControlLogic() {
  467           if (dirty) {
  468               dirty = false;
  469               setRoomSize(dirty_roomsize);
  470               setDamp(dirty_damp);
  471               setPreDelay(dirty_predelay);
  472               setGain(dirty_gain);
  473           }
  474       }
  475   
  476       public void setRoomSize(float value) {
  477           roomsize = 1 - (0.17f / value);
  478   
  479           for (int i = 0; i < combL.length; i++) {
  480               combL[i].feedback = roomsize;
  481               combR[i].feedback = roomsize;
  482           }
  483       }
  484   
  485       public void setPreDelay(float value) {
  486           delay.setDelay((int)(value * samplerate));
  487       }
  488   
  489       public void setGain(float gain) {
  490           this.gain = gain;
  491       }
  492   
  493       public void setDamp(float value) {
  494           double x = (value / samplerate) * (2 * Math.PI);
  495           double cx = 2 - Math.cos(x);
  496           damp = (float)(cx - Math.sqrt(cx * cx - 1));
  497           if (damp > 1)
  498               damp = 1;
  499           if (damp < 0)
  500               damp = 0;
  501   
  502           // damp = value * 0.4f;
  503           for (int i = 0; i < combL.length; i++) {
  504               combL[i].setDamp(damp);
  505               combR[i].setDamp(damp);
  506           }
  507   
  508       }
  509   
  510       public void setLightMode(boolean light)
  511       {
  512           this.light = light;
  513       }
  514   }
  515   

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