parravicini.org

Virtual Audio Engineering

Stefano Lucato made an amazing demo playing the SWAM – Ms Sax S over a wonderful track. He played the demo in real-time, just in some few takes, using a keyboard, an expression pedal and a modulation wheel. Listen…

[audio:http://www.parravicini.org/swam/demo/Demo_SaxS_YP_SL_eq1_ExtndVers.mp3|titles=Demo_SaxS_YP_SL_eq1_ExtndVers]

I posted the extended version; on the samplemodeling site you can find the short version:

[audio:http://www.sample-modeling.com/Demos/Soprano/Demo_SaxS_YP_SL.mp3|titles=Demo_SaxS_YP_SL]
Announcement link:
http://www.samplemodeling.com/en/news.php

Benvenuto su parravicini.org.

Il nuovo sito è in corso di aggiornamento… torna presto a trovarci!

EN – Based on Stefano Lucato idea, I’m developing a new series of virtual acoustic instruments based on a new technology we call SWAM, Synchronous Wavelength Acoustic Modeling.

SWAM is a powerful technology that give to virtual acoustic instruments high quality realism, using fewer memory consumption than sample based instruments and fewer cpu load than physical modeling based instruments.

IT – Da un’idea di Stefano Lucato, sto sviluppando una serie di nuovi strumenti acustici virtuali basati su una nuova tecnologia che abbiamo chiamato SWAM, Synchronous Wavelength Acoustic Modeling.

SWAM è una tecnologia potente che fornisce agli strumenti acustici virtuali un realismo di altissima qualità, usando molta meno memoria degli strumenti basati su campioni e molta meno cpu degli strumenti totalmente basati sui modelli fisici.

Ottava
******

Un'oscillazione che ha frequenza f2 si dice che dista un'ottava da un'oscillazione con frequenza f1 quando

f2 = 2 * f1

Pi˘ in generale un'oscillazione con frequenza f2 dista N ottave da un'oscillazione con frequenza f1 quando

f2 = 2^N * f1

da cui:

2^N = f2/f1 => N = log2(f2/f1) = log(f2/f1) / log(2) [oct]


Frequenze di transizione (cutoff)
*********************************

Per un filtro, una frequenza di transizione Ë quella frequenza che discrimina la banda passante dalla banda di reiezione. Per i filtri passa-basso e passa-alto ci sarà solo una frequenza di transizione mentre per i filtri passa-banda e elimina-banda ci saranno due frequenze di transizione.
In genere per discriminare tra banda passante e banda di reiezione si trova il punto in frequenza in cui il valore assoluto della risposta in frequenza è pari alla metà del valore massimo*:

|H(fc)| = 1/2 * max(|H(f)|)

ovvero -3dB rispetto il valore massimo


* più precisamente, per un filtro passa-basso:
|H(fc)| = 1/2 * |H(0)|

per un filtro passa-alto:
|H(fc)| = 1/2 * lim |H(f)|
 f->oo


Larghezza di banda (bandwidth)
******************************

La larghezza di banda (bandwidth) B di un filtro passa banda o elimina banda è la distanza tra le due frequenze di transizione (cutoff)

B = fH - fL [Hz]

La larghezza di banda si può anche scrivere esprimendo fH come distanza in ottave da fL (fH = 2^N * fL):

B = 2^N * fL - fL = fL * (2^N - 1)


Centro-banda
*************

La frequenza di centro-banda si calcola come media geometrica (e non aritmetica!) delle frequenze di transizione:

fo = sqrt(fL*fH)


Fattore di qualità
******************

Il fattore di qualit‡ Q di un filtro passa banda o elimina banda è il rapporto tra la frequenza di centro-banda e la larghezza di banda:

Q = fo/B


ma:

fo = sqrt(fL*fH) = sqrt(fL * 2^N * fL) = fL * sqrt(2^N)

B = fL * (2^N - 1)

Q = sqrt(2^N) / (2^N - 1)
Filtri analogici

Un filtro analogico del secondo ordine è descritto dalla funzione di trasferimento (NB: per i filtri analogici userò sempre il pedice "a"):

         Ba(s)     ba0*s^2 + ba1*s + ba2 
Ha(s) = ------- = -----------------------
         Aa(s)     aa0*s^2 + aa1*s + aa2 
 
A seconda della combinazione dei coefficienti ba0, ba1, ba2, aa0, aa1, aa2, il filtro sar‡ un passa-basso, passa-alto, passa-banda o elimina-banda.
Per studiare un filtro analogico, ricordiamo che la risposta in frequenza si ottiene sostituendo ad s la variabile jW:

          Ba(jW)    -ba0*W^2 + j*ba1*W + ba2
Ha(jW) = -------- = --------------------------
          Aa(jW)    -aa0*W^2 + j*aa1*W + aa2
 
Di seguito vengono proposti alcuni filtri del secondo ordine tra i più usati; ovviamente ne esistono molti altri.
E' possibile trovare numerosi riferimenti ad implementazioni elettroniche tramite amplificatori operazionali, resistori, condensatori e induttori.

LPF - Low Pass Filter
---------------------

Un passa-basso può essere ottenuto ponendo i coefficienti:

ba0 = 0
ba1 = 0
ba2 = Wo^2
aa0 = 1
aa1 = Wo/Q
aa2 = Wo^2

ottenendo quindi

                Wo^2
Ha(s) = -----------------------
         s^2 + (Wo/Q)*s + Wo^2
 
ovvero

                   Wo^2
Ha(jW) = --------------------------
          -W^2 + j*(Wo/Q)*W + Wo^2

 
Il filtro Ë quindi definito dai due parametri Wo e Q, che ora andremo a scoprire cosa rappresentano.


Studio per valori di W < Wo
...........................

              Wo^2
lim Ha(jW) = ------ = 1
W->0+         Wo^2
 
|Ha(0)| = 1
<Ha(0) = 0°

Per valori di W < Wo la risposta è approssimativamente piatta e pari a 0dB.


Studio per valori di W > Wo
...........................

                        Wo^2
lim   Ha(jW) = lim     ------ = 0-
jW->+oo        jW->+oo  -W^2

|Ha(joo)| = 0
<Ha(joo) = 180°

Per valori di W > Wo la risposta va come -W0^2/W^2; al raddoppio della pulsazione (1 ottava):

Ha(2*W)   -Wo^2/(4*W^2)
------- = ------------- = 1/4 => -12dB/oct
 Ha(W)      -Wo^2/W^2
 
La pendenza del filtro è quindi di -12dB/oct.


Studio per valori di W = Wo
...........................

                    Wo^2
Ha(jWo) = ------------------------ = -jQ
           -Wo^2 + jWo^2/Q + Wo^2

|Ha(jWo)| = Q
<Ha(jWo) = -90°

Ovvero Q è il guadagno del filtro alla pulsazione Wo.

NB: spesso si definisce come frequenza di cutoff la frequenza alla quale il filtro ha un'ampiezza di -3dB; 
per questo filtro è vero soltanto se Q = 1/sqrt(2) = 0.707 (ovvero -3dB)



HPF - High Pass Filter
----------------------

Un passa-alto puÚ essere ottenuto ponendo i coefficienti:

ba0 = 1
ba1 = 0
ba2 = 0
aa0 = 1
aa1 = Wo/Q
aa2 = Wo^2

ottenendo quindi

                  s^2
Ha(s) = -----------------------
         s^2 + (Wo/Q)*s + Wo^2
 
ovvero

                  -W^2
Ha(jW) = --------------------------
          -W^2 + j*(Wo/Q)*W + Wo^2


Il filtro è quindi definito dai due parametri Wo e Q, che ora andremo a scoprire cosa rappresentano.


Studio per valori di W < Wo
...........................

                       -W^2        0-
lim   Ha(0) = lim   -------- = -------- = 0-
jW->0+        jW->0+   Wo^2      Wo^2
 
|Ha(0)| = 0
<Ha(0) = 180°

L'andamento della risposta in frequenza per valori di W < Wo va come -W^2/Wo^2; al raddoppio della pulsazione (1 ottava):

Ha(2*W)   -4*W^2/Wo^2
------- = ----------- = 4 => 12dB/oct
 Ha(W)     -W^2/Wo^2
 
La pendenza del filtro per valori di W > Wo è quindi di 12dB/oct.


Studio per valori di W > Wo
...........................

                          -W^2
lim    Ha(jW) =   lim    ------- = 1
jW->+oo         jW->+oo   -W^2

|Ha(joo)| = 1
<Ha(joo) = 0°

Per valori di W > Wo la risposta Ë approssimativamente piatta e pari a 0dB.

Studio per valori di W = Wo
...........................

                   -Wo^2
Ha(jWo) = ------------------------ = jQ
           -Wo^2 + jWo^2/Q + Wo^2

|Ha(jWo)| = Q
<Ha(jWo) = 90°

Ovvero Q è il guadagno del filtro alla pulsazione Wo.

NB: spesso si definisce come frequenza di cutoff la frequenza alla quale il filtro ha un'ampiezza di -3dB; 
per questo filtro è vero soltanto se Q = 1/sqrt(2) = 0.707 (ovvero -3dB)


BPF - Band Pass Filter
----------------------

A seconda della combinazione dei coefficienti, potremo avere diversi tipi di filtri passa-banda:

BPF1 (constant skirt gain, peak = Q)
====================================

ba0 = 0
ba1 = Wo
ba2 = 0
aa0 = 1
aa1 = Wo/Q
aa2 = Wo^2

ottenendo quindi

                 Wo*s
Ha(s) = -----------------------
         s^2 + (Wo/Q)*s + Wo^2
 
ovvero

                 j*Wo*W
Ha(jW) = --------------------------
          -W^2 + j*(Wo/Q)*W + Wo^2


Il filtro è quindi definito dai due parametri Wo e Q, che ora andremo a scoprire cosa rappresentano.


Studio per valori di W < Wo
...........................

                   j*Wo*W      j*0+
lim Ha(0) = lim   -------- = -------- = j*0+
jW->0+      jW->0+  Wo^2       Wo^2
 
|Ha(0)| = 0
<Ha(0) = +90°


L'andamento della risposta in frequenza per valori di W < Wo va come j*W/Wo; al raddoppio della pulsazione (1 ottava):

Ha(2*W)   j*2*W/Wo
------- = -------- = 2 => 6dB/oct
 Ha(W)     j*W/Wo

La pendenza del filtro per valori di W < Wo Ë quindi di 6dB/oct.


Studio per valori di W > Wo
...........................

                    j*Wo*W
lim Ha(jW) = lim   -------- = j*0-
jW->+oo     jW->+oo  -W^2

|Ha(joo)| = 0
<Ha(joo)| = -90°

L'andamento della risposta in frequenza per valori di W > Wo va come -j*Wo/W; al raddoppio della pulsazione (1 ottava):

Ha(2*W)   -j*Wo/2*W
------- = --------- = 1/2 => -6dB/oct
 Ha(W)     -j*Wo/W

La pendenza del filtro per valori di W > Wo è quindi di 6dB/oct.


Studio per valori di W = Wo
...........................

                  j*Wo^2
Ha(jWo) = ------------------------ = Q
           -Wo^2 + jWo^2/Q + Wo^2

|Ha(jWo)| = Q
<Ha(jWo) = 0°

Ovvero Q è il guadagno del filtro alla pulsazione Wo.


BPF2 (constant skirt gain, peak = 0dB)
======================================
Il filtro ha caratteristiche simili al precedente tranne per il fatto che il valore di Ha(jWo)
Ë sempre 0dB; variando Q si va a variare solo la forma della campana e non il valore di picco.

ba0 = 0
ba1 = Wo/Q
ba2 = 0
aa0 = 1
aa1 = Wo/Q
aa2 = Wo^2

ottenendo quindi

               (Wo/Q)*s
Ha(s) = -----------------------
         s^2 + (Wo/Q)*s + Wo^2
Si vuole implementare un filtro LPF biquad parametrico come plugin VST; i parametri
che Ë possibile variare sono la frequenza di cutoff fo e il fattore di qualità Q.

Teoria:
Un filtro biquad è descritto dalla trasformata Z

        b0 + b1*z^-1 + b2*z^-2
 H(z) = ------------------------
        a0 + a1*z^-1 + a2*z^-2
 
Per ottenere i coefficienti del filtro che andiamo ad implementare, partiamo da un 
filtro analogico di secondo ordine e lo trasformiamo in un filtro digitale.

Un filtro passa-basso (LPF - Low Pass Filter) di secondo ordine è descritto
dalla trasformata di Laplace:

                 Wo^2
 Ha(s) = -----------------------
          s^2 + (Wo/Q)*s + Wo^2
 
essendo Q il fattore di qualit‡, ovvero il guadagno del filtro alla pulsazione
di cutoff e Wo (rad/s) la pulsazione di cutoff. 
Questa pulsazione in realtà non coincide con quella del filtro digitale in quanto 
bisogna fare un'operazione di "pre-warping", ovvero fare in modo che la trasformazione 
da filtro analogico a digitale preservi la frequenza di cutoff.
Detta wo = 2 * pi_greco * fo la pulsazione voluta e Fs la frequenza di campionamento, 
il pre-warping si calcola così:

 Wo = 2*Fs*tan(wo/(2*Fs));

A questo punto si applica la trasformazione da dominio analogico a dominio digitale,
sostituendo ad s l'espressione della traformazione bilineare:

        2     1 - z^-1
 s <-- --- * ----------
        T     1 + z^-1

Dopo qualche passaggio algebrico si ottengono i coefficienti:

 b0 = Wo^2 * T^2 * Q;
 b1 = 2*b0;
 b2 = b0;
 a0 = b0 + 2*Wo*T + 4*Q;
 a1 = 2*b0 - 8*Q;
 a2 = b0 - 2*Wo*T + 4*Q;

Per filtrare il nostro segnale basterà eseguire la trasformata Z inversa di H(z):

 a0*y(n)+ a1*y(n-1) + a2*y(n-2) = b0*x(n) + b1*x(n-1) + b2*x(n-2) 
 
ovvero:

 y(n) = (b0/a0)*x(n) + (b1/a0)*x(n-1) + (b2/a0)*x(n-2) - (a1/a0)*y(n-1) - (a2/a0)*y(n-2)



Pratica:

I seguenti passi si riferiscono a MSVisualC++ 6, ma sono del tutto analoghi ad altri ambienti
di sviluppo C++.

1) Creare la cartella "VST_LPF_biquad"

2) Creare un nuovo progetto scegliendo una applicazione DLL vuota (File -> New -> Win32 Dynamic-Link Library);
contestualmente viene creato un Workspace.

3) All'interno della cartella "VST_LPF_biquad" copiare le seguenti cartelle prese dal VST SDK 2.4:
"pluginterfaces", "win" e "vst2.x" (sotto "public.sdk\source")

4) Aggiungere al progetto i file contenuti nelle cartelle prese dal VST SDK 2.4 (Project -> Add to Project -> Files...)

5) Aggiungere alle Project Options (Project -> Settings... -> C/C++) la seguente stringa: 

 /I "./"
 
 Questa opzione è importante! Serve per dire a VisualC++ dove cercare i file per l'inclusione (quelli
 specificati dal #include) diversi da quelli standard.
 
6) Ora finalmente possiamo implementare il plugin; prima di tutto dobbiamo capire quali parametri
andremo a variare e che range dovranno assumere; poi perÚ bisogna mapparli tra 0.0 e 1.0, perchÈ cosÏ
vuole VST:

Parametri:
VST (da 0.0 a 1.0):
fCutoff - cutoff frequency (Hz) 0.0 -> 20 Hz, 1.0 -> 5 kHz, 0.5 -> 2.5 kHz (default)
fQ - quality factor 0.0 -> -12 dB, 1.0 -> +12 dB, 0.5 -> 0 dB (default)

Reali:
fCut = fMin + (fMax - fMin) * fCutoff
Q = 10^((qMin + (qMax - qMin) * fQ)/20) (inversione dei dB)


1- Cosa vuol dire VST?
Virtual Studio Technology. E' la tecnologia implementata da Steinberg (www.steinberg.de) per realizzare host e plugin per la creazione di processori di segnali audio (effetti e virtual instruments).
Dato che Steinberg trae beneficio dalla disponibilità di plugins, viene incoraggiato lo sviluppo di plugin di terze parti. Per far sì che tutti i plugin funzionino correttamente con le applicazioni host, Ë disponibile un SDK (Software Development Kit) con alcune istruzioni su come iniziare lo sviluppo di plugin e su come mantenerne la compatibilit‡.

2- Cos'Ë un host VST?
E' un'applicazione che fornisce i flussi audio che vengono processati dal codice dei plugin VST. Di solito Ë un programma che simula via software uno studio di registrazione (es. Cubase).

3- Cos'Ë un plugin VST?
Essenzialmente, un plugin VST Ë un puro processore audio, e non un'applicazione. E' un componente utilizzato all'interno di una'applicazione host.
PuÚ essere un processore di suono (un "effetto") oppure un virtual instrument (VSTi), ovvero un simulatore di uno strumento o un sintetizzatore musicale. Steinberg fornisce un SDK (Software Development Kit) in C++, anche se esistono traduzioni in altri linguaggi (es. in Delphi: http://www.axiworld.be/vst.html).
Dal punto di vista dell'applicazione host, un plugin VST Ë una "scatola nera" con un numero arbitrario di ingressi, uscite (MIDI o Audio) e parametri. L'applicazione host non deve conoscere i dettagli implementativi del plugin per poterlo usare. Il plugin puÚ utilizzare qualsiasi parametro sia necessario per svolgere l'elaborazione ma, in funzione delle capacit‡ dell'applicazione host, puÚ permettere che i cambiamenti ad alcuni parametri possano essere automatizzati dall'applicazione host.
Il codice sorgente di un plugin VST Ë indipendente dalla piattaforma, ma l'installazione dipende dal sistema:
- Su Windows, un plugin VST Ë una DLL (Dynamic Link Library) multi-threading. La directory di default per i plugin VST installati nel sistema Ë definibile nel registro di configurazione di Windows sotto la chiave "HKEY_LOCAL_MACHINE\SOFTWARE\VST\<VSTPluginsPath>".
Ogni host puÚ avere una sua specifica directory dove si aspetta di trovare i plugin (consultare il manuale utente dell'applicazione host utilizzata).
- Su Mac OS X, un plugin VST Ë un Bundle. E' necessario definire il nome del plugin nel file plist.info con la chiave CFBundleName.
- Su BeOS e SGI (MOTIF, UNIX), un plugin VST Ë una shared Library.

4- Dove si reperisce il VST SDK?
Il VST SDK Ë reperibile dalla sezione "3rd Party Developers" del sito di Steinberg (http://www.steinberg.de/324_1.html).

5- Quali sono i requisiti per scrivere un plugin VST?
- Bisogna avere conoscenze di base di programmazione in C++ e di Elaborazione Numerica dei Segnali.
- Bisogna avere un'applicazione host in grado di ospitare plugin VST (un host freeware Ë disponibile qui: http://www.hermannseib.com/english/)
- Bisogna avere un ambiente di sviluppo C++ (es. MS VisualC++)
- Bisogna Scaricare il VST SDK dal sito di Steinberg (vedi punto 4).

6- Come si puÚ procedere per scrivere un plugin VST?
Ovviamente non esiste un'unica risposta a questa domanda, il modo di procedere nella creazione di un plugin Ë limitato solo dai confini della fantasia e della bravura del programmatore.
Comunque sia, una linea guida sul modo di procedere per la creazione di un plugin VST potrebbe essere la seguente:

- Disegnare uno schema a blocchi della funzionalit‡ da implementare

- Mettere in evidenza quali siano i parametri controllabili dall'utente

- Associare ai parametri altrettanti nomi di variabili di tipo float; se lo si ritiene opportuno si possono creare apposite classi o strutture dati per descrivere l'insieme dei parametri.
 ATTENZIONE: VST assume che i parametri varino tra 0.0 e 1.0, estremi compresi, come scritto nella documentazione:
 "All parameters - the user parameters, acting directly or indirectly on that data, as automated by the host, are 32 bit floating-point data. They must always range from 0.0 to 1.0 inclusive [0.0, +1.0], regardless of their internal or external representation."
 Es. se un parametro in realt‡ assume solo 2 stati A e B, per discriminarli, il controllo da effettuare sar‡ del tipo:
 if(valore < 0.5) A;
 else B;

- Creare il codice della logica di elaborazione nel corpo del metodo processReplacing() e/o processDoubleReplacing(), secondo lo schema a blocchi disegnato

ATTENZIONE: 
"Audio processing in the plug is accomplished by one of 3 methods, namely process (), processReplacing () and processDoubleReplacing (). While process () takes input data, applies its processing algorithm, and then adds the result to the output (accumulating), processReplacing () and processDoubleReplacing (), overwrite the output buffer.

Note:
The accumulating process mode is deprecated in VST 2.4. Please implement processReplacing (mandatory!) and processDoubleReplacing (optional).
Audio data processed by VST Plug-Ins is 32 bit (single precision) and optionally 64 bit (double precision) floating-point data. The default used range is from -1.0 to +1.0 inclusive [-1.0, +1.0] (where 1.0 corresponds to 0dB, 0.5 to -6dB and 0.0 to -oodB). Note that an effect could generate values above this range."


- Creare i file:

 <<nome_plugin>>main.cpp <-- contiene l'istanziazione del plugin
 <<nome_plugin>>.h <-- contiene la dichiarazione delle costanti, delle classi, dei parametri e dei metodi e l'inclusione dei file necessari per l'implementazione del plugin
 <<nome_plugin>>.cpp <-- contiene l'implementazione del plugin



contenuto del file <<nome_plugin>>main.cpp:

-------------------------------------------------------------------------
#ifndef __<<nome_plugin>>__
#include "<<nome_plugin>>.h"
#endif

AudioEffect* createEffectInstance (audioMasterCallback audioMaster)
{
 return new <<ClassePlugin>> (audioMaster);
}
-------------------------------------------------------------------------



contenuto del file <<nome_plugin>>.h:

-------------------------------------------------------------------------
#ifndef __<<nome_plugin>>__
#define __<<nome_plugin>>__

#include "<<sorgenti_vst_sdk>>/audioeffectx.h" // include il sorgente dell'SDK in cui Ë definita la classe AudioEffectX

// il plugin estende la classe AudioEffectX
class <<ClassePlugin>> : public AudioEffectX
{
public:
 <<ClassePlugin>> (audioMasterCallback audioMaster); // dichiarazione costruttore
 ~<<ClassePlugin>> (); // dichiarazione distruttore

 virtual void processReplacing (float **inputs, float **outputs, VstInt32 sampleFrames);
 // ...
 // dichiarazione di tutti gli altri metodi necessari
}

#endif
-------------------------------------------------------------------------




contenuto del file <<nome_plugin>>.cpp:

-------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
// includere tutte le librerie che servono...

#ifndef __<<nome_plugin>>__
#include "<<nome_plugin>>.h"
#endif

/* NB: con la versione 2.4 di VST, il metodo process() Ë DEPRECATO, come scritto nella documentazione:
 * "Accumulating process mode is deprecated in VST 2.4! Use AEffect::processReplacing instead!"
 *
 * Usare il metodo:
 * processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames)
 * per campioni a 32 bit
 */

void <<ClassePlugin>>::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames)
{
 /*
 * Qui ci va la logica di processing
 */
}

// a seguire l'implementazione di tutti i metodi dichiarati nel file <<nome_plugin>>.h

-------------------------------------------------------------------------