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
26package 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 */
34public 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}
200