1 /*
   2  * Copyright (c) 2007, 2013, 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 
  26 package com.sun.media.sound;
  27 
  28 /**
  29  * A simple look-ahead volume limiter with very fast attack and fast release.
  30  * This filter is used for preventing clipping.
  31  *
  32  * @author Karl Helgason
  33  */
  34 public final class SoftLimiter implements SoftAudioProcessor {
  35 
  36     float lastmax = 0;
  37     float gain = 1;
  38     float[] temp_bufferL;
  39     float[] temp_bufferR;
  40     boolean mix = false;
  41     SoftAudioBuffer bufferL;
  42     SoftAudioBuffer bufferR;
  43     SoftAudioBuffer bufferLout;
  44     SoftAudioBuffer bufferRout;
  45     float controlrate;
  46 
  47     @Override
  48     public void init(float samplerate, float controlrate) {
  49         this.controlrate = controlrate;
  50     }
  51 
  52     @Override
  53     public void setInput(int pin, SoftAudioBuffer input) {
  54         if (pin == 0)
  55             bufferL = input;
  56         if (pin == 1)
  57             bufferR = input;
  58     }
  59 
  60     @Override
  61     public void setOutput(int pin, SoftAudioBuffer output) {
  62         if (pin == 0)
  63             bufferLout = output;
  64         if (pin == 1)
  65             bufferRout = output;
  66     }
  67 
  68     @Override
  69     public void setMixMode(boolean mix) {
  70         this.mix = mix;
  71     }
  72 
  73     @Override
  74     public void globalParameterControlChange(int[] slothpath, long param,
  75                                              long value) {
  76     }
  77 
  78     double silentcounter = 0;
  79 
  80     @Override
  81     public void processAudio() {
  82         if (this.bufferL.isSilent()
  83                 && (this.bufferR == null || this.bufferR.isSilent())) {
  84             silentcounter += 1 / controlrate;
  85 
  86             if (silentcounter > 60) {
  87                 if (!mix) {
  88                     bufferLout.clear();
  89                     if (bufferRout != null) bufferRout.clear();
  90                 }
  91                 return;
  92             }
  93         } else
  94             silentcounter = 0;
  95 
  96         float[] bufferL = this.bufferL.array();
  97         float[] bufferR = this.bufferR == null ? null : this.bufferR.array();
  98         float[] bufferLout = this.bufferLout.array();
  99         float[] bufferRout = this.bufferRout == null
 100                                 ? null : this.bufferRout.array();
 101 
 102         if (temp_bufferL == null || temp_bufferL.length < bufferL.length)
 103             temp_bufferL = new float[bufferL.length];
 104         if (bufferR != null)
 105             if (temp_bufferR == null || temp_bufferR.length < bufferR.length)
 106                 temp_bufferR = new float[bufferR.length];
 107 
 108         float max = 0;
 109         int len = bufferL.length;
 110 
 111         if (bufferR == null) {
 112             for (int i = 0; i < len; i++) {
 113                 if (bufferL[i] > max)
 114                     max = bufferL[i];
 115                 if (-bufferL[i] > max)
 116                     max = -bufferL[i];
 117             }
 118         } else {
 119             for (int i = 0; i < len; i++) {
 120                 if (bufferL[i] > max)
 121                     max = bufferL[i];
 122                 if (bufferR[i] > max)
 123                     max = bufferR[i];
 124                 if (-bufferL[i] > max)
 125                     max = -bufferL[i];
 126                 if (-bufferR[i] > max)
 127                     max = -bufferR[i];
 128             }
 129         }
 130 
 131         float lmax = lastmax;
 132         lastmax = max;
 133         if (lmax > max)
 134             max = lmax;
 135 
 136         float newgain = 1;
 137         if (max > 0.99f)
 138             newgain = 0.99f / max;
 139         else
 140             newgain = 1;
 141 
 142         if (newgain > gain)
 143             newgain = (newgain + gain * 9) / 10f;
 144 
 145         float gaindelta = (newgain - gain) / len;
 146         if (mix) {
 147             if (bufferR == null) {
 148                 for (int i = 0; i < len; i++) {
 149                     gain += gaindelta;
 150                     float bL = bufferL[i];
 151                     float tL = temp_bufferL[i];
 152                     temp_bufferL[i] = bL;
 153                     bufferLout[i] += tL * gain;
 154                 }
 155             } else {
 156                 for (int i = 0; i < len; i++) {
 157                     gain += gaindelta;
 158                     float bL = bufferL[i];
 159                     float bR = bufferR[i];
 160                     float tL = temp_bufferL[i];
 161                     float tR = temp_bufferR[i];
 162                     temp_bufferL[i] = bL;
 163                     temp_bufferR[i] = bR;
 164                     bufferLout[i] += tL * gain;
 165                     bufferRout[i] += tR * gain;
 166                 }
 167             }
 168 
 169         } else {
 170             if (bufferR == null) {
 171                 for (int i = 0; i < len; i++) {
 172                     gain += gaindelta;
 173                     float bL = bufferL[i];
 174                     float tL = temp_bufferL[i];
 175                     temp_bufferL[i] = bL;
 176                     bufferLout[i] = tL * gain;
 177                 }
 178             } else {
 179                 for (int i = 0; i < len; i++) {
 180                     gain += gaindelta;
 181                     float bL = bufferL[i];
 182                     float bR = bufferR[i];
 183                     float tL = temp_bufferL[i];
 184                     float tR = temp_bufferR[i];
 185                     temp_bufferL[i] = bL;
 186                     temp_bufferR[i] = bR;
 187                     bufferLout[i] = tL * gain;
 188                     bufferRout[i] = tR * gain;
 189                 }
 190             }
 191 
 192         }
 193         gain = newgain;
 194     }
 195 
 196     @Override
 197     public void processControlLogic() {
 198     }
 199 }