Anleitung
Eigene Module programmieren
Mit einer kleinen Skriptsprache baust du eigene Module für den Analog Modular Simulator: Regler, Schalter, Buchsen und LEDs deklarieren, die Signalverarbeitung pro Sample beschreiben – fertig ist ein Modul, das im Rack klingt wie jedes andere.
Für KI-Assistenten: Die Datei AGENTS.md beschreibt die komplette Sprache mit allen Funktionen, Grenzen und Beispielen – gib sie deinem Assistenten, und er schreibt direkt lauffähige Module.
AGENTS.md herunterladenSchnellstart
Ein eigenes Modul ist eine Textdatei mit der Endung .amsmod. Sie lässt sich in jedem Texteditor schreiben.
- Lade das Beispiel
fold.amsmodherunter oder kopiere den Code unten in eine neue Datei. - Öffne den Simulator und wähle Datei → Eigene Module laden … (oder in der Modulbibliothek unten „Eigene Module laden …“).
- Wähle die Datei. Jedes Modul erscheint als Vorschau mit laufenden LEDs – mit „Hinzufügen“ landet es im Rack und unter „Eigene Module“ in der Bibliothek.
// Wavefolder with a level meter – an example custom module for the Analog Modular Simulator.
module fold "Fold" hp 6 theme black {
desc de "Wavefolder: faltet die Welle je nach FOLD mehrfach um – aus einem Sinus wird ein obertonreicher Klang."
desc en "Wavefolder: folds the wave over itself depending on FOLD – a sine turns into a rich, buzzy tone."
knob amount "FOLD" 0..1 = 0.3
knob depth "CV" 0..1 = 0.5 size s tip "CV amount"
switch mode "MODE" "SIN" "TRI" = 0
in x "IN" audio
in cv "CV" cv
out y "OUT" audio
led hot red
meter level "LEVEL"
process {
g = 1 + 4 * clamp(amount + cv / 10 * depth, 0, 1)
s = x / AUDIO * g
y = (mode == 0 ? sin(s * PI / 2) : fold(s, -1, 1)) * AUDIO
hot = abs(y) > 4.5
level = peak(y)
}
}fold.amsmod herunterladenAufbau einer Datei
Eine Datei enthält einen oder mehrere Blöcke module id "Name" { … }. Jeder Block wird ein eigenes Modul; die id (Kleinbuchstaben, Ziffern, _) ist seine feste Kennung, der Name steht auf der Frontplatte.
Im Block stehen die Deklarationen – je eine pro Zeile –, eigene Funktionen (fn) und genau ein process { … }. Kommentare beginnen mit // oder #. Innerhalb runder und eckiger Klammern darf eine Zeile umbrechen.
Reservierte Namen
Schlüsselwörter, Funktionen und Konstanten sind reserviert und können kein Name sein – ein Eingang heißt also nicht voct, hz oder gate. Neben allen Funktionen der Referenz sind das:
module desc knob switch button in out led meter state buffer const process fn for return if else and or not true false PI TAU E C4 AUDIO GATE ENV SR
Kopf und Texte
Hinter module id "Name" stehen optional die Breite hp 4 bis hp 32 (ohne Angabe wählt das Layout die kleinste passende Breite), das Aussehen theme alu, black, cream, blue oder wasp und ein Untertitel subtitle "TEXT".
desc "Text" beschreibt das Modul in der Bibliothek und in der Vorschau. Mit desc de "…" und desc en "…" gibt es je Sprache einen Text; angezeigt wird die Sprache des Simulators.
Bedienelemente und Buchsen
Jede Deklaration bekommt eine id (der Name im Code) und meist eine Beschriftung in Anführungszeichen (höchstens 8 Zeichen, wie bei echter Hardware meist in Großbuchstaben). Mit [n] hinter der id wird daraus ein Array aus n gleichen Elementen – siehe Schleifen und Arrays.
knob id "LABEL" min..max = start– Drehregler. Der Wert hat die Einheit des Reglers und wird geglättet. Optionen:log(logarithmisch, min > 0),int(ganzzahlig, ungeglättet),unit hz|s|ms|db|pct|v|st|bpm(Anzeige im Tooltip;pcterwartet 0..1),size s|m|l,tip "Tooltip",at x y,skin """<svg>…""".switch id "TITEL" "A" "B" ["C"] = 0– Kippschalter mit 2 oder 3 Stellungen; der Wert ist 0, 1 oder 2.button id "LABEL" = off– rastender Taster mit LED; der Wert ist ein Wahrheitswert (true/false).in id "LABEL" audio|pitch|gate|cv– Eingangsbuchse, Wert in Volt, 0 ohne Kabel. Die Signalart wählt nur das Testsignal der Vorschau. Mitnormal andereliest ein leerer Eingang den Wert eines anderen Eingangs.out id "LABEL" audio|pitch|gate|env|mod|cv– Ausgangsbuchse in Volt. Ein Ausgang behält seinen Wert, bis er neu zugewiesen wird. Die Signalart erscheint im Tooltip am Stecker;via a, bstatt der Art reicht die Art von Eingängen durch.led id red|green|yellow|blue– LED, Helligkeit 0..1 oder ein Wahrheitswert; sie leuchtet etwa 150 ms nach, damit auch kurze Impulse sichtbar sind.meter id "LABEL"– Pegelkette aus sechs LEDs wie am Output-Modul, Wert 0..1 – meistlevel = peak(signal).
Zustand, Puffer, Konstanten
state id = 0– ein Wert, der von Sample zu Sample erhalten bleibt (Zahl oder Wahrheitswert). Lokale Variablen gelten dagegen nur im aktuellen Sample. Als Array:state id[8] = 0oder mit Listestate id[] = [0, 1, 0, 1].buffer id 500 ms– Verzögerungsleitung fürwriteundread/readi; höchstens vier Puffer, zusammen 8 Sekunden.const id = 2 * PI– Konstante, die beim Übersetzen ausgerechnet wird (Zahlen, Konstanten und reine Funktionen). Als Tabelle:const skala[] = [0, 2, 4, 7, 9].
Der process-Block
process { … } läuft einmal pro Sample – bei 48 kHz also 48 000-mal je Sekunde. Er besteht aus Zuweisungen (=, +=, -=, *=, /=, auch an Array-Elemente a[i] = … und als x, y = f(…)), if … { } else { }, for i in a..b { } und write(puffer, wert). Jedes Modul braucht pro Sample eine feste, begrenzte Rechenzeit – deshalb haben Schleifen eine feste Anzahl Durchläufe.
Eine lokale Variable entsteht mit ihrer ersten Zuweisung und darf erst gelesen werden, wenn sie auf jedem Weg zugewiesen ist – ein if ohne else reicht nicht. Werte über mehrere Samples gehören in einen state.
Es gibt zwei Typen: Zahlen und Wahrheitswerte. Sie mischen sich nie von selbst. Bedingungen brauchen Wahrheitswerte (x > 1, high(x)), Rechnungen Zahlen: num(b) macht 0/1, gate(b) 0/10 V. An einen Ausgang gehört immer eine Zahl.
if und bedingung ? a : b rechnen nur den gewählten Zweig. and und or werten dagegen immer beide Seiten aus, damit rise(a) and rise(b) keine Flanke verpasst.
Wie jedes Modul im Rack sieht ein eigenes Modul seine Eingänge ein Sample verzögert – Rückkopplungen über Kabel sind erlaubt. Sichere Mathematik: Division durch 0 ergibt 0, ungültige Werte an Ausgängen werden zu 0, Ausgänge sind auf ±100 V begrenzt.
Operatoren
Von stark nach schwach bindend: Klammern und Aufrufe · ^ (Potenz) · Vorzeichen - + · * / % · + - · Vergleiche < <= > >= == != (nicht verkettbar) · not bzw. ! · and bzw. && · or bzw. || · ? :.
Spannungen und Konstanten
Wie im echten Eurorack: Audio ±5 V, Hüllkurven 0 bis 10 V, Gates und Trigger 0/10 V (ein Gate gilt ab 1 V als an und unter 0,1 V als aus), Tonhöhe 1 V pro Oktave mit 0 V = C4 (261,63 Hz).
Die eingebauten Generatoren liefern normierte Werte (±1 bzw. 0…1) – in Volt rechnet man erst an der Buchse: y = saw(f) * AUDIO. Zeiten stehen in Sekunden (pulse in Millisekunden), Frequenzen in Hz.
Konstanten: PI, TAU, E, C4 (261,6256), AUDIO (5), GATE und ENV (10), SR (Abtastrate zur Laufzeit), true, false.
Funktionen
Argumente mit Standardwert dürfen fehlen. num steht für eine Zahl, bool für einen Wahrheitswert, buffer und jack für den Namen eines Puffers bzw. einer Buchse (Ein- oder Ausgang).
Mathe
abs(x: num) → num- Betrag.
sign(x: num) → num- Vorzeichen: −1, 0 oder 1.
floor(x: num) → num- Abrunden.
ceil(x: num) → num- Aufrunden.
round(x: num) → num- Runden (halbe Werte von 0 weg).
frac(x: num) → num- Nachkommaanteil
x − floor(x), immer 0…1. sqrt(x: num) → num- Quadratwurzel; negative Werte ergeben 0.
exp(x: num) → num- e hoch x (x höchstens 80).
log(x: num) → num- Natürlicher Logarithmus; Werte ≤ 0 zählen wie 1e-30.
log2(x: num) → num- Logarithmus zur Basis 2.
exp2(x: num) → num- 2 hoch x.
sin(x: num) → num- Sinus (Bogenmaß).
cos(x: num) → num- Kosinus (Bogenmaß).
tan(x: num) → num- Tangens (Bogenmaß), begrenzt auf ±1 000 000.
tanh(x: num) → num- Weiche Sättigung, ab |x| = 3 genau ±1.
atan(x: num) → num- Arkustangens.
atan2(y: num, x: num) → num- Winkel des Punkts (x, y) in −π…π.
min(a: num, b: num) → num- Kleinerer Wert.
max(a: num, b: num) → num- Größerer Wert.
clamp(x: num, lo: num, hi: num) → num- Wert auf
lo…hibegrenzen. mix(a: num, b: num, t: num) → num- Überblendung:
a + (b − a) · t(t = 0 → a, t = 1 → b). mod(a: num, b: num) → num- Rest der Division, abgerundet (auch für negative Zahlen positiv bei positivem b).
pow(a: num, b: num) → num- Potenz
a^b; negative Basis mit gebrochenem Exponenten wird gespiegelt. fold(x: num, lo: num = -5, hi: num = 5) → num- Wavefolding: faltet x zwischen
loundhihin und her. wrap(x: num, lo: num = 0, hi: num = 1) → num- Umbruch: x läuft in
lo…hiim Kreis. softclip(x: num, lim: num = 5) → num- Weiche Begrenzung auf ±lim (linear bis 80 %).
Umrechnung
hz(v: num) → num- Tonhöhe in V/Oct → Frequenz in Hz (0 V = C4), dieselbe Stimmung wie der VCO.
voct(f: num) → num- Frequenz in Hz → V/Oct.
semi(v: num) → num- Auf Halbtöne runden (1/12 V).
dbamp(db: num) → num- Dezibel → Verstärkungsfaktor.
ampdb(x: num) → num- Verstärkungsfaktor → Dezibel.
gate(b: bool) → num- Wahrheitswert → 0 oder 10 V (für Gate-Ausgänge).
num(b: bool) → num- Wahrheitswert → 0 oder 1.
connected(jack: jack) → bool- Ist an dieser Buchse ein Kabel gesteckt? Geht für Eingänge und Ausgänge – ein ungesteckter Ausgang kann so in einen internen Bus gehen wie die Ausgänge 1–4 des Calculus.
Oszillatoren und Zufall
phasor(hz: num, reset: bool = false) → num- Phase 0…1 mit
hzDurchläufen pro Sekunde (taktstabil in f64);resetsetzt sie auf 0 (Hard-Sync). sine(hz: num, reset: bool = false) → num- Sinus-Oszillator ±1.
tri(hz: num, reset: bool = false) → num- Dreieck-Oszillator ±1.
saw(hz: num, reset: bool = false) → num- Sägezahn ±1, bandbegrenzt (PolyBLEP);
reset= Hard-Sync. square(hz: num, pw: num = 0.5, reset: bool = false) → num- Rechteck ±1 mit Pulsbreite
pw(0,02…0,98), bandbegrenzt;reset= Hard-Sync. noise() → num- Weißes Rauschen ±1.
random(trig: bool) → num- Zufallswert 0…1, neu bei jedem
trig.
Trigger, Gates, Zähler
rise(x: num) → bool- Steigende Flanke: genau im Sample, in dem x über 1 V steigt (Schmitt-Trigger 1 V / 0,1 V).
fall(x: num) → bool- Fallende Flanke: genau im Sample, in dem x unter 0,1 V fällt.
high(x: num) → bool- Pegel mit Hysterese: an ab 1 V, aus unter 0,1 V.
changed(x: num) → bool- Wahr, wenn x sich seit dem letzten Sample geändert hat.
prev(x: num) → num- Wert von x im vorigen Sample.
pulse(trig: bool, ms: num = 5) → bool- Impuls von
msMillisekunden abtrig(nachtriggerbar) – für Trigger-Ausgänge und LEDs. counter(up: bool, reset: bool = false, n: num = 0) → num- Zählt bei jedem
uphoch,resetsetzt auf 0; mitn≥ 1 läuft er im Kreis 0…n−1. every(trig: bool, n: num) → bool- Wahr beim 1., (n+1)., (2n+1). … Trigger – ein Takt-Teiler.
toggle(trig: bool) → bool- Kippt bei jedem Trigger zwischen falsch und wahr (Flipflop).
latch(set: bool, reset: bool) → bool- Wird bei
setwahr und beiresetfalsch (reset gewinnt). sah(x: num, trig: bool) → num- Sample & Hold: übernimmt x bei jedem Trigger und hält den Wert.
timer(trig: bool) → num- Sekunden seit dem letzten Trigger.
Hüllkurven und Glätten
ad(trig: bool, a: num, d: num) → num- Hüllkurve 0…1: steigt in
aSekunden, fällt indSekunden; neuer Trigger startet ab dem aktuellen Pegel. adsr(gate: bool, a: num, d: num, s: num, r: num) → num- Hüllkurve 0…1 mit Gate (Wahrheitswert): Attack, Decay, Sustain (0…1), Release – Zeiten für den vollen Hub.
lag(x: num, s: num) → num- Weiches Folgen mit Zeitkonstante
sSekunden (Einpol-Tiefpass) – Glide, Glättung. slew(x: num, up: num, down: num = up) → num- Begrenzt die Änderung auf
upbzw.downVolt pro Sekunde (lineares Gleiten). smooth(x: num) → num- Glättung um 15 ms gegen Knistern.
peak(x: num) → num- Spitzenwert für Pegelketten: |x|/5, steigt sofort, fällt in etwa 300 ms.
Filter
lp1(x: num, hz: num) → num- Tiefpass 1. Ordnung (6 dB/Oktave).
hp1(x: num, hz: num) → num- Hochpass 1. Ordnung (6 dB/Oktave).
lp(x: num, hz: num, res: num = 0) → num- Tiefpass 2. Ordnung (State-Variable, 12 dB/Oktave);
res0…1 betont die Grenzfrequenz. bp(x: num, hz: num, res: num = 0) → num- Bandpass 2. Ordnung.
hp(x: num, hz: num, res: num = 0) → num- Hochpass 2. Ordnung.
notch(x: num, hz: num, res: num = 0) → num- Kerbfilter 2. Ordnung.
dcblock(x: num) → num- Entfernt Gleichspannung (Hochpass 20 Hz).
Verzögerung
read(buf: buffer, s: num) → num- Wert des Puffers von vor
sSekunden (interpoliert, mindestens 1 Sample, höchstens die Pufferlänge). readi(buf: buffer, n: num) → num- Wert des Puffers von vor
nganzen Samples. write(buf: buffer, x: num)- Schreibt x in den Puffer (als eigene Anweisung, einmal je Sample).
Zustand je Aufruf
Funktionen mit Gedächtnis – Oszillatoren, Flanken, Zähler, Hüllkurven, Filter – haben an jeder Stelle im Text einen eigenen Zustand. Zweimal sine(220) sind zwei unabhängige Oszillatoren. Dasselbe gilt für jeden Aufruf einer eigenen Funktion und jeden Durchlauf einer Schleife – zu verwalten gibt es nichts.
Steht ein solcher Aufruf in einem Zweig, der gerade nicht läuft, pausiert er: Die Phase steht still, der Zähler zählt nicht. Soll etwas immer weiterlaufen, gehört der Aufruf vor das if in eine Variable.
Eigene Funktionen
Mit fn name(a, b) { … return wert } fasst du Rechnungen zusammen, die mehrfach vorkommen. Funktionen stehen im Modulblock neben den Deklarationen und dürfen andere Funktionen aufrufen, aber nicht sich selbst. Beim Übersetzen wird jede Funktion an ihrer Aufrufstelle eingesetzt – ein Aufruf kostet so viel wie ihr Rumpf, nicht mehr.
fn allpass(x, a) { // own state per call site
state x1 = 0
state y1 = 0
y = a * (x - y1) + x1
x1 = x
y1 = y
return y
}
fn split(v, hard: bool = false) { // two results, a default value
if hard { return sign(v), abs(v) }
else { return v, abs(v) }
}
process {
y = allpass(allpass(x, 0.3), 0.6) // two independent allpasses
s, _ = split(y) // _ discards a value
}Parameter sind Zahlen; x: bool macht einen Wahrheitswert daraus, x = 0.5 einen Standardwert (Parameter mit Standardwert stehen am Ende). Im Rumpf dürfen Parameter verändert werden. return a, b liefert mehrere Werte, die x, y = f(…) übernimmt; _ verwirft einen. return steht am Ende der Funktion bzw. am Ende jedes Zweigs ihres letzten if.
state im Rumpf gibt jeder Aufrufstelle einen eigenen gespeicherten Wert – so entstehen eigene Bausteine wie ein Allpass oder ein Integrator, die sich wie eingebaute Funktionen verhalten. Eine Funktion liest alles im Modul (Regler, Buchsen, state, Konstanten), ändert aber nur ihre eigenen Werte: Ergebnisse gibt sie zurück, zugewiesen wird in process. Das Beispiel Integral baut so den Calculus nach.
Schleifen und Arrays
Ein Array ist eine Reihe gleichartiger Werte: state hist[4] = 0 (oder mit Liste), const scale[] = [0, 2, 4, 7, 9] (Länge aus der Liste) – und genauso Regler, Schalter, Taster, Buchsen, LEDs und Pegelketten. knob pitch[8] # … legt acht Regler mit den IDs pitch1 bis pitch8 an; ein # in der Beschriftung wird zur Nummer, mit at x y step dx dy stehen sie in festem Abstand.
const N = 8
knob pitch[N] "#" -2..2 = 0 unit v size s at 8 30 step 9.3 0 // pitch1 … pitch8, labels 1 … 8
button on[N] "" = on at 8 44 step 9.3 0
led l[N] green at 8 52 step 9.3 0
state hist[4] = 0
const scale[] = [0, 2, 4, 7, 9]
process {
n = counter(rise(clk), false, N)
cv = pitch[n] // index at run time
for i in 0..N { l[i] = n == i } // i = 0 … 7, unrolled when compiling
}Elemente liest und schreibt man mit a[i], gezählt ab 0. Der Index darf zur Laufzeit entstehen, etwa aus einem Zähler – er wird abgerundet und auf den gültigen Bereich begrenzt (einen Umlauf schreibt man selbst: a[n % N]). Bei connected(buchse[i]) muss er beim Übersetzen feststehen.
for i in 0..N { … } wiederholt den Rumpf für i = 0 bis N − 1. Die Grenzen sind feste ganze Zahlen (höchstens 256 Durchläufe); beim Übersetzen wird die Schleife entrollt, i ist in jedem Durchlauf eine Konstante. Jeder Durchlauf ist eine eigene Aufrufstelle – Oszillatoren oder Filter darin laufen unabhängig. break und while gibt es nicht: Die Rechenzeit je Sample steht immer fest.
Panel-Layout
Das Panel entsteht automatisch: oben die Regler, darunter Schalter und Taster, dann LEDs und Pegelketten; Ein- und Ausgänge sitzen unten (Ausgänge zuletzt, auf dunklem Feld). Jede Reihe wird mittig gesetzt.
Mit at x y steht ein Element genau dort (Mitte in Millimetern ab der linken oberen Ecke; das Panel ist 128,5 mm hoch und HP × 5,08 − 0,3 mm breit). Bei Arrays setzt at x y step dx dy das erste Element, jedes weitere steht um dx/dy versetzt. Das automatische Layout weicht festen Plätzen nicht aus – einen Abschnitt also ganz oder gar nicht von Hand setzen.
Passen die Bedienelemente nicht, meldet der Compiler layoutTooFull mit einem Vorschlag für die Breite.
Eigene Grafiken
Frontplatte und Kappen lassen sich mit SVG selbst gestalten. face zeichnet über die ganze Platte – unter Schrauben, Titel und Bedienelementen; als viewBox passt die Panelgröße in Millimetern (bei 6 HP z. B. 0 0 30 128.5). skin an einem knob oder button ersetzt die gezeichnete Kappe (die Skala und der Zeiger bzw. die LED bleiben).
Grafiken stehen in einem mehrzeiligen Text zwischen """ und """. Sie werden geprüft: Erlaubt sind nur Formen, Gruppen, Farbverläufe und Text – keine Skripte, Ereignis-Attribute, Stile, Klassen, Bilder, Filter oder Verweise nach außen. Verweise auf eigene IDs (fill=url(#verlauf)) sind erlaubt; IDs werden je Modul eindeutig gemacht.
Erlaubte Elemente
g path rect circle ellipse line polyline polygon text tspan defs linearGradient radialGradient stop clipPath
Erlaubte Attribute
x y x1 y1 x2 y2 cx cy r rx ry fx fy width height d points fill fill-opacity fill-rule stroke stroke-width stroke-opacity stroke-linecap stroke-linejoin stroke-dasharray stroke-dashoffset stroke-miterlimit opacity transform id offset stop-color stop-opacity gradientUnits gradientTransform spreadMethod clip-path clip-rule clipPathUnits font-size font-weight font-style text-anchor dominant-baseline letter-spacing visibility
// Custom artwork: front panel (face) and knob cap (skin) as SVG – a starting point for your own design.
module glow "Glow" hp 6 theme black {
desc de "Abschwächer mit eigener Frontplatte: Farbverlauf, Ring und eine eigene Knopfkappe – Vorlage für eigene Grafiken."
desc en "Attenuator with custom panel art: colour gradient, ring and a custom knob cap – a template for your own artwork."
face """
<svg viewBox="0 0 30 128.5">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#3a2150"/>
<stop offset="1" stop-color="#10223a"/>
</linearGradient>
</defs>
<rect width="30" height="128.5" fill="url(#bg)"/>
<circle cx="15" cy="25.8" r="10" fill="none" stroke="#b97a4b" stroke-width="0.4" stroke-dasharray="1 1"/>
</svg>
"""
// The automatic layout puts the first large knob at the top centre (y ≈ 25.8 mm) – the ring sits around it.
knob level "LEVEL" 0..1 = 0.7 size l skin """
<svg viewBox="-10 -10 20 20">
<circle r="10" fill="#b97a4b"/>
<circle r="7.2" fill="#3a2150" stroke="#f0dcc4" stroke-width="0.8"/>
</svg>
"""
in x "IN" audio
out y "OUT" audio
meter lvl "LEVEL"
process {
y = x * level
lvl = peak(y)
}
}Laden, Ersetzen, Teilen
Datei → Eigene Module laden … öffnet ein Fenster: Datei wählen (am Computer auch hineinziehen), ein kurzer Ladekreis, dann erscheint jedes Modul der Datei mit fertigem Panel und laufender Vorschau – LEDs und Pegelketten zeigen, wie es auf Testsignale reagiert; darunter steht, welchen Anteil eines CPU-Kerns das Modul höchstens braucht. Für jedes Modul entscheidest du: Hinzufügen oder nicht.
Gibt es im Patch schon ein Modul mit derselben id, heißt der Knopf „Ersetzen“: Alle Exemplare bekommen die neue Fassung und behalten Position, Reglerwerte (über die id der Regler) und Kabel (über die id der Buchsen).
Eigene Module gehören zum Patch: Sie werden mit ihm gespeichert und mit der Patch-Datei geteilt. In der Bibliothek stehen sie unter „Eigene Module“; über ⋯ bzw. Rechtsklick lassen sie sich als .amsmod speichern, aus einer Datei ersetzen oder entfernen.
Am Handy funktioniert alles genauso: Datei wählen, Vorschau ansehen, hinzufügen. Manche Dateimanager hängen .txt an – auch solche Dateien werden gelesen.
Fehlermeldungen
Findet der Compiler einen Fehler, zeigt das Fenster ihn sofort – immer als englischen Satz mit Zeile und Spalte und dem markierten Ausschnitt. Syntaxfehler betreffen die ganze Datei; Fehler in einem Modul nur dieses, die übrigen lassen sich trotzdem hinzufügen.
Jeder Fehlercode hat hier einen Eintrag; im Fenster führt ein Klick auf den Code direkt dorthin.
fileTooLargeThe file is larger than max KB.
fileTooManyLinesThe file has more than max lines.
fileNotTextThe file is not a text file – please save it as UTF-8.
fileEmptyThe file contains no module.
fileTooManyModulesA file may contain at most max modules.
lexBadCharUnexpected character “char”.
lexBadNumber“text” is not a valid number.
lexUnclosedStringThe text is not closed – the closing quotation mark is missing.
lexBadStringTexts must not contain control characters.
syntaxExpectedExpected expected, found found.
syntaxUnexpectedUnexpected: found.
syntaxTopLevelOutside a module only “module …” is allowed (found: found).
syntaxNoProcessThe module has no “process { … }” block.
syntaxDuplicateProcessA module has exactly one “process” block.
syntaxUnclosedBlockThe block is not closed – “}” is missing.
syntaxChainedCompareComparisons cannot be chained – join them with “and”, e.g. “a < x and x < b”.
syntaxNestingNested too deeply (at most max levels).
syntaxStatementA statement is expected here (assignment, “if”, “for”, “return” or “write”), found: found.
declDuplicate“name” is already declared.
declDuplicateModuleThe module ID “name” appears more than once in the file.
declReserved“name” is a reserved word and cannot be a name.
declBadIdThe module ID “name” may only contain lowercase letters, digits and _ and must start with a letter.
declEmptyNameThe module name must not be empty.
declIdTooLongNames have at most max characters.
declTextTooLongThe text is too long (at most max characters).
declBadRangeInvalid range: the minimum must be less than the maximum (with “log” both above 0, not combined with “int”).
declBadDefaultThe default value is outside the range.
declBadOptionThe option “name” is unknown or not allowed here (on arrays “at” needs “step”; “normal” is not possible there).
declDuplicateOptionThe positions of a switch must differ.
declBadThemeUnknown theme “name” (alu, black, cream, blue, wasp).
declBadHpThe width must be a whole number from min to max HP.
declBadSignalUnknown signal type “name”.
declBadColorUnknown LED colour “name” (red, green, yellow, blue).
declBadUnitUnknown unit “name” (hz, s, ms, db, pct, v, st, bpm).
declBadSizeUnknown knob size “name” (s, m, l).
declBadNormal“name” is not a suitable input.
declBadSwitchA switch has 2 or 3 positions.
declTooManyToo many what (at most max).
declBufferTooLongBuffers are at most max s long in total.
declNotConstOnly a constant expression is allowed here (numbers, constants, pure functions).
declDuplicateDescThe description is given twice.
declDuplicateFaceThe panel artwork (“face”) is given twice.
svgSyntaxThe SVG graphic is not well-formed (at “detail”).
svgElementThe SVG element “name” is not allowed (only shapes, groups, gradients and text).
svgAttributeThe SVG attribute “name” is not allowed (no scripts, styles, classes or links).
svgValueThe value of “name” is not allowed (references only to own IDs such as url(#gradient)).
svgTooLargeThe SVG graphic is too large (at most max KB).
nameUnknownUnknown name “name”.
callUnknownUnknown function “name”.
callArity“name” expects min to max arguments, not got.
callArgTypeArgument index of “name” must be expected.
callNeedsBuffer“name” needs a buffer (“buffer”).
callNeedsInput“name” needs the name of an input or output jack as its argument.
callUnusedResultThe result of “name” is not used – assign it to a variable.
callNoValue“name” returns no value and can only be used as a statement.
assignReadonly“name” cannot be assigned – knobs, inputs, constants, buffers, functions and loop variables are read-only.
assignTypeChange“name” already has a different type (number or boolean).
useBeforeAssign“name” is read before it is assigned on every path – use “state” for values across samples.
typeNeedBoolA boolean is expected here, e.g. “x > 1” or “high(x)”.
typeNeedNumA number is expected here – convert booleans with “num(b)” or “gate(b)”.
typeBoolToOutOutput “name” needs a voltage – “gate(b)” turns a boolean into 0/10 V.
fnRecursiveThe function “name” calls itself (directly or through other functions) – recursion is not allowed.
fnReturnOutside“return” is only allowed inside a function.
fnReturnNotLast“return” must be the last statement of the function or of a branch of its last “if”.
fnMissingReturnNot every path through “name” ends with “return”.
fnReturnMismatchAll “return” statements of “name” must give the same number of values of the same types.
fnResultCountThe number of results does not match: “name” gives count, so assign exactly that many names (a, b = name(…)).
fnWritesOutsideA function can only change its own values and state – return the value and assign “name” in process.
fnStatePlacement“state” belongs at module level or at the top level of a function body.
fnParamOrderParameters with a default value come last – “name” needs one as well.
loopBadRangeA loop needs whole-number constant bounds a..b with a < b and at most max steps.
arrayNotArray“name” is not an array.
arrayNeedsIndex“name” is an array – use an element such as “name[0]”.
arrayIndexRangeIndex index is outside “name” (0 to max).
arrayIndexConstHere the index of “name” must be known when compiling (a number, a constant or a loop variable).
arrayBadLengthThe length of “name” must be a whole-number constant from 1 to max.
arrayListLengthThe list for “name” has got values, but the array has len.
limitCodeThe program is too long (at most max words).
limitRegistersToo many values at once (at most max registers).
limitStateToo many stateful calls (at most max state slots).
limitCostThe module is too expensive (cost of at most max units per sample).
limitExpandFunctions and loops expand to too much code (more than max statements).
layoutTooFullThe controls do not fit on hp HP (suggestion: suggest HP).
layoutOverlap“a” and “b” overlap.
layoutOutside“name” lies outside the usable area.
layoutTitleThe module name does not fit on hp HP.
Hinweise (blockieren nicht)
warnUnusedControl“name” is never read.
warnOutputNeverSetOutput “name” is never assigned.
warnUnusedVar“name” is assigned but never read.
warnBufferNeverWrittenBuffer “name” is never written.
warnUnusedFunctionThe function “name” is never called.
Grenzen und Leistung
Damit kein Modul den Ton stören kann, hat jedes eine feste Obergrenze an Rechenzeit pro Sample. Eine Einheit entspricht grob einer einfachen Rechnung; Filter und Hüllkurven kosten mehr. Die Grenze reicht für Dutzende Filter oder ein ganzes Modul wie den Calculus samt Überabtastung (Beispiel Integral) – das Fenster beim Laden zeigt, welchen Anteil eines CPU-Kerns ein Modul höchstens braucht. Wie viele solcher Module gleichzeitig laufen, hängt vom Gerät ab.
| Kosten je Sample (Einheiten, ≈ 2 ns) | 1024 |
| Regler, Schalter und Taster je Modul | 32 |
| Eingänge je Modul | 16 |
| Ausgänge je Modul | 16 |
| LEDs und Pegelketten je Modul | 16 |
| Puffer je Modul · Gesamtlänge | 4 · 8 s |
| Programmlänge (Wörter) | 4096 |
| Zustandsplätze (Funktionen mit Gedächtnis) | 256 |
| Elemente je Array | 64 |
| Durchläufe je Schleife · Anweisungen nach Entrollen und Einsetzen | 256 · 20000 |
| Eigene Funktionen · Parameter · Rückgabewerte | 64 · 16 · 8 |
| Dateigröße · Zeilen | 128 KB · 5000 |
| Module je Datei | 16 |
| Eigene Module je Patch | 32 |
| Zeichen für Name · Beschriftung · Beschreibung | 20 · 8 · 160 |
| Breite in HP | 4–32 |
| Größe einer SVG-Grafik | 32 KB |
Beispiele
Alle Beispiele lassen sich herunterladen und direkt laden – sie decken die Funktionsgruppen ab und eignen sich als Vorlage.
Fold – Wavefolder mit PegelketteMathe, Schalter, LED und Pegelkette: Der Sinus wird je nach FOLD mehrfach gefaltet.
// Wavefolder with a level meter – an example custom module for the Analog Modular Simulator.
module fold "Fold" hp 6 theme black {
desc de "Wavefolder: faltet die Welle je nach FOLD mehrfach um – aus einem Sinus wird ein obertonreicher Klang."
desc en "Wavefolder: folds the wave over itself depending on FOLD – a sine turns into a rich, buzzy tone."
knob amount "FOLD" 0..1 = 0.3
knob depth "CV" 0..1 = 0.5 size s tip "CV amount"
switch mode "MODE" "SIN" "TRI" = 0
in x "IN" audio
in cv "CV" cv
out y "OUT" audio
led hot red
meter level "LEVEL"
process {
g = 1 + 4 * clamp(amount + cv / 10 * depth, 0, 1)
s = x / AUDIO * g
y = (mode == 0 ? sin(s * PI / 2) : fold(s, -1, 1)) * AUDIO
hot = abs(y) > 4.5
level = peak(y)
}
}Divider und Toggle – zwei Module in einer DateiTaktteiler mit Reset und ein Flipflop: Flanken, Zähler, Impulse und Gates.
// Two clock tools in one file – every `module` block becomes a module of its own.
module divider "Divider" hp 4 {
desc de "Taktteiler: gibt jeden n-ten Takt als Trigger weiter (÷2 bis ÷8), RST beginnt von vorn."
desc en "Clock divider: passes on every n-th clock as a trigger (÷2 to ÷8), RST starts over."
knob div "DIV" 2..8 = 4 int
in clk "CLK" gate
in reset "RST" gate
out q "OUT" gate
led act yellow
process {
t = rise(clk)
n = counter(t, rise(reset), div)
fire = t and n == 1
q = gate(pulse(fire, 10))
act = pulse(fire, 60)
}
}
module toggle "Toggle" {
desc de "Flipflop: jeder Takt schaltet den Ausgang um – halbes Tempo mit 50 % Tastverhältnis, INV liefert das Gegenteil."
desc en "Flip-flop: every clock flips the output – half tempo at 50 % duty cycle, INV gives the opposite."
in clk "CLK" gate
out q "Q" gate
out nq "INV" gate
led on green
process {
s = toggle(rise(clk))
q = gate(s)
nq = gate(not s)
on = s
}
}Glow – eigene Frontplatte und KnopfkappeVorlage für eigene Grafiken: SVG-Farbverlauf als Frontplatte, eigene Kappe am großen Regler.
Drift – gleitende ZufallsspannungZufall bei jedem Takt oder in freier Rate, weich geglättet.
// Random, smoothly gliding control voltage – brings filters, pitch or effects to life.
module drift "Drift" hp 4 {
desc de "Zufallsspannung: springt bei jedem Takt (ohne Takt in freier Rate) auf einen neuen Wert und gleitet weich dorthin."
desc en "Random voltage: jumps to a new value on every clock (without a clock at a free rate) and glides there smoothly."
knob rate "RATE" 0.05..10 = 0.5 log unit hz
knob glide "GLIDE" 0..2 = 0.3 unit s
knob range "RANGE" 0..10 = 5 unit v
in clk "CLK" gate
out y "OUT" cv
led tick yellow
process {
ph = phasor(rate)
wrapped = ph < prev(ph)
t = connected(clk) ? rise(clk) : wrapped
y = lag(random(t) * 2 - 1, glide) * range
tick = pulse(t, 40)
}
}Quantizer – Halbtöne und PentatonikTonhöhe runden, mit Trigger halten, bei jedem neuen Ton ein Gate.
// Quantizer: snaps a control voltage to semitones or the major pentatonic scale.
module quant "Quantizer" {
desc de "Quantisierer: rundet V/Oct auf Halbtöne oder die Dur-Pentatonik; mit Trigger hält er den Ton bis zum nächsten Takt."
desc en "Quantizer: rounds V/Oct to semitones or the major pentatonic scale; with a trigger it holds the note until the next clock."
switch scale "SCALE" "CHR" "PEN" = 0
in x "IN" pitch
in clk "TRIG" gate
out y "OUT" pitch
out g "GATE" gate
process {
s = semi(x)
// Pentatonic: round the semitone within the octave down to 0, 2, 4, 7 or 9.
oct = floor(s)
n = round((s - oct) * 12)
pen = n >= 9 ? 9 : n >= 7 ? 7 : n >= 4 ? 4 : n >= 2 ? 2 : 0
q = scale == 0 ? s : oct + pen / 12
y = connected(clk) ? sah(q, rise(clk)) : q
g = gate(pulse(changed(y), 5))
}
}LPG – Low-Pass-GateEine Hüllkurve öffnet Filter und Lautstärke zugleich.
// Low-pass gate: one envelope opens filter and volume together – the typical "plonk".
module lpg "LPG" hp 4 {
desc de "Low-Pass-Gate: Jeder Trigger lässt Lautstärke und Helligkeit gemeinsam ausklingen – wie ein angeschlagenes Instrument."
desc en "Low-pass gate: each trigger lets volume and brightness decay together – like a struck instrument."
knob decay "DECAY" 0.02..3 = 0.4 log unit s
knob tone "TONE" 0..1 = 0.6
in x "IN" audio
in trig "TRIG" gate
out y "OUT" audio
led env green
process {
e = ad(rise(trig), 0.002, decay)
cutoff = 80 * exp2(e * (2 + tone * 6))
y = lp(x, cutoff, 0.1) * e
env = e
}
}Chorus – modulierte VerzögerungPuffer, write und read mit einem langsamen Sinus.
// Chorus: a slowly wavering delay doubles the sound.
module chorus "Chorus" hp 6 {
desc de "Chorus: mischt das Signal mit einer Kopie, deren Verzögerung ein langsamer Sinus schwanken lässt – breit und schwebend."
desc en "Chorus: mixes the signal with a copy whose delay is swayed by a slow sine – wide and shimmering."
knob rate "RATE" 0.05..3 = 0.4 log unit hz
knob depth "DEPTH" 0..1 = 0.5
knob blend "MIX" 0..1 = 0.5
in x "IN" audio
out y "OUT" audio
buffer line 50 ms
process {
write(line, x)
d = 0.012 + 0.008 * depth * sine(rate)
y = mix(x, read(line, d), blend)
}
}Osc – Oszillator mit Hard-SyncBandbegrenzter Sägezahn und Rechteck mit V/Oct und Sync.
// Oscillator with saw, square with pulse width and hard sync – band-limited (PolyBLEP).
module osc "Osc" hp 6 {
desc de "Oszillator: Sägezahn und Rechteck (Pulsbreite per Regler), V/Oct-Eingang und Hard-Sync – bandbegrenzt, ohne Aliasing."
desc en "Oscillator: saw and square (pulse width by knob), V/Oct input and hard sync – band-limited, without aliasing."
knob tune "TUNE" -2..2 = 0 unit v tip "Coarse tune (octaves)"
knob pw "PW" 0.05..0.95 = 0.5 unit pct
in pitch "V/OCT" pitch
in sync "SYNC" gate
out sw "SAW" audio
out sq "SQR" audio
process {
f = hz(pitch + tune)
r = rise(sync)
sw = saw(f, r) * AUDIO
sq = square(f, pw, r) * AUDIO
}
}Steps – 8-Schritt-SequencerArrays aus Reglern, Tastern und LEDs, ein Index zur Laufzeit und eine Schleife.
// Steps: an 8-step sequencer – arrays of knobs, buttons and LEDs, a runtime index and a loop.
module steps "Steps" hp 16 {
desc de "8-Schritt-Sequencer: Jeder Takt geht einen Schritt weiter, Taster schalten Schritte stumm, LEN kürzt die Folge, RST beginnt von vorn."
desc en "8-step sequencer: every clock moves one step on, buttons mute steps, LEN shortens the sequence, RST starts over."
const N = 8
// Arrays: pitch1 … pitch8, on1 … on8, l1 … l8 – "#" in a label becomes the step number, "at … step" spaces them.
knob pitch[N] "#" -2..2 = [0, 0.25, 0.583, 0.25, 1, 0.583, 0.333, 0.75] unit v size s at 8 30 step 9.3 0
button on[N] "" = on at 8 44 step 9.3 0
led l[N] green at 8 52 step 9.3 0
knob len "LEN" 1..N = N int at 40.5 70
in clk "CLK" gate at 14 100
in rst "RST" gate at 28 100
out cv "CV" pitch at 53 100
out g "GATE" gate at 67 100
state n = 0 // current step (0 = step 1)
state armed = false // the first clock after start or RST plays step 1
process {
t = rise(clk)
if rise(rst) {
n = 0
armed = false
} else if t {
if armed { n = (n + 1) % len } else { armed = true }
}
cv = pitch[n] // index at run time
g = gate(high(clk) and on[n])
for i in 0..N { l[i] = n == i } // unrolled: eight assignments
}
}Integral – der Calculus als SkriptEin komplexes Modul: eigene Funktionen mit eigenem Zustand, Integrator in f64 mit Überabtastung, eine Schleife über den Bus und connected() an Ausgängen.
// Integral: a two-channel function generator written as a script – the built-in Calculus rebuilt with custom
// functions, per-call state, loops, arrays and connected() on outputs.
//
// Channel 1 and 4 are the same function "channel" called twice; each call has its own state. The core is a
// voltage-dependent integrator in f64 that carries the remaining time over segment ends (exact cycle length up to
// audio rate) and runs twice per sample with a half-band filter (anti-aliasing). Known differences to the built-in
// module: RISE, FALL and RESP are smoothed like every knob, there is no idle shortcut (the load is always the worst
// case) and the half-band filter computes in f64.
module integral "Integral" hp 20 theme black {
desc de "Funktionsgenerator als Skript: zwei Kanäle (Hüllkurve, LFO, Slew bis Audiorate), zwei Offsets, SUM/OR/INV – ein Nachbau des Calculus."
desc en "Function generator as a script: two channels (envelope, LFO, slew up to audio rate), two offsets, SUM/OR/INV – the Calculus rebuilt."
// Channel 1 (left)
in sig1 "SIGNAL" cv at 8 25
in trig1 "TRIG" gate at 20 25
button cycle1 "CYCLE" at 10 37
led cyc1 red at 17 37
knob rise1 "RISE" 0.0005..180 = 0.3 log unit s size l at 26 40
knob fall1 "FALL" 0.0005..180 = 0.3 log unit s size l at 26 58
knob resp1 "RESP" 0..1 = 1 / 3 unit pct tip "Response: log – linear – exp" at 26 75
in rcv1 "RISE" cv at 8 48.5
in bcv1 "BOTH" cv at 8 60
in fcv1 "FALL" cv at 8 71.5
in cin1 "CYCLE" gate at 8 83
// Channel 4 (right, mirrored)
in sig4 "SIGNAL" cv at 93.3 25
in trig4 "TRIG" gate at 81.3 25
button cycle4 "CYCLE" at 91.3 37
led cyc4 red at 84.3 37
knob rise4 "RISE" 0.0005..180 = 0.3 log unit s size l at 75.3 40
knob fall4 "FALL" 0.0005..180 = 0.3 log unit s size l at 75.3 58
knob resp4 "RESP" 0..1 = 1 / 3 unit pct tip "Response: log – linear – exp" at 75.3 75
in rcv4 "RISE" cv at 93.3 48.5
in bcv4 "BOTH" cv at 93.3 60
in fcv4 "FALL" cv at 93.3 71.5
in cin4 "CYCLE" gate at 93.3 83
// Channels 2 and 3, attenuverters 1–4 and the bus
in in2 "IN 2" cv at 40 25
in in3 "IN 3" cv at 61.3 25
knob att[4] "#" -1..1 = [1, 0, 0, 1] unit pct at 50.65 36 step 0 14.5
out unity1 "UNITY" env at 9 99.5
led u1p green at 17.2 95
led u1n red at 17.2 100
out var[4] "#" env at 25 99.5 step 14 0
led u4p green at 84.1 95
led u4n red at 84.1 100
out unity4 "UNITY" env at 92.3 99.5
out eor "EOR" gate at 9 113.5
led eorl yellow at 17.2 111.5
out orout "OR" cv at 32 113.5
out sum "SUM" cv at 48 113.5
led sump green at 56 109
led sumn red at 56 114
out inv "INV" cv at 64 113.5
led eocl yellow at 84.1 111.5
out eoc "EOC" gate at 92.3 113.5
const LIN = 1 / 3 // linear mark of RESP
const EXPO = 2 / 3 // exponential mark
const IEXP = 1 / 2.085 // curve constants: log ≈ 2×, exp ≈ 0.5×, hyper-exp ≈ 0.3× the linear time
const IHYP = 1 / 3.3
const MAXR = 10 / 50e-6 // fastest segment: 10 V in 50 µs
// Linear up to ±10 V, then soft up to ±10.5 V (supply rails).
fn rail(x) {
ax = abs(x)
return ax <= 10 ? x : sign(x) * (10 + 0.5 * tanh((ax - 10) * 2))
}
// First-order allpass – every call keeps its own state.
fn allpass(x, a) {
state x1 = 0
state y1 = 0
y = a * (x - y1) + x1
x1 = x
y1 = y
return y
}
// Two samples at double rate (mid first) → one sample: polyphase half-band.
fn halfband(mid, cur) {
return 0.5 * (allpass(allpass(cur, 0.07986643), 0.54532365) + allpass(allpass(mid, 0.28382935), 0.8344119))
}
// Rate factor at voltage v: slower near the top for log, faster for exp.
fn response(v, curve) {
a = abs(v) + 1
if abs(curve - LIN) < 0.0001 { return 1 }
else if curve <= LIN {
lg = 3 / a
return lg + (1 - lg) * (curve * 3)
} else if curve <= EXPO { return 1 + (a * IEXP - 1) * ((curve - LIN) * 3) }
else { return a * IEXP + (a * a * IHYP - a * IEXP) * ((curve - EXPO) * 3) }
}
// Integrate `left` seconds towards the apex (rising) or the signal; the time left at a segment end carries over.
fn advance(v, up: bool, apex, sig, left, rup, rdn, curve, cyc: bool) {
done = false
for k in 0..3 {
if not done {
target = up ? apex : sig
d = target - v
if up and d <= 0 { up = false }
else if d == 0 {
if cyc {
up = true
apex = 8
} else { done = true }
} else {
rising = d > 0
rate = (rising ? rup : rdn) * response(v, curve)
need = abs(d) / rate
if need > left {
v += (rising ? rate : -rate) * left
done = true
} else {
v = target
left -= need
if up { up = false }
else if not rising and cyc {
up = true
apex = 8
}
done = left <= 0
}
}
}
}
return v, up, apex
}
// One function channel: unity output, end-of-rise state and cycling.
fn channel(sig, trig, trise, tfall, curve, cbtn: bool, cin, rcv, fcv, both) {
state v = 0
state up = false
state apex = 10
state rising = false
state cgate = false
state sprev = 0
cgate = cgate ? cin > 2 : cin >= 2.5
cyc = cbtn or cgate
if rise(trig) and not rising {
up = true
apex = cyc ? 8 : 10
}
b = exp2(clamp(both, -10, 10))
rup = min(10 / max(trise, 0.00001) * max(1 - 0.11 * rcv, 0.12) * b, MAXR)
rdn = min(10 / max(tfall, 0.00001) * max(1 - 0.11 * fcv, 0.12) * b, MAXR)
c = clamp(curve, 0, 1)
dt = 0.5 / SR
mid = 0.5 * (sig + sprev)
sprev = sig
v, up, apex = advance(v, up, apex, mid, dt, rup, rdn, c, cyc)
vmid = v
v, up, apex = advance(v, up, apex, sig, dt, rup, rdn, c, cyc)
rising = up or sig > v
return rail(halfband(vmid, v)), not rising and v > 0.01, cyc
}
process {
u1, hi1, cy1 = channel(sig1, trig1, rise1, fall1, resp1, cycle1, cin1, rcv1, fcv1, bcv1)
u4, hi4, cy4 = channel(sig4, trig4, rise4, fall4, resp4, cycle4, cin4, rcv4, fcv4, bcv4)
unity1 = u1
unity4 = u4
eor = gate(hi1)
eoc = gate(not hi4)
var[0] = rail(u1 * att[0])
var[1] = rail((connected(in2) ? in2 : 5) * 2 * att[1])
var[2] = rail((connected(in3) ? in3 : 2.5) * 2 * att[2])
var[3] = rail(u4 * att[3])
// Bus: only channels whose variable output is not patched.
bus = 0
top = 0
for k in 0..4 {
t = connected(var[k]) ? 0 : var[k]
bus += t
top = max(top, t)
}
s = rail(bus)
sum = s
inv = -s
orout = top
cyc1 = cy1
cyc4 = cy4
eorl = hi1
eocl = not hi4
u1p = u1 / 8
u1n = -u1 / 8
u4p = u4 / 8
u4n = -u4 / 8
sump = s / 10
sumn = -s / 10
}
}AGENTS.md für KI-Assistenten
Die Datei AGENTS.md beschreibt die Sprache vollständig und knapp auf Englisch: Aufbau, alle Deklarationen, Typregeln, jede Funktion mit Signatur, Grenzen, Fehlercodes, eine Checkliste und alle Beispiele. Sie wird bei jedem Update aus dem Compiler erzeugt und ist damit immer aktuell.
Gib sie deinem KI-Assistenten zusammen mit deiner Idee („ein Bitcrusher mit Rate- und Bits-Regler“) – heraus kommt eine .amsmod-Datei, die du direkt laden kannst.