~Proceedings ICMCISMCI2014 14-20 September 2014, Athens, Greece
ing tasks rather than (musical) sound synthesis. Mozzi 5
uses generic sample types for several of its unit generators, however, most are specialized for integer types. While
several libraries allow generic sample types, none of them
permit generic algorithms for customizing the unit generators. What is typically seen are suffixes added to unit
generator names to designate different behaviors, such as
different interpolation policies. Csound/SndObj and Supercollider/UGen++, for example, take this approach.
Synthesis libraries need to have a mechanism for keeping unit generators synchronized with a sampling domain.
Synchronization typically occurs according to either: (1)
a pull model whereby unit generators simply read a sample rate variable whenever control parameters are updated
or (2) a push model whereby unit generators are notified
of a change in sample rate. While the pull model is simpler to implement, the push model lends itself better to
optimizations involving pre-computing certain intermediate
variables, such as phase increment factors. In addition to
the push or pull approach, the sample rate is typically either
defined globally to be used by all unit generators or defined
locally within each unit generator. Defining the sample rate
locally permits unit generators to run at multiple sample
rates. Maximilian and Ugen++ unit generators read a global
sampling rate variable to stay synchronized. This has the advantage of simplicity, but does not allow unit generators to
run with multiple sample rates. CSL, NSound, and Marsyas
allow the sample rate to be specified locally for each unit
generator, thus allowing multiple sample rates. However,
the unit generator sample rates must be synchronized manually. In JamomaDSP, sig++, SndObj and STK, the unit
generator base classes have a virtual method permitting
specific tasks to be executed by unit generators when the
sampling rate changes. STK also allows unit generators to
ignore notifications of a change in the global sampling rate
so they can be used in a multi-rate context.
3. LIBRARY DESIGN
The purpose of this section is to introduce some of the
motivation and design decisions underlying Gamma. Since
the purpose of this paper is not to introduce the library in
detail, it is recommendation that interested readers peruse
the available documentation on the Gamma homepage 6.
3.1 Design Motivations
The overall goal of Gamma is to provide an easy-to-use
library for constructing complex, yet efficient synthesis
instruments and effects that can run on a wide variety of
platforms. This goal implies a design that
1. has a standard set of unit generators (oscillators,
noise, sample player, envelopes, filters, and variable
delays),
2. has a short-time Fourier transform (STFT),
3. performs single-sample processing,
5 http://sensorium.github.com/Mozzi/
6 http://www.mat.ucsb.edu/gamma
4. supports generic types, and
5. strives for low per-object memory and CPU consumption.
C++ was desired largely for its zero-overhead rule of "what
you don't use, you don't pay for" [15] and for its templates
which support generic programming. Generic typing is especially useful for signal processing as many processing
algorithms are, at their core, simply algebraic formulations.
Single-sample processing was preferred over block-based
processing as it makes the least assumptions about how unit
generators should be used and keeps control parameter and
processing updates separate. Low memory/CPU consumption has obvious performance benefits, but is also seen as
an important component of scalability. A well-made library
should run efficiently on as many platforms as possible,
especially those with limited resources.
At the moment, there are no other sound synthesis libraries
satisfying all of these design requirements. The Synthesis
Toolkit [10, 11] comes close, but lacks an STFT class and
does not support generic types.
3.2 Unit Generators
Unit generators in Gamma are divided into generators and
filters. Generators produce a sequence of samples and filters
transform an input sample into an output sample. The basic
generators and filters are listed and described in Fig. 1 and
Fig. 2, respectively.
Unit generators are implemented as function objects [16].
Function objects are essentially objects with an overloaded
function call operator that performs the object's main action.
The main action for unit generators is simply to process
the next sample. Generators overload the nullary function
call operator while filters overload the unary function call
operator. For example, the next output of a generator gen
is obtained by calling gen () and the next output of a filter
f it is obtained by calling f it (x) where x is the input.
4. PROCESSING ABSTRACTIONS
Gamma provides two primary abstractions that greatly extend the range of application of its provided unit generators.
The first of these is the use of generics for unit generator
sample and parameter types and processing algorithms. The
second abstraction is assignable sampling domains where
unit generators can operate under arbitrarily defined onedimensional sampling domains.
4.1 Generic Types
Generic types are used to increase the versatility of generators and filters without needing to change their underlying
algorithm. Gamma uses C++ templates to allow concrete
classes to be made according to generic types. The advan
tage of this approach over, for example, macros or typedefs,
is that the library can easily accommodate different sample types in application code without needing to resort to
multiple explicit compilations. This makes it easy to define
- 1383 -