zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit 1b004be9e189fe3b958e198703ce902a1318717c
parent 67c31f76ac368bfad1e1c76065a48582fe8da08a
Author: fundamental <mark.d.mccurry@gmail.com>
Date:   Thu, 19 Mar 2015 15:07:57 -0400

Doc: Add Basic Architectural Internals Description

Diffstat:
Adoc/architecture.txt | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 155 insertions(+), 0 deletions(-)

diff --git a/doc/architecture.txt b/doc/architecture.txt @@ -0,0 +1,155 @@ +ZynAddSubFX Architecture +======================== +:author: Mark McCurry + +In order to understand how to effectively navigate the source code and to +better understand the relationships between the internal components of +ZynAddSubFX. +To start off, the coarsest division of the codebase can be in breaking the +source into three areas: + +Backend:: + Realtime Data and Audio/Midi Handling +Middware:: + Non-Realtime algorithms used to initialize realtime data and communication + core +UI:: + Any User Interface (graphical or otherwise) that binds to a middleware + instance in order to permit modification of any parameters used in + synthesis. + +These three components communicate to each other _almost_ exclusively through +the use of OSC messages using librtosc or liblo. +In this document, I hope to define all of the edge cases where communication +(by design) does not use message passing as interactions are somewhat +complicated lock free operations. + +Before getting into each layer's details, the following threads may exist: + +Main Thread:: + The original program thread, responsible for repeatedly polling events + from NSM, LASH, general in-process UI, and middleware +Middleware Helper Thread:: + Responsible for handling any procedures which may block normal event + processing such as XML de-serialization +Audio Output Thread:: + Thread responsible for performing synthesis and passing it to driver level + API for the sound to be output +MIDI Input Thread:: + Thread responsible for handling midi events. This thread is only active if + the audio output and the midi input drivers are not the same type. + +Now for the meat of things: + +The Backend +----------- + +Historically the realtime side of things has revolved around a single instance +of the aptly named class 'Master', which is the host to numerous +implementation pointers and instances of all Parts which in turn contain more +parameters and notes. +This instance generally assumes that it is in full control of all of its data +and it gets regularly called from the IO subsystem to produce some output +audio in increments of some set block size. +All classes that operate under a given instance of 'Master' assume that they +have a fixed: + +Buffer Size:: + Unit of time to calculate at once and interval to perform interpolations + over +Oscillator Size:: + Size of the Additive Synthesis Oscillator Interpolation Buffer +Sample Rate:: + Number of samples per second +Allocator:: + Source for memory allocations from a resizable memory pool + +Changing any of these essentially requires rebuilding all child data +structures at the moment. + +Most of the children objects can be placed into the categories: + +Parameter Objects:: + Objects which contain serialize able parameters for synthesis and little to + no complex math +Synthesis Objects:: + Objects which initialize with parameter objects and generate output audio or + control values for other synthesis objects +Container Objects:: + Objects which are responsible for organizing a variety of synthesis and + parameter objects and for routing the outputs of each child object to the + right destination (e.g. 'Part' and 'Master') +Hybrid Objects:: + Objects which have _Odd_ divisions between what is a parameter, and what + is destined for synthesis (e.g. 'PADnoteParameters' and 'OscilGen') + +The normal behavior of these objects can be seen by observing a call of +_OutMgr::tick_ which first flushes the midi queue possibly constructing a few +new notes via _Part::NoteOn_, then _Master::AudioOut_ is called. +This is the root of the synthesis calls, but before anything is synthesized, +OSC messages are dispatched which typically update system parameter and +coefficients which cannot be calculated in realtime such as padnote based +wavetables. +Most data is allocated on the initialization of the add/sub/pad synthesis +engine, however anything which cannot be bounded easily then is allocated via +the tlsf based allocator. + + +The MiddleWare +-------------- + +Now in the previous section, details on how exactly messages were delivered +was only vaguely mentioned. +Anything unclear should hopefully be clarified here. +The primary message handling is taken care of by two ring buffers 'uToB' and +'bToU' which are, respectively, the user interface to backend and the backend +to user interface ringbuffers. +Additionally, Middleware handles non-realtime processing, such as 'Oscilgen' +and 'PADnoteParameters'. + +To handle these cases, any message from a user interface is intercepted. +Non-realtime requests are handled in middleware itself and other messages are +forwarded along. +This permits some internal messages to be sent that the UI has never directly +requested. +A similar process occurs for messages originating from the backend. + +A large portion of the middleware code is designed to manage up-to-date +pointers to the internal datastructures, in order to avoid directly accessing +anything via the pointer to the 'Master' datastructure. + +Loading +~~~~~~~ + +In order to avoid access to the backend datastructures typically a replacement +object is sent to the backend to be copied from or from which a pointer swap +can occur. + +Saving +~~~~~~ + +This is where the nice pristine hands off approach sadly comes to an end. +There simply isn't an effective means of capturing all parameters without +taking a large amount of time. +In order to permit the serialization of parameter objects, the backend is +partially 'frozen'. +This essentially prevents the backend from processing most messages from the +user interface and when this occurs the parameters which are too be serialized +can be guaranteed to be constant and thus safe to access across threads. +This class of read-only-operation can be seen as used in parameter copy/paste +operations and in saving full instances as well as instruments. + + +The User Interface +------------------ + +From an architectural standpoint the important thing to note about the user +interface is that virtual every widget has a location which is composed of a +base path and a path extension. +Most messages going to a widget are solely to this widget's one location +(occasionally they're to a few associated paths). + +This structure makes it possible for a set of widgets to get relocated +(rebase/reext) to another path. +This occurs quite frequently (e.g. "/part0/PVolume" -> "/part1/PVolume") and +it may be the occasional source of bugs.