Shortcuts: ugen reference, language reference
This page is for users who are familiar with the language. Potential users who don't quite know what they're doing yet should go through the tutorial first.
| Noisemakers | Filters |
|---|---|
| Analysis | Utility |
| Custom | |
Generates random noise.
connect(Noise(), speaker);
Generates a sawtooth wave.
sawosc = SawOsc(440); connect(sawosc, speaker); sawosc.phase = 0.5;
Generates a sine wave.
sinosc = SinOsc(440); connect(sinosc, speaker); sinosc.phase = 0.5;
Plays audio from a file. Uses ffmpeg, so it can decode many formats.
play("audio.mp3", speaker);
sound = SndIn("audio2.mp3");
if sound == nil then
print("could not load audio2.mp3");
else
print("loaded " .. sound.filename);
print("length in seconds:", sound.duration / second);
end
Generates a square wave.
sqrosc = SqrOsc(440); connect(sqrosc, speaker); sqrosc.phase = 0.5;
Passes through inputs with the given amount of delay.
connect(mic, Delay(3 * seconds), speaker);
Passes input signals through scaled by the given amount.
An alias for PassThru.
Passes input signals through scaled by the given amount.
passthru = PassThru(0.5);
connect(mic, passthru, speaker);
print("scaling mic by", passthru.gain);
An envelope generator. At every sample, compares the rectified sample coming in to the Follower's last value. If the sample is bigger, output that. If not, scale its previous value such that it decays with the given half-life.
follower = Follower(0.1 * second);
connect(mic, follower, blackhole);
while yield(0.01 * seconds) do
print(string.rep(",", follower.last * 50))
end
Outputs 0.0 except when next is set; in that case it assumes next's value for 1 sample.
impulse = Impulse(); connect(impulse, speaker); while yield(440) do impulse.next = 1; end
Outputs and sustains whatever value you give it.
step = Step(); connect(step, speaker); while true do step.next = 1; yield((1.0 / 440.0) * second) step.next = -1; yield((1.0 / 440.0) * second) end
Although included in the executable, many of ckv's built-in ugens are written in Lua because it's simple and usually good enough. Here is an example:
function Gain(gain)
return {
gain = gain or 1.0,
tick = function(self)
self.last = UGen.sum_inputs(self);
end
}
end
For an in-depth explanation of how object-oriented programming can work in Lua, I recommend the Lua wiki page on OOP. Basically, objects are just tables (similar to PHP arrays in that each is an array and a hashtable in one). g:tick() is actually short-hand for g.tick(g, 1.0), which is short-hand for Gain["tick"](g). Gain returns a table with a tick function that expects a gain object as its argument.
A ugen's tick method is called once for each audio sample. tick need not return anything, but it should set the ugen's last to the ugen's output for this sample.
UGen.sum_inputs(self) returns the sum of the last values for all connected ugens. It's best to call this function only once per tick because the value isn't cached. UGen has no other interesting methods yet.
If you write a ugen you think other people would find useful, consider sharing it in the showcase!
A lot of the overhead in ugen processing is requesting samples from them all, so a lot of the time creating them using Lua's C API is overkill. However, many unit generators could use the extra speed (like Delay) or access to C APIs (like SndIn).
My recommendation is to check out the audio/ugen/ directory of ckv's source code. There are four steps to adding a unit generator.
Next time you build ckv, your ugen will be included and automatically available!
Variables don't need to be declared in ckv, so n = 1.0 creates the variable n if it doesn't exist. However, the scope of variables created this way is the entire file.
To declare a local variable, use the local keyboard before its name, as in this example:
function play_sine(freq) local s = SinOsc(freq) connect(s, speaker) yield(second) disconnect(s, speaker) end fork(play_sine, 400) fork(play_sine, 600)
If we hadn't used local in that example, the second play_sine thread would have overwritten the global s variable, which is probably not what we want…
To share variables between files, put them in global. global is a table that is shared by all running scripts. You can put anything you want in it, like variables, functions, or ugens.
Since it is a table, global.clock = Clock() and global["clock"] = Clock() are equivalent.
connect connects a bunch of ugens together and disconnect disconnects two ugens. c is an alias for connect and d is an alias for disconnect.
d = Delay(3 * seconds) connect(mic, d, speaker) disconnect(d, speaker)
yield causes your thread to sleep for a certain amount of time, allowing other threads to run. y and sleep are aliases for yield.
yield(2 * second)
print("2 seconds later")
sleep(2 * second)
print("2 seconds later")
audio_ffwd skips forward in the audio stream, making the virtual machine catch up silently in the background. The function returns immediately. All ugens are ticked for the skipped samples, but their output is discarded.
audio_ffwd(second)
while yield(100 * ms) do print(now() / second) end
Clock allows you to schedule shreds according to a tempo that can change over time.
clock = Clock(300)
fork(function()
while yield(1, clock) do
print(now() / second)
end
end)
while yield(500 * ms) do
clock.bpm = 0.9 * clock.bpm
end
sync yields an amount of time that puts the thread in sync with a certain period. For example, the threads in this example will only ever call print at the start of a beat:
yield(second)
sync(2 * second) -- wake at beginning of next 2-second cycle
clock = Clock()
while yield(0.3, clock) do
fork(function()
sync(1, clock)
print(now(clock))
end)
end
fork and fork_eval are used for creating new threads.
fork(print, "time to play")
fork(function()
while yield(second) do
print("boop!")
end
end)
fork_eval("print(\"why did I eval this?\")")
When you don't know how long a thread should sleep, have it sleep on an event. The thread can be woken whenever you want by calling broadcast on the event object.
fun_time = Event:new();
fork(function()
while yield(fun_time) do
print("got the event!");
end
end)
while true do
fun_time:broadcast();
yield(random() * 4 * second);
end
Output stuff to the screen using print.
print("begin")
print("the number four is:", 4)
print(1, 2, 3, "do", "re", "mi")