Référence du langage Neuroscript (Rust v3)
Neuroscript est le DSL canonique (Rust) pour les transformations MIDI dans Neurode MIDI. Les scripts sont compilés hors des threads temps réel en programmes déterministes.
Exemple rapide
# Garder les notes du canal 1 et limiter la vélocité
keep note where ch == 1
when vel > 110: event(vel=110)Instructions
Déclarations
var threshold
const max_vel=110keep / drop
keep note, cc
drop clock, realtime
keep note where ch == 2 and vel > 80when
when note in C3..C4: event(note=note + 12)
when type == cc and cc == 1: emit.cc(74, value)event
Modifier l'événement courant :
event(note=clamp(note + 12, 0, 127))
event(vel=clamp(vel + 10, 1, 127))
event(ch=2)emit
Créer de nouveaux événements :
emit.cc(74, value)
emit.noteOn(note, vel)
emit.noteOff(note)
emit.bend(0)
emit.pc(12)
emit.aftertouch(64)send
Envoyer l'événement courant (optionnellement en retard) :
send
send through
send after 1/8drop (événement)
Supprimer l'événement courant :
dropdestinations de routage
Acheminer des valeurs vers des registres, compteurs, variables ou champs :
counter("notes") + 1 -> counter("notes")
42 -> r("global", "latch")
note + 12 -> note
vel -> valueSélecteurs
Les sélecteurs sont des filtres par type d'événement pour keep/drop :
note,cc,pc,bend,aftertouchclock,start,stop,continue,realtimeall
Combiner avec des virgules :
drop clock, start, stop, continue, realtimeConditions
Champs
type— Type d'événementch— Canal (1-16)note— Numéro de notevel— Vélocitécc— Numéro de CCvalue— Valeur de CCprog— Numéro de programmebend— Valeur de pitch bend
Opérateurs
- Égalité :
==,!= - Comparaison :
<,<=,>,>= - Appartenance :
in - Booléens :
and,or,not
Exemples :
keep note where ch == 1 and vel > 90
drop note where vel < 20
drop cc
when note in 60..72: event(vel=clamp(vel + 5, 1, 127))Expressions
Littéraux
- Entiers :
0,127,-1 - Notes :
C4,F#3,Bb-1 - Chaînes :
"global","latch"
Fonctions intégrées
Fonctions de transformation musicale
transpose(semitones) - Transposer la note de n demi-tons (limité à 0-127)
when type == note : {
transpose(+12) # Monter d'une octave
send
}transposeWrap(semitones) - Transposer la note avec bouclage
transposeWrap(+15) # Boucle si résultat > 127transposeReject(semitones) - Transposer la note, supprimer l'événement si hors limites
transposeReject(-5) # Supprime l'événement si résultat < 0 ou > 127transposeMode(semitones, mode) - Transposer avec constante de mode explicite
Constantes de mode : clamp (0), wrap (1), reject (2)
transposeMode(+12, wrap) # Boucler si hors limites
transposeMode(-5, reject) # Supprimer si hors limitesoctave(n) - Déplacer de n octaves (limité à 0-127)
when type == note : {
octave(+1) # Monter d'une octave (équivalent à transpose(+12))
send
}octaveWrap(n) - Déplacer de n octaves avec bouclage
octaveWrap(+2) # Boucle si résultat > 127octaveReject(n) - Déplacer de n octaves, supprimer si hors limites
octaveReject(-2) # Supprime l'événement si résultat < 0 ou > 127octaveMode(n, mode) - Déplacer de n octaves avec constante de mode explicite
octaveMode(+1, wrap) # Boucler si hors limites
octaveMode(-1, reject) # Supprimer si hors limitesclampNote(min, max) - Limiter la note à une plage
clampNote(C2, C6) # Contraindre à une plage de deux octaveswrapNote(min, max) - Boucler la note dans une plage
wrapNote(C3, C5) # Boucler dans la plageFonctions de transformation de vélocité
velScale(factor) - Multiplier la vélocité par un facteur (limité à 1-127)
when type == note : {
velScale(0.8) # Réduire à 80%
send
}velAdd(delta) - Ajouter à la vélocité (limité à 1-127)
velAdd(-10) # Réduire la vélocité de 10velClamp(min, max) - Limiter la vélocité à une plage
velClamp(30, 100) # Contraindre la dynamique de vélocitévelFixed(value) - Définir la vélocité à une valeur fixe
velFixed(80) # Toutes les notes à vélocité 80Fonctions de transformation de canal
chSet(channel) - Définir le canal MIDI (1-16)
chSet(2) # Forcer tous les événements sur le canal 2chClamp(min, max) - Limiter le canal à une plage
chClamp(1, 8) # Contraindre aux canaux 1-8Fonctions utilitaires
clamp(value, min, max) - Limiter une valeur à la plage [min, max]
event(vel=clamp(vel + 20, 1, 127))scale(value, inMin, inMax, outMin, outMax) - Mettre à l'échelle une valeur d'une plage d'entrée à une plage de sortie
# Mettre à l'échelle la valeur CC 0-127 à la plage de vélocité 40-127
event(vel=scale(value, 0, 127, 40, 127))wrap(value, min, max) - Boucler une valeur de manière circulaire dans une plage
event(note=wrap(note + 12, 0, 127))min(a, b) - Retourner le minimum de deux valeurs
max(a, b) - Retourner le maximum de deux valeurs
abs(value) - Retourner la valeur absolue
Fonctions de compteur
counter(name) - Lire et incrémenter un compteur nommé
when type == note : {
# N'émettre qu'une note sur 4
when counter("every4") % 4 == 0 : send
}Fonctions de registre
r(scope, key) - Lire la valeur du registre avec portée
r("global", "lastNote")
r("source", "accumulator")
r("channel", "count")r(key) - Lire le registre dans la portée par défaut (locale à la route)
r("lastVel")Portées de registre
Les registres sont isolés par portée pour éviter les fuites d'état entre routes, sources et destinations.
| Portée | Durée de vie | Isolation | Cas d'usage |
|---|---|---|---|
global | Durée de vie de l'app | Partagé entre toutes les routes | Compteurs globaux, état de l'app |
source | Par source MIDI | Isolé par appareil d'entrée | Suivre l'état par source |
route | Par route | Isolé par config de route | Portée par défaut, état local à la route |
local | Par route | Alias pour route | Identique à route |
channel | Par source+canal | Isolé par canal d'entrée | Suivi d'état par canal |
dest | Par destination | Isolé par appareil de sortie | Suivre l'état par dest |
dest_channel | Par dest+canal | Isolé par canal de sortie | État par canal de sortie |
path | Par chemin de route | Isolé par paire source→dest | Suivre l'état par connexion |
path_channel | Par chemin+canal | Isolé par source→dest+canal | État de chemin à grain fin |
Exemples de portée de registre
# Compteur global (partagé entre toutes les routes)
when type == note : {
r("global", "totalNotes") = r("global", "totalNotes") + 1
}
# État spécifique à la source
when type == note : {
r("source", "lastNote") = note
}
# État isolé par canal
when type == note and ch == 1 : {
r("channel", "leadCount") = r("channel", "leadCount") + 1
}
# État de routage spécifique au chemin
when type == cc : {
r("path", "lastCC") = cc
}Fonctions d'émission
Les fonctions d'émission créent de nouveaux événements MIDI indépendants de l'événement courant.
Événements de note
emit.noteOn(channel, note, velocity) - Envoyer Note On
emit.noteOn(1, note + 12, vel) # Doubleur d'octaveemit.noteOff(channel, note, velocity) - Envoyer Note Off
emit.noteOff(1, note, 0)Changement de contrôle
emit.cc(channel, controller, value) - Envoyer un changement de contrôle 7 bits
when type == cc and cc == 1: emit.cc(74, value) # Mod wheel → cutoffemit.cc14(channel, controller, value) - Envoyer un changement de contrôle 14 bits (haute résolution)
emit.cc14(1, 1, 16383) # Plage complète de modulation 14 bitsPitch et programme
emit.bend(channel, value) - Envoyer Pitch Bend (-8192 à 8191)
emit.bend(1, 0) # Centrer le pitchemit.pc(channel, program) - Envoyer Program Change (0-127)
emit.pc(1, 5) # Sélectionner le programme 5emit.bank(channel, msb, lsb) - Envoyer Bank Select (CC#0 + CC#32)
emit.bank(1, 0, 8) # Banque 0, sous-banque 8Pression
emit.aftertouch(channel, value) - Envoyer Channel Pressure/Aftertouch
emit.aftertouch(1, 64)System Exclusive
emit.sysex(bytes) - Envoyer un message System Exclusive
emit.sysex([0xF0, 0x43, 0x10, 0x7F, 0x00, 0xF7])NRPN
emit.nrpn(channel, parameter, value) - Envoyer Non-Registered Parameter Number
emit.nrpn(1, 128, 64)Comportement de send
send
Émet l'événement courant et arrête le traitement des règles suivantes.
when type == note and note < 60 : {
event(ch=2)
send # Envoyer à la destination, arrêter le traitement
}
# Les règles ci-dessous ne s'exécuteront pas pour les notes < 60send through
Émet l'événement courant et continue le traitement des règles suivantes.
when type == note and note < 60 : {
emit.noteOn(1, note + 12, vel) # Ajouter une octave
send through # Continuer aux règles suivantes
}
# Cette règle S'EXÉCUTERA même pour les notes < 60
when type == note : {
event(vel=velScale(0.8))
send
}Utilisez send through lorsque vous voulez :
- Ajouter des notes d'harmonie tout en gardant l'original
- Appliquer plusieurs transformations en séquence
- Router vers plusieurs destinations avec un traitement différent
send after
Planifier un envoi retardé (nécessite une synchronisation de tempo ou un délai en millisecondes) :
send after 100ms
send after 1/8 # Délai d'une croche
send after 1/4 # Délai d'une noireExemples complets
Zone lead avec boost
keep note where ch == 1
event(note=clamp(note + 12, 0, 127))
event(vel=clamp(vel + 10, 1, 127))Supprimer les drums et le bruit CC
drop note where ch == 10
drop cc where cc in 64..127Mod wheel vers cutoff
when type == cc and cc == 1: emit.cc(74, value)Pour tester un événement unique, utilisez le Simulateur Neuroscript.
