/* |
|
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package com.sun.media.sound; |
|
/** |
|
* A simple look-ahead volume limiter with very fast attack and fast release. |
|
* This filter is used for preventing clipping. |
|
* |
|
* @author Karl Helgason |
|
*/ |
|
public final class SoftLimiter implements SoftAudioProcessor { |
|
float lastmax = 0; |
|
float gain = 1; |
|
float[] temp_bufferL; |
|
float[] temp_bufferR; |
|
boolean mix = false; |
|
SoftAudioBuffer bufferL; |
|
SoftAudioBuffer bufferR; |
|
SoftAudioBuffer bufferLout; |
|
SoftAudioBuffer bufferRout; |
|
float controlrate; |
|
public void init(float samplerate, float controlrate) { |
|
this.controlrate = controlrate; |
|
} |
|
public void setInput(int pin, SoftAudioBuffer input) { |
|
if (pin == 0) |
|
bufferL = input; |
|
if (pin == 1) |
|
bufferR = input; |
|
} |
|
public void setOutput(int pin, SoftAudioBuffer output) { |
|
if (pin == 0) |
|
bufferLout = output; |
|
if (pin == 1) |
|
bufferRout = output; |
|
} |
|
public void setMixMode(boolean mix) { |
|
this.mix = mix; |
|
} |
|
public void globalParameterControlChange(int[] slothpath, long param, |
|
long value) { |
|
} |
|
double silentcounter = 0; |
|
public void processAudio() { |
|
if (this.bufferL.isSilent() |
|
&& (this.bufferR == null || this.bufferR.isSilent())) { |
|
silentcounter += 1 / controlrate; |
|
if (silentcounter > 60) { |
|
if (!mix) { |
|
bufferLout.clear(); |
|
if (bufferRout != null) bufferRout.clear(); |
|
} |
|
return; |
|
} |
|
} else |
|
silentcounter = 0; |
|
float[] bufferL = this.bufferL.array(); |
|
float[] bufferR = this.bufferR == null ? null : this.bufferR.array(); |
|
float[] bufferLout = this.bufferLout.array(); |
|
float[] bufferRout = this.bufferRout == null |
|
? null : this.bufferRout.array(); |
|
if (temp_bufferL == null || temp_bufferL.length < bufferL.length) |
|
temp_bufferL = new float[bufferL.length]; |
|
if (bufferR != null) |
|
if (temp_bufferR == null || temp_bufferR.length < bufferR.length) |
|
temp_bufferR = new float[bufferR.length]; |
|
float max = 0; |
|
int len = bufferL.length; |
|
if (bufferR == null) { |
|
for (int i = 0; i < len; i++) { |
|
if (bufferL[i] > max) |
|
max = bufferL[i]; |
|
if (-bufferL[i] > max) |
|
max = -bufferL[i]; |
|
} |
|
} else { |
|
for (int i = 0; i < len; i++) { |
|
if (bufferL[i] > max) |
|
max = bufferL[i]; |
|
if (bufferR[i] > max) |
|
max = bufferR[i]; |
|
if (-bufferL[i] > max) |
|
max = -bufferL[i]; |
|
if (-bufferR[i] > max) |
|
max = -bufferR[i]; |
|
} |
|
} |
|
float lmax = lastmax; |
|
lastmax = max; |
|
if (lmax > max) |
|
max = lmax; |
|
float newgain = 1; |
|
if (max > 0.99f) |
|
newgain = 0.99f / max; |
|
else |
|
newgain = 1; |
|
if (newgain > gain) |
|
newgain = (newgain + gain * 9) / 10f; |
|
float gaindelta = (newgain - gain) / len; |
|
if (mix) { |
|
if (bufferR == null) { |
|
for (int i = 0; i < len; i++) { |
|
gain += gaindelta; |
|
float bL = bufferL[i]; |
|
float tL = temp_bufferL[i]; |
|
temp_bufferL[i] = bL; |
|
bufferLout[i] += tL * gain; |
|
} |
|
} else { |
|
for (int i = 0; i < len; i++) { |
|
gain += gaindelta; |
|
float bL = bufferL[i]; |
|
float bR = bufferR[i]; |
|
float tL = temp_bufferL[i]; |
|
float tR = temp_bufferR[i]; |
|
temp_bufferL[i] = bL; |
|
temp_bufferR[i] = bR; |
|
bufferLout[i] += tL * gain; |
|
bufferRout[i] += tR * gain; |
|
} |
|
} |
|
} else { |
|
if (bufferR == null) { |
|
for (int i = 0; i < len; i++) { |
|
gain += gaindelta; |
|
float bL = bufferL[i]; |
|
float tL = temp_bufferL[i]; |
|
temp_bufferL[i] = bL; |
|
bufferLout[i] = tL * gain; |
|
} |
|
} else { |
|
for (int i = 0; i < len; i++) { |
|
gain += gaindelta; |
|
float bL = bufferL[i]; |
|
float bR = bufferR[i]; |
|
float tL = temp_bufferL[i]; |
|
float tR = temp_bufferR[i]; |
|
temp_bufferL[i] = bL; |
|
temp_bufferR[i] = bR; |
|
bufferLout[i] = tL * gain; |
|
bufferRout[i] = tR * gain; |
|
} |
|
} |
|
} |
|
gain = newgain; |
|
} |
|
public void processControlLogic() { |
|
} |
|
} |