~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 -
Top of page Top of page