package de.maramuse.soundcomp.shaping;
/*
* Copyright 2010 Jan Schmidt-Reinisch
*
* SoundComp - a sound processing library
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; in version 2.1
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* This is a distortion third order characteristics effect for nonlinear signal distortion
*
* previous to applying the characteristics, the signal has to be clipped to [-1; 1]
*
* theory for the characteristics:
*
* 2 branches: first for (x of ymax) <=1:
*
* y=ax-bx^3
*
* derived y' = a - 3bx^2
*
* Extrema at y'=0: a-3bx^2=0 => Xn=sqr(a/3b)
*
* Values of Extrema:
*
* y=a*sqr(a/3b) - b * a/3b * sqr(a/3b) = a*sqr(a/3b) - a/3 * sqr(a/3b) = 2/3 a sqr(a/3b) = 2/3 a Xn
*
* Therefore y(Extrema)=1 : y=1 => a = 3/(2Xn)
* for b: 1 = 3/2 - b Xn^3 => b Xn^3 = 1/2, b = 1/2 Xn^-3
*
* Function therefore y = (3/2 Xn^-1) x - (1/2 Xn^-3)x^3
*
* Minimum: Xn=1, y=1: a=3/2, b=1/2
* Maximum: y(1)=-1
*
* -1 = 3/2 Xn^-1 - 1/2 Xn^-3
* 1 = 1/2 Xn^-3 - 3/2 Xn^-1
* Xn = -3/2 + 1/2 Xn^-2
*
* Xn^3 - 1/2 + 3/2 Xn^2 = 0
*
* Xn (min) = 0.5, a(max)=3, b(max)=4
* Xn (max) = 1.0, a(min)=3/2, b(min)=1/2
*
* a=3/2 Xn^-1; b=(1/2 Xn^3) = 4/27 a^3
*
* Branch for x(Extrema)>1:
*
* Only condition is y(1)=1, so: 1 = a - b. Limits are a < 3/2, b < 1/2.
* Linear solution is a(min)=1, b(min)=0
*
* Final Parametrisation:
* Linear at a=1, b=0. Then increment a until a=1.5, let b grow by the same.
* Then until a=3, b=4/27*a^3.
*
* Since the characteristics parameter m should be 0<=m<=1,
*
* a = 2 * m +1
* b = ((m<0.25)?(a-1):(4/27*a^3));
*
* Input signal must be limited to [-1; 1]
* characteristics parameter must be clipped to [0; 1]
*
* @author jssr67
*
*/
import de.maramuse.soundcomp.process.NamedSource;
import de.maramuse.soundcomp.process.ParameterMap;
import de.maramuse.soundcomp.process.ProcessElement;
import de.maramuse.soundcomp.process.SourceStore;
import de.maramuse.soundcomp.process.StandardParameters;
import de.maramuse.soundcomp.process.Stateful;
import de.maramuse.soundcomp.process.TypeMismatchException;
import de.maramuse.soundcomp.process.UnknownConnectionException;
import de.maramuse.soundcomp.process.ValueType;
import de.maramuse.soundcomp.process.StandardParameters.Parameter;
import de.maramuse.soundcomp.util.ReadOnlyMap;
import de.maramuse.soundcomp.util.ReadOnlyMapImpl;
import de.maramuse.soundcomp.util.NativeObjects;
public class Third implements ProcessElement, Stateful {
public long nativeSpace=-1;
public Third() {
NativeObjects.registerNativeObject(this);
}
Third(boolean s) {
}
private static ReadOnlyMapImpl<Integer, ValueType> destMap=new ReadOnlyMapImpl<Integer, ValueType>(),
sourceMap=new ReadOnlyMapImpl<Integer, ValueType>();
static{
sourceMap.put(StandardParameters.OUT.i, ValueType.STREAM);
destMap.put(StandardParameters.IN.i, ValueType.STREAM);
destMap.put(StandardParameters.NONLINEARITY.i, ValueType.STREAM);
}
private final ReadOnlyMapImpl<Integer, SourceStore> sourceStoreMap=new ReadOnlyMapImpl<Integer, SourceStore>();
private static ParameterMap inputsMap=new ParameterMap();
private static ParameterMap outputsMap=new ParameterMap();
static{
inputsMap.put(StandardParameters.NONLINEARITY);
inputsMap.put(StandardParameters.IN);
outputsMap.put(StandardParameters.OUT);
}
SourceStore nonlin, in;
double nonlinval, inval, outval=0d, _outval=0d;
String instanceName, abstractName;
@Override
public ReadOnlyMap<Integer, ValueType> getDestinationTypes() {
return destMap;
}
@Override
public void setSource(int connectionIndex, NamedSource source,
int sourceIndex) throws UnknownConnectionException,
TypeMismatchException {
SourceStore ss=new SourceStore(source, sourceIndex);
sourceStoreMap.put(connectionIndex, ss);
if(!source.getSourceTypes().containsKey(sourceIndex))
throw new UnknownConnectionException("attempt to set invalid signal shaping source");
if(connectionIndex==StandardParameters.IN.i){
in=ss;
}else if(connectionIndex==StandardParameters.NONLINEARITY.i){
nonlin=ss;
}else
throw new UnknownConnectionException("attempt to provide unkown signal shaping parameter");
}
@Override
public ReadOnlyMap<Integer, ValueType> getSourceTypes() {
return sourceMap;
}
@Override
public double getValue(int index) {
return outval;
}
@Override
public void advanceState() {
nonlinval=Math.abs(nonlin.getValue());
if(nonlinval>1.0)
nonlinval=1.0;
inval=in.getValue();
if(inval<-1.0)
inval=-1.0;
else if(inval>1.0)
inval=1.0;
double a=2*nonlinval+1d;
double b=((nonlinval<0.25) ? (a-1) : (4d/27d*a*a*a));
_outval=a*inval-b*inval*inval*inval;
}
@Override
public void advanceOutput() {
outval=_outval;
}
@Override
public String getInstanceName() {
return instanceName;
}
@Override
public void setInstanceName(String instanceName) {
this.instanceName=instanceName;
}
@Override
public String getAbstractName() {
return abstractName;
}
@Override
public void setAbstractName(String abstractName) {
this.abstractName=abstractName;
}
@Override
public long getNativeSpace() {
return nativeSpace;
}
@Override
public ReadOnlyMap<Integer, SourceStore> getSourceMap() {
return sourceStoreMap;
}
/**
* @see de.maramuse.soundcomp.process.ProcessElement#clone()
*/
@Override
public Third clone() {
Third c=new Third();
c.abstractName=abstractName;
return c;
}
/*
* (non-Javadoc)
*
* @see de.maramuse.soundcomp.process.ProcessElement#outputsByName()
*/
@Override
public ReadOnlyMap<String, Parameter> outputsByName() {
return outputsMap;
}
/*
* (non-Javadoc)
*
* @see de.maramuse.soundcomp.process.ProcessElement#inputsByName()
*/
@Override
public ReadOnlyMap<String, Parameter> inputsByName() {
return inputsMap;
}
}