maelzel.core.offline Module¶
Functions¶
|
Render to a soundfile / creates a context manager to render offline |
Classes¶
|
An OfflineRenderer is created to render musical objects to a soundfile |
Class Inheritance Diagram¶
digraph inheritanceb0f1f33e55 { bgcolor=transparent; rankdir=LR; size="8.0, 12.0"; "ABC" [fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",tooltip="Helper class that provides a standard way to create an ABC using"]; "OfflineRenderer" [URL="#maelzel.core.offline.OfflineRenderer",fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",target="_top",tooltip="An OfflineRenderer is created to render musical objects to a soundfile"]; "Renderer" -> "OfflineRenderer" [arrowsize=0.5,style="setlinewidth(0.5)"]; "Renderer" [URL="api/maelzel.core.renderer.Renderer.html#maelzel.core.renderer.Renderer",fillcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=10,height=0.25,shape=box,style="setlinewidth(0.5),filled",target="_top",tooltip="Abstract class for realtime and offline rendering"]; "ABC" -> "Renderer" [arrowsize=0.5,style="setlinewidth(0.5)"]; }- maelzel.core.offline.render(outfile='', events=None, sr=None, wait=None, ksmps=None, verbose=None, nchnls=None, workspace=None, tail=None, run=True, endtime=0.0, show=False, tempfolder='', fmt='wav', **kws)[source]¶
Render to a soundfile / creates a context manager to render offline
When not used as a context manager the events / objects must be given. The soundfile will be generated immediately.
When used as a context manager the
events
argument should be left unset. Within this context any call tomaelzel.core.MObj.play()
will be redirected to the offline renderer and at the exit of the context all events will be rendered to a soundfile. Also, any pure csound events scheduled viaplaySession()._sched(...)
will also be redirected to be renderer offline.This enables to use the exact same code when doing realtime and offline rendering.
- Parameters:
outfile – the generated file. If None, a file inside the recording path is created (see
recordPath
). Use “?” to save via a GUI dialog orevents (
Optional
[Sequence
[Union
[SynthEvent
,MObj
,Event
,Sequence
[MObj
|SynthEvent
]]]]) – the events/objects to play. This can only be left unset if usingrender
as a context manager (see example).sr (
Optional
[int
]) – sample rate of the soundfile (config ‘rec.sr’)ksmps (
Optional
[int
]) – number of samples per cycle (config ‘rec.ksmps’)nchnls (
Optional
[int
]) – number of channels of the rendered soundfilewait (
Optional
[bool
]) – if True, wait until recording is finished. If None, use the config ‘rec.blocking’verbose (
Optional
[bool
]) – if True, show the output generated by the csound subprocesstail (
Optional
[float
]) – extra time added at the end of the render, usefull when rendering reverbs or long decaying sound. If None, uses use config ‘rec.extratime’run – if True, perform the render itself
tail – extra time at the end, usefull when rendering reverbs or long deaying sounds
endtime – if given, sets the end time of the rendered segment. A value of 0. indicates to render everything. A value is needed if there are endless events
workspace (
Optional
[Workspace
]) – if given, this workspace overrides the active workspace
- Return type:
- Returns:
the
OfflineRenderer
used to render the events. If the outfile was not given, the path of the recording can be retrieved fromrenderer.outfile
Example
>>> a = Chord("A4 C5", start=1, dur=2) >>> b = Note("G#4", dur=4) >>> render("out.wav", events=[ ... a.events(chain=1), ... b.events(chan=2, gain=0.2) ... ])
This function can be also used as a context manager, similar to
maelzel.playback.play()
. In that caseevents
must beNone
:>>> from maelzel.core import * >>> scale = Chain([Note(n) for n in "4C 4D 4E 4F 4G".split()]) >>> playSession().defInstr('reverb', r''' ... |kfeedback=0.6| ... amon1, amon2 monitor ... a1, a2 reverbsc amon1, amon2, kfeedback, 12000, sr, 0.6 ... outch 1, a1-amon1, 2, a2-amon2 ... ''') >>> with render() as r: ... scale.play('.piano') # .play here is redirected to the offline renderer ... r.sched('reverb', priority=2)
See also
OfflineRenderer
,maelzel.playback.play()
- class maelzel.core.offline.OfflineRenderer(outfile='', sr=None, ksmps=None, numchannels=None, tail=0.0, verbose=None, endtime=0.0, session=None)[source]¶
An OfflineRenderer is created to render musical objects to a soundfile
OfflineRenderer as context manager
The simplest way to render offline is to use an OfflineRenderer as a context manager (see also
render()
). Within this context any .play call will be collected and everything will be rendered when exiting the context (see example below)- Parameters:
outfile (
str
) – the path to the rendered soundfile. If not given, a path within the record path [1] is returnedsr (
Optional
[int
]) – the sr of the render (config key: ‘rec.sr’)ksmps (
Optional
[int
]) – the ksmps used for the recordingnumchannels (
Optional
[int
]) – number of channels of this renderer. If not set, this will depend on the scheduled events and the final call to .renderverbose (
Optional
[bool
]) – if True, debugging output is show. If None, defaults to config (key: ‘rec.verbose’)endtime – used when the OfflineRenderer is called as a context manager
If rendering offline in tandem with audio samples and other csoundengine’s functionality, it is possible to access the underlying csoundengine’s Renderer via the
.renderer
attribute(see
recordPath()
)Example
Render a chromatic scale in sync with a soundfile
>>> from maelzel.core import * >>> import sndfileio >>> notes = [Note(n, dur=0.5) for n in range(48, 72)] >>> chain = Chain(notes) >>> defPresetSoundfont('piano', sf2path='/path/to/piano.sf2') >>> samples, sr = sndfileio.sndread('/path/to/soundfile') >>> with render('scale.wav') as r: ... chain.play(instr='piano') ... # This allows access to the underlying csound renderer ... r.renderer.playSample((samples, sr))
When exiting the context manager the file ‘scale.wav’ is rendered. During the context manager, all calls to .play are intersected and scheduled via the OfflineRenderer. This makes it easy to switch between realtime and offline rendering by simply changing from
play
torender()
- a4¶
A value for the reference frequency
- assignBus(kind='', value=None, persist=False)[source]¶
Assign a bus of the given kind
- Return type:
int
- Returns:
the bus token. Can be used with any bus opcode (busin, busout, busmix, etc)
- csoundRenderer: csoundengine.offline.Renderer¶
The actual csoundengine.Renderer
- definedInstrs()[source]¶
Get all instruments available within this OfflineRenderer
All presets and all extra intruments registered at the active Session (as returned via getPlaySession) are available
- Returns:
csoundengine.Instr} with all instruments available
- Return type:
dict {instrname
- endtime¶
Default endtime
- getInstr(instrname)[source]¶
Get the csoundengine’s Instr corresponding to instrname
- Parameters:
instrname (
str
) – the name of the csoundengine’s Instr- Return type:
Instr
|None
- Returns:
If found, the csoundengine’s Instr
- getSession()[source]¶
Return the Session associated with this OfflineRenderer, if any
- Return type:
Session
|None
- includeFile(path)[source]¶
Add an include clause to this renderer.
OfflineRenderer keeps track of includes so trying to include the same file multiple times will generate only one #include clause
- Parameters:
path (
str
) – the path of the file to include- Return type:
None
- instrs: dict[str, csoundengine.instr.Instr]¶
An index of registered Instrs, mapping name to the Instr instance
- isRendering()[source]¶
True if still rendering
- Return type:
bool
- Returns:
True if rendering is still in course
- ksmps¶
ksmps value (samples per block)
- lastOutfile()[source]¶
Last rendered outfile, None if no soundfiles were rendered
- Return type:
str
|None
Example
>>> r = OfflineRenderer(...) >>> r._sched(...) >>> r.render(wait=True) >>> r.lastOutfile() '~/.local/share/maelzel/recordings/tmpsasjdas.wav'
- lastRenderProc()[source]¶
Last process (subprocess.Popen) used for rendering
- Return type:
Popen
|None
Example
>>> r = OfflineRenderer(...) >>> r._sched(...) >>> r.render("outfile.wav", wait=False) >>> if (proc := r.lastRenderProc()) is not None: ... proc.wait() ... print(proc.stdout.read())
- makeTable(data=None, size=0, sr=0, tabnum=0)[source]¶
Create a table in this renderer
- Parameters:
data (
Union
[ndarray
,list
[float
],None
]) – if given, the table will be created with the given datasize (
int
) – if data is not given, an empty table of the given size is created. Otherwise, this parameter is ignoredsr (
int
) – the sample rate of the data, if applicabletabnum (
int
) – leave it as 0 to let the renderer assign a table number
- Return type:
int
- Returns:
the assigned table number
- openLastOutfile(timeout=None)[source]¶
Open last rendered soundfile in an external app
Will do nothing if there is no outfile. If the render is in progress this operation will block.
- Parameters:
timeout – if the render is not finished this operation will block with the given timeout
- Return type:
str
- Returns:
the path of the soundfile or an empty string if no soundfile was rendered
- play(obj, **kws)[source]¶
Schedule the events generated by this obj to be renderer offline
- Parameters:
obj (
MObj
) – the object to be played offlinekws – any keyword passed to the .events method of the obj
- Return type:
SchedEventGroup
- Returns:
the offline score events
- playSample(source, delay=0.0, dur=0, chan=1, gain=1.0, speed=1.0, loop=False, pos=0.5, skip=0.0, fade=None, crossfade=0.02)[source]¶
Play a sample through this renderer
- Parameters:
source (int | str | TableProxy | tuple[np.ndarray, int] | audiosample.Sample) – a soundfile, a TableProxy, a tuple (samples, sr) or a maelzel.snd.audiosample.Sample
delay – when to play
dur – the duration. -1 to play until the end (will detect the end of the sample)
chan – the channel to output to
gain – a gain applied
speed – playback speed
loop – should the sample be looped?
pos – the panning position
skip – time to skip from the audio sample
fade (float | tuple[float, float] | None) – a fade applied to the playback
crossfade – a crossfade time when looping
- Return type:
csoundengine.offline.SchedEvent
- Returns:
a csoundengine.offline.SchedEvent
- prepareInstr(instr, priority)[source]¶
Reify an instance of instr at the given priority
This method also prepares any resources and initialization that the given Instr might have
- Parameters:
instr (
Instr
) – a csoundengine’s Instrpriority (
int
) – the priority to instantiate this instr with. Priorities start with 1
- Return type:
bool
- Returns:
False
- prepareSessionEvent(sessionevent)[source]¶
Prepare a session event
- Parameters:
sessionevent (
Event
) – the session event to prepare. This is mostly used internally- Return type:
None
- readSoundfile(soundfile, chan=0, skiptime=0.0)[source]¶
Read a soundfile into the renderer
- Parameters:
soundfile (
str
) – the soundfile to readchan – the channel to read, 0 to read all channels
skiptime – start reading from this time
- Return type:
int
- registerInstr(name, instrdef)[source]¶
Register a csoundengine.Instr to be used with this OfflineRenderer
Note
All
csoundengine.Instr
defined in the play Session are available to be rendered offline without the need to be registered- Parameters:
name (
str
) – the name of this presetinstrdef (
Instr
) – the csoundengine.Instr instance
- Return type:
None
- registerPreset(presetdef)[source]¶
Register the given PresetDef with this renderer
- Parameters:
presetdef (
PresetDef
) – the preset to register. Any global/init code declared by the preset will be made available to this renderer- Return type:
bool
- Returns:
to adjust to the Renderer parent class we always return False since offline rendering does not need to sync
- registeredPresets: dict[str, presetdef.PresetDef]¶
Maps preset name to preset definition
- releaseBus(busnum)[source]¶
Signal that we no longer use the given bus
- Parameters:
busnum (
int
) – the bus token as returned byOfflineRenderer.assignBus()
- Return type:
None
- render(outfile='', wait=None, verbose=None, openWhenDone=False, compressionBitrate=None, endtime=None, ksmps=None, tail=None)[source]¶
Render the events scheduled until now.
You can access the rendering subprocess (a
subprocess.Popen
object) vialastRenderProc()
- Parameters:
outfile – the soundfile to generate. Use “?” to save via a GUI dialog, None will render to a temporary file
wait (
Optional
[bool
]) – if True, wait until rendering is doneverbose (
Optional
[bool
]) – if True, show output generated by csound itself (print statements and similar opcodes still produce output)endtime (
Optional
[float
]) – if given, crop rendering to this absolute time (in seconds)compressionBitrate (
Optional
[int
]) – the compression bit rate when rendering to .ogg (in kb/s, the default can be configured inconfig['.rec.compressionBitrate']
openWhenDone – if True, open the rendered soundfile in the default application
ksmps (
Optional
[int
]) – the samples per cycle used when renderingtail (
Optional
[float
]) – an extra time at the end of the render to make room for long decaying sounds / reverbs. If given, overrides the tail parameter given at init.
- Return type:
str
- Returns:
the path of the rendered file
Example
>>> from maelzel.core import * >>> scale = Chain([Note(n) for n in "4C 4D 4E 4F 4G".split()]) >>> playback.playSession().defInstr('reverb', r''' ... |kfeedback=0.6| ... amon1, amon2 monitor ... a1, a2 reverbsc amon1, amon2, kfeedback, 12000, sr, 0.6 ... outch 1, a1-amon1, 2, a2-amon2 ... ''') >>> presetManager.defPresetSoundfont('piano', '/path/to/piano.sf2') >>> renderer = playback.OfflineRenderer() >>> renderer.schedEvents(scale.events(instr='piano')) >>> renderer._sched('reverb', priority=2) >>> renderer.render('outfile.wav')
- renderedSample()[source]¶
Returns the last rendered soundfile as a
maelzel.snd.audiosample.Sample
- Return type:
- renderedSoundfiles: list[str]¶
A list of soundfiles rendered with this renderer
- sched(instrname, delay=0.0, dur=-1.0, priority=1, args=None, whenfinished=None, relative=True, **kws)[source]¶
Schedule a csound event
This method can be used to schedule non-preset based instruments when rendering offline (things like global effects, for example), similarly to how a user might schedule a non-preset based instrument in real-time.
If an OfflineRenderer is used as a context manager it is also possible to call the session’s ._sched method directly since its _sched callback is rerouted to call this OfflineRenderer instead
- Parameters:
instrname (
str
) – the instr. namedelay – start time
dur – duration
priority – priority of the event
args (
Union
[list
[float
],dict
[str
,float
],None
]) – any pfields passed to the instr., starting at p5whenfinished – this argument does nothing under this context. It is only present to make the signature compatible with the interface
relative – dummy argument, here to conform to the signature of csoundengine’s Session.sched, which is redirected to this method when an OfflineRenderer is used as a context manager
**kws – named pfields
- Return type:
SchedEvent
- Returns:
the offline.ScoreEvent, which can be used as a reference by other offline events
Example
Schedule a reverb at a higher priority to affect all notes played. Notice that the reverb instrument is declared at the play Session (see
play.getPlaySession()
). All instruments registered at this Session are immediately available for offline rendering.>>> from maelzel.core import * >>> scale = Chain([Note(n) for n in "4C 4D 4E 4F 4G".split()]) >>> playback.playSession().defInstr('reverb', r''' ... |kfeedback=0.6| ... amon1, amon2 monitor ... a1, a2 reverbsc amon1, amon2, kfeedback, 12000, sr, 0.6 ... outch 1, a1-amon1, 2, a2-amon2 ... ''') >>> presetManager.defPresetSoundfont('piano', '/path/to/piano.sf2') >>> with playback.OfflineRenderer() as r: ... r._sched('reverb', priority=2) ... scale.play('piano')
- schedEvent(event)[source]¶
Schedule a SynthEvent or a csound event
- Parameters:
event (
SynthEvent
|Event
) – aSynthEvent
- Return type:
SchedEvent
- Returns:
a SchedEvent
- schedEvents(coreevents, sessionevents=None, whenfinished=None)[source]¶
Schedule multiple events as returned by
MObj.events()
- Parameters:
coreevents (
list
[SynthEvent
]) – the events to schedulesessionevents (
Optional
[list
[Event
]]) – csound events as packed within a csoundengine.session.SessionEventwhenfinished (
Optional
[Callable
]) – dummy arg, here to conform to the signature of the parent. Only makes sense in realtime
- Return type:
SchedEventGroup
- Returns:
a
csoundengine.offline.SchedEventGroup
. This can be used to modify scheduled events viaset()
,automate()
orstop
Example
>>> from maelzel.core import * >>> scale = Chain([Note(m, 0.5) for m in range(60, 72)]) >>> renderer = OfflineRenderer() >>> renderer.schedEvents(scale.events(instr='piano')) >>> renderer.render('outfile.wav')
- property scheduledEvents: dict[int, SchedEvent]¶
The scheduled events
- showAtExit¶
Display the results at exit if running in jupyter
- sr¶
The sr. If not given, [‘rec.sr’] is used
- tail¶
Extra time at the end of rendering to make space for reverbs or long-decaying sounds
- timeRange()[source]¶
The time range of the scheduled events
- Return type:
tuple
[float
,float
]- Returns:
a tuple (start, end)