0123456789
list icon
list icon

NTS-1 digital kit NTS-1 digital kit

SoundCloud

Partager

NTS-1 digital kit

PROGRAMMABLE SYNTHESIZER KIT

Support pour votre achat

Support

En savoir plus

Sites connexes

Artistes correspondants

2021.08.06
Moe Shop
Show All

DOTEC-AUDIO x Nu:Tekt] Comment faire des effets personnalisés pour le NTS-1

Créer des plugins personnalisés pour le NTS-1 peut sembler assez intimidant, mais ne vous inquiétez pas !
Dans cette série d'articles en 5 parties, nous allons explorer avec DOTEC-AUDIO comment créer des effets personnalisés pour le NTS-1, étape par étape !

Article #1: Faire fonctionner les effets personnalisés avec le NTS-1

Bonjour à tous ! Je m'appelle Shinji Iijima et je travaille pour DOTEC-AUDIO, une société de développement de plugins.

Jusqu'à présent, nous avons principalement développé des plugins audio uniquement pour PC et iPhone / iPad.

https://dotec-audio.com/

Le "Nu:Tekt NTS-1 digital kit" (ci-après abrégé en "NTS-1") est un kit de synthétiseur numérique à construire soi-même, extrêmement intéressant tout en étant très abordable. Alors, pourquoi DOTEC-AUDIO, un développeur de plugins pour PC, s'intéresse-t-il à ce synthétiseur ? C'est parce que ce synthétiseur vous permet d'écrire vos propres programmes et d'installer ensuite les sons (oscillateurs) et les effets que vous avez développés dans le NTS-1.
 
Un autre aspect intéressant du NTS-1 est qu'il dispose d'une entrée audio stéréo (entrée ligne). Cela signifie que vous pouvez brancher un générateur de sons ou un instrument de musique et utiliser cet appareil non seulement comme un synthétiseur mais aussi comme une unité d'effets autonome et compacte.
En d'autres termes, vous obtenez un synthétiseur que vous pouvez programmer librement, ainsi que des effets. Quand vous entendez cela, cela vous donne vraiment envie de l'essayer vous-même, n’est-ce pas ?
 
OK, alors commençons. Jetez un coup d'œil à cette vidéo !

Il s'agit d'un "bit crusher", un effet qui vous donne un son brut, de faible résolution.
 
Et...
Il s'agit de l'effet "vinyl stop", qui vous donne le son de l'arrêt d'un disque vinyle avec votre main.
 
Ces deux effets contiennent une partie de la fonctionnalité multi-effets du "DeeFX" de DOTEC-AUDIO, adaptée au NTS-1.
Comme vous pouvez le constater, ce qui est intéressant dans ce système, c'est que vous obtenez non seulement les oscillateurs du NTS-1, mais aussi des effets que vous pouvez appliquer à l'entrée audio !
 
Dans cette série d'articles, j'aimerais vous parler du développement d'effets pour le NTS-1, en me concentrant sur ces deux effets.
 
Dans cette série, nous publierons et fouillerons dans le code source de ces effets, mais pour commencer, je vais vous expliquer comment télécharger ces deux effets et les installer dans le NTS-1.
Si vous avez déjà acheté une NTS-1, téléchargez ces effets et jouez avec !

Comment installer

  • 1/ Installez le Sound Librarian NTS-1
    Avant toute chose, installez le logiciel   NTS-1 Sound Librarian dans votre ordinateur (Win/Mac).
    Suivez ces instructions pour installer le logiciel, et assurez-vous de connecter le NTS-1 à votre ordinateur.
  • 2/ Téléchargez les fichiers d'effets

    Ensuite, téléchargez les fichiers d'effets développés par DOTEC-AUDIO.

    Téléchargez le ownload Bitcrusher ici
    Téléchargez l'effet Stop ixi 

    Decompressing this ZIP file will give you the “bitcrusher.ntkdigunit” and “stopfx.ntkdigunit” files. 
  • 3/ Chargez ces fichiers dans le Sound Librarian
     
    Pour charger les fichiers, allez dans le menu "File", cliquez sur "Import User Unit" et sélectionnez les fichiers que vous avez décompressés.
     
    Une fois que vous avez fait cela, l'effet "bitcrusher" est ajouté à la catégorie "USER MODULATION FX", et l'effet "stopfx" est ajouté à la catégorie "USER DELAY FX".
    Enfin, allez dans le menu " Send/Recv. " et sélectionnez " Send User Data " pour transférer ces deux effets vers le NTS-1.
     
    Une fois les effets transférés, vous pourrez sélectionner " XXX " dans " Mod " sur le NTS-1, et " XXX " pour le delay.
Amusez-vous bien à expérimenter ces effets.
 
A la prochaine fois !
 

Article #2: Comment se préparer à développer des effets pour le NTS-1

Bonjour, tout le monde ! Je suis Shinji Iijima de DOTEC-AUDIO.
 

La dernière fois, nous avons parlé des effets que nous avons réalisés pour le NTS-1. Les avez-vous déjà essayés ? Cette fois, je vais vous expliquer l'environnement dont vous aurez besoin pour créer vos propres effets (et oscillateurs) originaux pour le NTS-1.

En fait, les bases que vous devez connaître se trouvent sur la page "logue SDK", donc cette fois, je vais vous donner quelques détails supplémentaires. Je dois préciser que tout ce que j'explique est disponible gratuitement. De plus, l'un des aspects les plus attrayants de ce projet est que vous pouvez développer sous Windows, macOS ou Linux, donc tout le monde peut le faire.

(Notez que les étapes d'installation diffèrent avec chaque OS).

logue SDK
https://korginc.github.io/logue-sdk/
 

Pour commencer, parlons de ce qu'est un "SDK". SDK est l'abréviation de "Software Development Kit", c'est-à-dire un ensemble d'outils nécessaires au développement d'un logiciel spécifique.

Par exemple, vous utiliserez le SDK Windows pour développer des applications pour Windows, ou le SDK iOS pour développer des applications pour l'iPhone. Lorsque nous développons pour le NTS-1, nous utilisons le "logue SDK". https://github.com/korginc/logue-sdk
 

Le SDK qui est publié ici est le SDK de logue lui-même. La meilleure chose à faire est d'utiliser le logiciel Git pour obtenir la dernière version du SDK, mais pour ce faire, vous devez savoir comment utiliser Git.

 

Pour cette raison, il suffit d'aller sur la page Web indiquée ci-dessus et de cliquer sur le bouton vert "Cloner ou télécharger" pour télécharger les fichiers sous forme d'archive ZIP. Notez que le SDK est parfois mis à jour, il est donc préférable d'utiliser Git si vous voulez toujours obtenir la dernière version du SDK.

MSYS2 (pour Windows)
https://www.msys2.org/
 

La question la plus épineuse à traiter lorsque vous envisagez de construire un environnement de développement NTS-1 pour Windows est "MSYS2". J'aimerais m'expliquer à ce sujet maintenant. Toutes les commandes GNU utilisées avec le SDK de logue exécutent des commandes avec le système d'exploitation Unix comme prérequis.

 

Pour Linux, qui est fondamentalement Unix, ou macOS, qui est basé sur Unix, vous serez en mesure d'installer en utilisant un minimum d'outils. Cependant, pour Windows, vous aurez besoin d'un environnement supplémentaire capable d'exécuter des commandes Unix. En termes simples, MSYS2 est une plate-forme permettant d'exécuter des commandes Unix sous Windows. C'est pourquoi, si vous développez avec Windows et MSYS2, vous exécuterez vos commandes dans la fenêtre "MSYS2".

 

Faites une recherche sur le Web sur "NTS-1 MSYS2", et vous trouverez des articles qui énumèrent les étapes spécifiques requises. Je vous encourage vivement à utiliser ces sites comme référence.


GNU Arm  Chaine d'outils embarqués
https://github.com/korginc/logue-sdk/tree/master/tools/gcc
Pour développer des programmes, vous avez besoin d'un "compilateur", qui est un logiciel qui convertit les programmes écrits par les humains en langage machine qui est lisible par un ordinateur. Avec le SDK de logue, nous utiliserons la "GNU Compiler Collection" (GCC en abrégé).

Le GCC est un sujet tellement compliqué en soi que nous pourrions écrire un livre épais à son sujet, donc je vais sauter cette étape pour le moment. Il suffit de dire que la "GNU Arm Embedded Toolchain" expliquée sur cette page est le nom d'un ensemble d'outils de programmation qui inclut le GCC.

Le terme "Arm Embedded" fait référence à la puce "STM32F4" dotée d'un CPU Arm et utilisée sur le NTS-1. La chaîne d'outils à laquelle nous faisons référence est utilisée pour développer des programmes qui fonctionnent sur ce CPU.

Comme vous pouvez le voir dans le fichier Readme, tout ce que vous devez faire pour installer est d'exécuter le script shell préparé.



GNU Make
https://github.com/korginc/logue-sdk/tree/master/tools/make

 "Make" est un outil qui vous permet de configurer une seule commande pour vous épargner la peine d'exécuter de nombreuses commandes et de compiler vos fichiers à chaque fois. (Make est un autre outil sur lequel nous pourrions consacrer un livre entier).

Make ne nécessite pas d'installation spéciale supplémentaire sur macOS, Linux et Windows (et MSYS2), mais nous allons expliquer où il est nécessaire de l'installer.

 


Info-ZIP
https://github.com/korginc/logue-sdk/tree/master/tools/zip

Il s'agit d'un outil utilisé pour créer des fichiers ZIP, et comme pour GNU Make, il ne nécessite pas d'installation spéciale sur pratiquement toutes les plateformes.

logue-cli (optional)
https://github.com/korginc/logue-sdk/tree/master/tools/logue-cli
 

Il s'agit d'un composant "facultatif", et tout le monde n'en a pas besoin. Considérez-le comme la variante en ligne de commande de "Sound Librarian", que nous avons décrit la dernière fois. Notez qu'il existe une version Windows et macOS de Sound Librarian, mais pas une version Linux.

 

C'est pourquoi, si vous envisagez de développer sous Linux, vous devrez installer cet outil. Une fois que vous avez installé ces outils avec succès, construisons (créons le programme pour) les composants d'exemple inclus dans le SDK.


Nous utiliserons les étapes du "Demo Project Build". (Waves)” ( https://korginc.github.io/logue-sdk/) pour créer.

Si vous avez exécuté les commandes avec succès, un fichier nommé "waves.ntkdigunit" sera généré. Il doit être chargé dans le dossier "USER OSCILLATORS" de Sound Librarian sous le nom de "waves". Si vous êtes arrivé jusqu'ici, l'étape suivante est la programmation ! C'est un peu une corvée pour mettre les choses en place et les faire fonctionner.

La bonne nouvelle est qu'il y a beaucoup d'informations sur Internet à propos de ces outils, donc je vous encourage à fouiller sur le Web pour mettre en place votre environnement !

 



A la prochaine !

Article #3: Créer un effet personnel, Part 1

Bonjour, mon nom est Frank Shigetora, et je suis un sound producer ˆ DOTEC-AUDIO. 
Il est temps pour nous de créer notre propre effet, Je vais être en charge de cette partie, en tant que Designer DSP. 

Avez-vous déjà configuré votre environnement de développement, comme nous l'avons expliqué la dernire fois ? 
Je vais’suivre ceux d'entre vous qui ont utilisé la ligne de commande ou qui créent un environnement de programme pour la premire fois.

avec MSYS2, nous allons nous déplacer vers un autre dossier en utilisant la commande “cd ” en remplacement de la souris. Maintenant je vais vous montrer un moyen un peu plus facile de le faire. 
Pour les noms de dossiers vraiment longs, tapez quelques-uns des caractères initiaux, puis appuyez sur la touche TAB pour compléter automatiquement les noms qui correspondent, en commençant par le premier caractère. 
Si vous voulez voir une liste du contenu du dossier actuel, tapez la commande“ls” et appuyez sur Entrée. 
Donc, vous devez juste vous rappeler “cd” pour déplacer dans une autre dossier, et “ls” pour lister son contenu. 
Pour remonter d'un niveau dans le répertoire, tapez “cd ..” (deux fois). 
Actuellement, sur les systems basés sur UNIX, nous utilisons le terme“directory” pour un dossier; mais depuis que nous utilisons Windows ainsi que le Mac, nous le remplaçons par le terme“folder” pour maintenant. (Ceci ne devrait pas vous hérisser le poil mais c’est juste au cas où...) 

Si nous installons “logue SDK” dans le dossier utilisateur ’s de MSYS2, notre travail ultérieur sera plus facile. 
Exemple: lors de l'installation de MSYS2 directement sur le lecteur C: dans Windows 
C:\msys64\home\\korg 

Ok, Passons maintenant aux choses sérieuses, Tout d'abord, téléchargeons le fichier ci-dessous, 

NTS-1 Effect template 

et décompressez les fichiers vers ce dossier. 
korg\v1.1\platform\nutekt-digital 

Ici nous avons les templates pour créer le Mod (modulator), Delay, et effets de Reverb. Actuellement, il y a déjà un template ’dans le logue SDK, mais nous voudrions créer ceci d’une manière plus simple pour que notre leçon se déroule plus facilement pour cette fois-ci. Le fait est que si vous n’êtes pas familier avec la structure de dossier, vous pourriez confondre les structures des dossiers types et ne pas savoir où copier les fichiers.

A propos de la structure du dossier Template utilisé cette fois-ci

Le Template créé par DOTEC-AUDIO utilise cette fois-ci la structure affichée ci-dessous. 
- ld (folder) 
- tpl (folder) 
- main.c 
- Makefile 
- manifest.json 
- project.mk 


Le premier dossier “ld” contient le fichier de définition pour les effects que nous allons créer. Sans entrer dans trop de détails, tentez juste de vous rappeler que les effets que nous créons sont soit des “Mod,” “Delay” et “Reverb,” et que le contenu de leur dossier est différent de celui-ci. 
Dans le même sens, “tpl” contient les fichiers qui sont les modèles pour chaque type d'effet. “ld” et “tpl” doivent être sélectionnés correctement en fonction du type d'effet que vous souhaitez créer, mais vous n'aurez pas besoin de les modifier. 
“Makefile”, “manifest.json” et “project.mk” sont les fichiers de définitions. Les fichiers que vous devrez réécrire en fonction des effets que vous allez créer sont les suivants “manifest.json” et “project.mk”. 
Nous expliquerons où éditer à la fin de cet article. 

Le fichier “main.c” contient le programme d’effets. 

Aujourd’hui, nous allons créer un “bitcrusher”, qui est dans la démo. Cet effet est une version simplifiée de l’effet bitcrusher trouvé dans DeeFX, fait pour nous pour apprendre. 

Ceci mis à part, jouons avec l'effet final. 
Télécharger le  bitcrusher files et décompressez les fichiers dans ce folder: korg\v1.1\platform\nutekt-digital 

En utilisant MSYS2, nous irons (déplacer ) dans “korg\v1.1\platform\nutekt-digital\bitcrusher”, ce qui est facile à faire si vous utilisez l'astuce de la touche TAB que j'ai expliquée plus tôt. 
Une fois que vous êtes entré dans le dossier "bitcrusher" en utilisant la commande cd, utilisez "make" comme nous l'avons expliqué la dernière fois. 
Vous vous en souvenez ? Tapez "make" et appuyez sur la touche Entrée. 
Vous verrez un long bloc de caractères défiler, et enfin vous verrez : 
Packaging to ./bitcrusher.ntkdigunit 

C’est fait 

…Si tout va bien. Un fichier nommé “bitcrusher.ntkdigunit” devrait être généré dans le dossier "build". Comme la dernière fois, nous allons transférer ce fichier vers le NTS-1. Cet effet est fortement appliqué lorsque vous utilisez le bouton A. 

Très bien, regardons le code source! 
Utilisez Notepad, Hidemaru ou l’éditeur de texte que vous préférez, ouvrez le “main.c” fichier localisé dans le dossier “bitcrusher”. 

Au début du fichier, vous pouvez lire : 

#include "usermodfx.h" 
#include "float_math.h" 

Nous aurons besoin de la première ligne pour créer modfx. La seconde ligne est requise pour les opérations à virgule flottante.

Deux méthodes sont utilisées pour indiquer la taille des nombres dans les données audio numériques : la décimale fixe et la virgule flottante. La virgule flottante est couramment utilisée dans notre vie quotidienne, de la même manière que lorsque nous disons "50 % de plus". C'est plus facile à comprendre pour les gens. Toutefois, l'inconvénient de la virgule flottante est que les calculs sont traités plus lentement. 

La décimale fixe est traitée plus rapidement, mais elle est plus difficile à comprendre. Il est également plus difficile à gérer, car il existe différents formats (comme le format Q) pour déterminer le nombre de chiffres des entiers et des décimaux. 

Pourtant, prenez courage ! Le NTS-1 a un processeur intégré qui est dédié à la gestion de la virgule flottante ! 
Il n'y a aucune raison de ne pas l'utiliser, alors faisons-le. 

Maintenant, passons à la partie suivante. 

static float rate, lastSampleL,lastSampleR; 
static uint32_t count; 

Il s'agit ici de mettre une étiquette sur les "boîtes" où nous mettons les données appelées "variables". 
Cela crée trois variables statiques de type float appelées "rate", "lastSampleL" et "lastSampleR", et une variable statique de type uint32_t appelée "count". 
Ces éléments sont appelés " déclarations de variables (globales) " et constituent une partie essentielle du programme... vous pouvez donc effectuer une recherche avec une expression telle que : 

"Déclarations de variables en C" pour en savoir plus ! En faisant cela, vous apprendrez également la signification et les types de "static". 
Pour faire simple, si vous ne voulez pas que le contenu de la variable soit effacé à chaque fois qu'un processus est exécuté, mettez-la en "static", et le contenu sera maintenu. 
Le type "static" a d'autres utilisations. Essayez de chercher "variables statiques". 

D'accord, et ensuite ? 

void MODFX_INIT(uint32_t platform, uint32_t api) { lastSampleL = 0.f; lastSampleR = 0.f; count = 0; } 

Ce qui précède est ce que nous appelons une "fonction". 
Une fonction est représentée par une étiquette pour chaque processus de programme par fonction, appelée nom de fonction. Vous pouvez appeler les fonctions par leur nom à partir de toutes sortes d'endroits dans le programme, et les utiliser en combinaison pour créer une seule chaîne de résultats - votre logiciel. Les fonctions sont un peu comme les pièces individuelles utilisées dans une automobile. 

Value returned Function name (argument) 
{ 
Processes to execute 
} 

Ce qui précède est le format utilisé pour les fonctions. La "valeur retournée" désigne le type de données utilisé pour obtenir les résultats du processus exécuté par la fonction, et l'argument" est la donnée et son type qui sont donnés à la fonction lorsqu'elle est appelée. 

Cette fois, nous écrivons un processus d’initialisation. Le contenu de la variable que nous avons créée est remis à "0", mais que signifie le "f" de "0.f" ? Cela signifie simplement que nous nous engageons sur un type de flottant. Essayez de faire une recherche sur ce sujet. 
Et que dire du "void" dans la valeur retournée ? Cela signifie que rien n'est renvoyé dans les résultats. Puisque la première fois que nous effectuons le processus d'initialisation est aussi la dernière fois, nous obtenons la valeur "void". 
C'est tout ce que cette fonction a à faire - bien ! 

Maintenant, nous passons au traitement principal. Nous allons d'abord passer à l'explication de la fonction qui se trouve tout en bas. 

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, ça a déjà l'air assez difficile ! 
Il s'agit simplement de récupérer les données des boutons A et B et de placer ces valeurs dans notre variable. 
Lorsque vous tournez les boutons, le système NTS-1 appelle cette fonction, et lorsque cette fonction est appelée, le type de bouton " index " et sa valeur, " value ", sont reçus comme argument. 
Cette valeur est au format décimal fixe, comme nous l'avons vu précédemment, et nous devons donc d'abord la convertir en virgule flottante. Dans cet exemple, nous convertissons le format décimal fixe Q31 en virgule flottante 32 bits, et nous stockons le résultat dans une variable appelée "valf". Nous utilisons "const" lorsque le contenu des données ne doit pas être modifié. Essayez de faire une recherche à ce sujet si vous avez besoin de plus de détails. 

Ce résultat est traité à l'aide de l'instruction "switch case". Expliqué simplement, cela montre le traitement qui se produit lorsque les variables que nous avons spécifiées à l'aide de "switch" correspondent à ces cas respectifs. 
Cette fois, nous utilisons uniquement les données du bouton A, donc le contenu de "valf" est stocké dans la variable "rate" lorsque les données k_user_modfx_param_time (nom de l'index du bouton A) sont reçues. 
La variable "rate" est utilisée pour le traitement principal, c'est pourquoi j'ai expliqué cette fonction en premier. 


Maintenant nous arrivons à la partie principale.

void MODFX_PROCESS(const float *main_xn, float *main_yn, 
const float *sub_xn, float *sub_yn, 
uint32_t frames) 

Avec la fonction nommée MODFX_PROCESS, nous pouvons obtenir ces six arguments : 

Main input: main_xn 
Main output: main_yn 
Sub input from the oscillator: sub_xn 
Sub output from the oscillator: sub_yn 
Total number of frames: frames 

Les noms de variables marqués d'un astérisque appellent par référence. Ils indiquent que dans cette fonction, lorsqu'une valeur est modifiée, la variable qui l'appelle change également. Cela signifie essentiellement que les données reçues sont l'original et non une copie. Vous voudrez peut-être étudier davantage les appels par référence et le passage de valeurs ! 

Bien que vous ayez pu vous en rendre compte en regardant l'argument, tout ce que vous avez à faire est d'effectuer les divers traitements d'effets pour les données équivalentes au nombre d'images dans "main_xn", et de les écrire comme sortie dans main_yn. 
Nous n'utiliserons pas de sub input/output cette fois-ci. 
Voici le début : 

for(uint32_t i = 0; i < frames; i++){ 

Cela signifie que tout ce que nous devons faire est de répéter ce qui se trouve entre les crochets { } le nombre de fois spécifié. C'est ce qu'on appelle une instruction "for", et nous l'utilisons pour répéter le traitement pendant le nombre d'images. 

// Prepare the L and R audio 
const float inL = main_xn[i * 2]; 
const float inR = main_xn[i * 2 + 1]; 

Les deux barres obliques (//) au début marquent un commentaire. Utilisez-les pour laisser des notes dans votre programme, car elles n'auront aucun effet sur le fonctionnement du programme. 
Maintenant, en ce qui concerne l'entrée, "main_xn", l'audio stéréo (canaux) alterne entre la gauche et la droite. 
Nous pouvons les traiter alternativement, mais pour des raisons de clarté, nous les transférons à chaque fois dans des variables, "inL" et "inR". 
En revenant en arrière pour expliquer la structure de "main_xn", nous pouvons voir le texte "LRLRLR", qui représente les ensembles de "LR" en fonction du nombre d'images. La quantité de données est égale à deux fois le nombre de cadres. Les variables contenant plusieurs ensembles de données sont appelées "array variables.". Vous devriez être en mesure de comprendre ce terme si vous faites une recherche à ce sujet. La quantité de ces données est appelée le nombre d'éléments. Le nombre d'éléments dans "main_xn" est le double du nombre de cadres écrits dans "frames". 

Nous spécifions quel numéro d'élément utiliser avec des crochets []. La partie difficile est que les numéros commencent par zéro, donc main_xn[0] indique le numéro du premier élément. 
Ainsi, dans cette instruction "for", la valeur de la variable "i" augmente de un à chaque boucle, tant que la valeur est inférieure à "frames". 
Comme "i" commence par zéro, il sera le même en termes de nombre de fois que le nombre de cadres, mais seulement tant qu'il se termine un nombre inférieur à « frames ». 
Vous vous dites peut-être "pourquoi les chiffres ne commencent-ils pas par 1 ?". Dans ce cas, la boucle se terminerait une fois que le nombre atteint le même nombre que "frames". Cela dit, il n'y a qu'une seule chance que les nombres soient identiques... donc si vous faites une erreur de programmation et dépassez le nombre, vous vous retrouverez avec une boucle sans fin. Si cela se produit, le programme peut se figer, ou peut-être le processus suivant commencera-t-il à zéro, ce qui pourrait être pratique... Cela correspond parfaitement au sujet suivant. 

Vous verrez que la notation se lit comme "[i * 2]", "[i * 2 + 1]" et ainsi de suite. Puisque LR arrive en alternance, le numéro zéro est "L", et le numéro 1 est "R". C'est facile à comprendre quand on y pense de cette façon : quand "i" est 0, [0 * 2][0 * 2 + 1] produit "0" et "1" ; quand "i" est 1,2... et ainsi de suite. 

Maintenant que j'arbore un air suffisant après avoir expliqué tout cela, nous allons entrer dans les secrets du fonctionnement du bitcrusher. 
Pour expliquer d'abord comment fonctionne le bitcrusher, cet effet vous donne un taux d'échantillonnage plus "granuleux", puisque des taux d'échantillonnage plus élevés produisent un son plus détaillé. Ce que j'entends par "plus granuleux", c'est que le taux de rafraîchissement des données d'échantillonnage est plus faible, ou que les étapes (résolution) sont moins définies. Cette fois-ci, nous allons diminuer le taux de rafraîchissement et changer la sortie des données une fois tous les un certain nombre de fois. Si l'entrée de données est continue comme "12345678", cet effet vous donne "11335577". Cela vous donne un son comme si vous réduisiez le taux d'échantillonnage, mais vous pourriez argumenter que "ce n'est pas un effet bitcrusher (réduction de la quantification) !". Je comprends tout à fait cette objection. 
Le fait est que la plupart des gens auront du mal à comprendre si nous parlons soudainement d'opérations sur les bits. De plus, les effets bitcrusher qui existent aujourd'hui vous donnent un son direct en réduisant le taux d'échantillonnage, et non un effet léger en supprimant simplement quelques bits. C'est pourquoi je vous ai donné une explication plus intéressante. Très bien, passons à autre chose ! 

// The larger the value, the grainier the sample 
uint32_t skip = rate * 64; 

C'est ici que nous utilisons les données précédemment reçues des boutons. Enfin, les voilà ! Les données que nous avons reçues vont de 0 à 1, donc si nous voulons obtenir une valeur de 64 à partir du chiffre maximum (1), nous devons le multiplier par 64. Pour faciliter la compréhension, nous remplaçons cette variable par la variable "skip". 

// Update lastSample only when count is 0 
if(count == 0){ 
lastSampleL = inL; 
lastSampleR = inR; 
} 

Vous comprendrez mieux cette partie au fur et à mesure que nous avancerons. D'abord, je vais vous expliquer le traitement. 
Il met à jour la variable qui maintient la sortie, mais seulement lorsque la variable "count" est "0". En d'autres termes, lorsque la valeur n'est pas "0", "lastSmpleL/R" est la même valeur que la dernière fois. 
"count" commence par zéro comme il a été initialisé (mentionné précédemment), et est soudainement mis à jour à la valeur "inL/R". 

// Continue lastSample sound 
main_yn[i * 2] = lastSampleL; 
main_yn[i * 2 + 1] = lastSampleR; 
count++; 

Ceci fait l'inverse de ce que nous avons fait en séparant l'entrée en L et R. La sortie est écrite dans "main_yn" ici pour les sons L et R. En fait, nous écrivons le même son que nous venons de mettre dans "inL/R". Rien n'a changé ! 
Au fait, après cela, nous voyons un "count++". Cela signifie que l'on augmente le "count" de un. 
C'est pourquoi "0" devient "1". Avec cette méthode, nous pouvons également écrire "i++" pour l'instruction "for", afin que "i" soit incrémenté d'une unité à chaque passage. Une autre façon d'écrire ceci est "count = count + 1". 
Ce qui se passe lorsque le compte atteint "1", c'est que "lastSampleL/R" n'est pas mis à jour au début de la passe suivante. Le même échantillon de données continue à être émis. Alors, quand est-ce que l'on revient à zéro ? 

// reset count to 0 if skip exceeded 
if(count > (int)skip) count = 0; 

Oui, juste ici. 
Auparavant, nous recevions une valeur du bouton, et si cette valeur multipliée par 64 dépassait le "compte", celui-ci était remis à zéro. 
Ce qui se passe, c'est qu'avec des valeurs de "skip" plus grandes, les mêmes données sont émises sans tenir compte de l'entrée, et nous avons donc une baisse du taux de rafraîchissement des données d'échantillonnage. 
Nous avons terminé ! 

Enfin, nous avons ceci : 

} 
} 

En fait, elles sont importantes. La parenthèse fermée en haut est pour l'instruction "for", et les parenthèses en bas marquent la fin de la fonction. En fait, nous utilisons ces parenthèses {} pour entourer toutes sortes de processus. 
Plus le processus est long, plus il y a de chances que nous oubliions de mettre la parenthèse finale ! Faites attention à cela, car cette seule omission peut faire échouer votre compilation. 
Ce genre d'erreur est également difficile à trouver, et il est courant que les programmeurs perdent beaucoup de temps à la chercher. 
Bien sûr, je ne fais pas exception ! Lorsque vous trouvez ce genre d'erreur, cela vous donne vraiment envie de jeter l'ordinateur par la fenêtre et de vous frapper la tête ! 

Le programme cette fois-ci est "Modfx", et nous pouvons créer celui-ci à partir du modèle "tmpMod". 
Je vous encourage à le comparer avec le fichier "main.c", et à voir ce qui a été ajouté. 
Essayez de remplacer les valeurs dans "skip" par d'autres valeurs et testez-les ! 

Enfin, nous allons saisir le nom de l'effet que nous avons créé dans le paramètre "name" de "manifest.json" et dans le paramètre "PROJECT" de "project.mk". Vous pouvez faire cela en utilisant un éditeur de texte. 

Prochain article : StopFX Ce sera un sacré défi, alors j'espère que vous l'attendez avec impatience ! 
Merci beaucoup d'avoir lu ce long article !
 

Article #4: Créer un effet personnel, Part 2

Hello, I’m Frank Shigetora, sound producer at DOTEC-AUDIO.
We’ll be making another effect this time, so as the designer of the DSP I’ll be in charge of this part.

I’ll be omitting the things we already covered in article #3, so please take things in order!
This time, we’ll be creating a rather complex effect, even more so than last time.

Pretty cool, eh—it’s like stopping a record. Now I’ll show you everything you need to know to make this effect.

Please download the sample of stopfx and unzip to:
korg\v1.1\platform\nutekt-digital

First of all, we’ll see the effect in operation. Use MSYS2 to go to this folder:
korg\v1.1\platform\nutekt-digital\stopfx
and execute the “make” command. If you have no idea what I just said, please go back and have a look at article #3.

So, did it work? Now, let’s have a look at the source code!
Let’s open the “main.c” file, located in the “stopfx” folder.

At the beginning of the file, you see:

#include "userdelfx.h"
#include "float_math.h"
#include "buffer_ops.h"


We’ll need the first line to create delfx (the delay type). Next, do you remember the second line?
Yes, as with last time, we need this for floating point operations.
On the third line, we have something new. This is necessary to store lots of sounds into memory on the NTS-1.

As you might imagine, you’ll need a lot of memory to store long files of music data. The SDK of the NTS-1 includes a function for memory operations (handling memory), and the third line of our code includes the statement “#include buffer_ops.h” for doing this.

Here’s a new word to remember: “buffer.” You’ve probably heard this word.
A buffer is a place where data for temporary processing is stored or kept. This time, we’ll use the buffer for the array we created last time. We want to store a lot of sound data in this buffer, so we need to make a large array.


Next, we get into the functions. We initialize as we did last time:

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; }

Take note at the function used specially for delfx. About the only thing different is the name, right.
For the other variables, we can just use “0” or the default value; but for the buffer, we use the “buf_clr_f32()” function for initialization.

Let’s stop here to look at the last function, which I’ll explain.

void DELFX_PARAM(uint8_t index, int32_t value)

This function is used in the same way as last time, but it just has a different name. If you don’t remember about this one, go back and review article #3 and it will all come back.

Now we’re at last moving into the heart of the matter, the DSP processing.

void DELFX_PROCESS(float *xn, uint32_t frames)

With the function named DELFX_PROCESS, we can get these two arguments:

Main Input/output: main_xn
Total number of frames: frames

Wow, only two this time, as opposed to six last time! We’ve got it easy.
The key point here is that with “modfx”, input and output was separated last time, but this time “xn” is used for both input and output.
How this works is that “xn” contains input data, and this is processed so that the data overwritten at the end point of the function becomes the output data.

// 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; }

This puts all of the input into the buffer we’ve prepared. This is the “for” statement we used for looping last time, and the “if” statement that makes conditional decisions. “frames” is the number of frames for one channel, so we are setting this value to twice that amount for two channels (LR). What’s confusing is the “++” that comes after the “z”. “z” increases after the “zth” input to the buffer.
As for the array, ah, it starts from zero, right? For that reason, this is reset to zero, to avoid an unwanted crash in case we get to the number represented by BUFFER_LEN. As I wrote last time, this condition can be written in different ways. We’ve standardized it this time so that the conditions are met when reaching or exceeding the value. You should note that the NTS-1 won’t break if the software crashes, so don’t worry about making mistakes!
In this way, we return the value to zero and keep the buffer going (meaning that we keep writing to it). This is called a “ring buffer” or “cyclic buffer”.

Next, we’ll get the value ready for actually slowing down the playback speed.

// Reset when knob is turned all the way to the left
if(rate < 0.1f){ isStop = 0; rate = 0.f; }else{

This is the processing for returning to the original playback speed when the knob is turned all the way to the left. When the knob is turned all the way to the left, if the conditions are set to “rate== 0.f”, this is pretty difficult to use, so let’s set this to “0.1f” or less to give ourselves a margin to work with. We don’t want the knob to react unintentionally with only a slight move of the finger.

“isStop” is used for determining whether the stop effect is activated or not. In the C language, we can use the numbers 1 and 0 as a substitute for true and false respectively, so we’ll set this to “0” prior to the stop effect.
An appropriate value is picked up from the knob when the effect is changed, so we’ll reset the “rate” as well.
Take note at how this block of code ends with “else”. We use this statement when we want to add a process that occurs when the previous conditions are not met. When I first saw that code, I read it as “erase”... That was pretty stupid, right! ?  

So, when the conditions of the “else” statement are met, the knob is being turned, so the stop effect is processed.
First, we’ll set the initial value when the knob is first turned.
We’re getting into rocky waters here!

// 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

The exclamation point that comes before “isStop” doesn’t mean that it’s mad—it means a negation, or rather “not”. When “isStop” is not true, or in other words when it’s false the code inside the “if” statement is processed.
Since we previously set this to “0”, this is processed. However, we don’t want processing to occur when the knob begins to turn, so we set “isStop” to “1” directly after. This means that the processing is skipped until the knob is turned all the way to the left and the value is reset. This kind of processing is called a “flag”. It's the same flag as when we say, “set the XXX flag”.
In some cases, the knob may be in an appropriate position in the same way as before... so in those cases, we skip past the previous conditions and reset “rate” in order to jump here.

The “p” variable indicates the playback point of the buffer. Since we’ve stored two times the amount of “frames” worth of data in the buffer, we subtract until the point where we started storing data, and then return that value. You might wonder, “so are we again dividing this by two?”, but this will become clear afterwards. Lastly, we write the process for the “if” statement in case “p” is a negative value. For example, if the buffer goes through one cycle and is finished writing data, subtracting the total number from that position would give us a negative number. That’s why we subtract the difference from the end.
This time, we’ve got a workable buffer size, so we don’t get a negative result. Still, we need to get into the habit of thinking about minus locations in our array.

It seems like things are suddenly getting more complicated when we talk about buffers, but it’s no problem. You’ll get it!
By the way, you’ll note that this “if” statement doesn’t have the brackets {}. Since we’re just processing the last line, we can leave them out. This is convenient to remember when you want to do a little test by adding the “if” part.

We’re not finished yet with our trip down the weird road of “else” (that was a little joke).

for(uint32_t i = 0; i < frames; i++){ uint32_t length_mono = BUFFER_LEN / 2;

Since the “for” statement has popped up, from here we’ll apply the effect and get busy overwriting the “xn” variable.
You might ask here why we don’t double “frames” here. This is because this is a stereo signal, so we process two lines at a time.
You’ll understand this later, so it’s okay if this doesn’t make sense yet.

First, we use the length equivalent to the monaural part of the buffer to set a variable name that’s easy to understand.
Always halving the value gets to be a pain, and the code gets harder to make out with the added formulas.
As this is an integer, the variable type is set to “uint32_t”.

// 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;

Here's the important part!

The framework for playing back slowly is that we compute the value between two samples and slowly advance the sample. Just as when we count slowly, “o---ne, tw----o,” we calculate the lengthened section from two points.
The “p” variable type is “float”, so we get non-rounded numbers like 1.2 and 5.4. When it comes to arrays, there isn’t such a thing as “location 1.2”. That’s why we assume that for the value in location “1.2”, the value is 20% closer to the first value.
Following this, we can postulate that the values “1, 2, 3” will advance in this manner for extraction: “1.3, 1.6, 1.9, 2.2, 2.5...” In the case of waveform height, these values represent the waveform at its peak.

Continuing with this explanation, we’re converting “p” to an integer in the first line. This way of writing code by (type) is called “casting”. We use this for converting types.
By converting to integer, for instance we can change the value of “7.4” in “p” to “7”, dropping the decimal and then storing that in “prev”.
On the second line, the number subtracted from the “prev” that was converted to an integer from the original “p” is stored in “slope”.
Yes! This is the rounded-down decimal. In the previous example, that would be 0.4.

Maybe some of you have already figured this out. We use this method to get a value that’s 40% more than the value in the “pth” location.
So, where does this go? On the third line, of course that’s the next array after the “pth”. We put that into “next”.
This is for the final “if” statement, so that the “next” does not exceed the array. You’ve seen this before, right?

// 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];

Finally—the climax of the story!

“s1” is the first sound of between the two points previously mentioned, and “s2” is the L and R we’ve prepared, meaning the last sound.
“prev” and “next” are the array numbers of the two points we just got.
The part about multiplying by two and making a pair is the same as what we explained before, but since LR is input alternately into the buffer, we need to extract the L sound from even-numbered locations in the array, and the R sound from odd-numbered locations in the array.
The “i” variable in the “for” statement increments one by one, so if we calculate 0, 1, 2, 3 and so on respectively, the results get separated into even and odd numbers.
The reason why we divided by two at the beginning to get the value for “p” was to get the doubled number here!
So that’s what this was all about...

float currentL = 0.f,currentR = 0.f; currentL = s1L + (s2L - s1L)*slope; currentR = s1R + (s2R - s1R)*slope;

We created the first variable to temporarily put the location of our current sound in. We initialize with no sound.
The next formula calculates the value for deriving the value for the previous median point. We’re only adding several percent here, the difference from the next value to the initial value. For instance, if the values progress like “0, 10”, the value of the location when we increase 20% from zero is:

0+(10-0)*0.2 = 2

This is the value when the “p” we mentioned before was the 1.2nd position.

So, all of a sudden... congratulations!
You’ve made it over the toughest hurdle.

// Overwrite as output.
xn[i * 2] = currentL; xn[i * 2 + 1] = currentR;

Since we were happily able to calculate the value we wanted for the sound, we update “xn” in LR order!
The maximum number in this loop was “frames”, which uses the same “i” value to process LR, so if we turn the knob halfway we’re all right.
We can do that from the beginning as well, we since there was no need to separate them, we just spin that knob around.
Be bold about it!

// 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

Then, we advance “p” according to the rate for the next loop.
The “+=” means that we add the right side to the left side. The reason why we make this a reciprocal number is because when you turn the knob all the way down to make the “rate” equal zero, the number advances the fastest, and we want to stop it at “1”. At zero, the array advances one by one, which is standard playback speed. The more you turn the knob to the right, for instance advancing 0.3 at a time, the slower it goes.
The next “if” statement returns the value to zero after “p” advances the amount of one channel.
We finish up by adding the closing brackets for the “for” and “else”.

Now our journey is over!

This program is “Delfx”, and we can create this one from the “tmpDelay” template.
If you compare this with “main.c”, you’ll see where you need to write the program.

Finally, as with last time we’ll type the name of the effect we created into the “name” parameter in “manifest.json” and in the “PROJECT” parameter of “project.mk”.


Although this effect is a simplified version of the stop effect found in DOTEC-AUDIO’s “DeeFX,” the principles are the same. I think you’ve gotten a lot out of looking at this one.
Next up is our last article: a final summary!

This concludes my part, the DSP lecture.Thanks very much for reading this long article!

Article #5: Créer un Vocoder

Hello again everyone! I’m Shinji Iijima from DOTEC-AUDIO.
How did you enjoy the previous two articles, where Frank Shigetora gave us the details on DSP processing?

Honestly, it probably seemed pretty intimidating, I imagine. That said, processing the digital data that was input, outputting and then repeating forms the basics of all digital effects programs.

The sample programs this time were all under 100 lines long, so I really hope that you’ll take this opportunity to use the Web to search and learn more in order to understand about these programs, and about the elements of NTS-1 effect development and matters regarding oscillator (synthesizer) development that we unfortunately couldn’t cover this time.

This time, we wrap up this series of articles, so we won't be delving into the programs.

However, I’d like to announce that at DOTEC-AUDIO, we’ve put our full efforts into making a 16-band vocoder and a dedicated oscillator that works on the NTS-1! So, I’ll be giving some basic explanations this time about these things, as well as about the plugins for PCs that DOTEC-AUDIO has released.

So, as for the vocoder we’ve developed, it’s a full-fledged 16-band vocoder.
Connect a mic (which we call a “modulator”) to the NTS-1, and make the oscillator sound while you vocalize to get a cool robotized voice.

How did you like that? Isn't it amazing how you can make a 16-band vocoder run on this compact digital synthesizer?

We made this happen by taking advantage of some hidden tricks offered by the specs of the NTS-1. It’s a little complicated, but I’ll explain about this here.

First of all, the NTS-1 features an AUDIO IN jack.


We can plug in a line-level audio input to this jack and apply effects or mix the oscillator sound with this input. (This is one of the fantastic specs that this unit offers.) However, the input needs to be line-level, and the input audio gets mixed with the oscillator sound, which are hurdles we had to figure out how to overcome.

- About the line input (audio input level)
Since the NTS-1 input requires line input, we would ordinarily need a mic amp or a mixer to amplify the sound if we wanted to plug in a mic.
Also, since this jack was designed for line input, this unit also features an attenuator that is necessary to attenuate the levels.

Luckily, the NTS-1 has “global parameter” settings, which can be used to change how various operations work on this unit.
I’d like you to refer to the “Global Parameters” section in the Owner’s Manual that comes with the NTS-1 (on the lower right-hand part of page 16). There’s a parameter called “Input Trim.”

If you can’t find it in your Owner’s Manual, please download the latest one from here. It also might be necessary to update your NTS-1 via PC.

The factory default setting for this parameter is “6:-6DB”, but by setting this to “0:0DB” we can disable the attenuation. Use this setting when you want to plug in a dynamic mic or similar device without using an amp. (You don't need to make this setting for a line input device.)

If the input volume is still too low, we can overcome this by setting the vocoder parameter A to “Gain” and adjusting this knob to amplify the volume by 100 times maximum.

- About the input audio and oscillator sound
A vocoder uses the audio from a mic input (the “modulator”) to process the sound of a synthesizer (the “carrier”). Although both of these elements require separate inputs, the NTS-1 does not have this feature.
What happens is that the sounds of the synth and external input get mixed together and then processed by the effect. You can try it out and see what I mean.

To circumvent this, we came upon a bold idea to separate the sounds by using the differential in the stereo audio.
For example, if you have the same synthesizer sound in both the left and right channels, but then add the mic sound to just one channel, you’re able to extract the audio from the mic by using the differential from both signals. Does this line of reasoning make sense?

So, it can be shown as the following formula;

(mic signals + synth signals) – synth signals = mic signals

Using this method on the NTS-1, we separate the audio from the oscillator sound and process them to make our vocoder this time a reality!

That aside, we should note that there’s a little trick involved in connecting the mic, some examples of which are shown below. (Prerequisite: get the L channel as mic input)

- Use a monaural-output dynamic microphone - From the beginning, prepare a sound source where your voice is heard only on the L channel (R channel should be silent), and use line input to bring the stereo signal into the NTS-1 - Use the line input from a mixer to bring the stereo signal containing only the mic sound on the L channel into the NTS-1 - Use a splitter cable to connect the mic to only the L channel

  • Example of connection using a dynamic microphone

  • Example of connection using a splitter cable

Using a monaural-output microphone is the simplest way. If you use a stereo-output microphone, you can get the same result with a splitter cable. Have a try!

With all that said, you can download a trial version of our vocoder together with the oscillator as a set for free. You can try it for 3 minutes in a trial version. And a full version is now sold for $10 (excluding tax) on the DOTEC-AUDIO website.

https://dotec-audio.com/nts1.html?lang=en

Frankly, I’m just blown away by the fact that we can use the NTS-1 as a vocoder.

Since you’re here, I’d like to give you a rundown of who we are at DOTEC-AUDIO as well as our original product lineup.

DOTEC-AUDIO is a software brand that’s managed and operated cooperatively by musician and engineer Frank Shigetora and by fmfmsoft Corp.
Our general business is the development and sales of plugins for music-related software like DAWs that operate on Windows, macOS and iOS (for some products).

The KORG Gadget2 includes a maximizer made by DOTEC-AUDIO called “DeeMax,” so you might have heard of us from that.

Six years have passed since DOTEC-AUDIO got started, and we now have a much larger lineup of products, including products for musicians doing music production on a PC and products for content distribution. If you’re interested, please check out our website. There are a few products there in particular that have a deep connection with what we’ve talked about in this article series.

- DeeFX Multi-Effect

English website: https://dotec-audio.com/deefx.html

We briefly touched on this product in the first article. This is a multi-effect that gives you standard distortion, filter and delay effects, but in a package that we’ve worked hard to fine-tune, making it super easy to use.
The design combines these three effects, which you can individually switch on and off and use as standalone effects processors.

Adding to this are bitcrusher and vinyl stop effects, which are simplified versions of what we featured in these article for use on the NTS-1.

This is a product that’s popular even with many pro musicians.

- “DeeVocoder” vocoder

English website: https://www.dotec-audio.com/deevocoder.html

As the name suggests, this product is the vocoder made by DOTEC-AUDIO. This vocoder features a filter bank-type vocoder (which is now experiencing a revival) switchable between eight, 16 and 32 bands, as well as formant shift capabilities.

Unlike FFT-type products, our vocoder offers superior playability with no latency, giving you the power to create that impudent-sounding robot voice that’s normally created with analog vocoders.

We’ve placed emphasis on the positioning of this product as an effect, as by using this familiar synth plugin for sidechain input, the vocoder as a carrier opens you up to endless sonic creation possibilities. Of course, any audio input to the sidechain can be used as a carrier, so you can enjoy using this plugin effect at your whim for not only synth sounds but also guitar, bass guitar, drums and more.

In this way, since this is a plugin for use with DAWs, it offers a different kind of freedom from the vocoder that works with the NTS-1.

DOTEC-AUDIO products all feature a demo version that you can try for free (no need for user registration or other trouble). If you use software for plugins such as VSTs or audio units, we definitely hope you’ll try out our products!

- In conclusion

This article series is part of a proposal that we made to KORG. The reason for doing this was that we wanted to talk about how the NTS-1 is such a massively appealing platform for programming.

What’s so fantastic about the NTS-1 is that its compact hardware and ability to program give you such a high degree of freedom.

Further, as audio programming pros we’re so excited about how the NTS-1 has an audio input and can be used as a synthesizer or as an effects unit. It’s really interesting how the NTS-1 offers all this, while still being eminently affordable. That’s why we proposed this article series—we wanted to tell as many people as possible about the fascination of this product.

As we’ve written over and over again, the basics of audio programming are the same, whether you’re using the NTS-1 or working with a DAW plugin.

Although there may be an infinite number of ways to make your solution work, it all comes down to how you want to build the sound you’re looking for, between the input and the output.

We hope that this article series has given an opportunity to as many users as possible to build an interest in understanding how their favorite music and synth apps are designed, while playing around with the NTS-1.

I’d like to offer a heartfelt thanks to everyone for reading along with this article series!
Also, my thanks to the people involved at KORG, who agreed to publish this series.

Hope to see all of you someday!

Frank and Iijima at DOTEC-AUDIO