lua_opdef — Définit un opcode dans Lua durant l'initialisation. L'opcode peut prendre n'importe quel nombre d'arguments de sortie et/ou d'entrée, de n'importe quel type.
Définit un opcode en Lua durant l'initialisation. L'opcode peut prendre n'importe quel nombre d'arguments de sortie et/ou d'entrée, de n'importe quel type. Le code est exécuté durant l'initialisation, typiquement depuis l'en-tête de l'orchestre. On peut déclarer et définir des variables globales et locales, des fonctions, des tables et des classes. Les objets définis dans la portée globale de Lua restent accessibles durant toute l'exécution, et ils sont visibles depuis tout autre code Lua situé dans le même fil de Csound.
![]() |
Note |
---|---|
Par défaut, tous les objets définis dans Lua sont dans la portée globale. Pour maintenir les objets confinés dans leur propre bloc de code, c'est-à-dire pour que les objets ne soient visibles que dans leur portée lexicale, il faut les déclarer avec l'attribut local. Cette particularité de Lua a souvent tendance à troubler les débutants. Il faut aussi tenir compte du fait que les tableaux de Lua sont indexés à partir de 1 à l'inverse de ceux de C et d'autres langages de programmation qui sont indéxés à partir de 0. |
Sname -- Le nom de l'opcode.
Sluacode -- Un bloc de code Lua, de n'importe quelle
longueur. On peut entourer les blocs multi-lignes par des accolades
doubles ({{ }}
). Le code est évalué une seule fois
durant l'initialisation.
Le code Lua doit définir toutes les fonctions qui seront appelées depuis Csound, en utilisant les conventions de nommage suivantes où opcodename doit être remplacé par le nom choisi pour l'opcode :
opcodename_init
pour le sous-programme de l'opcode
de taux-i.
opcodename_kontrol
pour le sous-programme de l'opcode
de taux-k.
opcodename_audio
pour le sous-programme de l'opcode
de taux-a.
opcodename_noteoff
pour le sous-programme de fin de
note.
Chacune de ces fonctions Lua recevra trois arguments légers (pointeurs) : l'objet CSOUND, l'instance de l'opcode et un pointeur vers les arguments de l'opcode, dont le code Lua doit convertir le type en celui d'une structure ctype de LuaJIT FFI contenant les arguments de sortie, d'entrée et les variables d'état de l'opcode. En utilisant, LuaJIT FFI, les éléments de cette structure seront accessibles comme s'ils étaient des types de Lua.
Chacune de ces fonctions Lua doit retourner 0 en cas de succès et 1 en cas d'échec.
Les fonctions Lua peuvent faire absolument tout ce que l'on veut, tout en sachant que si l'on désire une exécution en temps réel, il faut prendre soin de désactiver le ramasse-miettes de Lua et observer d'autres recommandations pour le code en temps réel.
Voici un exemple d'opcode Lua, implémentant un filtre en échelle de Moog. Afin de pouvoir les comparer, un opcode défini par l'utilisateur et l'opcode natif de Csound produisant les mêmes sonorités avec le même algorithme sont également montrés et chronométrés... L'exemple utilise le fichier luamoog.csd.
Exemple 447. Exemple d'opcode Lua.
<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>