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
init(float samplerate, float controlrate)48     public void init(float samplerate, float controlrate) {
49         this.controlrate = controlrate;
50     }
51 
52     @Override
setInput(int pin, SoftAudioBuffer input)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
setOutput(int pin, SoftAudioBuffer output)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
setMixMode(boolean mix)69     public void setMixMode(boolean mix) {
70         this.mix = mix;
71     }
72 
73     @Override
globalParameterControlChange(int[] slothpath, long param, long value)74     public void globalParameterControlChange(int[] slothpath, long param,
75                                              long value) {
76     }
77 
78     double silentcounter = 0;
79 
80     @Override
processAudio()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
processControlLogic()197     public void processControlLogic() {
198     }
199 }
200