Offline Rendering

Offline Rendering#

Offline rendering follows real-time processing very closely. The OfflineEngine class represents a csound process running in offline mode and is an almost drop-in replacement to the Engine class.

High-level rendering is implemented via the OfflineSession class, which has the same interface as a Session

Example low-level API#

from csoundengine import OfflineEngine
import random

engine = OfflineEngine()

engine.compile(r'''
instr vco
    pset 0, 0, 0, 263, 0.1, 1, 1000, 0.1, 0.5
    ifreq = p4
    iamp = p5
    ktransp = p6
    kcutoff = p7
    ifade = p8
    ifilterq = p9
    kfreq = ifreq * ktransp
    aout = vco2:a(iamp, kfreq)
    iresonance = 0.6
    idistortion = 0.
    aout = lpf18(aout, kcutoff, iresonance, idistortion)
    aout *= linsegr:a(0, ifade, 1, ifade, 0)
    outch 1, aout
endin
''')

f0 = 130
for factor in [1, 2.76, 5.3, 5.4, 8.93]:

    freq = f0 * factor
    # with unique=True a fractional p1 is generated, this makes it
    # possible to automate the event later
    # NB: csoundengine parses instr code and is aware of names given to pfields
    event = engine.sched('vco', engine.now, 8, unique=True,
                        ifreq=freq, iamp=0.1, kcutoff=freq*3, ifade=freq/f0* 0.5, ifilterq=0.8)
    transp = random.uniform(0.92, 1.08)
    # start automation of p6 at time=2. This case, modify the transposition factor,
    # from 1 to a random value between 0.92 and 1.08
    engine.automatep(event, 6, (0, 1, 5, transp), delay=2)
    # The pfield can also be given by name. With overtake=True the value given
    # to the pfield at init is used as the starting point
    engine.automatep(event, 'kcutoff', (0, 0, 3, freq*1.1), overtake=True, delay=2 + factor*0.3)

# Advance time to actually perform the events. We give some extra time
# to account for fade outs.
engine.perform(extratime=5)
engine.stop()
_images/offlineengine-example1.png

Example (high-level API)#

from csoundengine import *
from pitchtools import *

session = OfflineSession(sr=44100, nchnls=2)

session.defInstr('saw', r'''
  kmidi = p5
  outch 1, oscili:a(0.1, mtof:k(kfreq))
''')

events = [
    session.sched('saw', 0, 2, kmidi=ntom('C4')),
    session.sched('saw', 1.5, 4, kmidi=ntom('4G')),
    session.sched('saw', 1.5, 4, kmidi=ntom('4G+10'))
]

# offline events can be automated just like real-time events
events[0].automate('kmidi', (0, 0, 2, ntom('B3')), overtake=True)

events[1].set(delay=3, kmidi=67.2)
events[2].set(kmidi=80, delay=4)
session.render("out.wav")

It is possible to create an OfflineSession out of an existing Session, by calling session.makeRenderer. This creates an OfflineSession with all Instr and resources (tables, include files, global code, etc.) in the Session already defined.

from csoundengine import *
session = Session()
session.defInstr('test', ...)
table = session.readSoundfile('path/to/soundfile')
session.sched('test', ...)
session.playSample(table)

# Render offline
renderer = session.makeRenderer()
renderer.sched('test', ...)
renderer.playSample('path/to/soundfile')
renderer.render('out.wav')

An alternative way to render offline given a live Session is to use the rendering() method:

from csoundengine import *
session = Session()
session.defInstr('test', ...)
table = session.readSoundfile('path/to/soundfile')

with session.rendering('out.wav') as r:
    r.sched('test', ...)
    r.playSample(table)