![NTS-1 digital kit](https://cdn.korg.com/de/products/upload/ff3798231500ed6717faad7216a17efc_pc.jpg)
![NTS-1 digital kit](https://cdn.korg.com/de/products/upload/00b6c56cc46b0872301eb620ffdf79a3.jpg)
SoundCloud
![](https://cdn.korg.com/de/products/upload/92115d698c059dd463f2bf35a32c9d47_pc.png)
NTS-1 digital kit
PROGRAMMABLE SYNTHESIZER KIT
Unterstützung für Ihren Einkauf
Support
Mehr Infos
Verwandte Seiten
Verwandte Produkte
Verwandte News
[DOTEC-AUDIO x Nu:Tekt] So erstellt man eigene Effekte für den NTS-1
Das Erstellen von benutzerdefinierten Plug-ins für den NTS-1 mag abschreckend wirken, aber keine Sorge!
In dieser 5-teiligen Artikelserie werden wir zusammen mit DOTEC-AUDIO erkunden, wie man Schritt für Schritt eigene Effekte für das NTS-1 erstellt!
Artikel #1: Selbst erstellte Effekte mit dem NTS-1 verwenden
Hallo und guten Tag! Mein Name ist Shinji Iijima von der Plugin-Entwicklungsfirma DOTEC-AUDIO.
Bis jetzt haben wir ausschließlich Audio-Plugins für PC und iOS (iPhone/iPad) entwickelt.
https://dotec-audio.com/
![](https://cdn.korg.com/de/products/upload/aa8aa86dee37dafc8a195ca4655d972d_pc.jpg)
Das „Nu:Tekt NTS-1 digital kit" (im Folgenden als NTS-1 abgekürzt) ist ein äußerst interessanter und zugleich erschwinglicher Bausatz für digitale DIY Synthesizer. Warum also ist DOTEC-AUDIO, ein PC-Plugin-Entwickler, an diesem Synthesizer so interessiert? Es liegt daran, dass man mit diesem Synthesizer eigene Programme schreiben und die so entwickelten Sounds (Oszillatoren) und Effekte in den NTS-1 direkt verwenden und einsetzen kann.
Eine weitere interessante Eigenschaft des NTS-1 ist, dass er einen Stereo-Audio-Eingang (Line-Eingang) besitzt. Das bedeutet, dass Sie einen Klangerzeuger oder schlichtweg ein Musikinstrument anschließen können. Auf diesem Wege lässt sich der NTS-1 nicht nur als Synthesizer, sondern auch als kompaktes, eigenständiges Effektgerät verwenden.
Mit anderen Worten: Mit dem NTS-1 erhalten Sie einen Synthesizer, den man (inklusive sämtlicher Effekte) komplett frei programmieren kann. Der Gedanke, einen eigenen Synthesizer inklusive seiner Effekte eigens programmieren zu können, lässt selbst bei wenig geübten Programmierern die Begeisterung aufflammen. Lassen Sie Ihrer Kreativität freien Lauf.
Okay, dann fangen wir mal an. Schauen Sie sich zunächst dieses Video an!
Schauen Sie sich ebenfalls dieses Video an!
Beide zuvor genannten Effekte enthalten einen Teil der Multieffekt-Funktionalität von DOTEC-AUDIOs "DeeFX", die auf den NTS-1 übertragen wurde.
In dieser Serie werden wir den Quellcode der Effekte veröffentlichen und zugleich vertiefen; aber zunächst möchte ich erklären, wie Sie diese beiden Effekte herunterladen und im NTS-1 installieren können.
Wenn Sie bereits einen NTS-1 gekauft haben, sollten Sie diese Effekte unbedingt herunterladen, installieren und auf jeden Fall ausgiebig testen! Dabei wünsche ich jetzt schon viel Spaß.
So installieren Sie die Effekte
- 1/ Installieren Sie zunächst den NTS-1 Sound Librarian
Installieren Sie zunächst die Software NTS-1 Sound Librarian auf Ihrem Computer (Win/Mac).
Folgen Sie diesen Anweisungen, um die Software zu installieren. Stellen Sie sicher, dass Sie den NTS-1 korrekt an Ihren Computer angeschlossen haben. - 2/ Laden Sie die Effektdateien herunter
Laden Sie als nächstes diese von DOTEC-AUDIO entwickelten Effektdateien herunter.
Download Bitcrusher
Download Stop FX
Wenn Sie diese ZIP-Datei dekomprimieren, erhalten Sie die Dateien "bitcrusher.ntkdigunit" und "stopfx.ntkdigunit". Laden Sie diese Dateien in den Sound Librarian. - 3/ Laden Sie die Daten in den Sound Librarian
Um die Dateien zu laden, gehen Sie in das Menü "File", klicken Sie auf "Import User Unit" und wählen Sie die dekomprimierten Dateien aus. Wenn Sie dies getan haben, wird der Effekt "bitcrusher" zur Kategorie "USER MODULATION FX" und der Effekt "stopfx" zur Kategorie "USER DELAY FX" hinzugefügt.
Gehen Sie zuletzt in das Menü "Send/Recv." und wählen Sie "Send User Data", um diese beiden Effekte an den NTS-1 zu übertragen.
Sobald die Effekte übertragen sind, können Sie "XXX" aus "Mod" am NTS-1 und "XXX" für das Delay auswählen. Viel Spaß beim Experimentieren mit diesen Effekten.
Bis zum nächsten Mal!
Artikel #2: Vorbereitung für die Entwicklung
Hallo, zusammen! Ich bin Shinji Iijima von DOTEC-AUDIO.
Letztes mal haben wir über die Effekte gesprochen, die wir bereits für den NTS-1 programmiert hatten. Haben Sie diese schon einaml ausprobiert? Diesmal möchte ich Ihnen die Entwicklungsumgebung erklären, die Sie benötigen, um Ihre eigenen, originalen Effekte (und Oszillatoren) für den NTS-1 zu erstellen.
Die Grundlagen, die Sie hierfür benötigen, finden Sie auf der Unterseite zum “logue SDK” , aber diesmal möchte Ihnen ein paar weitergehende Details mit an die Hand geben. Ich möchte außerdem darauf hinweisen, dass alle Dinge, die ich hier anspreche, absolut kostenlos sind. Besonders interessant ist sicherlich auch, dass Sie die Möglichkeit haben, unter Windows, macOS oder Linux zu programmieren, also bestehen auch hier keine größeren Hürden. (Beachten Sie bitte allerdings, dass die einzelnen Installationsschritte je nach verwendetem Betriebssystem unterschiedlich ausfallen können.)
logue SDK
https://korginc.github.io/logue-sdk/
Lassen Sie uns für den Einstieg einmal darüber sprechen, was das “SDK” eigentlich ist. SDK steht für “Software Development Kit”, also eine Sammlung an Tools, die benötigt werden, um eine bestimmte Software zu entwickeln. So verwendet man beispielsweise das Windows SDK, um Programme für Windows zu erstellen, oder das iOS SDK zur Entwicklung von iPhone Apps. Um neue Inhalte für den NTS-1 zu erstellen, verwenden wir das “logue SDK”.
https://github.com/korginc/logue-sdk
Bei dem SDK, das sie hier finden, handelt es sich bereits um das logue SDK. Am besten verwenden Sie die Git Software, um sicherzustellen, dass Sie immer über die aktuellste Version des SDK verfügen, aber für diesen Fall müssen Sie zunächst einmal wissen, wie diese verwendet wird.
Aus diesem Grund genügt es auch einfach, die oben aufgeführte Website aufzusuchen und die grüne “Clone or Download” Schaltfläche anzuklicken, um die Dateien als ZIP-Archiv herunterzuladen. Beachten Sie aber bitte, dass das SDK von Zeit zu Zeit upgedatet wird, weshalb es tatsächlich besser wäre, die Git Software zu verwenden, um immer über die aktuellste Version des SDK zu verfügen.
MSYS2 (für Windows)
https://www.msys2.org/
Das sicherlich schwierigste Thema beim Aufbau einer NTS-1 Entwicklungsumgebung unter Windows dürfte wohl “MSYS2” sein. Daher möchte ich dies hier etwas genauer erklären. Alle GNU Befehle, die mit dem logue SDK verwendet werden, führen unter der Haube Befehle innerhalb des Unix Betriebssystems aus.
Unter Linux, das im Prinzip nichts anderes ist als Unix, oder macOS, das auf Unix basiert, müssen daher nur eine geringe Anzahl an Tools installiert werden. Unter Windows jedoch wird eine zusätzliche Umgebung benötigt, die Unix-Befehle ausführen kann. Vereinfacht ausgedrückt handelt es sich bei MSYS2 um eine Plattform, mit der sich Unix Befehle auch unter Windows ausführen lassen. Daher führen Sie Ihre Befehle auch innerhalb des “MSYS2 “-Fensters aus, wenn Sie unter Windows und MSYS2 programmieren.
Suchen Sie einfach online nach “NTS-1 MSYS2”, und Sie werden unterschiedliche Artikel finden, die die hierfür benötigten Schritte aufführen. Ich möchte Ihnen hiermit ans Herz legen, diese Websites als Referenzen zu verwenden.
GNU Arm Embedded Toolchain
https://github.com/korginc/logue-sdk/tree/master/tools/gcc
Um Programme entwickeln zu können, benötigen Sie einen “Compiler”, also eine Software, die Programme, die von Menschen geschrieben wurden, in eine Maschinensprache übersetzt, die von einem Computer gelesen werden kann. Innerhalb des logue SDKs werden wir die “GNU Compiler Collection” (kurz GCC) verwenden.
Die GCC selbst ist bereits ein so komplexes Thema, dass wir ein dickes Buch damit füllen könnten, weshalb ich dies einmal für den Moment überspringen möchte. Für den Einstieg soll es genügen, dass die “GNU Arm Embedded Toolchain”, die auf dieser Seite besprochen wird, ein Set aus Programmierwerkzeugen darstellt, das auch die GCC enthält.
Die Bezeichnung “Arm Embedded” bezieht sich auf den “STM32F4” Chip mit einer Arm CPU, der im NTS-1 verwendet wird. Die Toolchain, auf die wir uns beziehen, wird zur Entwicklung von Programmen verwendet, die auf dieser CPU laufen.
Wie Sie der Readme-Datei entnehmen können, müssen Sie lediglich das vorbereitete Shell-Skript ausführen, um die Installation durchzuführen.
GNU Make
https://github.com/korginc/logue-sdk/tree/master/tools/make
Bei “Make” handelt es sich um ein Tool, dass es Ihnen ermöglicht, einen einfachen Befehl zu konfigurieren, um Ihnen den Aufwand zu ersparen, mehrere Befehle hintereinander auszuführen und die Dateien jedes Mal auf’s Neue kompilieren zu müssen. (Auch Make ist ein Tool, über das wir sicherlich ein komplettes Buch verfassen könnten.)
Make benötigt keine zusätzliche Installation auf macOS, Linux und Windows (und MSYS2), aber wir werden gesondert erklären, wo es anderweitig installiert werden muss.
Info-ZIP
https://github.com/korginc/logue-sdk/tree/master/tools/zip
Dies ist ein Tool, mit dem sich ZIP-Archive erstellen lassen, und genau wie GNU Make benötigt es keine spezielle Installation auf jeglichen Plattformen.
logue-cli (optional)
https://github.com/korginc/logue-sdk/tree/master/tools/logue-cli
Hierbei handelt es sich um eine “optionale” Komponente, und nicht jeder wird sie benötigen. Stellen Sie sich diese am besten wie eine kommandozeilenbasierte Version der "Sound Librarian Software” vor, die wir beim letzten Mal angesprochen hatten. Beachten Sie hierbei bitte, dass die Sound Librarian Software sowohl für Windows als auch für macOS verfügbar ist, nicht aber für Linux.
Aus diesem Grund benötigen Sie dieses Tool, falls Sie vorhaben, unter Linux zu entwickeln.
Nachdem Sie alle diese Komponenten erfolgreich installiert haben, erzeugen Sie zunächst einmal das Programm (Build) für die Beispielkomponenten, die im SDK enthalten sind.
Wir verwenden hier die Schritte für das “Demo Project Build (Waves)” .
https://korginc.github.io/logue-sdk/) to build.
Wenn Sie die Befehle erfolgreich ausgeführt haben, wird eine Datei mit dem Namen “waves.ntkdigunit” erzeugt. Diese sollte nun als “waves.&rdquo in den “USER OSCILLATORS” Bereich der Sound Librarian Software geladen werden. Wenn Sie so weit gekommen sind, ist der nächste Schritt bereits das Programmieren! Zugegebenermaßen ist es zunächst ein wenig aufwendig, alle benötigten Komponenten lauffähig einzurichten.
Die gute Nachricht ist jedoch, dass es im Internet zahlreiche Informationen über diese Tools gibt, weshalb ich Ihnen empfehlen möchte, sich dort nach Lösungsansätzen umzusehen, falls sich Schwierigkeiten bei der Einrichtung Ihrer Entwicklungsumgebung ergeben sollten.
Bis zum nächsten Mal!
Artikel #3: Original-Effekt erstellen, Teil 1
Hallo, mein Name ist Frank Shigetora, und ich bin Sound Producer bei DOTEC-AUDIO.
Da es nun für uns an der Zeit ist, gemeinsam unseren Effekt zu kreieren, bin ich als Entwickler des DSPs für diesen Abschnitt zuständig.
Haben Sie bereits Ihre Entwicklungsumgebung eingerichtet, wie wir es im letzten Teil dieser Artikelreihe erklärt hatten?
Ich gehe einmal davon aus, dass Sie zuvor lediglich die Command Zeile benutzt haben oder zum ersten Mal eine Programmierumgebung erstellen.
Innerhalb von MSYS2 können wir zu einem anderen Ordner wechseln, indem wir statt die Maus zu verwenden den Befehl “cd
Für sehr lange Ordnernamen geben Sie zunächst die ersten paar Zeichen ein und drücken Sie dann die TAB-Taste, um die passenden Ordnernamen anzeigen zu lassen, die mit diesen Zeichen beginnen.
Wenn Sie eine Übersicht über alle Inhalte des aktuellen Ordners anzeigen möchten, geben Sie den Befehl “ls” ein und drücken Sie Enter.
Sie sollten also zunächst einmal wissen, dass Sie mit “cd” in einen anderen Ordner wechseln und mit “ls” dessen Inhalte anzeigen können.
Um eine Stufe innerhalb des Dateipfads zurückzugehen, geben Sie “cd ..” (mit zwei Punkten) ein.
Tatsächlich verwenden wir auf UNIX-basierten Sytemen den Begriff “directory” für einen Ordner. Aber da wir ja auch Windows und Mac Computer verwenden, bleiben wir zunächst einmal bei dem Begriff “folder” . (Dies sollte eigentlich kein größeres Problem darstellen, aber trotzdem wollten wir einmal darauf hingewiesen haben.)
Wenn wir das “logue SDK” in den user’s Hauptordner von MSYS2 installieren, wird uns die Arbeit später leichter fallen.
Beispiel: Bei der Installation von MSYS2 direkt auf das Laufwerk C: unter Windows:
C:\msys64\home\
Okay, wenden wir uns nun den interessanten Themen zu. Lassen Sie uns zunächst einmal die unten stehende Datei downloaden.
NTS-1 Effect Template
Entpacken Sie danach die Dateien in diesen Ordner.
korg\v1.1\platform\nutekt-digital
Hier haben wir die Templates (Vorlagen), die wir zur Erstellung von Modulations-, Delay- und Reverb-Effekten benötigen. Tatsächlich findet sich sogar ein Template innerhalb des loge SDKs, aber wir möchten die Sache etwas vereinfachter angehen, damit wir unseren Workshop reibungslos durchführen können. Denn falls Sie sich noch nicht wirklich mit der Ordnerstruktur auskennen sollten, könnte es passieren, dass Sie die Strukturen der Beispielordner untereinander verwechseln und nicht mehr wissen, wo genau Sie nun ihre Dateien hin kopieren sollen.
Über die Template Ordnerstruktur, die wir in diesem Beispiel verwenden wollen.
Die Templates, die von DOTEC-AUDIO erzeugt wurden, verwenden in diesem Beispiel die folgende Struktur:
- ld (Ordner)
- tpl (Ordner)
- main.c
- Makefile
- manifest.json
- project.mk
Der erste Ordner “ld” beinhaltet die Definitionsdatei für die Effekte, die wir erzeugen wollen. Ohne zu tief ins Detail gehen zu wollen, erinnern wir uns einmal daran, dass wir Effekte aus den “Mod,” “Delay” und “Reverb,” Kategorie erstellen können, und dass deren Ordnerinhalte anders aussehen als diese hier.
Genauso beinhaltet der “tpl” Ordner die Templatedatein für unterschiedliche Effekttypen. “ld” und “tpl” müssen also je nach Art des Effekts korrekt ausgewählt warden, den Sie erzeugen wollen, aber sie müssen nicht editiert werden.
Bei “Makefile”, “manifest.json” und “project.mk” handelt es nich um Defitionsdateien. Die Dateien, die Sie im Zuge Ihrer Effektentwicklung umschreiben müssen, sind “manifest.json” und “project.mk”.
Wir werden am Ende des Artikels genauer erklären, wo die Dateien bearbeitet werden müssen.
Die “main.c” Datei beinhaltet das eigentliche Effektprogramm.
Diesmal warden wir einen “bitcrusher” Effekt erstellen, wie er sich auch in der Demo befindet. Dieser Effekt ist eine vereinfachte Version des Bitcrusher Effekts aus DeeFX, die für unsere Lernzwecke erstellt wurde.
Spielen wir doch zunächst einmal etwas mit dem fertigen Effekt herum.
Laden Sie zunächst die
Bitcrusher Dateien herunter und entpacken Sie die Dateien in diesen Ordner: korg\v1.1\platform\nutekt-digital
Unter MSYS2 gehen wir nun zu “korg\v1.1\platform\nutekt-digital\bitcrusher”, was ganz einfach funktioniert, wenn Sie den TAB-Tasten-Trick verwenden, den ich weiter oben erklärt hatte.
Sobald Sie den “bitcrusher” Ordner mittels des cd Befehls geöffnet haben, verwenden Sie “make” , wie wir es beim letzten Mal erklärt hatten.
Erinnern Sie sich noch? Geben Sie “make” ein und drücken Sie die Enter Taste.
Sie warden eine lange Kette von Zeichen vorbeiziehen sehen, bis schließlich folgendes angeszeigt wird:
Packaging to ./bitcrusher.ntkdigunit
Done
...falls alles glatt läuft. Eine Datei mit dem Namen “bitcrusher.ntkdigunit” sollte im “build” Ordner erzeugt werden. Wie beim letzten Mal auch werden wir diese Datei in den NTS-1 übertragen. Dieser Weffekt wirkt sich besonders stark aus, wenn Sie den Drehregler A bewegen.
Alles klar, dann schauen wir uns doch einmal den Quellcode an!
Wechseln Sie zu Notepad, Hidemaru oder welchen Text-Editor Sie auch immer bevorzugen mögen, und öffnen Sie dann die “main.c” Datei, die sich im “bitcrusher” Ordner befindet.
Am Anfang der Datei sehen Sie:
#include "usermodfx.h"
#include "float_math.h"
Wir werden die erste Zeile benötigen, um modfx zu erstellen. Die zweite Zeile wird für Fließkommaoperationen benötigt.
Es gibt zwei unterschiedliche Methoden zur Größenanzeige von Zahlen in Audiodateien: Feste Dezimalwerte und Fließkommawerte. Fließkommawerte werden für gewöhnlich in unserem alltäglichen Leben verwendet, in der gleichen Weise wie wir sagen "50% mehr." Dies ist einfach leichter zu verstehen. Die Schattenseite hiervon ist allerdings, dass die entsprechenden Berechnungen langsamer ausgeführt werden.
Feste Dezimalwerte werden schneller umgesetzt, lassen sich aber nicht ganz so einfach nachvollziehen. Sie sind auch schwieriger zu handhaben, weil es unterschiedliche formate gibt 8wie z. B. das Q Format), mit denen die Anzahl der Ziffern für ganze Zahlen und Dezimalzahlen festgelegt werden.
Verzagen Sie aber nicht! Der NTS-1 verfügt über einen Prozessor, der darauf spezialisiert ist, Fließkommaberechnungen durchzuführen!
Es gibt keinen Grund dazu, ihn nicht zu verwenden, also tun wir das doch einfach!
Nun aber zum nächsten Abschnitt.
static float rate, lastSampleL,lastSampleR;
static uint32_t count;
Dies hat zur Aufgabe, die “boxes” zu benennen, in die wir Daten ablegen, die “variables.” genannt werden.
Hieraus eregeben sich drei statische Fließtyp Variablen, die als “rate”, “lastSampleL” und “lastSampleR”, benannt werden, sowie eine uint32_t-type statische Variable, die “count” heißt.
Diese heißen “(global) variable declarations,” und stellen einen Dreh- und Angelpunkt des Programms dar. Starten Sie doch einmal eine Websuche mit einer Phrase wie
“C variable declarations”, um mehr darüber zu erfahren. Dadurch sollten Sie auch Genaueres zur Bedeutung und den unterschiedlichen Typen des Begriffs “static” erfahren.
Einfach ausgedrückt: Sollten Sie nicht wollen, dass der Inhalt der variable jedes Mal gelöscht wird, wenn ein Prozess ausgeführt wird, setzen sie diese auf “static”, und der Inhalt wird beibehalten.
Der “static” Typ wird anders verwendet. Suchen Sie doch einfach mal nach “static variables”.
Okay, wie geht’s weiter?
void MODFX_INIT(uint32_t platform, uint32_t api) { lastSampleL = 0.f; lastSampleR = 0.f; count = 0; }
Das Obenstehende wird als “function” bezeichnet.
Eine Funktion wird durch eine Bezeichnung für jeden Programmprozess dargestellt, der selbst als Function Name bezeichnet wird. Funktionen lassen sich über ihren Namen von allen Möglichen Stellen des Programms aufrufen und in Kombinationen miteinander verwenden, um eine einzelne Kette von Ergebnissen — Ihre Software zu erzeugen. Funktionen sind ähnlich einzelnen Bauteilen, wie sie für ein Automobil verwendet werden.
Value returned Function name (argument)
{
Processes to execute
}
Hier wird das Format beschrieben, das für Funktionen verwendet wird. Der Ausdruck “value returned” bezieht sich darauf, welche Art Daten verwendet warden, um die Ergebnisse des Prozesses zu erhalten, der von der Funktion ausgeführt wird. Die Bezeichnung “argument” bezieht sich auf die Daten und deren Typ, die der Funktion geliefert werden, wenn diese aufgerufen wird.
Diesmal schreiben wir einen Initialisierungsprozess. Der Inhalt der Variable, die wir erzeugt haben, wird auf “0” zurückgesetzt, aber wofür steht eigentlich das “f” in “0.f”? Dies bedeutet einfach nur, dass wir uns auf einen Float Typ festlegen. Führen Sie hierzu doch einfach einmal eine Online-Suche durch.
Okay, was ist mit dem “void” im zurückgelieferten Wert gemeint? Dies bedeutet, dass keine Werte in den Ergebnissen zurückgeliefert wurden. Daher ist das erste Mal, dass wir den Initialisierungsprozess durchführen, auch das letzte Mal, dass wir den “void” Wert erhalten..
Prima, das ist schon alles, was diese Funktion erledigen soll.
Nun wollen wir uns der hauptsächlichen Berechnung widmen. Wir springen direct zur Erklärung der Funktion, die sich ganz unten befindet.
void MODFX_PARAM(uint8_t index, int32_t value)
{
const float valf = q31_to_f32(value);
switch (index) {
case k_user_modfx_param_time:
rate = valf;
break;
default:
break;
}
}
Wow, das sieht schon ziemlich kompliziert aus!
Alles, was hier passiert, ist, dass Werte von den beiden A- und B-Drehreglern empfangen werden, die dann wiederum in unsere Variable übertragen werden.
Wenn diese Drehregler bewegt werden, ruft der NTS-1 diese Funktion auf. Nach dem Aufrufen dieser Funktion werden der Reglertyp “index” und sein Wert “value” als Argument empfangen.
Dieser Wert wird als fixe Dezimalzahl angegeben, wie wir es vorab besprochen hatten, weshalb wir ihn zunächst einmal in das Fließkomma-Format umwandeln müssen. In diesem Beispiel konvertieren wir das fixe Dezimalformat Q31 in das 32-bit Fließkommaformat und speichern das Ergebnis in einer Variable mit dem Namen “valf”. Wenn der Inhalt der Daten nicht verändert werden soll, verwenden wir “const”. Führen Sie doch auch einmal hierzu eine Online-Suche durch, falls Sie weitere Details benötigen.
Dies wird durch den “switch case” Ausdruck ausgeführt. Einfach ausgedrückt zeigt dies die Berechnung an, die durchgeführt wird, wenn die Variablen, die wir mit dem Ausdruck “switch” spezifiziert hatten, mit diesen speziellen Fällen übereinstimmen.
Diesmal verwenden wir lediglich die Daten des Drehreglers A, also werden die Inhalte von “valf” in der “rate” Variable gespeichert, wenn die k_user_modfx_param_time (Indexname von Drehregler A) Daten empfangen werden.
Die Variable “rate” wird für die Hauptberechnung verwendet, weshalb ich diese Funktion als erste erklärt habe.
Nun geht’s weiter zum Hauptteil..
void MODFX_PROCESS(const float *main_xn, float *main_yn,
const float *sub_xn, float *sub_yn,
uint32_t frames)
Mit der Funktion MODFX_PROCESS können wir diese sechs Argumente erhalten:
Main Input: main_xn
Main Output: main_yn
Sub Input vom Oszillator: sub_xn
Sub Output vom Ozillator: sub_yn
Gesamtzahl der Frames: frames
Die Variablennamen, die mit einem Sternchen versehen sind, rufen eine Referenz auf. Sie zeigen an, dass sich in dieser Funktion, wenn ein Wert geändert wird, auch die Variable ändert, die den Aufruf startet. Das bedeutet im Prinzip, dass die Daten, die empfangen werden, das Original darstellen und keine Kopie. Ich möchte Ihnen empfehlen, sich tiefergehend mit den Themen Call by Reference und Weitergabe von Werten zu beschäftigen.
Obwohl Sie es sich vielleicht schon beim Anschauen des Arguments gedacht haben, ist alles, was Sie tun müssen, die verschiedenen Effektberechnungen für die Daten entsprechend der Anzahl der Frames in “main_xn” durchzuführen, und diese dann als Output an main_yn zu senden.
Diesmal werden wir die Sub In- und Outputs nicht verwenden.
Hier ist der Einleitungsteil::
for(uint32_t i = 0; i < frames; i++){
Dies besagt, dass wir das, was sich innerhalb der Klammern { } befindet, so oft wiederholen müssen wie angegeben. Dies wird als “for” Ausdruck bezeichnet, und wir verwenden es, um die Berechnung für die Anzahl der Frames zu wiederholen.
// Prepare the L and R audio
const float inL = main_xn[i * 2];
const float inR = main_xn[i * 2 + 1];
Die beiden Slashes (//) am Anfang markieren einen Kommentar. Verwenden Sie diese, um Notizen in Ihrem Programm zu hinterlassen, da sie so keine Auswirkung auf den Ablauf des Programms haben.
Nun wechseln die Stereo Audiokanäle aufgrund der Eingabe für “main_xn” zwischen links und rechts hin und her.
Wir können diese Berechnungen abwechselnd durchführen, aber um für etwas mehr Klarheit zu sorgen, übertragen wir sie jedes Mal in die Variablen “inL” und “inR”.
Gehen wir einen Schritt zurück, um die Struktur von “main_xn” zu erklären, sehen wir den Text “LRLRLR”, der einen Datensatz von “LR” anhand der Anzahl der Frames darstellt. Die Datenanzahl entspricht der doppelten Frame-Anzahl. Variablen, die mehrere Datensätze enthalten, werden als “array Variablen.” bezeichnet. Sie sollten in der Lage sein, diesen Begrff zu verstehen, nachdem Sie eine entsprechende Online-Suche danach durchgeführt haben. Die Anzahl dieser Daten ist als Anzahl der Elemente bekannt. Die Anzahl der Elemente in “main_xn” ist doppelt so hoch wie die Anzahl der Frames, die in “frames” geschrieben werden.
Um genauer zu spezifizieren, welches Element genau verwendet werden soll, nutzen wir die rechteckigen Klammern [ ]. Der schwierige Teil daran ist, dass die Zahlen bei 0 starten, also zeigt main_xn[0] das erste Element an.
Also steigt in diesem “for” Ausdruck der Wert der Variable “i” bei jedem Durchgang um den Wert 1, solange der Wert geringer ist als “frames”.
Da “i” mit 0 beginnt, entspricht die Anzahl der Durchgänge auch derer der Frames, allerdings nur solange die Anzahl unterhalb von “frames” endet.
Sie denken jetzt vielleicht You might be thinking, “Warum starten die Zahlen nicht bei 1?”. In dem Fall würde die Schleife enden, sobald der Wert die gleiche Zahl erreicht wie “frames”. So gesehen besteht also nur eine einzige Chance, dass die Werte gleich sein können. Machen Sie also einen Fehler in Ihrer Programmierung und überschreiten diesen Wert, landen Sie in einer Endlosschleife. Sollte das passieren, könnte das Programm abstürzen. Oder der darauffolgende Prozess würde bei 0 beginnen, was ganz praktisch sein könnte. Dies stellt eine ziemlich gute Überleitung zum nächsten Thema dar.
Sie werden sehen, dass die Schreibweise wie folgt aussieht: “[i * 2]”, “[i * 2 + 1]” und so weiter. Da LR abwechselnd erscheint, ist der Wert 0 “L”, und der Wert 1 “R”. Es ist ganz einfach zu verstehen, wenn Sie sich den Sachverhalt wie folgt vorstellen: Wenn “i” gleich 0 sind [0 * 2][0 * 2 + 1], ergeben “0” und “1”; wenn “i” 1 ist, 2... und so weiter.
Da ich also nun ein selbtsgefälliges Grinsen im Gesicht trage, nachdem ich all dies erklärt habe, kommen wir nun endlich zu Geheimnis, wie der Bitcrusher funktioniert.
Um zunächst einmal zu erklären, was ein Bitcrusher eigentlich tut: Dieser Effekt führt zu einer “groberen” Samplerate, da höhere Samplerates einen detaillierteren Klang abbilden können. Was ich mit “grober” meine, ist dass die Samples in größeren Abständen abgetastet werden bzw. die einzelnen Schritte (Auflösung) weniger definiert sind. Diesmal werden wir die Abtastrate verringern und den Datenoutput mit einer gewissen Regelmäßigkeit abändern. Wenn der Datenoutput regelmäßig ist wie z. B. “12345678”, erzeugt dieser Effekt einen Output von “11335577”. Dies resultiert in einem Klang, der einer Reduzierung der Samplerate sehr ähnlich ist, allerdings könnte man argumentieren “dies ist kein richtiger Bitcrusher Effekt (Reduzierung der Quantisierung)!” Diesen Einwand kann ich vollkommen nachvollziehen.
Fakt ist, dass viele Leute Verständnisschwierigkeiten damit haben könnten, wenn wir plötzlich beginnen, über bit Opetarionen zu sprechen. Außerdem liefern die ganzen Bitcrusher Effekte, die es heutzutage gibt, eher einen harschen Sound, indem sie die Samplerate reduzieren, keinen unterschwelligen Effekt durch das Auslassen einzelner Samples. Aus diesem Grund habe ich Ihnen eine etwas interessantere Erklärung geliefert. Okay, machen wir weiter!
// The larger the value, the grainier the sample
uint32_t skip = rate * 64;
Hier verwenden wir die Daten, die wir vorher von den Drehreglern erhalten haben. Endlich ist es soweit! Die Daten, die wir erhaten haben, reichen von 0 bis 1. wollen wir also einen Wert von 64 aus dem maximalen Wert (1) erhalten, müssen wir ihn mit 64 multiplizieren.
Um dies leichter verständlich zu gestalten, ersetzen wir dies durch die Variable “skip”.
// Update lastSample only when count is 0
if(count == 0){
lastSampleL = inL;
lastSampleR = inR;
}
Sie werden diesen Abschnitt besser versthen, wenn wir etwas weiter voran geschritten sind. Zunächst einmal werde ich die Berechnung erklären.
Dies erneuert die Variable die den Output enthält, allerdings nur dann, wenn die “count” Variable gleich “0” ist. Anders ausgedrückt: Wenn der Wert nicht “0” ist, ist “lastSmpleL/R” der gleiche Wert wie zuvor.
“count” startet bei 0, da wir sie initialisiert hatten (siehe oben), und wird plötzlich auf den “inL/R” Wert gesetzt.
// Continue lastSample sound
main_yn[i * 2] = lastSampleL;
main_yn[i * 2 + 1] = lastSampleR;
count++;
Dies macht genau das Gegenteil von dem, was wir gerade getan haben, indem der Input in L und R aufgeteilt wird. Der Output wird hier für die L und R Sounds in “main_yn” geschrieben. Im Prinzip schreiben wir hier also den selben Sound, den wir gerade in “inL/R” abgelegt haben. Nichts hat sich verändert.
Übrigens sehen wir hier auch ein “count++” am Ende. Dies bedeutet, dass der “count” um eins erhöht wird.
Daher wird “0” zu “1”. Mit dieser Methode können wir auch “i++” für den “for&rdquo Ausdruck schreiben. Dadurch wird “i” bei jedem Durchgang um eins erhöht. Eine weitere Möglichkeit hierfür wäre “count = count + 1”.
Wenn der Zähler den Wert “1” erreicht, wird “lastSampleL/R” beim Start des nächsten Durchgangs nicht mehr abgeändert. Die gleichen Sample-Daten werden ausgegeben. Also wann kommen wir wieder zurück zu null?
// reset count to 0 if skip exceeded
if(count > (int)skip) count = 0;
Ja, genau hier!
Zuvor hatten wir einen Wert von einem Drehregler empfangen, und wenn dieser Wert multipliziert mit 64 den “count” überstieg, wurde er auf 0 zurückgesetzt.
Also werden bei größeren “skip” Werten genau dieselben Daten ausgegeben, ohne den eigentlichen Input zu beachten, wodurch wir eine Absenkung der Abtastrate für die Sampledaten erhalten.
Wir sind also am Ziel!
Zum Schluss haben wir noch dies hier:
}
}
Tatsächlich ist dieser Teil ziemlich wichtig. Die geschlossene Klammer ganz oben bezieht sich auf den “for” AUsdruck, und die Klammern ganz unten markieren das Ende der Funktion. Generell nutzen wir diese Art Klammern { } um alle möglichen Arten von Prozessen einzuschließen.
Je länger ein Prozess ist, desto höher ist auch die Wahrscheinlichkeit, dass wir vergessen, die abschließende Klammer zu setzen! Achten Sie also sorgfältig darauf, denn alleine diese Unachtsamkeit wird dazu führen, dass Ihr Build nicht funktionieren wird.
Außerdem ist eine solche Art von Fehler ziemlich schwer aufzuspüren. Es ist nicht ungewöhnlich, das Programmierer enormen Zeitaufwand betreiben müssen, um einen solchen Fehler aufzuspüren.
Natürlich stele auch ich hier keine Ausnahme dar! Wenn Sie diese Art Fehler finden, wollen Sie am liebsten den Computer aus dem Fenster werfen und sich selbst an den Kopf schlagen.
Diesmal ist das Programm “Modfx”, und wir können es aus dem “tmpMod” Template erzeugen.
Ich ermutige sie, dies mit der “main.c” Datei zu vergleichen, damit Sie erkennen, was hinzugefügt wurde.
Versuchen Sie auch einmal, die Were in “skip” durch andere zu ersetzen und testen Sie dann das Ergebnis.
Zum Schluss werden wir noch den Namen des Effekts, den wir kreiert haben, in den “name” Parameter in “manifest.json” und im “PROJECT” Parameter in “project.mk” eingeben. Hierfür können Sie einen Text Editor verwenden.
Das nächste Thema: StopFX
Das wird eine ziemliche Herausforderung, weshalb ich hoffe, dass Sie sich darauf freuen!
Vielen Dank, dass Sie diesen langen Artikel gelesen haben!
Artikel #4: Original-Effekt erstellen, Teil 2
Hallo, ich bin Frank Shigetora, Sound Producer bei DOTEC-AUDIO. Diesmal werden wir einen weiteren Effekt erstellen, weshalb ich in meiner Eigenschaft als Designer der DSP für diesen Teil zuständig bin.
Ich setze die Themen als gegeben voraus, die bereits in Artikel Nummer 3 abgedeckt wurden, bitte gehen Sie also der Reihenfolge nach.
Diesmal werden wir einen recht komplexen Effekt kreieren, sogar noch komplexer als der letzte.
Ziemlich cool, oder? Hört sich an, als würde man eine Schallplatte plötzlich anhalten. Nun werde ich Ihnen alles zeigen, was man dazu benötigt, diesen Effekt zu erstellen.
Bitte laden Sie das Beispiel "stopfx" herunter und entpacken es nach:
korg\v1.1\platform\nutekt-digital
Zunächst einmal sehen wir uns den Effekt bei der Ausführung an. Nutzen Sie MSYS2, um in den Ordner
korg\v1.1\platform\nutekt-digital\stopfx
zu wechseln und führen Sie dann den „make“ Befahl aus. Sollten Sie gerade kein Wort verstanden haben, möchte ich Sie bitten, sich noch einmal den Artikel Nummer 3 anzusehen.
So, hat es funktioniert? Sehen wir uns doch einmal den Quellcode an.
Öffnen wir einmal die Datei „main.c“, die sich im Ordner „stopfx“ befindet.
Am Anfang der Datei finden Sie folgendes:
#include "userdelfx.h"
#include "float_math.h"
#include "buffer_ops.h"
Die erste Zeile benötigen wir zum Erzeugen des delfx (des Delay Typen), erinnern Sie sich noch an die zweite Zeile?
Genau, wie beim letzten Mal benötigen wir diese für die Fließkomma-Operationen.
In der dritten Zeile stoßen wir auf etwas neues. Dies ist nötig, um viele verschiedene Sounds in den Speicher des NTS-1 schreiben zu können.
Wie Sie sich sicherlich vorstellen können, benötigt man sehr viel Speicherplatz, um lange Musikdateien zu sichern. Das SDK des NTS-1 beinhaltet eine Funktion für Speicheroperationen (Verwaltung des Speichers), weshalb die dritte Zeile unseres Codes den Ausdruck “#include buffer_ops.h”für eben diesen Zweck enthält.
Hier gilt es gleich einen neuen Begriff zu lernen: „Buffer“. Vermutlich haben Sie das Wort schon einmal irgendwo gehört. Ein Buffer ist ein Ort, an dem Daten für eine Verarbeitung kurzzeitig gespeichert oder beibehalten werden. Diesmal werden wir den Buffer für das Array verwenden, das wir beim letzten Mal erzeugt hatten. Wir wollen eine ganze Menge Sounddaten in diesem Buffer speichern, also müssen wir ein großes Array anlegen.
Als nächstes schauen wir uns die Funktionen an. Wir führen wie beim letzten Mal den Initialisierungsprozess durch:
void DELFX_INIT(uint32_t platform, uint32_t api) { buf_clr_f32(s_delay_ram, BUFFER_LEN); z = 0; z2 = 0; prev = 0; next = 0; p = 0.f; slope = 0.f; isStop = 0; }
Beachten Sie hierbei bitte, dass diese Funktion speziell für delfx verwendet wird. Tatsächlich ist der einzige Unterschied aber der Name.
Für die anderen Variablen können wir einfach „0“ als Startwert verwenden; für den Buffer allerdings nutzen wir die Funktion “buf_clr_f32()”zur Initialisierung.
Halten wir hier kurz ein, um uns die letzte Funktion genauer anzuschauen, die ich auch erklären werde.
void DELFX_PARAM(uint8_t index, int32_t value)
Diese Funktion wird genauso verwendet wie beim letzten Mal, sie hat allerdings einen anderen Namen. Falls Sie sich nicht daran erinnern können, gehen Sie bitte noch einmal zum Artikel Nummer 3 zurück. Dadurch sollte Ihnen vieles klarer erscheinen.
Nun geht es aber endlich an’s Eingemachte, das DSP Processing.
void DELFX_PROCESS(float *xn, uint32_t frames)
Mit der Funktion DELFX_PROCESS können wir die beiden folgenden Argumente erhalten:
Main Input/output: main_xn
Total number of frames: frames
Wow, diesmal sind es sogar nur zwei, nicht sechs wie beim letzten Mal! Das sieht doch ganz einfach aus. Der Dreh- und Angelpunkt in diesem Fall ist, dass beim letzten Mal bei „modfx“ Input und Output getrennt waren, aber in diesem Fall wird „xn“ sowohl für Input als auch für Output verwendet.
Dies funktioniert dadurch, dass „xn“ Input-Daten enthält, die so verarbeitet werden, dass die Daten, die am Ende der Funktion überschrieben werden zu den Output-Daten werden.
// Copy to sampling buffer
for(uint32_t i = 0; i < frames * 2; i++){ s_delay_ram[z++] = xn[i]; if(z > BUFFER_LEN - 1) z = 0; }
Dies legt alle Input-Daten in den Buffer ab, den wir bereits vorbereitet haben. Hierbei handelt es sich um den „For“ Ausdruck, den wir beim letzten Mal zum Loopen verwendet hatten, sowie einen „if“ Ausdruck, das Entscheidungen anhand bestimmter Bedingungen fällt. „frames“ bezeichnet die Anzahl an Frames für einen Kanal, also setzen wir diesen Wert für zwei Kanäle (LR) auch auf das Zweifache. Etwas verwirrend mag das „++“ hinter dem „z“ erscheinen. „z“ vergrößert sich nach dem „zth“ Input in den Buffer.
Wie auch beim Array beginnt hier alles bei 0, richtig? Daher wird der Wert beim Reset auf 0 gesetzt, um einen ungewollten Crash zu verhindern, falls wir den Wert erreichen, der durch BUFFER_LEN repräsentiert wird. Wie ich schon beim letzten Mal ausführte, kann diese Bedingung auf unterschiedliche Arten geschrieben werden. Diesmal haben wir uns für eine Standardlösung entschieden, so dass die Bedingungen erfüllt werden, sobald der Wert erreicht oder überschritten wird. Bitte beachten Sie, dass der NTS-1 nicht beschädigt wird, wenn die Software abstürzt, machen Sie sich also keine Sorgen über mögliche Fehler.
Auf diese Weise kehren wir zum Wert Null zurück und lassen den Buffer aktiv (wir schreiben also weiterhin hinein). Dies nennt sich „Ring Buffer“ oder „Cyclic Buffer“.
Als nächstes erzeugen wir den Wert, mit dessen Hilfe sich die Wiedergabegeschwindigkeit verlangsamen lässt.
// Reset when knob is turned all the way to the left
if(rate < 0.1f){ isStop = 0; rate = 0.f; }else{
Dies ist der Prozess, mit dem zu der originalen Wiedergabegeschwindigkeit zurückgekehrt werden kann, wenn der Drehregler ganz nach links gedreht wird. Wenn die Bedingungen auf “rate== 0.f“ gesetzt werden, gestaltet sich die Anwendung denkbar schwierig, also setzen wir sie besser auf “0.1f”oder kleiner, um uns etwas Spielraum zu verschaffen, mit dem wir arbeiten können. Wir wollen ja nicht, dass der Regler unbeabsichtigt reagiert, wenn sich unsere Finger nur minimal bewegen.
“isStop”wird dazu genutzt, festzulegen, ob der Stop-Effekt aktiviert ist oder nicht. In der Sprache C können wir die Zahlen 1 und 0 jeweils als Ersatz für wahr und falsch verwenden, also setzen wir den Wert vor dem Stop-Effekt auf 0.
Wenn der Effekt geändert wird, wird ein passender Wert von dem Regler abgeholt, also werden wir auch die „rate“ zurücksetzen.
Beachten Sie auch bitte, dass dieser Codeabschnitt mit einem „else“ endet. Diesen Ausdruck verwenden wir dann, wenn wir einen Prozess hinzufügen möchten, der dann auftritt, wenn vorgegebene Bedingungen nicht erreicht werden. Als ich diesen Code zum ersten Mal gesehen hatte, habe ich das als „erase“ gelesen… Das war zugegebenermaßen nicht wirklich clever. :-)
Wenn also die Bedingungen des „else“ Ausdrucks erfüllt werden, wird der Regler gedreht und somit der Stop-Effekt ausgeführt.
Sehen wir uns zunächst einmal den Initialwert an, wenn der Regler zum ersten Mal bewegt wird. Hier wird es langsam etwas ungemütlich.
// Determine the beginning stop point of the knob This is disabled when the knob is turning, since “isStop” is “1”.
if(!isStop){ isStop = 1; rate = 0.f; p = (z - frames * 2.f) / 2.f; if(p < 0.f)p = (BUFFER_LEN - 2.f * p) / 2.f; } // this is not the closing bracket for “else”, but for the “if” statement just before
Das Ausrufezeichen vor dem “isStop” bedeutet nicht, dass es verrutscht ist – es bedeutet vielmehr eine Verneinung, oder ganz einfach „nicht“. Wenn also „isStop“ nicht wahr ist, bzw. anders ausgedrücktfalsch ist, wird der Code innerhalb des „if“-Ausdrucks ausgeführt. Da wir diesen Wert vorher auf „0“ gesetzt hatten, wird also die Ausführung angestoßen. Trotzdem wollen wir nicht, dass die Verarbeitung erneut startet, wenn der Regler anfängt, sich zu drehen, also setzten wir „isStop“ direkt danach auf „1“. Dies bedeutet, dass die Verarbeitung ausgesetzt wird, bis der Regler wieder ganz nach links gedreht und der Wert zurückgesetzt wurde. Diese Art der Verarbeitung wird als „Flag“ bezeichnet. Es handelt sich um die gleichen Begriff "Flag" als wenn wir sagen würden „setze die XXX Flag“.
In einigen Fällen kann es vorkommen, dass der Regler in der gleichen Position wie vorher auch auf dem richtigen Wert sitzt… In einem solchen Fall überspringen wir die vorherigen Bedingungen und setzen „rate“ zurück, um direkt hierher zu springen.
Die Variable „p“ zeigt den Wiedergabepunkt des Buffers an. Da wir die doppelte Menge Daten ders Wertes „frames“ im Buffer abgelegt haben, subtrahieren wir nun bis zu dem Punkt, an dem wir begonnen haben, Daten zu speichern, und kehren dann zu diesem Wert zurück. Sie werden sich nun vielleicht fragen „also dividieren wir das ganze nun wieder durch zwei?“, aber das sollte hinterher etwas klarer erscheinen. Als letztes schreiben wir einen Prozess für den „if“ Ausdruck, für den Fall, dass „p“ ein negativer Wert sein sollte. Wenn der Buffer beispielsweise einen Zyklus durchlaufen hat und damit aufgehört hat, Daten zu schreiben, würde das einfache Subtrahieren des Werts von dieser Position aus eine negative Zahl ergeben. Daher subtrahieren wir die Differenz vom Endresultat. Diesmal haben wir eine anpassbare buffergröße, weshalb wir kein negatives Ergebnis erhalten werden. Trotzdem sollten wir uns angewöhnen, uns über Minuswerte in unseren Arrays Gedanken zu machen.
Es scheint, als würden sich die Dinge sehr plötzlich verkomplizieren, sobald wir über Buffer sprechen, aber dies stellt kein großes Problem dar. Sie werden es verstehen.
Übrigens dürfte Ihnen wohl aufgefallen sein, dass dieser „if“-Ausdruck über keine geschweiften Klammern { } verfügt. Da wir lediglich die letzte Zeile verarbeiten, werden diese hier nicht zwingend benötigt. Dies ist ganz nützlich zu wissen, wenn Sie einen kleinen Test durchführen wollen, indem Sie den „if“-Teil hinzufügen.
Noch sind wir nicht heil über die seltsame Straße des „else“-Ausdrucks gekommen (ein kleiner Scherz am Rande).
for(uint32_t i = 0; i < frames; i++){ uint32_t length_mono = BUFFER_LEN / 2;
Da hier der “for”-Ausdruck erscheint, warden wir ab dieser Stelle den Effekt anwenden und kontinuierlich die „xn“ Variable überschreiben.
Sie werden sich vermutlich fragen, warum wir hier die „frames“ nicht einfach verdoppeln. Dies liegt darin begründet, dass hier ein Stereosignal vorliegt, also zwei Zeilen gleichzeitig verarbeitet werden müssen.
Sie werden dies später verstehen, also ist es okay, falls es Ihnen im Moment noch nicht als sinnvoll erscheint.
Zunächst einmal verwenden wir das Längenequivalent zum Mono Teil des Buffers, um eine Variable mit einem leicht verständlichen Namen festzulegen. Die ganze Zeit den Wert zu halbieren macht nicht gerade Spaß, und der Code wird durch die zusätzlichen Formeln noch unverständlicher. Da es sich hierbei um eine Ganzzahl handelt, wird der Typ der variable auf „uint32_t“ gesetzt.
// Get the before-after index of the playback position, and the interpolating coefficient
prev = (uint32_t)p; slope = p - prev; next = prev + 1; if(next > length_mono - 1) next = 0;
Jetzt kommt der wichtige Part!
Das Grundgerüst dafür, das Audiosignal langsamer wiederzugeben, ist, dass wir den Wert zwischen zwei Samples berechnen und das Sample langsam vorwärtsbewegen. Genauso als würden wir langsam „ei----ns, zw---ei, usw.“ zählen, errechnen wir den verlängerten Abschnitt von zwei Punkten aus.
Der Typ der „p“-Variable ist „float“, also erhalten wir ungerundete Tahlen wie 1,2 und 5,4. Bei Arrys gibt es allerdings keine „Position 1,2“. Aus diesem Grund nehmen wir an, dass der Wert für den Wert an der Position „1,2“ 20% näher zum ersten Wert liegt.
Demzufolge können wir also voraussetzen, dass die Werte „1, 2, 3“ für die Verzögerung auf die gleiche Art voranschreiten: „1,3 , 1,6, 1,9, 2,2, 2,5…“. Im Fall von Wellenformhöhen repräsentieren diese Werte die Wellenform an ihrem Höchstwert.
Aufbauend auf diese Erklärung wandeln wir „p“ nun in der ersten Zeile zu einer Ganzzahl um. Diese Art, Code nach Typen zu schreiben, wird als „Casting“ bezeichnet. Wir nutzen dies, um Typen umzuwandeln.
Durch die Umwandlung in eine Ganzzahl können wir beispielsweise den Wert „7,4“ von „p“ in „7“ abwandeln, indem die Nachkommastelle wegfällt, und dann diesen Wert in „prev“ abspeichern.
In der zweiten Zeile wird dann die Zahl, die von „prev“ im Zuge der Umwandlung des ursprünglichen Werts „p“ in eine Ganzzahl abgezogen wurde, in „slope“ gespeichert.
Ja! Hierbei handelt es sich um die abgerundete Dezimalzahl. Im voranstehenden Beispiel wäre das also 0,4.
Vielleicht hatten sich das einige von Ihnen bereits gedacht. Wir verwenden diese Methode, um einen Wert zu erhalten, der 40% über dem Wert im „pth“ Platzhalter liegt.
Wo führt das alles nun eigentlich hin? In der dritten Zeile erstellen wir das nächste Array nach „pth“. Diese Information legen wir in „next“ ab.
Dies ist für den letzten „if“-Ausdruck wichtig, damit der Wert „next“ nicht das Array übersteigt. Das haben Sie zuvor schon einmal gesehen, richtig?
// Get the sound of the playback position
float s1L = s_delay_ram[prev * 2]; float s2L = s_delay_ram[next * 2]; float s1R = s_delay_ram[prev * 2 + 1]; float s2R = s_delay_ram[next * 2 + 1];
Nun aber endlich zum eigentlichen Höhepunkt der ganzen Geschichte!
„s1“ ist der erste Sound zwischen den beiden Punkten, die wir vorhin angesprochen hatten, und „s2“ stellt L und R dar, die wir vorbereitet hatten, also den letzten Sound.
„prev“ und „next“ sind die Array-Zahlen der beiden Punkte, die wir gerade erhalten haben. Der Abschnitt, in dem das Ganze mit zwei multipliziert und ein Paar erstellt wird, funktioniert genauso, wie wir es bereits erklärt hatten, aber da LR abwechselnd in den Buffer eingegeben wird, müssen wir den L-Sound aus den mit geraden Zahlen nummerierten Bereichen des Arrays extrahieren, und das gleiche mit den R-Sounds für die mit ungeraden Zahlen nummerierten Bereiche des Arrays tun.
Die Variable „i“ im „for“-Ausdruck erhöht sich um den Wert eins, weshalb die Ergebnisse bei der Berechnung von 0, 1, 2, 3 und so weiter in gerade und ungerade Zahlen aufgeteilt werden.
Der Grund, warum wir am Anfang durch zwei dividiert hatten, um den Wert „p“ zu erhalten, lag darin begründet, an dieser Stelle die verdoppelten Zahlen zu erhalten. Darum also das Ganze…
float currentL = 0.f,currentR = 0.f; currentL = s1L + (s2L - s1L)*slope; currentR = s1R + (s2R - s1R)*slope;
Wir haben die erste Variable erstellt, um kurzzeitig die Position unseres aktuellen Sounds darin abzulegen. Wir initialisieren sie ohne Sound.
Die nächste Formel errechnet den Wert, von dem sich der Wert des vorangegangen Mittelpunkts ableiten lässt. Wir fügen hier nur einige Prozente hinzu, nämlich die Differenz vom nächsten Wert zum vorausgegangenen Wert. Würden die Werte beispielsweise „0, 10“ betragen, läge der Wert der Position, an der wir null um 20% erhöhen, bei:
0+(10-0)*0.2 = 2
Dies wäre also der Wert, wenn „p“ an der sich an der Position 1,2 befinden würde.
Folglich darf ich Ihnen gratulieren, denn Sie haben die schwerste Hürde gemeistert.
// Overwrite as output.
xn[i * 2] = currentL; xn[i * 2 + 1] = currentR;
Da wir nun in der glücklichen Lage waren, den Wert zu berechnen, den wir für den Sound haben wollten, enrneuern wir nun „xn“ in der Reihenfolge LR.
Der Höchstwert dieser Schleife war „frames“, der den gleichen Wert „i“ verwendet, um LR zu verarbeiten, also sollten wir auch dann keine Probleme bekommen wenn wir den Regler auf die Mittelstellung bringen. Dies können wir auch direkt zu Anfang tun und den Regler so oft wir wollen hin und her bewegen.
Sie dürfen zu Recht stolz darauf sein!
// Advance the playback point according to the “rate” amount p += 1.f - rate; if(p > (float)length_mono - 1.f) p = 0.f; } // “for” ends here } // “else” ends here
Nun führen wir “p” gemäß der „rate“ des nächsten Loops fort.
Das „+=“ bedeutet, dass wir die rechte Seite zur linken Seite hinzufügen. Der Grund, warum wir daraus eine Kehrzahl generieren, ist, dass der Wert am schnellsten voranschreitet, wenn man den Drehregler ganz nach links dreht, um „rate“ auf null zu setzen, und wir möchten, dass er bei „1“ anhält. Beim Wert null schreitet das Array um einen Wert nach dem anderen voran, was der normalen Wiedergabegeschwindigkeit entspricht. Je weiter man den Drehregler nach rechts dreht, also beispielsweise den Wert jedes Mal um 0,3 erhöht, umso langsamer wird die Wiedergabe.
Der nächste „if“-Ausdruck setzt den Wert auf null zurück, sobald „p“ den Wert eines Kanals überschreitet.
Wir führen unsere Arbeit durch die abschließenden Klammern für die „for“- und „else“-Bedingung zu Ende.
Nun sind wir am Ziel unserer Reise angekommen!
Dieses Program ist ein „Delfx“, und wir können es daher aus dem „tmpDelay“ Template erstellen.
Wenn Sie dieses mit „main.c“ vergleichen, werden Sie sehen, wohin Sie das Programm schreiben müssen.
Zum Abschluss schreiben wir den Namen des Effekt, den wir soeben erstellt haben, in den „name“ Paramater in „manifest.json“ sowie in den „PROJECT“ Parameter in „project.mk“.
Obwohl es sich bei diesem Effekt lediglich um eine vereinfachte Version des Stop-Effekts handelt, der sich in „DeeFX“ von DOTEC-AUDIO wiederfindet, sind die verwendeten Prinzipien dennoch die gleichen. Ich glaube, Sie konnten hiervon eine ganze Menge mitnehmen.
Beim nächsten Mal finden Sie hier unseren letzten Artikel: Eine abschließende Zusammenfassung!
Hiermit wäre mein Part, die DSP Lektion, abgeschlossen.
Vielen Dank, dass Sie diesen langen Artikel gelesen haben!
Artikel #5 (vorerst Letzter): Entwicklung eines eigenen Vocoders
Hallo zusammen, ich bin Shinji Iijima von DOTECT-AUDIO.
Ich hoffe, Ihnen haben die letzten beiden Artikel von Frank Shigetora zu den Details des DSP-processing gefallen?
Ich kann mir vorstellen, dass die vorangegangenen Artikel stellenweise sehr einschüchternd waren, oder? Allerdings sind die Verarbeitung der digitalen Signale am Eingang, die Datenausgabe und das Wiederholen verschiedener Prozesse, die Grundlage sämtlicher digitalen Effektprogrammierung.
Die aktuell gewählten Beispielprogramme waren unter 100 Zeilen lang und daher ich hoffe, dass Sie die Begeisterung, mehr über die Programmierung und der im Internet zur Verfügung stehenden Hilfestellungen, nutzen Ihr Wissen weiter zu vertiefen. Auf diesem Wege können Sie immer weiter in die Materie eintauchen und mehr Details über die einzelnen Elemente der NTS-1 Effektentwicklung sowie Oszillatoren (Synthesizer) erfahren, auch wenn wir hier nicht alles im Details bisher erklären konnten.
Mit diesem 5. Artikel schließen wir diese Serie zunächst ab, so dass wir nicht mehr auf die Programme tiefer eingehen werden.
Ich möchte jedoch ankündigen, dass wir bei DOTEC-AUDIO unsere ganze Kraft in die Entwicklung eines 16-Band-Vocoders und eines speziellen Oszillators gesteckt haben, der mit dem NTS-1 perfekt funktioniert! Daher werde ich dieses Mal einige grundlegende Erklärungen zu diesem Thema abgeben, ebenso wie zu den PC-Plugins, die DOTEC-AUDIO bisher veröffentlicht hat.
Was also den von uns entwickelten Vocoder betrifft, so handelt es sich um einen vollwertigen 16-Band-Vocoder.
Schließen Sie ein Mikrofon (das wir "Modulator" nennen) an den NTS-1 an und lassen Sie den Oszillator erklingen, während Sie vokalisieren, um eine coole Roboter-Stimme zu erhalten.
Wie hat es Ihnen gefallen? Ist es nicht erstaunlich, wie man einen 16-Band-Vocoder auf diesem kompakten digitalen Synthesizer zum Laufen bringen kann?
Wir haben es am Ende nur geschafft, indem wir einige versteckte Tricks des NTS-1 genutzt haben. Der tiefe Einstiegt hat sich also durchaus gelohnt. Es ist etwas kompliziert, aber ich werde es hier nun detailliert erklären.
![](https://cdn.korg.com/de/products/upload/ccc1694877b5e0e3ff6299f78e4baefd_pc.jpg)
Zunächst einmal verfügt der NTS-1 über eine AUDIO IN-Buchse.
An diese Buchse können wir ein Audiosignal mit Line-Pegel anschließen, dieses auf diesem Wege mit NTS-1 Effekten versehen oder den Oszillatorsound mit dem Eingangssignal mischen (das ist eine der fantastischsten Eigenschaften dieses Instruments). Das Eingangssignal wird am Ende mit dem Oszillator-Sound gemischt. Das ist keine einfache Aufgabe, aber wir werden es schon meistern.
- Zum Line-Eingang (Audio-Eingangspegel)
Da der NTS-1 Audio-Eingang einen Line-Pegel am Eingang erfordert, würden wir normalerweise einen Mikrofonverstärker oder ein Mischpult anschließen. Durch das Anschließen an einem Mischpult lässt sich der Pegel des Mikrofons einfach nach oben korrigieren.
Auf der anderen Seite bräuchten wir aber ebenso ein Dämpfungsglied, um im Falle eines zu hohen Pegels, jenen reduzieren zu können.
![](https://cdn.korg.com/de/products/upload/2aa24589ebdd86f9bc5e001633f4d3be_pc.png)
Doch glücklicherweise verfügt der NTS-1 über diverse "Globale Parameter", mit denen sich die generelle Einstellungen im Instrument verändern lassen.
Sehen Sie sich diesbezüglich bitte den Abschnitt "Globale Parameter" in der Bedienungsanleitung des NTS-1 an (unten rechts auf Seite 16). Dort gibt es einen Parameter namens "Input Trim".
Bitte laden Sie gegebenenfalls die aktuellste Bedienungsanleitung
hier herunter. Installieren Sie mittels PC auch das neueste Betriebssystem auf Ihrem NTS-1.
Die werkseitige Voreinstellung für diesen Parameter ist "6:-6DB", aber durch das Verändern auf "0:0DB" können wir die Dämpfung sogar komplett deaktivieren. Verwenden Sie diese Einstellung, wenn Sie ein dynamisches Mikrofon oder ein ähnliches Gerät anschließen möchten, ohne einen Verstärker bzw. ein Mischpult zu verwenden. (Für ein Gerät mit Line-Eingang brauchen Sie diese Einstellung nicht vorzunehmen.)
Wenn die Eingangslautstärke immer noch zu niedrig ist, können wir dies beheben, indem wir den Vocoder-Parameter A auf "Gain" setzen und diesen Regler so einstellen, dass die Lautstärke maximal um den Faktor 100 verstärkt wird.
- Zum Audio-Eingang und dem Klang des Oszillators
Ein Vocoder verwendet den Ton eines Mikrofoneingangs (den "Modulator"), um den Klang eines Synthesizers (den "Carrier") zu verarbeiten. Normalerweise benötigen diese beiden Elemente separate Audio-Eingänge, die der NTS-1 allerdings nicht besitzt.
Was hier hingegen passiert, ist, dass die Klänge des Synthesizers und des externen Audio-Signals zusammengemischt und dann vom Effekt verarbeitet werden. Probieren Sie es selbst einmal aus und Sie werden verstehen, worauf ich hinaus will.
Um dieses Mischen allerdings zu verhindern, sind wir auf die kühne Idee gekommen, die Klänge zu trennen, indem wir die Differenz des Stereo Audio-Signals nutzen.
Wenn Sie z. B. den gleichen Synthesizer-Sound im linken und im rechten Kanal verwenden, aber den Mikrofon-Sound nur einem Kanal hinzufügen, können Sie den Sound des Mikrofons extrahieren, indem Sie die Differenz beider Signale verwenden. Das ergibt definitiv einen Sinn.
Lesen Sie sich diese Formel einfach durch und Sie werden sofort erkennen, was gemeint ist:
(Mikro-Signal + Synth-Signal) - Synth Signal = Mikro Signals
Auf diesem Wege trennen wir erfolgreich das Audio-Signal vom Oszillator-Sound am NTS-1 und verarbeiten es separat, um unseren Vocoder überhaupt realisieren zu können.
Abgesehen davon sollten Sie beim Anschluss eines Mikrofons den folgenden kleinen Trick beachten. (Die Aufgabenstellung: der L-Kanal (links) soll als Mikrofoneingang verwendet werden können)
- Bereiten Sie von Anfang an eine Tonquelle vor, bei der Ihre Stimme nur auf dem L-Kanal (links) zu hören ist (der R-Kanal (rechts) sollte stumm sein). Verwenden Sie den Line-Eingang, um das Stereosignal in den NTS-1 zu bringen.
- Verwenden Sie den Line-Eingang eines Mischpults, um das Stereosignal, das nur das Mikrofon-Signal auf dem L-Kanal (links) enthält in den NTS-1 zu leiten.
- Verwenden Sie ein Splitterkabel, um das Mikrofon nur an den L-Kanal (links) anzuschließen.
-
Direkter Anschluss eines dynamischen Mokrofons
-
Anschluss einer Audio-Quelle über ein Splitterkabel
Der einfachste Weg ist es, ein monaurales Mikrofon anzuschließen. Wenn Sie ein Stereo-Mikrofon verwenden wollen, müssen Sie zusätzlich ein Splitterkabel verwenden um zum gleichen Ergebnis zu gelangen. Probieren Sie es gerne aus.
![](https://cdn.korg.com/de/products/upload/8d99d11025c91d5c6be381a936b9d348_pc.jpg)
Wie zuvor schon erwähnt, können Sie eine kostenlose Testversion unseres Vocoders zusammen mit dem Oszillator als Set herunterladen. Dieses können Sie dann 3 Minuten lang testen. Die Vollversion steht für 10$ (zzgl. Steuern) auf unserer DOTEC-AUDIO Webseite zum Kauf zur Verfügung.
https://dotec-audio.com/nts1.html?lang=en
Ehrlich gesagt, bin ich schlichtweg von der Tatsache überwältigt, dass wir den NTS-1 als Vocoder verwenden können.
Wenn Sie schon einmal bis hierher gelesen haben, möchte ich Ihnen kurz vorstellen, wer wir bei DOTEC-AUDIO sind und welche Produkte wir generell anbieten.
DOTEC-AUDIO ist eine Software-Marke, die von dem Musiker und Ingenieur Frank Shigetora und der Firma fmfmsoft Corp. gemeinsam geführt und betrieben wird.
Unser Hauptbetätigungsfeld ist die Entwicklung und der Vertrieb von DAW-Plugins die unter Windows, macOS und iOS (für einige Produkte) verwendet werden können.
![](https://cdn.korg.com/de/products/upload/df1e550e6af4499d93ff2f7ab1fdd2c2_pc.jpg)
Die APP KORG Gadget2 beispielsweise enthält einen Maximizer von DOTEC-AUDIO namens "DeeMax". Eventuell haben Sie auf diesem Wege schon einmal von uns gehört.
Seit fünf Jahren schon existiert die Firma DOTEC-AUDIO und wir haben mittlerweile eine große Bandbreite an Produkten anzubieten. Diese reicht von Musikproduktions-Software, um eigene Songs am PC zu produzieren, bis hin zu Lösungen, um eigene Musik zu veröffentlichen und zu vertreiben. Wenn Sie daran interessiert sind, schauen Sie sich bitte auch gerne unsere Website an. Dort gibt es zudem jede Menge Produkte, die einen engen Bezug zu dieser DOTEC-AUDIO Artikelserie haben.
![](https://cdn.korg.com/de/products/upload/c6b4adc6008482c9b8cc4c75f8479a30_pc.png)
- DeeFX Multi-Effekt
Englische Webseite:
https://dotec-audio.com/deefx.html
Wir haben dieses Produkt im ersten Artikel dieser Serie kurz angesprochen. Es handelt sich hierbei um einen Multieffekt, der Ihnen die Standard-Effekte Distortion, Filter und Delay liefert. Dieses Paket haben wir so erarbeitet, dass es sehr einfach zu bedienen ist und keine Fragen offen bleiben.
Durch das Design klar getrennt, zeigt das Plugin diese drei Effekte, die Sie einzeln ein- und ausschalten und als eigenständige Effektprozessoren verwenden können.
Hinzu kommen die Bitcrusher- und Vinyl-Stop-Effekte, die vereinfachte Versionen dessen sind, was wir in diesem Artikel für die Verwendung mit dem NTS-1 vorgestellt haben.
Dies ist ein Produkt, das auch bei vielen professionellen Musikern sehr beliebt ist.
![](https://cdn.korg.com/de/products/upload/76bb00b62498949d66d3f22a85564906_pc.png)
Der "DeeVocoder"
Englische Webseite:
https://www.dotec-audio.com/deevocoder.html
Wie der Name schon sagt, handelt es sich bei diesem Produkt um den Vocoder der Firma DOTEC-AUDIO. Dieser Vocoder verfügt über einen Filterbank-Vocoder (der jetzt ein Revival erlebt), der zwischen 8-, 16- und 32-Bändern umschaltbar ist und über die Formant-Shift-Funktion verfügt.
Im Gegensatz zu FFT-basierten Produkten bietet unser Vocoder eine überragende Spielbarkeit ohne Latenz und gibt Ihnen die Möglichkeit, eine typisch frech-klingende Roboterstimme zu erzeugen, die normalerweise nur mit analogen Vocodern erzeugt werden kann.
Wir haben großen Wert auf die Positionierung dieses Produkts als Effekt gelegt, denn durch die Verwendung dieses Synth-Plugins als Sidechain-Eingang eröffnet Ihnen der Vocoder als „Carrier“ endlose klangliche Gestaltungsmöglichkeiten. Natürlich kann jeder Audioeingang im Sidechain als „Carrier“ verwendet werden, so dass Sie diesen Plugin-Effekt nach Belieben nicht nur für Synthesizer-Sounds, sondern auch für Gitarre, Bassgitarre, Schlagzeug und mehr einsetzen können.
Da es sich um ein Plugin für den Einsatz mit DAWs handelt, bietet es eine andere Art von Freiheit als der Vocoder, der mit dem NTS-1 arbeitet.
Alle Produkte von DOTEC-AUDIO verfügen über eine Demoversion, die Sie kostenlos ausprobieren können (ohne Benutzerregistrierung oder andere Hürden). Wenn Sie ohnehin Software-Plugins wie VSTs oder Audio-Units verwenden, sollten Sie unsere Produkte unbedingt einmal ausprobieren!
Diese Artikelserie ist Teil einer Idee, die wir gemeinsam mit KORG entwickelt haben. Der Grund dafür war, dass wir darüber berichten wollten, warum der NTS-1 eine so derart ansprechende Plattform für die eigene Programmierung ist.
Das Fantastische am NTS-1 ist, dass seine kompakte Hardware und die Möglichkeit zu programmieren ein sehr hohes Maß an Freiheit bietet.
Außerdem sind wir als professionelle Entwickler so sehr davon begeistert, dass das NTS-1 einen Audioeingang hat und als Synthesizer oder als Effektgerät verwendet werden kann. Es ist wirklich interessant, wie der NTS-1 all das bietet und dabei noch eminent günstig ist. Deshalb haben wir diese Artikelserie angeregt - wir wollten möglichst vielen Menschen die Faszination dieses Produkts nahebringen.
Wie wir immer wieder betont haben, sind die Grundlagen der Audioprogrammierung die gleichen, egal ob Sie den NTS-1 verwenden oder mit einem DAW-Plugin arbeiten.
Auch wenn es unendlich viele Möglichkeiten gibt, kommt es immer wieder darauf an, wie Sie den gewünschten Klang der am Eingang anliegt, am Ausgang klingen lassen wollen.
Wir hoffen, dass wir mit dieser Artikelserie möglichst vielen Anwendern die Möglichkeit gegeben haben, den generellen Aufbau der bevorzugten Musik- und Synthesizer-Apps besser zu verstehen, währenddessen Sie mit dem NTS-1 experimentieren.
Ich möchte mich ganz herzlich bei allen bedanken, die bei dieser Artikelserie mitgelesen haben!
Ich hoffe, Sie alle eines Tages wiederzusehen!
Frank und Iijima von DOTEC-AUDIO