The purposes of the Lua opcodes are:
Using the Lua opcode family, you can interact with the Lua interpreter and just-in-time compiler (luajit) embedded in Csound as follows:
Lua is Portuguese for "moon." And Lua (http://www.lua.org) is a lightweight, efficient dynamic programming language, designed for embedding in C/C++ and extending with C/C++. Lua has a stack-based calling mechanism and provides a toolkit of features (tables, metatables, anonymous functions, and closures) with which many styles of object-oriented and functional programming may be implemented. Lua's syntax is only slightly harder than Python's.
Lua is already one of the fastest dynamic languages; yet LuaJIT by Mike Pall (http://luajit.org) goes much further, giving Lua a just-in-time optimizing trace compiler for Intel architectures. LuaJIT includes an efficient foreign function interface (FFI) with the ability to define C arrays, structures, and other types in Lua. The speed of LuaJIT/FFI ranges from several times as fast as Lua, to faster (in some contexts) than optimized C.
Here is an example of a Lua opcode, implementing a Moog ladder filter. For purposes of comparison, a user-defined opcode and the native Csound opcode that compute the same sound using the same algorithm also are shown, and timed.. The example uses the file luamoog.csd.
Example 12. Example of a Lua opcode.
<CsoundSynthesizer> <CsInstruments> sr = 48000 ksmps = 100 nchnls = 1 gibegan rtclock lua_opdef "moogladder", {{ local ffi = require("ffi") local math = require("math") local string = require("string") local csoundApi = ffi.load('csound64.dll.5.2') ffi.cdef[[ int csoundGetKsmps(void *); double csoundGetSr(void *); struct moogladder_t { double *out; double *inp; double *freq; double *res; double *istor; double sr; double ksmps; double thermal; double f; double fc; double fc2; double fc3; double fcr; double acr; double tune; double res4; double input; double i; double j; double k; double kk; double stg[6]; double delay[6]; double tanhstg[6]; }; ]] local moogladder_ct = ffi.typeof('struct moogladder_t *') function moogladder_init(csound, opcode, carguments) local p = ffi.cast(moogladder_ct, carguments) p.sr = csoundApi.csoundGetSr(csound) p.ksmps = csoundApi.csoundGetKsmps(csound) if p.istor[0] == 0 then for i = 0, 5 do p.delay[i] = 0.0 end for i = 0, 3 do p.tanhstg[i] = 0.0 end end return 0 end function moogladder_kontrol(csound, opcode, carguments) local p = ffi.cast(moogladder_ct, carguments) -- transistor thermal voltage p.thermal = 1.0 / 40000.0 if p.res[0] < 0.0 then p.res[0] = 0.0 end -- sr is half the actual filter sampling rate p.fc = p.freq[0] / p.sr p.f = p.fc / 2.0 p.fc2 = p.fc * p.fc p.fc3 = p.fc2 * p.fc -- frequency & amplitude correction p.fcr = 1.873 * p.fc3 + 0.4955 * p.fc2 - 0.6490 * p.fc + 0.9988 p.acr = -3.9364 * p.fc2 + 1.8409 * p.fc + 0.9968 -- filter tuning p.tune = (1.0 - math.exp(-(2.0 * math.pi * p.f * p.fcr))) / p.thermal p.res4 = 4.0 * p.res[0] * p.acr -- Nested 'for' loops crash, not sure why. -- Local loop variables also are problematic. -- Lower-level loop constructs don't crash. p.i = 0 while p.i < p.ksmps do p.j = 0 while p.j < 2 do p.k = 0 while p.k < 4 do if p.k == 0 then p.input = p.inp[p.i] - p.res4 * p.delay[5] p.stg[p.k] = p.delay[p.k] + p.tune * (math.tanh(p.input * p.thermal) - p.tanhstg[p.k]) else p.input = p.stg[p.k - 1] p.tanhstg[p.k - 1] = math.tanh(p.input * p.thermal) if p.k < 3 then p.kk = p.tanhstg[p.k] else p.kk = math.tanh(p.delay[p.k] * p.thermal) end p.stg[p.k] = p.delay[p.k] + p.tune * (p.tanhstg[p.k - 1] - p.kk) end p.delay[p.k] = p.stg[p.k] p.k = p.k + 1 end -- 1/2-sample delay for phase compensation p.delay[5] = (p.stg[3] + p.delay[4]) * 0.5 p.delay[4] = p.stg[3] p.j = p.j + 1 end p.out[p.i] = p.delay[5] p.i = p.i + 1 end return 0 end }} /* Moogladder - An improved implementation of the Moog ladder filter DESCRIPTION This is an new digital implementation of the Moog ladder filter based on the work of Antti Huovilainen, described in the paper \"Non-Linear Digital Implementation of the Moog Ladder Filter\" (Proceedings of DaFX04, Univ of Napoli). This implementation is probably a more accurate digital representation of the original analogue filter. This is version 2 (revised 14/DEC/04), with improved amplitude/resonance scaling and frequency correction using a couple of polynomials,as suggested by Antti. SYNTAX ar Moogladder asig, kcf, kres PERFORMANCE asig - input signal kcf - cutoff frequency (Hz) kres - resonance (0 - 1). CREDITS Victor Lazzarini */ opcode moogladderu, a, akk asig, kcf, kres xin setksmps 1 ipi = 4 * taninv(1) /* filter delays */ az1 init 0 az2 init 0 az3 init 0 az4 init 0 az5 init 0 ay4 init 0 amf init 0 if kres > 1 then kres = 1 elseif kres < 0 then kres = 0 endif /* twice the \'thermal voltage of a transistor\' */ i2v = 40000 /* sr is half the actual filter sampling rate */ kfc = kcf/sr kf = kcf/(sr*2) /* frequency & amplitude correction */ kfcr = 1.8730 * (kfc^3) + 0.4955 * (kfc^2) - 0.6490 * kfc + 0.9988 kacr = -3.9364 * (kfc^2) + 1.8409 * kfc + 0.9968; /* filter tuning */ k2vg = i2v * (1 - exp(-2 * ipi * kfcr * kf)) /* cascade of 4 1st order sections */ ay1 = az1 + k2vg * (tanh((asig - 4 * kres * amf * kacr) / i2v) - tanh(az1 / i2v)) az1 = ay1 ay2 = az2 + k2vg * (tanh(ay1 / i2v) - tanh(az2 / i2v )) az2 = ay2 ay3 = az3 + k2vg * (tanh(ay2 / i2v) - tanh(az3 / i2v)) az3 = ay3 ay4 = az4 + k2vg * (tanh(ay3 / i2v) - tanh(az4 / i2v)) az4 = ay4 /* 1/2-sample delay for phase compensation */ amf = (ay4 + az5) *0.5 az5 = ay4 /* oversampling */ ay1 = az1 + k2vg * (tanh((asig - 4 * kres * amf * kacr) / i2v) - tanh(az1 / i2v)) az1 = ay1 ay2 = az2 + k2vg * (tanh(ay1 / i2v) - tanh(az2 / i2v )) az2 = ay2 ay3 = az3 + k2vg * (tanh(ay2 / i2v) - tanh(az3 / i2v)) az3 = ay3 ay4 = az4 + k2vg * (tanh(ay3 / i2v) - tanh(az4 / i2v)) az4 = ay4 amf = (ay4 + az5) * 0.5 az5 = ay4 xout amf endop instr 1 prints "No filter.\n" kfe expseg 500, p3*0.9, 1800, p3*0.1, 3000 kenv linen 10000, 0.05, p3, 0.05 asig buzz kenv, 100, sr/(200), 1 ; afil moogladder asig, kfe, 1 out asig endin instr 2 prints "Native moogladder.\n" kfe expseg 500, p3*0.9, 1800, p3*0.1, 3000 kenv linen 10000, 0.05, p3, 0.05 asig buzz kenv, 100, sr/(200), 1 afil moogladder asig, kfe, 1 out afil endin instr 3 prints "UDO moogladder.\n" kfe expseg 500, p3*0.9, 1800, p3*0.1, 3000 kenv linen 10000, 0.05, p3, 0.05 asig buzz kenv, 100, sr/(200), 1 afil moogladderu asig, kfe, 1 out afil endin instr 4 prints "Lua moogladder.\n" kres init 1 istor init 0 kfe expseg 500, p3*0.9, 1800, p3*0.1, 3000 kenv linen 10000, 0.05, p3, 0.05 asig buzz kenv, 100, sr/(200), 1 afil init 0 lua_ikopcall "moogladder", afil, asig, kfe, kres, istor out afil endin instr 5 giended rtclock ielapsed = giended - gibegan print ielapsed gibegan rtclock endin </CsInstruments> <CsScore> f 1 0 65536 10 1 i 5.1 0 1 i 4 1 20 i 5.2 21 1 i 4 22 20 i 5.3 42 1 i 2 43 20 i 5.4 63 1 i 2 64 20 i 5.5 84 1 i 3 85 20 i 5.6 105 1 i 3 106 20 i 5.7 126 1 i 1 127 20 i 5.8 147 1 i 1 148 20 i 5.9 168 1 i 4 169 20 i 4 170 20 i 4 171 20 e </CsScore> </CsoundSynthesizer>
Copyright (c) 2011 by Michael Gogins. All rights reserved.