01-multi-rate-synthesis.py - Doing synthesis at very high sampling rate.
============================================================================================================================================


In numerical audio computing, it is sometimes useful to be able to process
a signal with much more timing precision than what the usual sampling rates
offer. A typical case is when the synthesis algorithm generates aliasing in 
the output signal. The solution is to increase the sampling rate, so the 
nyquist frequency, and to use anti-aliasing filters when converting from one 
rate to another.

Pyo allows to compute chunks of code at different sampling rates than the
one with which the server was started. You should do this only for the objects
you need to process with a higher sampling rate, without changing the server's
sampling rate, otherwise the program will be very CPU consuming.

You start a new resampling block with the method:

    Server.beginResamplingBlock(x)

where `x`, a power-of-two, is the resampling factor. A negative power-of-two
will start a downsampling block of code.

To close the block, simply call:

    Server.endResamplingBlock()

Everything between the two calls will be computed with the new sampling rate.

Audio signals must be resampled before used with a different sampling rate.
The Resample object does this. Inside a resampling block, it will convert the
signal to the new sampling rate, and outside the resampling block, it will
convert the signal back to the original sampling rate. Its `mode` argument lets
choose the quality of the interpolation/decimation filter used to resample the
signal.

.. code-block:: python

    from pyo import *
    
    s = Server().boot()
    
    # We create a new class for our upsampled Frequency modulation synthesis. We
    # use only the modulation index as parameter in order to simplify the code.
    class UpSampFM:
        """
        Frequency modulation synthesis that can be computed a higher sampling rate.
    
        :Args:
    
            fmindex: float or PyoObject, optional
                The modulation index of the FM synthesis.
            upfactor: int (power-of-two), optional
                Resampling factor.
            filtmode: int, optional
                The interpolation/decimation mode. See Resample's man page
                for details.
            
        """
    
        def __init__(self, fmindex=20, upfactor=8, filtmode=32):
    
            # Convert the modulation index argument to audio signal.
            self.fmindex = Sig(fmindex)
    
            # Get a reference to the audio server.
            server = self.fmindex.getServer()
    
            # Start an upsampled block of code.
            server.beginResamplingBlock(upfactor)
    
            # Resample the audio signals. Because the drive signal is only a
            # control signal, a linear interpolation is enough.
            self.fmindexUp = Resample(self.fmindex, mode=1)
    
            # Generate the FM synthesis.
            self.fmUp = FM(carrier=492, ratio=2, index=self.fmindexUp)
    
            # Close the upsampled block.
            server.endResamplingBlock()
    
            # Convert back the synthesized signal to the current sampling rate.
            # We use a good decimination filter to eliminate aliasing.
            self.output = Resample(self.fmUp, mode=filtmode)
    
        # Define some useful methods.
        def out(self):
            self.output.out()
            return self
    
        def sig(self):
            return self.output
    
    
    # Control for the modulation index parameter of the synthesis.
    index = Sig(20)
    index.ctrl([SLMap(5, 50, "lin", "value", 20)], title="Modulation Index")
    
    # FM synthesis at current sampling rate.
    fm1 = FM(carrier=492, ratio=2, index=index)
    
    # FM synthesis with increased sampling rate.
    fm2 = UpSampFM(index)
    
    # Interpolator to compare the two processes.
    output = Interp(fm1, fm2.sig(), 0, mul=0.5).out()
    output.ctrl([SLMap(0, 1, "lin", "interp", 0)], title="Up Sampling: without <=> with")
    
    sp = Spectrum(output)
    
    s.gui(locals())

