VST Plugins in C++ mit WDL-OL: Signalverarbeitung durch INSERT-Effekte und ihre Auswirkungen

Intention

In diesem Artikel dokumentiere ich meine ersten Erkenntnisse, die ich als Informatiker, Musiker und Cubase-Benutzer bei der Entwicklung von VST Plugins in C++ mache. Der Artikel wirkt daher sehr Ich-bezogen, da ich mich auf meinen eigenen Explorations-Lerntyp konzentriere. Ich bin allerdings kein Egozentriker :-/ …

Vorwort

Als musik- und programmierbegeisterterter DAW-Nutzer (ich bin Cubase Fan), fehlt einem oft noch der Draht zu der eigentlichen Audiosignalverarbeitung, die sich durch (Hall-/Echo-/Sustain-…) Effekt-Algorithmen und Heuristken manifestiert und dem eigentlich Werkzeug das Leben einhaucht, dass es benötigt um eine Audioverarbeitungsaufgabe mit mathematischen Modellen zu erfüllen.

Es ist mir als Informatiker bewusst, dass digitale Signale über eine Soundkarte gesendet werden, um dann über einen A/D-Wandler im analogen, physischen Raum Schalldruckschwankungen über Lautsprechermembranen zu erreichen. Ich kenne WAV-Dateien, und weiß dass diese in unterschiedlichen Sample-Raten (44100 Hz, 22050 Hz, 48000 Hz, 96000 Hz… [Samples/Sekunde])  und Auflösungen (z.B. 16 Bit, 24 Bit, 32 Bit float) existieren.

Als Cubase-Nutzer kenne ich die ASIO-Einstellungen (Menü Geräte –> Geräte konfigurieren) und habe bereits den Parameter Buffer Size bemerkt, welcher sich proportional auf die Eingangs- und Ausgangslatenzen, sowie reziprok auf meine CPU-Auslastung auswirkt.

asio_buffer_size

Als Musiker weiß ich geringe Latenzzeiten natürlich zu schätzen :-). Wer kann schon ein Instrument mit dem richtigen Timing auf den Takt einspielen, wenn er das akustische Ergebnis verzögert hört.

Nach dem Artikel Rechenlast beim Mixen verringern aus der c’t musik kreativ 2016 – S. 113 splittet man aufgrund dieses Buffers die Aufnahme in 3 Teile:

  1. Aufnahmephase: Alle komplexen INSERT-Effekte rausnehmen, kleine Sample Buffer einstellen, alle Spuren in Mono Aufnehmen die nicht zwingend Stereo sein müssen (Gesang, Kick, Bass….). Der Mix wird hierdurch auch transparenter, wenn man die Mono-Spuren im Nachgang im Stereopanorama verteilt.
  2. Mixingphase: Aufwendige Effekte hinzuschalten, Sample-Buffer erhöhen
  3. Masteringphase: Rohmix auf Stereospur mit 24-Bit Wortbreite rendern, hohen Sample-Buffer einstellen, so dass man die Masteringeffektkette wieder auf 96 khz fahren kann.

VST Insert-Effekte

Das Grundgerüst

Nachdem ich nun mit dem WDL-OL Framework von Oli Larkin und Visual Studio 2015 ein kompilierfähiges C++-Grundgerüst dank des Tutorials von Martin Finke erzeugt hatte, bemerkte ich schnell den Anwendungsbereich der Signalverarbeitung, der sich für mich anfänglich unkompliziert durch Probieren und Testen offenbaren sollte. Dabei machte ich ein paar (für den Profi triviale) Feststellungen, die ich hier niederschreiben möchte.

Wie sieht nun ein INSERT-Effekt in C++ aus? Was bekomme ich von der DAW von außen geliefert? Was kann ich damit machen? Wie gebe ich es an die DAW zurück?

basic1

In C++ passiert bei einem INSERT-Effekt alles in der Funktion „ProcessDoubleReplacing“, die als Übergabeparameter

  • die Amplitudenwerte des zu bearbeitenden Signals (*input)
  • die Amplitudenwerte des bearbeiteten Signals (*output)
  • die Puffergröße der ASIO-Einstellungen (hier 512)

als Zeiger erhält. Da diese Zeiger auf den tatsächlichen Wert im output-Buffer zeigen, kann die Funktion ohne Rückgabeparameter auskommen und wird VOID deklariert.

Digitale Verzerrung

Einer der einfachsten INSERT Effekte ist die digitale Verzerrung, bei der lediglich die Amplituden abgeschnitten werden müssen. Im Gegensatz zu einer weichen „analogen“ Verzerrung, wie sie durch die Röhren eine Gitarrenverstärkers statt finden kann und durch die Klangcharakteristik eines hart aufgehängten Lautsprechers in ihrer „Wärme“ abgerundet wird, legt die digitale Verzerrung lediglich durch das Abschneiden von Amplituden (auch Clipping genannt) wert auf Distortion / Verzerrung um jeden Preis.

Ein Gitarrenröhrenverstärker fügt dem Signal zunehmend geradzahlige Harmonische (Obertöne) zu und das Signal wird zunehmend weich begrenzt (soft clipping – Wärme).

Bei der digitalen Verzerrung nähern wir uns einer Rechteckform des Signals an (hard clipping – Digitale Verzerrung).

softvshardclipping

Links: Hard-Clipping (Digitale Verzerrung)
Rechts: Soft-Clipping (Wie bei einer Röhre)

Der Hard-Clipping-Algorithmus (links) flacht also nicht wie rechts die Spitzen ab sondern nimmt einfach den überschreitenden Wert als gegebenes Maximum hin. Programmiert sieht das folgendermaßen aus:

mainfunction

mThreshold: Beinhaltet einen Wert zwischen 0 und 1, der mit einem Regler eingestellt wurde.

fmin(): Gibt hier den input-Amplitudenwert zurück, es sei denn mThreshold wird überschritten – dann wird mThreshold zurückgegeben.

fmax(): Gibt den input-Amplitudenwert zurück, es sei den mThreshold wird unterschritten, dann wird -mThreshold zurückgegeben.

 

Oszillation auf die Welle „draufmodulieren“

Ich schrieb eine Klasse „Oscillator.cpp“, die eine Sinus- bzw. Cosinusschwingung generierte. Zunächst dachte ich, dass das simple „addieren“ einer Welle auf die andere eine völlig andere Klangcharakteristik erzeugen würde…. aber es erzeugte eher etwas wie das „Zusammenmischen“ von zwei Signalquellen, so als ob man ein Signal mit dem Mischpult in das andere „reindreht“. Wenn ich so recht drüber nachdenke kann ein Lautsprecher ja auch nur „eine“ Welle erzeugen… diese Welle muss sich aber je Signalquelle unterscheiden… Man erreicht also das „Mixen“ mehrerer Signalquellen durch das aufaddieren von Schwingungen. Und an dieser Stelle wird einem auch klar, dass eine natürliche Schwingung, beispielsweise der Ton eines Instrumentes, aus mehreren Teilschwingungen besteht und nur durch unsere Erfahrung als zwei oder mehrere unterschiedliche Signalquellen interpretiert werden kann. So hat zum Beispiel bei einem Schlagzeug die Bass-Drum einen hohen „Klick-Anteil“, einen druckbringenden „Körper-Anteil“ und einen tiefen „Bass-Anteil“, den man über einen Equalizer separieren kann, und der sich alleinstehend völlig anders anhört.

basic3

Phasenauslöschung: Wenn sich Wellen gegenseitig auslöschen

Ich richtete den linken und den rechten Kanal nun aufeinander und dachte dass sich das „zugemischte“ Signal durch die 180 Grad Phasenverschiebung (um PI) vollständig auslöschen müsste. In Wirklichkeit wurde es aber nur leiser. Ich vermutete, dass hier die kreisförmige Ausdehnung des Schalls aus den Boxen eine Rolle spielt. Wenn man dies bereits im Rechner macht, ist das Resultat bekanntermaßen Stille.

sin(x)+sin(x+PI)=0;

Der Cosinus ist quasi eine Phasenverschiebung um 90 Grad. In einer DAW könnte dies durch das Verschieben einer Audiospur nach rechts bedeuten.

phase_02

180 Grad ist dann noch weiter geschoben…

phase_03

(Bild-Quelle: delamar.de)

Jetzt werden beim Zusammenmischen der beiden Quellen die Amplitudenwerte addiert, wie es im Abschnitt „Oszillation auf die Welle draufmodulieren“ bereits gezeigt wurde. Und genau diese Tatsache führt also zu einer Phasenauslöschung! Eine Phasenauslöschung kann also auch durch die ungünstige Lage von Audiomaterial auf der Zeitachse stattfinden. Wen man das Material ein Stück nach rechts oder links schiebt (natürlich so, dass es nicht merkbar ist) könnte das Resultat auch wieder anders aussehen.

Wenn ich nun die Boxen aufeinander richte erhalte ich tatsächlich Stellungen im Raum, an denen das Signal fast verschwunden ist. Durch die kreisförmige Ausdehnung des Schalls um die Boxen wird es aber nicht vollständig terminiert.

Hier habe ich mein Experiment dokumentiert:

Schalte ich den Cubase Stereo Out Mix auf Mono, indem ich das „Stereo Enhancer“ Plugin in den INSERT-Effekten des Stereo-Outs auf Mono hinzuschalte (links unten ist ein MONO-Button wenn es geladen wurde), ist das Signal wirklich verschwunden bevor es den Computer verlassen hat (Phasenauslöschung bereits Digital).

VST Instrumente

Der Unterschied zu Insert-Effekten

Im Tutorial von Martin Finke wird ein Python-Skript verwendet, um aus den IPlugSamples ein Template für ein neues Projekt zu generieren. Als Grundlage meiner bisherigen Projekte wurde immer das selbe Startprojekt IPlugEffekt verwendet. Meine Vermutung war zunächst, dass jetzt ein anderes Template als Grundlage für ein VST Instrument genutzt wird. Allerdings kann man einen VST Insert Effect mit wenigen Handgriffen in der Quelldatei „ressource.h“ in ein VST Instrument umwandeln.


// #define PLUG_CHANNEL_IO "1-1 2-2"
#if (defined(AAX_API) || defined(RTAS_API))
#define PLUG_CHANNEL_IO "1-1 2-2"
#else
// no audio input. mono or stereo output
#define PLUG_CHANNEL_IO "0-1 0-2"
#endif

 

// …
#define PLUG_IS_INST 1

// …
#define EFFECT_TYPE_VST3 „Instrument|Synth“

// …
#define PLUG_DOES_MIDI
[/javascript]

„0-1 0-2“ bedeutet kein Input und einen Output (Mono) oder  kein Input und zwei Outputs.

PLUG_IS_INST 1“ macht aus dem INSERT Effect endgültig ein Instrument.

Inputs sind bei Instrumenten unnötig, da ein Instrument selber als Signalquelle für den Gesamt-Mix genutzt wird. Die Funktion „ProcessDoubleReplacing“ bekommt als Übergabeparameter allerdings weiter hin die Inputs geliefert. Ich habe noch nicht herausgefunden wofür das gut sein soll ;-).

Inputs sind bei Instrumenten unnötig, da ein Instrument selber als Signalquelle für den Gesamt-Mix genutzt wird. Die Funktion „ProcessDoubleReplacing“ bekommt als Übergabeparameter allerdings weiter hin die Inputs geliefert. Ich habe noch nicht herausgefunden wofür das gut sein soll ;-).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.