A compact self-contained realtime audio-synthesis engine

Lyd is an embeddable signal processing language and engine. Suitable for among other things realtime audio effect and instrument synthesis and mixing. It can form the audio core of games, virtual instruments, real-time audio mixing and editing experiments.

What lyd does

Lyd for the curious

Lyd is a language for describing the signal processing involved in generating and recombining audio.

A sound that is played back as 12 notes in quick succession with different pan and pitch. The instrument played is this:

reverb (0.1, 0.223,
         low_pass (0.1, hz=440.0, 0.01,
           pulse(200 + sin(190) * 1.4, square(8.0)) * adsr(0.12, 0.12, 0.8, 0.30)
         )
        ) * volume=1.0 * 0.4

Sources

Lyd for musicans/composers

Lyd is currently mostly an application programmers toy, out of the box at the moment lyd contains a mediocre autogenerated approximation of an OPL2 FM synthesizer in its patch set. When you launch lyd you can use it as a synthesizer with an attached MIDI keyboard or with MIDI sequencers supporting ALSA midi. Lyd should ble able to enable processing plug-ins for various audio synthesis standards exposing the lyd language and efficiency in interaction to allow live tweaking of the lyd code when composing.

Pass an instrument definition to the -i argument to reprogram MIDI program 0. This allows testing one-off synthesizers easy from the commandline.

lyd -i 'square(hz + square(40)) * adsr(0.2,0.2,0.8,0.4)'

Add -w 0.5 -d 0.3 to add a random scale walker that plays a new random note nearby on the scale every 0.5seconds that lasts for 0.3seconds, this provides a way to get an idea how a patch sounds.

The OSC support needs rethinking, and probably re-integration with the midi support. Allowing repatching instruments used with MIDI on the fly from OSC.

Lyd for programmers

You can use lyd as an audio canvas, or general signal processing engine. Lyd is writtene to make sure it is easy to incorporate in other projects, either by linking to a shared lyd installation or for static inclusion by copying the source of the core (See ).

Lyd allows instantiated programmed voices, LydVoice that render sound in real time. The voices are defined in the lyd language using constants, variables, arithmetic, noise, oscillators, envelopes and PCM data sources. Voices can instantiated right now or queued with sample precision. Voices can be killed to immediately stop playback or released allowing ADSRs to control the amplitude of your voice and provide smooth volume scaling, multiply by adsr(0.0,0.0,1.0,0.0); for immediate full volume and immediate cut-off. When a voice is released all ADSRs will enter their release phase and decay to 0.0; silence. Silent voices are detected and automatically killed.

Variables in the programs (all strings that are not numbers or recognized as keywords in the language reference) can be manipulated in realtime or programmed with time/value key frames with per-key interpolation. Realtime control is not sample accurate, but accurate to the number of samples requested from lyd in a set of samplings Controlling variables allows specifying or modifying in real-time the hz, filters, volume, reverb or similar, of multiple oscillators and filters, providing a natural and automatic bridge between lyd and C or any scripting language potentially wrapping the lyd API.

There is a MIDI playback API, where timing is broken, (for testing midi playback for testing/development, the daemon and its ALSA midi support is currently used. If this feature is used in a game a possible workaround is to statically include lyd (lyd is small so it would perhaps be a good idea anyways) and tweak the tempo logic in the midi decoder source.

Directly processing a lyd voice allows running the virtual machine on user provided input/output buffers, allowing to easily wrap the core vm with minimal inderection.

See the examples for illustrations of the above concepts. examples in the source tree.. .

performance

Running under Fedora 13, with Intel(R) Core(TM)2 Duo CPU L9400 @ 1.86GHz, lyd is able to render the following number of concurrent voices for the following programs. Respectively with ALSA and with ALSA through pulse-audio, both of them using the only period size ALSA seems to accept with my soundcard.

alsapulseaudiocode
194 198 sin(hz) * adsr(0.5, 0.10, 0.4, 0.30) + sin(hz=440*1.3) * adsr(0.5, 0.10, 0.4, 0.30) + sin(hz) * adsr(0.5, 0.10, 0.4, 0.30) + sin(hz*3.4) * adsr(0.5, 0.10, 0.4, 0.30) + sin(hz*8) * adsr(0.5, 0.10, 0.4, 0.30) + sin(hz*7) * adsr(0.5, 0.10, 0.4, 0.30) + sin(hz*4) * adsr(0.5, 0.10, 0.4, 0.30) + sin(hz*5) * adsr(0.5, 0.10, 0.4, 0.30) + noise() * ddadsr(0.2,0.4,0.4,0.1,0.4)
284 261 (saw(sin(hz=440.0 + reverb(1.0,0.2,sin(20 + 2 * (noise() - 0.5)))))+ square(hz*3) / triangle(hz*2)) * adsr(0.5, 0.10, 0.4, 0.30)
432 389 (sin(hz=440.0 + sin(20))+ square(hz*3) / triangle(hz*2)) * adsr(0.5, 0.10, 0.4, 0.30)
753 672 low_pass (1.0, 600.0, 1.0, reverb (0.8, 0.2, square(hz=440) * adsr(0.5, 0.10, 0.4, 0.30)))
959 819 reverb (0.8, 0.2, square(hz=440) * adsr(0.5, 0.10, 0.4, 0.30))
971 902 low_pass (1.0, 600.0, 1.0, square(hz=440) * adsr(0.5, 0.10, 0.4, 0.30))
1286 1186 noise() * adsr(0.5, 0.10, 0.4, 0.30)
1423 1250 sin(hz=440) * adsr(0.5, 0.10, 0.4, 0.30)
1622 1555 wave_loop('/tmp/foo.wav', hz=440) * adsr(0.5, 0.10, 0.4, 0.30)

Lyd for computer scientists

The following is a technical summary of what lyd actually is, it is not neccesary to understand any of it to make use of lyd's language and API. But is useful for people interested in how lyd technically works as well as people pondering to help extend lyds capabilities.

Lyd uses a Top Down Operator Precedence parser to generate an AST for the lyd expression. This parser form part of the lyd-compiler.c, which generates op-codes for the lyd vm. The compiler builds a LydProgram which is a struct defining op-codes and the values of their dependencies.

The op-codes in compiled LydPrograms are in processing order and arguments to the opcodes are either be positive literals or negative integer back-pointers to the outputs of preceding opcodes.

When a compiled program is instantiated to an active LydVoice it becomes an array of LydOpState entires, a struct containing the opcode and a processing context with local argument and output storage.

LydOpstate contains small chunk buffers. The chunk buffers are 128 samples long and 16-byte aligned, the op has one such buffer for the output and for arguments (allowing arguments to be interpolated over time with sample accuracy). The chunk buffers makes it possible to use gcc autovectorization using -ftree-vectorize for the code execute for the op-codes.

The execution of the vm is walking a linked list of processing states over a dispatch switch.

    for(state=vm->state;state->op != LYD_NONE; state = state->next)
      switch (state->op)
        {
          /* handling op codes and dispatch relevant code */
        }
    

The op-codes of lyds virtual machine, and the functions and operators of the lyd language are all defined in lyd-ops.inc Op-codes added following the pattern in this file are expanded as different macros when included in various parts of the source, providing both en enum, cases for the dispatch switch, integration with the compiler and the and the language reference.

Each render thread operates has it's own queue of voices to compute and an associated mutex and condition to manage execution flow without any busy waiting.

Roadmap/plans/todo/bugs (contributions welcome)

License

Lyd is licensed under the ISC license:

Copyright (c) 2010-2011 Øyvind Kolås <pippin@gimp.org>

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.