♪ Karplus-Strong

A modell működése és matematikája

🎯 Akusztikai koncepció: Miért fizikai modellezés?
Ahelyett, hogy hagyományos oszcillátorokkal próbálnánk meg kikeverni egy gitár hangját, a fizikai modellezés a hang forrását – magát a húrt – szimulálja. A pengetés energiáját egy rövid, szélessávú zajimpulzus adja, amely egy visszacsatolt késleltetővonalban kering. Ebből a kezdeti akusztikai energiakáoszból a hurok rezonanciája formálja ki a zenei hangot.

Az algoritmus lépésről lépésre

  1. Egy \(N\) mintányi késleltetővonalat véletlen értékekkel (zajimpulzussal) töltünk fel: ez a „pengetés”.
  2. Minden mintaperiódusban kivesszük a vonal végén lévő mintát – ez a kimenet.
  3. A kivett és az előző kivett minta átlagát (\(g\)-vel szorozva) visszaírjuk a vonal elejére.
  4. A kör ismétlődése miatt a jel periodikussá válik (a periódus a hurok hossza), az átlagolás pedig minden körben elsimítja: a magas felharmonikusok gyorsabban halnak el, a hang egyre tisztább és tompább lesz.

1. Differenciaegyenlet és a szűrő

Az eredeti Karplus-Strong hurok a késleltetett minta és az azt megelőző minta átlagát csatolja vissza:

\( y[n] = x[n] + g\cdot\frac{y[n-N] + y[n-N-1]}{2} \)

Az átviteli függvény: \( H(z)=\dfrac{1}{1-g\,z^{-N}\,\frac{1+z^{-1}}{2}} \). Az átlagoló szűrő egy egyszerű aluláteresztő: DC-n erősítése 1, a Nyquist-frekvencián 0 – ezért halnak el gyorsabban a magas felharmonikusok.

Az átlagoló szűrő amplitúdó- és fázismenete (\(\omega=2\pi f/f_s\)): \( |H_{lp}(e^{j\omega})|=\cos\tfrac{\omega}{2},\; \angle H_{lp}=-\tfrac{\omega}{2} \). A fázis lineáris, ezért a késleltetés minden frekvencián ugyanannyi.

⚙️ DSP Szemlélet: A hurokszűrő szerepe
A valós hurokban a magas frekvenciák veszítik el leghamarabb az energiájukat a közegellenállás és a belső súrlódás miatt. A visszacsatolásba épített átlagoló szűrő pontosan ezt az energiaveszteséget modellezi: minden egyes hurokfutás során simítja a jelet, így a hang természetesen tompul el.

2. Hangolási pontosság: a szűrő is késleltet

Az átlagoló szűrő minden frekvencián pontosan fél mintányi késleltetést ad, ezáltal a hurok tényleges hossza \(N+0{,}5\) minta (és a felharmonikusok pontosan egész számú többszörösei az alapnak). A pontos hangolás:

\( N + \tfrac12 = \dfrac{f_s}{f}\;\Rightarrow\; N=\dfrac{f_s}{f}-\tfrac12 \)

432 Hz-nél: 44100/432 − 0,5 = 101,58 minta (44100/432 = 102,083).

Súlyozott szűrő, \((1-S)+S\,z^{-1}\), esetén a késleltetés kis frekvencián \(S\) minta, ezért \(N=f_s/f-S\). A kódokban ez a damp paraméter (\(S=\tfrac12\) az eredeti átlagolás).

📐 Precíz DSP tervezés: Miért fontos a fél minta korrekció?
A digitális késleltetés hossza határozza meg az alapfrekvenciát. Mivel maga a szűrő is bevezet egy pici fáziskésést, ezt be kell kalkulálnunk a delay puffer méretbe, különösen magasabb frekvenciákon, különben a hang elhangolódik. Például 880 Hz-en (44,1 kHz-nél) a korrekció nélküli hangolás kb. 17 centtel mély lenne.

3. Törtrészes késleltetés (Fractional Delay)

A késleltetés ritkán egész szám. Két bevett megoldás:

  • Lineáris interpoláció – egyszerű, de saját aluláteresztő hatása van.
  • Allpass interpoláció – az amplitúdót nem változtatja: \( H_{ap}(z)=\dfrac{C+z^{-1}}{1+C z^{-1}},\; C=\dfrac{1-d}{1+d} \). Hangolt hangszereknél ez az előnyös; \(d\in[0{,}5;\,1{,}5]\) esetén jó (ekkor \(|C|\le\tfrac13\)), ezért a késleltetést úgy bontjuk egész és törtrészre, hogy a törtrész ebbe a sávba essen. Hátránya, hogy fázisa frekvenciafüggő, és a késleltetés gyors változtatásakor átmeneti zajok (kattanás) keletkezhetnek.
  • Köbös (Lagrange) interpoláció – ezt használja a gen~ és a SuperCollider kód: simább amplitúdómenetet ad, mint a lineáris, de nem allpass.

4. Lecsengési idő és T60

A T60 az az idő, amely alatt a hang amplitúdója 60 dB-lel, azaz az ezredrészére csökken. Minden periódus után a jel \(g\)-szeresére csökken. Egy másodpercben \(f\) periódus van, tehát a T60 idejére:

\( g^{\,f\cdot T_{60}}=0{,}001\;\Rightarrow\; g=10^{-3/(f\cdot T_{60})} \)

A hurokszűrő miatt a \(k\)-adik felharmonikus (\(kf\)) minden körben \(g\cos(\pi k f/f_s)\)-szeresére csökken (\(S=\tfrac12\) esetén), így lecsengése:

\( T_{60,k}=\dfrac{-3}{f\,\log_{10}\!\big(g\cos(\pi k f/f_s)\big)} \)

Példa: 432 Hz, \(T_{60}=3\) s, \(f_s=44100\) Hz esetén a 10. felharmonikus (4320 Hz) kb. 0,3 s alatt elhal – tízszer gyorsabban az alaphangnál. A beállított \(T_{60}\) tehát az alaphangra (közelítőleg) érvényes.

🎚️ Stabilitás és T60 vezérlés
A visszacsatolás mértéke (\(g\)) határozza meg a lecsengést. Ha \(g > 1\), a rendszer instabil: a jel exponenciálisan nő, ami digitális torzításhoz vezet. \(g=1\) határeset: a hang soha nem cseng le (a zaj egyenáramú komponense sem), ezért \(0

5. Gerjesztés: a zajimpulzus

A zaj a húr kezdeti alakját jelöli ki. A klasszikus algoritmusban hossza pontosan egy periódus (\(N\) minta): ekkor a hurok egyszer, teljesen betelik. Rövidebb impulzusnál a hurok csak részben töltődik fel, hosszabbnál a visszacsatolt példányok egymásra rakódnak. Minden pengetés más zajmintát használ, ezért minden hang kicsit más. A zaj átlaga (egyenáramú komponens) a hurokban csak \(g\)-vel csillapodik, ezért érdemes átlagmentesíteni.

6. Fizikai háttér: a húr és a hullámvezető

A húr kis kitéréseit a hullámegyenlet írja le: \( \dfrac{\partial^2 y}{\partial t^2}=c^2\dfrac{\partial^2 y}{\partial x^2} \), ahol a hullámsebesség \( c=\sqrt{T/\mu} \) (\(T\): húrfeszítés, \(\mu\): hosszegységre jutó tömeg). A megoldás két ellentétes irányba haladó hullám összege (d'Alembert); ezeket egy-egy késleltetővonal modellezi – ez a digitális hullámvezető (Smith). A rögzített végeken a hullám előjelet vált és visszaverődik; a veszteségek a hurokszűrőbe kerülnek. A hullám oda-vissza útja \(2L/c\) ideig tart, ez a periódus:

\( f=\dfrac{c}{2L}=\dfrac{1}{2L}\sqrt{\dfrac{T}{\mu}},\qquad N+\tfrac12=\dfrac{f_s}{f}=\dfrac{2L f_s}{c} \)

A Karplus–Strong hurok a két irányt egyetlen körben egyesíti (a két visszaverődés előjelváltása kiejti egymást), így a hurokhossz a hullám teljes körülfordulási ideje. Nagyobb feszítés vagy rövidebb húr magasabb, nehezebb húr mélyebb hangot ad.

7. Korlátok – merre tovább?

  • A hurokszűrő egyszerű; a valódi húr lecsengése bonyolultabb, összetettebb hurokszűrőt kíván.
  • Nincs inharmonicitás: a merev húrok (pl. zongora) felharmonikusai magasabbak a pontos többszöröseknél; ezt allpass-lánc (diszperzió) modellezi.
  • Nincs hangszertest-rezonancia és nincs húrok közötti csatolás (szimpatikus rezgés).
  • Vibrato és glissando a törtrészes késleltetés modulálását igényli.

Élő Karplus-Strong szintetizátor

Állítsd be a paramétereket, majd pengesd meg a húrt.

🎛️ Paraméterek útmutatója
  • Frekvencia: A késleltetési puffer méretét határozza meg (alaphangmagasság).
  • Csillapítás: A hurokszűrő vágási karaktere; alacsony értéknél fényesebb, magas értéknél tompább karakter.
  • Pengetési pozíció: A húr menti gerjesztési pont, amely fésűszűrőként viselkedik.
  • Dinamika: A gerjesztő zaj sávszélességét szűri, így a halkan pengetett hang lágyabb.
Pengesd meg a húrt: itt látod a számított késleltetést és visszacsatolást.

Oszcilloszkóp (valós idejű)

Spektrum (0–5 kHz, valós idejű)

Forráskódok

DSP környezetben történő megvalósításhoz szükséges kódstruktúrák.

Max/MSP gen~ – Részletesen kommentelt implementáció

// Puffer létrehozása a hurok késleltetéséhez (48kHz-es mintavételezéshez igazítva)
Delay dl(48000);

// Második puffer a pengetési pozíció (fésűszűrő) késleltetéséhez
Delay bd(24000);

// Memóriaváltozó az előző mintához (hurokszűrőhöz)
History y1(0);

// Memóriaváltozó a dinamika szűréshez
History dyn_mem(0);

// Paraméterek definiálása határokkal és alapértelmezett értékekkel
Param freq(432, min=20, max=20000);      // Hz (alapértelmezett 432 Hz)
Param damp(0.5, min=0, max=0.5);           // 0.0 - 0.5 (0 = fényes, 0.5 = az eredeti átlagoló szűrő)
Param t60(3, min=0.001);                 // Lecsengési idő másodpercben
Param beta(0.1, min=0, max=0.99);        // Pengetési pozíció a húrhossz törtrészeként (0 = ki)
Param dyn(0.5, min=0, max=1);            // Dinamika (0.0 - 1.0)

// Rövid zajimpulzus (bemeneti exciter) fogadása
burst = in1;

// Dinamika szűrő: aluláteresztő szűrő alkalmazása a gerjesztésen a leütési erő alapján
R_L = 0.95 * (1 - dyn);
exc_filtered = (1 - R_L) * burst + R_L * dyn_mem;
dyn_mem = exc_filtered;

// Feedback (visszacsatolási együttható) kiszámítása a T60 képlet alapján (frekvenciafüggő)
g = pow(0.001, 1 / (freq * t60));

// A késleltetési idő kiszámítása a hangmagasság és a szűrő fáziskésésének kompenzációjával
samps = max(1, samplerate / freq - damp);
d = dl.read(samps, interp="cubic"); // Olvasás köbös interpolációval a törtrészes késleltetéshez

// Kétpontos FIR aluláteresztő szűrő kiszámítása a hurokban
filtered = (1 - damp) * d + damp * y1;
y1 = d;

// Pengetési pozíció (fésűszűrő) megvalósítása a gerjesztésen: H = 1 - z^-M
M = max(1, floor(beta * samplerate / freq + 0.5));
pm = bd.read(M);
bd.write(exc_filtered);
exc = beta > 0 ? exc_filtered - pm : exc_filtered;

// A hurok frissítése: a gerjesztés és a visszacsatolt, szűrt jel összege beíródik a delaybe
dl.write(exc + g * filtered);
out1 = d; // Hang kiadása a kimenetre
Használat Max-ben: a gen~ bemenetére (in1) kb. egy periódus hosszú zajimpulzus kell (1000/freq ms), pl. [noise~] × [line~], a [line~]-nek 1, 0 2.3 üzenetet küldve 432 Hz-nél. A paramétereket üzenetként küldhetjük a gen~-nek (pl. freq 220, t60 2).

SuperCollider – Részletesen kommentelt implementáció

SynthDef(\ksManual, { |out=0, freq=432, t60=3, damp=0.5, amp=0.4, beta=0.1, dyn=0.5, t_trig=1|

    // Visszacsatolási tényező (g) kiszámítása a T60 lecsengési időből
    var g   = 0.001.pow(1 / (freq * t60));

    // Alap gerjesztő jel: fehérzaj impulzus, hossza pontosan egy periódus (betölti a hurkot)
    var rawExc = WhiteNoise.ar * Trig1.ar(t_trig, freq.reciprocal);

    // Dinamika szűrő: a zaj sávszélességének szűrése a dinamika paraméter alapján
    var rL  = 0.95 * (1 - dyn);
    var exc0 = OnePole.ar(rawExc, rL);

    // Pengetési pozíció fésűszűrője (kivonjuk a késleltetett zajt, ha beta > 0)
    var exc = exc0 - (DelayN.ar(exc0, 0.05, (beta * freq.reciprocal).max(ControlDur.ir)) * (beta > 0));

    // Helyi visszacsatolás olvasása a hurokból
    var fb  = LocalIn.ar(1);

    // Késleltetővonal a hurokban, kompenzálva a vezérlőblokk és a hurokszűrő (damp minta) késését
    var del = DelayC.ar(exc + fb, 0.1, (freq.reciprocal - ControlDur.ir - (damp * SampleDur.ir)).max(0.0001));

    // Kétpontos átlagoló (FIR) aluláteresztő a hurokban: (1-damp)*x[n] + damp*x[n-1]
    var lp  = (del * (1 - damp)) + (Delay1.ar(del) * damp);

    // Visszacsatolás írása a hurok végén a csillapított és skálázott jellel
    LocalOut.ar(lp * g);

    // Automatikus kilépés, ha a hang lecsengett és elhalkult
    DetectSilence.ar(del, 0.0001, 0.5, doneAction: 2);

    // Sztereó kimenetre küldés a megadott amplitúdóval
    Out.ar(out, (del * amp) ! 2);
}).add;

// Előbb indítsd el a szervert: s.boot;
// A SynthDef.add aszinkron: az alábbi sort a SynthDef hozzáadása után, külön futtasd.
Synth(\ksManual, [\freq, 432, \t60, 3, \damp, 0.5, \beta, 0.1, \dyn, 0.5]);
Korlát: a LocalIn/LocalOut egy vezérlőblokknyi (alapesetben 64 minta) késleltetést ad a visszacsatolásba, ezért a példa csak \(f<1/\text{ControlDur}\) alatt hangol pontosan (44,1 kHz-en kb. 690 Hz). Magasabb hangokhoz csökkentsd a blokkméretet: s.options.blockSize = 16; s.reboot;

Eredeti források és az Extended Karplus–Strong (EKS)

Az első \(N\) kimeneti minta véletlenszám, utána \( y[n]=\tfrac12\big(y[n-N]+y[n-(N+1)]\big) \). A zajkezelés és a szűrés kombinációja határozza meg a hangszín gazdagságát. (A hurok tényleges hossza \(N+\tfrac12\).)

Dob-változat: ha az átlag előjelét véletlenszerűen megfordítjuk (a + előjel valószínűsége \(b\)), \(b=1\)-nél húrhangot, \(b=\tfrac12\)-nél dobszerű, zajos hangot kapunk; \(b=0\)-nál a hang egy oktávval mélyebb lesz, csak páratlan felharmonikusokkal.

Az Extended Karplus–Strong (EKS) (Jaffe és Smith, 1983) a hurkot fizikailag motivált szűrőkkel egészíti ki:

  • \(H_p(z)=\dfrac{1-p}{1-p\,z^{-1}}\): pengetési irány (egypólusú aluláteresztő)
  • \(H_\beta(z)=1-z^{-\lfloor\beta N+1/2\rfloor}\): pengetési pozíció (fésűszűrő)
  • \(H_L(z)=\dfrac{1-R_L}{1-R_L\,z^{-1}}\): dinamika (aluláteresztő; a demó és a kódok „Dinamika” paramétere)
  • \(H_d(z)\): hurokszűrő / csillapítás, pl. \((1-S)+S\,z^{-1}\); \(S=\tfrac12\) az eredeti átlagolás
  • \(H_\eta(z)\): hangolás törtrészes késleltetéssel (elsőrendű allpass)
  • \(H_s(z)\): húrmerevség (allpass-lánc, inharmonicitás)
🎸 Akusztikai jelenség: A pengetési pozíció (Fésűszűrő-hatás)
Ha a húrt máshol pengetjük meg, azok a felharmonikusok, amelyeknek épp a pengetés pontján van csomópontjuk, nem gerjednek: a spektrum fésűszerűvé válik.

Pengetési pozíció – mit hallunk és miért?

A \(H_\beta(z)=1-z^{-\beta N}\) fésűszűrő a \(h\)-adik felharmonikuson \(2\,|\sin(\pi h\beta)|\) erősítést ad. Ha \(h\beta\) egész, a felharmonikus kiesik. Elég \(0<\beta\le\tfrac12\)-t vizsgálni, mert \(\beta\) és \(1-\beta\) ugyanazt a spektrumot adja.

  • \(\beta=\tfrac12\): minden páros felharmonikus kiesik (üreges hang).
  • \(\beta=\tfrac13\): minden harmadik hiányzik.
  • \(\beta=1/k\): minden \(k\)-adik felharmonikus hiányzik. Fizikailag: a húr közepén pengetve „üreges”, a hídhoz közel pengetve fényes, „orrhangú” hang keletkezik.

Az oszlopok a \(h\)-adik felharmonikus relatív amplitúdóját mutatják (\(|\sin(\pi h\beta)|\)); a rózsaszín oszlopok kieső felharmonikusok.

Források

  • K. Karplus, A. Strong: Digital Synthesis of Plucked-String and Drum Timbres, Computer Music Journal 7(2), 1983, 43–55.
  • D. A. Jaffe, J. O. Smith: Extensions of the Karplus-Strong Plucked-String Algorithm, Computer Music Journal 7(2), 1983, 56–69.
  • J. O. Smith: Physical Audio Signal Processing (online könyv, CCRMA) – az EKS fejezet: ccrma.stanford.edu/~jos/pasp/Extended_Karplus_Strong_Algorithm.html
  • J. O. Smith: Physical Modeling Using Digital Waveguides, Computer Music Journal 16(4), 1992, 74–91.
  • M. Karjalainen, V. Välimäki, T. Tolonen: Plucked-String Models: From the Karplus-Strong Algorithm to Digital Waveguides and Beyond, Computer Music Journal 22(3), 1998, 17–32.
  • T. I. Laakso, V. Välimäki, M. Karjalainen, U. K. Laine: Splitting the Unit Delay, IEEE Signal Processing Magazine 13(1), 1996, 30–60 (törtrészes késleltetés).

További feladatok és Szószedet

FeladatLeírás
HangolásmérésJátssz 432 Hz-et egész \(N=102\)-vel (fél minta korrekció nélkül) és a pontos képlettel (\(N=101{,}58\), törtrészes késleltetés); mérd a frekvenciát. Egész késleltetésnél a várt érték 430,2 Hz, kb. −7 cent.
SpektrogramRögzítsd a kimenetet, és nézd meg spektrogramon a felharmonikusok viselkedését: a magasak gyorsabban halnak el. Vesd össze a 10. felharmonikus lecsengését a \(T_{60,k}\) képlettel.
T60-mérésÁllíts be 3 s-ot, és mérd meg dB-skálán, mikor esik az alaphang 60 dB-lel. Ellenőrizd a \(g\) képletet.
Pengetési pozícióPróbáld ki \(\beta=\tfrac12,\tfrac13,\tfrac14\) értéket, és keresd meg a spektrumban a kieső felharmonikusokat.
Zajimpulzus hosszaVáltoztasd az impulzus hosszát (fél, egy, két periódus), és hallgasd meg a különbséget.
Dob-változatValósítsd meg az előjelcserés változatot SuperCollider-ben vagy gen~-ben, \(b=1;\,0{,}5;\,0\) értékekkel.
StabilitásEmeld \(g\)-t rövid időre 1 fölé (nagyon kis hangerővel!), és figyeld a jel növekedését.
📖 Szakmai Szószedet
  • Exciter: A hangot indító kezdeti gerjesztő jel.
  • Delay line: Késleltető vonal; a memóriapuffer, amely a hullám terjedési idejét tárolja.
  • Damping: Csillapítás; a hurokban lévő frekvenciafüggő energiaveszteség szimulációja.
  • T60: Az az idő, amely alatt a hang amplitúdója 60 dB-lel (az ezredrészére) csökken.
  • Felharmonikus (harmonic): az alapfrekvencia egész számú többszöröse.
  • Fésűszűrő (comb filter): az eredeti és egy késleltetett jel összege vagy különbsége; periodikusan ismétlődő kioltásokat okoz.
  • Törtrészes késleltetés (fractional delay): nem egész mintányi késleltetés, interpolációval.
  • Allpass: minden frekvenciát azonos erősítéssel átengedő, de a fázist alakító szűrő.
  • Digitális hullámvezető (waveguide): a hullámterjedés modellezése késleltetővonalakkal.
  • Nyquist-frekvencia: a mintavételi frekvencia fele (\(f_s/2\)).