Le SDK Neutone est un outil pour les chercheurs qui leur permet d'envelopper leurs propres modèles audio et de les exécuter dans une DAW à l'aide de notre plugin Neutone. Nous offrons des fonctionnalités pour le chargement des modèles localement dans le plugin et les contribuant à la liste par défaut des modèles disponibles pour quiconque exécute le plugin. Nous espérons que cela permettra aux chercheurs d'essayer facilement leurs modèles dans une DAW, mais de fournir aux créateurs une collection de modèles intéressants.
Juce est la norme de l'industrie pour la construction de plugins audio. Pour cette raison, la connaissance de C ++ est nécessaire pour être en mesure de créer des plugins audio même très simples. Cependant, il est rare que les chercheurs audio d'IA aient une vaste expérience avec C ++ et de pouvoir construire un tel plugin. De plus, c'est un investissement de temps sérieux qui pourrait être consacré à développer de meilleurs algorithmes. Neutone permet de créer des modèles en utilisant des outils familiers tels que Pytorch et avec une quantité minimale de code Python enveloppez ces modèles de telle sorte qu'ils peuvent être exécutés par le plugin Neutone. L'obtention d'un modèle opérationnel à l'intérieur d'une DAW peut être effectuée en moins d'une journée sans avoir besoin de code C ++ ou de connaissances.
Le SDK prend en charge la mise en mémoire tampon automatique des entrées et des sorties à votre modèle et à la fréquence d'échantillonnage à la volée et à la conversion stéréo-mono. Il permet un modèle qui ne peut être exécuté qu'avec un nombre prédéfini d'échantillons à utiliser dans la DAW à n'importe quel taux d'échantillonnage et toute taille de tampon de manière transparente. De plus, dans le SDK, les outils pour l'analyse comparative et le profilage sont facilement disponibles afin que vous puissiez facilement déboguer et tester les performances de vos modèles.
Vous pouvez installer neutone_sdk à l'aide de pip:
pip install neutone_sdk
Le plugin Neutone est disponible sur https://neutone.space. Nous proposons actuellement des plugins VST3 et Au qui peuvent être utilisés pour charger les modèles créés avec ce SDK. Veuillez visiter le site Web pour plus d'informations.
Si vous voulez simplement envelopper un modèle sans passer par une description détaillée de ce que tout nous a préparé ces exemples.
Le SDK fournit des fonctionnalités pour emballer les modèles Pytorch existants d'une manière qui peut les rendre exécutables dans le plugin VST. À son cœur, le plugin envoie des morceaux d'échantillons audio à une certaine fréquence d'échantillonnage comme entrée et attend la même quantité d'échantillons à la sortie. L'utilisateur du SDK peut spécifier à quel (s) échantillon (s) et taille de tampon sur leurs modèles fonctionnent de manière optimale. Le SDK garantit alors que la passe avant du modèle recevra l'audio à l'une de ces combinaisons (Sample_Rate, Buffer_Size). Quatre boutons sont disponibles qui permettent aux utilisateurs du plugin de nourrir des paramètres supplémentaires au modèle lors de l'exécution. Ils peuvent être activés ou désactivés selon les besoins via le SDK.
En utilisant la fonction d'exportation incluse, une série de tests est automatiquement exécutée pour garantir que les modèles se comportent comme prévu et sont prêts à être chargés par le plugin.
L'analyse comparative et le profilage des outils CLI sont disponibles pour le débogage et les tests supplémentaires des modèles enveloppés. Il est possible de comparer la vitesse et la latence d'un modèle sur une gamme de combinaisons de DAW communes simulées (Sample_Rate, Buffere_Size) ainsi que de profil la mémoire et l'utilisation du processeur.
Nous fournissons plusieurs modèles dans le répertoire des exemples. Nous passerons par l'un des modèles les plus simples, un modèle de distorsion, pour illustrer.
Supposons que nous ayons le modèle Pytorch suivant. Les paramètres seront couverts plus tard, nous nous concentrerons sur les entrées et les sorties pour l'instant. Supposons que ce modèle reçoive un tenseur de forme (2, buffer_size) comme une entrée où buffer_size est un paramètre qui peut être spécifié.
class ClipperModel ( nn . Module ):
def forward ( self , x : Tensor , min_val : float , max_val : float , gain : float ) -> Tensor :
return torch . clip ( x , min = min_val * gain , max = max_val * gain )Pour exécuter cela à l'intérieur du VST, le wrapper le plus simple que nous pouvons écrire est en sous-classe le Baseclass WaveFormTowAveFormBase.
class ClipperModelWrapper ( WaveformToWaveformBase ):
@ torch . jit . export
def is_input_mono ( self ) -> bool :
return False
@ torch . jit . export
def is_output_mono ( self ) -> bool :
return False
@ torch . jit . export
def get_native_sample_rates ( self ) -> List [ int ]:
return [] # Supports all sample rates
@ torch . jit . export
def get_native_buffer_sizes ( self ) -> List [ int ]:
return [] # Supports all buffer sizes
def do_forward_pass ( self , x : Tensor , params : Dict [ str , Tensor ]) -> Tensor :
# ... Parameter unwrap logic
x = self . model . forward ( x , min_val , max_val , gain )
return x La méthode qui fait la plupart du travail est do_forward_pass . Dans ce cas, ce n'est qu'un simple passthrough, mais nous l'utiliserons pour gérer les paramètres plus tard.
Par défaut, le VST s'exécute sous forme stereo-stereo mais lorsque Mono est souhaité pour le modèle, nous pouvons utiliser l' is_input_mono et is_output_mono pour informer le SDK et faire convertir les entrées et sorties automatiquement. Si is_input_mono est basculé, un tenseur en moyenne (1, buffer_size) sera passé comme une entrée au lieu de (2, buffer_size) . Si is_output_mono est basculé, do_forward_pass devrait renvoyer un tenseur mono (forme (1, buffer_size) ) qui sera ensuite dupliqué sur les deux canaux à la sortie du VST. Cela se fait dans le SDK pour éviter les allocations de mémoire inutiles pendant chaque pass.
get_native_sample_rates et get_native_buffer_sizes peuvent être utilisés pour spécifier toutes les fréquences d'échantillonnage ou tailles de tampon préférées. Dans la plupart des cas, ils ne devraient avoir qu'un seul élément, mais une flexibilité supplémentaire est fournie pour des modèles plus complexes. Dans le cas où plusieurs options sont fournies, le SDK essaiera de trouver le meilleur pour le réglage actuel de la DAW. Chaque fois que la fréquence d'échantillonnage ou la taille du tampon est différente de celle de la DAW, un wrapper est automatiquement déclenché qui se convertit au taux d'échantillonnage correct ou implémente une file d'attente FIFO pour la taille du tampon demandé ou les deux. Cela entraînera une petite pénalité de performance et ajoutera une certaine quantité de retard. Dans le cas où un modèle est compatible avec n'importe quelle fréquence d'échantillonnage et / ou taille tampon, ces listes peuvent être laissées vides.
Cela signifie que le tenseur x dans la méthode do_forward_pass est garanti d'être en forme (1 if is_input_mono else 2, buffer_size) où buffer_size sera choisi à l'exécution à partir de la liste fournie dans la méthode get_native_buffer_sizes . Le tenseur x sera également à l'un des taux d'échantillonnage de la liste fournie dans la méthode get_native_sample_rates .
Nous fournissons une fonction save_neutone_model HELPER qui enregistre des modèles sur le disque. Par défaut, cela convertira des modèles en torchscript et les exécutera à travers une série de vérifications pour s'assurer qu'elles peuvent être chargées par le plugin. Le fichier model.nm résultant peut être chargé dans le plugin à l'aide du bouton load your own . Lisez ci-dessous pour soumettre des modèles à la collection par défaut visible pour tous ceux qui utilisent le plugin.
Pour les modèles qui peuvent utiliser des signaux de conditionnement, nous fournissons actuellement quatre paramètres de bouton configurables. Dans le ClipperModelWrapper défini ci-dessus, nous pouvons inclure les éléments suivants:
class ClipperModelWrapper ( WaveformToWaveformBase ):
...
def get_neutone_parameters ( self ) -> List [ NeutoneParameter ]:
return [ NeutoneParameter ( name = "min" , description = "min clip threshold" , default_value = 0.5 ),
NeutoneParameter ( name = "max" , description = "max clip threshold" , default_value = 1.0 ),
NeutoneParameter ( name = "gain" , description = "scale clip threshold" , default_value = 1.0 )]
def do_forward_pass ( self , x : Tensor , params : Dict [ str , Tensor ]) -> Tensor :
min_val , max_val , gain = params [ "min" ], params [ "max" ], params [ "gain" ]
x = self . model . forward ( x , min_val , max_val , gain )
return x Pendant le passage avant, la variable params sera un dictionnaire comme ce qui suit:
{
"min" : torch . Tensor ([ 0.5 ] * buffer_size ),
"max" : torch . Tensor ([ 1.0 ] * buffer_size ),
"gain" : torch . Tensor ([ 1.0 ] * buffer_size )
} Les clés du dictionnaire sont spécifiées dans la fonction get_parameters .
Les paramètres prendront toujours des valeurs entre 0 et 1 et la fonction do_forward_pass peut être utilisée pour effectuer tout rediffusion nécessaire avant d'exécuter la méthode avant interne du modèle.
De plus, les paramètres envoyés par le plugin se présentent à une granularité au niveau de l'échantillon. Par défaut, nous prenons la moyenne de chaque tampon et renvoyons un seul flotteur (en tant que tenseur), mais la méthode aggregate_param peut être utilisée pour remplacer la méthode d'agrégation. Voir le fichier d'exportation Clipper complet pour un exemple de préservation de cette granularité.
Certains modèles audio retarderont l'audio pour une certaine quantité d'échantillons. Cela dépend de l'architecture de chaque modèle particulier. Pour que le signal humide et sec qui passe par le plugin soit aligné, les utilisateurs sont tenus de signaler le nombre d'échantillons de retard que leur modèle induit. Les calc_model_delay_samples peuvent être utilisés pour spécifier le nombre d'échantillons de retard. Les modèles RAVE ont en moyenne un tampon de retard (échantillons 2048) qui est communiqué statiquement dans la méthode calc_model_delay_samples et peut être vu dans les exemples. Les modèles implémentés avec le chevauchement-ADD auront un retard égal au nombre d'échantillons utilisés pour la crossfading, comme le montrent le wrapper du modèle DemUcs ou l'exemple de filtre spectral.
Le calcul du retard que votre modèle ajoute peut être difficile, d'autant plus qu'il peut y avoir plusieurs sources de retard différentes qui doivent être combinées (par exemple, un retard de conduite, un délai de filtre, un retard de tampon Loahead et / ou des réseaux de neurones formés à un audio sec et humide non aligné) . Il vaut la peine de passer un peu de temps à tester le modèle dans votre DAW pour vous assurer que le retard est correctement signalé.
L'ajout d'un tampon LookBehind à votre modèle peut être utile pour les modèles qui nécessitent une certaine quantité de contexte supplémentaire pour produire des résultats utiles. Un tampon LookBehind peut être activé facilement en indiquant le nombre d'échantillons de lookbehind dont vous avez besoin dans la méthode get_look_behind_samples . Lorsque cette méthode renvoie un nombre supérieur à zéro, la méthode do_forward_pass recevra toujours un tenseur de forme (in_n_ch, look_behind_samples + buffer_size) , mais doit toujours renvoyer un tenseur de forme (out_n_ch, buffer_size) des derniers échantillons.
Nous vous recommandons d'éviter d'utiliser un tampon de look-bind lorsque cela est possible, car il rend votre modèle moins efficace et peut entraîner des calculs gaspillés pendant chaque passe avant. Si vous utilisez un modèle purement convolutionnel, essayez plutôt de changer toutes les convolutions en convolutions mises en cache.
Il est courant que les modèles d'IA agissent de manière inattendue lorsqu'ils sont présentés avec des intrants en dehors de ceux présents dans leur distribution de formation. Nous fournissons une série de filtres communs (basses basses, passes élevées, pass de bande, arrêt de bande) dans le fichier Neutone_sdk / filters.py. Ceux-ci peuvent être utilisés pendant la passe avant pour restreindre le domaine des entrées dans le modèle. Certains d'entre eux peuvent induire une petite quantité de retard, consultez les exemples / Exemple_clipper_prefilter.py pour un exemple simple sur la façon de configurer un filtre.
Le plugin contient une liste par défaut de modèles destinés aux créateurs qui souhaitent les utiliser pendant leur processus créatif. Nous encourageons les utilisateurs à soumettre leurs modèles une fois qu'ils sont satisfaits des résultats qu'ils obtiennent afin qu'ils puissent être utilisés par la communauté dans son ensemble. Pour la soumission, nous avons besoin de métadonnées supplémentaires qui seront utilisées pour afficher des informations sur le modèle destiné aux créateurs et aux autres chercheurs. Cela sera affiché sur le site Web Neutone et à l'intérieur du plugin.
En sautant le modèle de clipper précédent, voici un exemple plus réaliste basé sur un modèle d'overdrive TCN aléatoire inspiré du micro-TCN.
class OverdriveModelWrapper ( WaveformToWaveformBase ):
def get_model_name ( self ) -> str :
return "conv1d-overdrive.random"
def get_model_authors ( self ) -> List [ str ]:
return [ "Nao Tokui" ]
def get_model_short_description ( self ) -> str :
return "Neural distortion/overdrive effect"
def get_model_long_description ( self ) -> str :
return "Neural distortion/overdrive effect through randomly initialized Convolutional Neural Network"
def get_technical_description ( self ) -> str :
return "Random distortion/overdrive effect through randomly initialized Temporal-1D-convolution layers. You'll get different types of distortion by re-initializing the weight or changing the activation function. Based on the idea proposed by Steinmetz et al."
def get_tags ( self ) -> List [ str ]:
return [ "distortion" , "overdrive" ]
def get_model_version ( self ) -> str :
return "1.0.0"
def is_experimental ( self ) -> bool :
return False
def get_technical_links ( self ) -> Dict [ str , str ]:
return {
"Paper" : "https://arxiv.org/abs/2010.04237" ,
"Code" : "https://github.com/csteinmetz1/ronn"
}
def get_citation ( self ) -> str :
return "Steinmetz, C. J., & Reiss, J. D. (2020). Randomized overdrive neural networks. arXiv preprint arXiv:2010.04237."Consultez la documentation des méthodes dans Core.py.py, ainsi que le modèle Overdrive aléatoire sur le site Web et dans le plugin pour comprendre où chaque champ sera affiché.
Pour soumettre un modèle, veuillez ouvrir un problème sur le référentiel GitHub. Nous avons actuellement besoin des éléments suivants:
model.nm sorti par la fonction save_neutone_model Le SDK fournit trois outils CLI qui peuvent être utilisés pour déboguer et tester des modèles enveloppés.
Exemple:
$ python -m neutone_sdk.benchmark benchmark-speed --model_file model.nm
INFO:__main__:Running benchmark for buffer sizes (128, 256, 512, 1024, 2048) and sample rates (48000,). Outliers will be removed from the calculation of mean and std and displayed separately if existing.
INFO:__main__:Sample rate: 48000 | Buffer size: 128 | duration: 0.014±0.002 | 1/RTF: 5.520 | Outliers: [0.008]
INFO:__main__:Sample rate: 48000 | Buffer size: 256 | duration: 0.028±0.003 | 1/RTF: 5.817 | Outliers: []
INFO:__main__:Sample rate: 48000 | Buffer size: 512 | duration: 0.053±0.003 | 1/RTF: 6.024 | Outliers: []
INFO:__main__:Sample rate: 48000 | Buffer size: 1024 | duration: 0.106±0.000 | 1/RTF: 6.056 | Outliers: []
INFO:__main__:Sample rate: 48000 | Buffer size: 2048 | duration: 0.212±0.000 | 1/RTF: 6.035 | Outliers: [0.213]
L'exécution de la vitesse de vitesse exécutera automatiquement des entrées aléatoires via le modèle à une fréquence d'échantillonnage de 48000 et des tailles de tampon de (128, 256, 512, 1024, 2048) et signaler le temps moyen pris pour exécuter l'inférence pour un tampon. À partir de cela, le 1/RTF est calculé, ce qui représente la quantité plus rapide que le modèle en temps réel. À mesure que ce nombre augmente, le modèle utilisera moins de ressources au sein de la DAW. Il est nécessaire que ce nombre soit supérieur à 1 pour que le modèle puisse être exécuté en temps réel sur la machine sur laquelle la référence est exécutée.
Les taux d'échantillonnage et les tailles de tampon en cours de test, ainsi que le nombre de fois où la référence est répétée en interne pour calculer les moyennes et le nombre de threads utilisés pour le calcul sont disponibles sous forme de paramètres. Exécutez python -m neutone_sdk.benchmark benchmark-speed --help pour plus d'informations. Lors de la spécification des fréquences d'échantillonnage personnalisées ou des tailles de tampon, chaque individu doit être transmise à la CLI séparément. Par exemple: --sample_rate 48000 --sample_rate 44100 --buffer_size 32 --buffer_size 64 .
Bien que la référence de vitesse soit rapide car les modèles sont généralement nécessaires pour être en temps réel, il est possible de rester coincé si le modèle est trop lent. Assurez-vous de choisir un nombre approprié de fréquences d'échantillonnage et de tailles de tampon à tester.
Exemple:
$ python -m neutone_sdk.benchmark benchmark-latency model.nm
INFO:__main__:Native buffer sizes: [2048], Native sample rates: [48000]
INFO:__main__:Model exports/ravemodel/model.nm has the following delays for each sample rate / buffer size combination (lowest delay first):
INFO:__main__:Sample rate: 48000 | Buffer size: 2048 | Total delay: 0 | (Buffering delay: 0 | Model delay: 0)
INFO:__main__:Sample rate: 48000 | Buffer size: 1024 | Total delay: 1024 | (Buffering delay: 1024 | Model delay: 0)
INFO:__main__:Sample rate: 48000 | Buffer size: 512 | Total delay: 1536 | (Buffering delay: 1536 | Model delay: 0)
INFO:__main__:Sample rate: 48000 | Buffer size: 256 | Total delay: 1792 | (Buffering delay: 1792 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 128 | Total delay: 1920 | (Buffering delay: 1920 | Model delay: 0)
INFO:__main__:Sample rate: 48000 | Buffer size: 128 | Total delay: 1920 | (Buffering delay: 1920 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 256 | Total delay: 2048 | (Buffering delay: 2048 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 512 | Total delay: 2048 | (Buffering delay: 2048 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 1024 | Total delay: 2048 | (Buffering delay: 2048 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 2048 | Total delay: 2048 | (Buffering delay: 2048 | Model delay: 0) L'exécution de la vitesse de référence calculera automatiquement la latence du modèle aux combinaisons de sample_rate=(44100, 48000) et buffer_size=(128, 256, 512, 1024, 2048) . Cela donne un aperçu général de ce qui se passera pour les paramètres de DAW communs. Le retard total est divisé en retard de tampon et en retard de modèle. Le retard du modèle est rapporté par le créateur du modèle dans l'emballage du modèle comme expliqué ci-dessus. Le retard de tampon est automatiquement calculé par le SDK en tenant compte de la combinaison de (sample_rate, buffer_size) spécifié par l'emballage (les natifs) et celui spécifié par le DAW au moment de l'exécution. L'exécution du modèle à sa combinaison native (sample_rate, buffer_size) entraînera un retard minimum.
Semblable à la référence de vitesse ci-dessus, les combinaisons testées de (sample_rate, buffer_size) peuvent être spécifiées à partir de la CLI. Exécutez python -m neutone_sdk.benchmark benchmark-latency --help pour plus d'informations.
$ python -m neutone_sdk.benchmark profile --model_file exports/ravemodel/model.nm
INFO:__main__:Profiling model exports/ravemodel/model.nm at sample rate 48000 and buffer size 128
STAGE:2023-09-28 14:34:53 96328:4714960 ActivityProfilerController.cpp:311] Completed Stage: Warm Up
30it [00:00, 37.32it/s]
STAGE:2023-09-28 14:34:54 96328:4714960 ActivityProfilerController.cpp:317] Completed Stage: Collection
STAGE:2023-09-28 14:34:54 96328:4714960 ActivityProfilerController.cpp:321] Completed Stage: Post Processing
INFO:__main__:Displaying Total CPU Time
INFO:__main__:-------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------
Name Self CPU % Self CPU CPU total % CPU total CPU time avg CPU Mem Self CPU Mem # of Calls
-------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------
forward 98.54% 799.982ms 102.06% 828.603ms 26.729ms 0 b -918.17 Kb 31
aten::convolution 0.12% 963.000us 0.95% 7.739ms 175.886us 530.62 Kb -143.50 Kb 44
...
...
Full output removed from GitHub.
L'outil de profilage exécutera le modèle à une fréquence d'échantillonnage de 48000 et une taille de tampon de 128 sous le profileur de Pytorch et sortira une série d'idées, telles que le temps total du processeur, l'utilisation totale de la mémoire du processeur (par fonction) et l'utilisation de la mémoire du processeur groupée (par groupe d'appels de fonction). Cela peut être utilisé pour identifier les goulots d'étranglement dans votre code de modèle (même dans l'appel du modèle dans l'appel do_forward_pass ).
Semblable à l'analyse comparative, il peut être exécuté à différentes combinaisons de fréquences d'échantillonnage et de tailles de tampon ainsi que différents nombres de threads. Exécutez python -m neutone_sdk.benchmark profile --help pour plus d'informations.
Nous accueillons toute contribution au SDK. Veuillez ajouter des types dans la mesure du possible et utiliser le formateur black pour la lisibilité.
La feuille de route actuelle est:
Le projet Audacitch a été une inspiration majeure pour le développement du SDK. Vérifiez-le ici