Lunar DSP Scripting
Lunar is the dsp scripting engine embedded within libzzub. At the time of writing, lunar supports LLVM bytecode exclusively, and hence supports any LLVM frontend - including C, C++, and, somewhere in the future, most likely Java and Python.
Lunar modules and source code is always being saved with ccm songs, so they play on most systems without any additional installation of plugins. Through LLVM, X86, X86-64, PowerPC, PowerPC-64, SPARC, Alpha, and IA-64 can be supported, however at the time of writing X86 is the only target.
A Simple Example
To make libzzub recognize your scripts, you need to provide at least two files: a manifest file describing your plugin and a script defining the way your plugin works. Both files need to be put into the subdirectory meant to contain Lunar scripts. For Aldrin, this is e.g. /usr/lib/lunar/fx or ~/.aldrin/lunar. Create a subdirectory with a name losely associated with your plugin (e.g. "simplesynth") and put your files into that directory.
manifest.xml
This is the file that describes the metadata of your plugin: name, author, parameters, required files and used scripts. Below is an example file describing a gain plugin.
<?xml version="1.0" encoding="utf-8"?>
<zzub>
<plugin
uri="@trac.zeitherrschaft.org/aldrin/lunar/effect/gain;1"
name="Lunar Gain"
shortname="Gain"
type="effect"
description="A simple gain effect demonstrating dsp scripting with Lunar."
author="Leonard Ritter"
email="contact at leonard-ritter+com">
<parameters>
<global>
<parameter
id="gain"
name="Gain (%)"
description="Gain of output signal"
type="float"
minvalue="0"
maxvalue="100"
defvalue="100"
precision="1"
state="true"/>
<parameter
id="mgain"
name="Master Gain (dB)"
description="Final gain of output signal"
type="float"
minvalue="-48"
maxvalue="12"
defvalue="0"
precision="0.01"
state="true"/>
<parameter
id="inertia"
name="Inertia (ms)"
description="Reaction time of amplitude"
type="float"
minvalue="0"
maxvalue="250"
defvalue="100"
precision="0.1"
state="true"/>
</global>
<track>
</track>
</parameters>
<attributes>
<attribute
id="gainscale"
name="Gain Scale"
minvalue="1"
maxvalue="16"
defvalue="1"/>
</attributes>
<files>
<file ref="gain.cpp"/>
<file ref="gain.h"/>
<file ref="gain.bc"/>
</files>
<modules>
<module language="llvm" ref="gain.bc">
<source language="c++" ref="gain.cpp" fxdef="gain.h"/>
</module>
</modules>
</plugin>
</zzub>
gain.cpp
The script defines the actual dsp code at work. It usually consists of two functions, process_events, which is called once per tick, and process_stereo, which is called everytime some audio data needs to be rendered.
The script is actual plain C++ code, which is required to be translated into LLVM bytecode before Lunar can load it. Lunar then compiles the code into machine code on the fly for best performance.
Below is the sample script for the gain plugin defined above.
#include <lunar/fx.h> #include "gain.h" extern "C" { float amp, mamp, famp, iamp, step; void init() { amp = 1.0; mamp = 1.0; famp = 0.0; iamp = 0.0; step = 1000.0f/(5.0f * samples_per_second); } void exit() { } void process_events() { if (globals.gain) { amp = *globals.gain / 100.0f; } if (globals.mgain) { mamp = dbtoamp(*globals.mgain, -48.0f); } famp = amp * mamp; if (globals.inertia) { if (!*globals.inertia) step = 1.0f; else step = 1000.0f/(*globals.inertia * samples_per_second); } } void process_stereo(float **i2, float **o2, int n) { float sn, oldiamp; dsp_set(o2[0],n,famp); dsp_set(o2[1],n,famp); sn = min(abs((famp - iamp)/step),n); if (sn > 1) { oldiamp = iamp; if (famp > iamp) { iamp = dsp_slope(o2[0],sn,oldiamp,step); iamp = dsp_slope(o2[1],sn,oldiamp,step); } else { iamp = dsp_slope(o2[0],sn,oldiamp,-step); iamp = dsp_slope(o2[1],sn,oldiamp,-step); } } dsp_mul(i2[0], o2[0], n); dsp_mul(i2[1], o2[1], n); dsp_clip(o2[0], n, 1); // signal may never exceed -1..1 dsp_clip(o2[1], n, 1); } } // extern "C"
and this is the gain header file:
#if defined(__cplusplus) extern "C" { #endif extern struct gparams { float *gain; float *mgain; float *inertia; } globals; #if defined(__cplusplus) } #endif
Module Reference
Lunar exposes various tool functions to code, most of them designed to process or mutilate soundbuffers. Here is the fx.h header file declaring symbols which Lunar exposes during runtime:
#if !defined(LUNAR_FX_H) #define LUNAR_FX_H #if defined(__cplusplus) extern "C" { #endif #define M_PI 3.14159265358979323846 float dsp_slope(float *b, int ns, float start, float step); void dsp_amp(float *b, int numsamples, float s); void dsp_copy(float *i, float *o, int numsamples); void dsp_add(float *i, float *o, int numsamples); void dsp_mul(float *i, float *o, int numsamples); void dsp_powmap(float *b, int numsamples, float c, float base, float offset, float factor); void dsp_copyamp(float *i, float *o, int numsamples, float s); void dsp_addamp(float *i, float *o, int numsamples, float s); void dsp_set(float *b, int numsamples, float s); void dsp_clip(float *b, int numsamples, float s); void dsp_zero(float *b, int numsamples); void dsp_fixdn(float *b, int numsamples); float dbtoamp(float db, float limit); float fixdn(float); // src typedef struct _dsp_src dsp_src_t; dsp_src_t *dsp_src_new(); void dsp_src_delete(dsp_src_t *o); int dsp_src_process(dsp_src_t *o, float *pin, int isize, float *pout, int osize, float ratio, int end_of_input, int *read, int *written); int dsp_src_reset(dsp_src_t *o); // math float pow(float, float); float log(float); float exp(float); float abs(float); float min(float, float); float max(float, float); float sin(float); float cos(float); float tan(float); // stdlib int printf(const char *format, ...); void *memset(void *b, int c, unsigned long len); extern int beats_per_minute; extern int ticks_per_beat; extern int samples_per_second; extern float samples_per_tick; extern int tick_position; extern float ticks_per_second; extern int track_count; #if defined(__cplusplus) } // extern "C" #endif #endif // LUNAR_FX_H
