I have been working on the chorus effect.
By introducing a new operator into Sonic Field, it has been possible to produce a very stable chorus effect. One could consider chorus to be an FM effect. However, the numeric stability of FM is very poor for sampled sounds (or so I have found). What I have come up with instead is a time shift. Rather than altering the sample rate based on a signal, I shift the point of samples. Thus, minute errors do not accumulate as they do in FM.The pitch shift then becomes the first differential of the time shift. In chorusing I make the time shift a sine wave to the pitch shift if also a sine wave (or a cosine if you want to be pedantic).
Here is the new operator:
/* For Copyright and License see LICENSE.txt and COPYING.txt in the root directory */
package com.nerdscentral.audio.time;
import java.util.List;
import com.nerdscentral.audio.SFConstants;
import com.nerdscentral.audio.SFSignal;
import com.nerdscentral.sython.Caster;
import com.nerdscentral.sython.SFPL_Context;
import com.nerdscentral.sython.SFPL_Operator;
import com.nerdscentral.sython.SFPL_RuntimeException;
public class SF_TimeShift implements SFPL_Operator
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public Object Interpret(final Object input, final SFPL_Context context) throws SFPL_RuntimeException
{
List<Object> lin = Caster.makeBunch(input);
try (SFSignal in = Caster.makeSFSignal(lin.get(0)); SFSignal shift = Caster.makeSFSignal(lin.get(1)))
{
try (SFSignal y = in.replicateEmpty())
{
int length = y.getLength();
if (shift.getLength() < length) length = shift.getLength();
for (int index = 0; index < length; ++index)
{
double pos = index + SFConstants.SAMPLE_RATE_MS * shift.getSample(index);
y.setSample(index, in.getSampleCubic(pos));
}
length = y.getLength();
for (int index = shift.getLength(); index < length; ++length)
{
y.setSample(index, in.getSample(index));
}
return Caster.prep4Ret(y);
}
}
}
@Override
public String Word()
{
return Messages.getString("SF_TimeShift.0"); //$NON-NLS-1$
}
}
Here is a piece demonstrating the effect:
And here is a chorus patch which was used:
sf.SetSampleRate(96000)
if not 'random' in dir():
import random
random.seed(System.currentTimeMillis())
def bandRand(min,max):
min=float(min)
max=float(max)
r1=random.random()
r2=random.random()
r=float(r1+r2)*0.5
r=r*(max-min)
r=r+min
return r
def chorus(
left,
right,
minDepth = 10.0,
maxDepth = 50.0,
maxRate = 0.1,
minRate = 0.05,
nChorus = 4.0,
minVol = 0.7,
maxVol = 1.0):
def inner(signal_):
def inner_():
signal=sf.Clean(signal_)
sigs=[]
l=sf.Length(+signal)
for inst in range(0,nChorus):
def in_inner():
print "Do"
lfo=sf.PhasedSineWave(l,bandRand(minRate,maxRate),random.random())
lfo=sf.NumericVolume(lfo,bandRand(minDepth,maxDepth))
nsg=sf.TimeShift(+signal,lfo)
lfo=sf.PhasedSineWave(l,bandRand(minRate,maxRate),random.random())
lfo=sf.NumericVolume(lfo,bandRand(minVol,maxVol))
lfo=sf.DirectMix(1,lfo)
nsg=sf.Multiply(lfo,nsg)
print "Done"
return sf.Finalise(nsg)
sigs.append(sf_do(in_inner))
ret=sf.Finalise(sf.Mix(sigs))
-signal
return ret
return sf_do(inner_)
return inner(left),inner(right)
(left,right)=sf.ReadFile("temp/a.wav")
left,right=chorus(
left,
right,
minDepth = 0.0,
maxDepth = 10.0,
minVol = 1.0,
maxVol = 1.0,
nChorus = 9.0)
left,right=chorus(left,right)
sf.WriteFile32((left,right),"temp/c.wav")
No comments:
Post a Comment