Sunday 8 February 2015

Spatial Chorus

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