DJ-Software: Engine, Analyse, Sammlung, Oberfläche und Fernsteuerung - #1
Draft
Flissel wants to merge 63 commits into
Draft
DJ-Software: Engine, Analyse, Sammlung, Oberfläche und Fernsteuerung#1Flissel wants to merge 63 commits into
Flissel wants to merge 63 commits into
Conversation
Groundwork so implementation can start from facts instead of guesses. Traktor reference: deck modes (track/remix/stem), real-time stem separation, pattern player, beatgrids, and the collection.nml format — an existing Python parser makes Traktor library import a cheap early feature. API landscape: Suno has no public developer API as of August 2026, only a curated partner program opened in July. Generation therefore goes behind an adapter interface starting with a reachable provider, so Suno becomes an added adapter later rather than a rewrite. Third-party reverse-engineered Suno wrappers are documented as not recommended. Building blocks: playback, time-stretching, beat/key detection and stem separation options, with a license table up front — several audio libraries are (A)GPL with commercial relicensing, which constrains the stack before anything is chosen. Agent concept: crowd-feedback loop with prior art, signal sources, role sketch, and the timing constraint that breaks the naive design — generation is too slow to close the loop live, so it plans ahead while fast controls handle the immediate response. Camera and biometric signals are flagged as GDPR-relevant. README: adds a docs index, marks the Suno blocker, extends the roadmap with dependencies, and records the open decisions. Adds .env.example. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Records the stack decision and implements phase 0 against it. Native over browser because of the separate headphone cue output — not latency. Serious DJing needs pre-listening on a second output, which browsers do not offer cleanly. Rust over C++ because the whole dependency set stays permissive (cpal Apache-2.0, symphonia MPL-2.0), so the still-open question of whether this becomes commercial is not pre-decided by the toolkit. audio-core: - track: decode mp3/flac/wav/aac/ogg to interleaved stereo, resample once at load so the callback carries no rate conversion - stretch: WSOLA time-stretch, written here rather than pulling in Rubber Band, which is GPL. The ideal read position advances undisturbed and the correlation search only corrects locally — accumulating the found offset would drift the timeline - deck: control via atomics only, so the audio path has no lock and no allocation - player: cpal output, generic over the device sample format musik-cli drives one deck from the shell: play/pause, tempo, keylock, seek. Verified by 11 tests: keylock holds pitch at 0.92x, 1.06x and 1.20x; varispeed shifts pitch with tempo as it should; the timeline does not drift; decode, mono-to-stereo and resampling preserve length and pitch. Sound on real hardware is not verified — no audio device here. Adds CI running fmt, clippy -D warnings and tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Closes the second open question. The practical effect is larger than it looks: (A)GPL libraries are now available, which removes the main constraint the audio ecosystem imposes. The reason is narrower than "non-commercial" and worth stating precisely — copyleft triggers on distribution, not on use. A tool that stays on one machine creates no obligations at all. Two caveats now carry the warnings instead: - Distributing the tool later, even for free, would require releasing the project's own code under GPL. - AGPL (Essentia) triggers on network use, not just distribution, which matters if the agent layer ever runs as a service. Sample side: CC BY-NC material becomes usable, a large part of Freesound. The attribution requirement survives, so the library schema needs license and author fields before any import — that cannot be reconstructed afterwards. Adjusts the passages that assumed the question was still open: the Rust choice, the self-written stretcher (Rubber Band is now an option, worth taking only once someone has heard both), and the reason to avoid reverse-engineered Suno wrappers — that risk was never licensing but a banned account. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
The project is used privately and not distributed. In that state the GPL dependencies create no obligations at all, so the two caveats recorded earlier are demoted to notes for if that changes, and the absence of a LICENSE file is now a stated choice rather than an oversight. Practical effect: library choice is unconstrained, including Rubber Band. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Turns the coarse roadmap into an engineering plan: crate layout, threading model, signal chain, phases with acceptance criteria, and risks. Three decisions shape everything else and are called out as such: - The headphone cue output forces master and cue onto one device with four outputs. Two devices means two clocks, and the drift would need async resampling in exactly the path that is kept free of it. This is a hardware question that has to be settled before phase 1, not during. - Atomics no longer suffice once tracks are swapped on a deck. A lock-free SPSC queue carries commands in, and a second one carries the old Arc back out — dropping a decoded track inside the callback frees a hundred megabytes and stalls the audio thread. - A beatgrid is an anchor plus BPM, optionally grid markers, and sync needs phase as well as tempo. Matching BPM with the wrong phase sounds just as wrong as the wrong tempo. Also fixes two things that are easy to build backwards: mixer FX belong post-fader so closing the fader fades the effect out instead of cutting it, and the cue tap belongs before the crossfader so a faded-out channel is still audible in the headphones. Records that the library schema needs license and attribution columns from the first migration, following from the CC BY requirement. Recommends egui for the UI over a Tauri frontend — a DJ surface redraws every frame anyway, and the UI/audio state coupling is too tight and too frequent to route through IPC. Crate choices verified against crates.io for existence and license. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Phase 2 of the build plan. Runs entirely without an audio device, so it is verifiable end to end here. Pipeline: spectral flux onset envelope, autocorrelation for the coarse period, a descent to the fundamental, then a comb filter for the exact period and phase. Each stage uses a different tool on purpose — autocorrelation compares the signal against a single shift and tolerates a small period error, while a comb filter sums over hundreds of pulses and a half-frame error misaligns the fiftieth pulse by twenty-five. The first draft used the comb for the coarse search and detected 128 BPM as 85; measuring the comb curve rather than reasoning about it is what found that. The octave ambiguity gets its own stage. Autocorrelation peaks at every multiple of the beat period, so 174 BPM correlates exactly as strongly at 87, and a prior centred near 120 picks the wrong one. The half period is a valley rather than a peak, so halving while correlation survives lands on the shortest period of the series. What remains is a genuine ambiguity — 87 BPM hip-hop with hats on eighths reads as 174 — which is why DJ software has a halve/double button. Anchors are corrected in the time domain. Spectral flux fires when a transient enters the analysis window, not when it sounds, leaving a systematic lead of about 15 ms — four percent of a beat at 128 BPM, enough to make sync useless. A search on a fine energy-rise envelope, bounded to half a period so it cannot jump to a neighbouring beat, brings that to 3 ms. Results are stored keyed by a hash of the decoded audio rather than the path, so renaming a folder does not discard the work and duplicate files are analysed once. Quantised to i16 before hashing so tag edits do not invalidate it. Refusing to answer is a valid answer: a sustained tone yields no tempo rather than a guessed one, gated on how far the autocorrelation peak exceeds the mean. The threshold separates the two measured extremes but has never been calibrated against a real library, and says so. Adds musik-analyze to drive it, egui recorded as the UI decision, and 24 tests covering detection, the octave descent, anchor accuracy, peak quantisation and the sidecar cache. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
VibeMind exposes tools over MCP (FastMCP stdio servers, read tools free and write tools behind approval) and groups domains under spaces/. That is the seam: the Rust core stays the audio engine and gains a thin MCP surface, so the Python/Rust split is a process boundary rather than a problem. Two findings that change decisions here. The license relief recorded earlier does not survive this path. VibeMind is MIT and headed for publication, and publication is distribution — so GPL dependencies would once again constrain the whole project. Everything currently vendored is MIT-compatible, which is a side effect of writing the stretcher and the tempo detection here rather than taking Rubber Band or aubio. That was a workaround for an open question at the time and now happens to hold the door open. Staying permissive is the cheaper default while this is undecided. The agent layer sketched in AGENTEN.md largely duplicates Brain and OpenFang. Delivering tools here and running the agents there removes an entire stage of work; the role sketch stays valid as domain design regardless of where it executes. Also notes that write-tool approval and live performance are in tension — nobody can confirm a dialog mid-mix, so time-critical calls need a grant covering the set rather than each action, which is the safety-side view of the open automatic-versus-assistant question. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Two passages still claimed GPL libraries were freely available, which holds for personal use but not for the VibeMind path. Both now carry the caveat and point at the detailed assessment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Phase 1. The mixer knows no decks, only sources that yield stereo — so an AUX input is a channel like any other, and the generation layer can attach at the same seam later. Signal chain per channel: trim, three-band EQ, filter, then a split into the cue tap and the fader/crossfader path. The cue tap sits before the *fader*, not merely before the crossfader: a track is prepared in the headphones while its fader is down, and a tap behind it would go silent exactly when it is needed. It sits after EQ and filter because tone changes must be audible before they reach the room. AUX carries two differences from a deck. It defaults to Thru so a microphone does not vanish when someone moves the crossfader, and it reads through a lock-free ring buffer because capture and playback run in separate callbacks with separate clocks — a late writer yields silence and a counted underrun rather than a stall. The three-band EQ took two wrong turns worth recording, both caught by the neutral-passthrough test: - Low plus high of a state-variable filter does not sum to the input; the k·bp term is missing. At unity gains the EQ was a notch around the crossover, and a 220 Hz tone lost over 80 percent of its level. - Forming the remainder subtractively is exact in time but not in magnitude, because the low band is phase-shifted. At 60 Hz the shift already reaches ~50°, so bass kill left 0.84 of the bass standing, and on the crossover frequency the output exceeded the input by half. Linkwitz-Riley of fourth order is the construction that actually holds: both branches meet in phase at -6 dB and sum back to unity. Adds musik-mix, which renders a transition to a WAV file offline — tempo-matched with keylock, crossfade with bass swap. Same signal path as the live engine, but verifiable without an audio device, which is the only way to judge anything in this environment. 50 engine tests: filter responses, EQ band isolation and passthrough, crossfader curves and Thru immunity, limiter ceiling and recovery, AUX underrun and overrun accounting, and the mixer routing itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Four more phases. Everything here is verifiable without an audio device, which is why it could be built ahead of the hardware. Sync couples tempo *and* phase. Matching BPM alone is the half that looks finished and isn't: two decks at exactly 128 BPM whose beats sit an eighth apart sound as wrong as two at different tempos. A beatgrid therefore lives on the deck as anchor plus BPM, held in atomics rather than behind a lock because the audio thread reads it. The correction jumps rather than eases the phase in — audible when large, but exact, and it always takes the shorter way around the beat. Once effective tempos match, phase stays locked on its own; no continuous regulation is needed. Covered by the phase 4 acceptance criterion: five minutes of rendering leaves the decks under 5 ms apart. Loops wrap at the loop boundary, not at the end of the callback block. Wrapping at the block would stretch each pass by up to one buffer, which is audible as an uneven bar. Beat loops snap the start to the grid, otherwise every pass begins mid-beat and stumbles. The library stores positions in milliseconds rather than frames. It is the interchange layer between analysis, Traktor import and deck, and a frame count without its sample rate is ambiguous — a resampled track would carry different numbers for the same instant. License and attribution columns are in the first migration, not a later one, because CC BY requires naming even non-commercially and the provenance of a thousand samples cannot be reconstructed afterwards. Upserts preserve existing values where the incoming record is empty. A folder rescan knows only paths and tags; without that rule it would erase the analysis, which is the expensive part. The Traktor importer reads tempo, grid anchor and hot cues from collection.nml. Field meanings come from public descriptions of an undocumented format and are tested only against hand-written samples — cue type mapping, key numbering and Windows volume handling are flagged as needing a check against a real collection. Adds musik-lib to scan folders, import from Traktor, and search by text or mixable tempo. 137 tests total. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Dichtes Material wurde abgewiesen, obwohl das Tempo richtig erkannt war. Ein Loop aus Kick, Bass und Hi-Hats kam auf eine Deutlichkeit von 2,06 und blieb an der Schwelle von 3,0 hängen — die Grobsuche hatte 127,8 BPM längst gefunden. Ursache ist das Maß, nicht die Schwelle. Spitze durch Mittelwert war an Klick-Tracks kalibriert, und die sind zwischen den Klicks still: die Autokorrelation liegt bei fast jeder Verschiebung nahe null, das Verhältnis wird riesig. Echte Musik hat durchgehend Energie, hebt damit den Sockel über den ganzen Suchbereich an und drückt das Verhältnis gegen 1 — auch bei schnurgeradem Beat. Der z-Wert misst stattdessen, ob eine Verschiebung aus dem Feld heraussticht. Ein konstanter Sockel verschiebt Spitze und Mittel gleich weit und kürzt sich heraus. Gemessen trennt er doppelt so deutlich: 3,46 gegen 1,71 statt 2,06 gegen 1,65. Der neue Test legt Klicks auf einen Dauerton — genau der Fall, an dem das alte Maß gescheitert ist.
Die Oberfläche darf den Mixer nicht direkt anfassen — sie läuft im UI-Thread, der Mixer im Audio-Callback. Atomics reichen für Skalare wie Position und Tempo, nicht für einen Kanalwechsel: ein neuer Track ist eine `Box<dyn Source>`, und die kann man nicht atomar tauschen. Also eine lock-freie SPSC-Schlange für Kommandos und ein zweiter Kanal zurück für abgelöste Quellen. Der Rückkanal ist kein Beiwerk: würde der Callback die alte Quelle einfach fallen lassen, liefe dort ein `free` über mehrere Megabyte Samples — eine Allokator-Operation im Echtzeitpfad, also genau das, was nicht passieren darf. `drain_loads` nimmt deshalb keinen neuen Track an, solange der Rückkanal voll ist. Damit ist bewiesen, dass die alte Quelle immer einen Platz findet, und im Callback wird nie etwas freigegeben. `Output` sucht ein Gerät und übergibt ihm den Mixer. Scheitert das Öffnen, kommt der Runner **mit** dem Fehler zurück — sonst wäre der ganze Mixer verloren, nur weil keine Soundkarte da ist, und die Oberfläche stünde still.
Bis hierher ließ sich alles nur über die Kommandozeile und über Tests bedienen. Jetzt gibt es die Anordnung, die man vom Pult kennt: zwei Decks oben, Mischpult in der Mitte, Plattenkiste unten. Decks zeigen zwei Wellenformen — Übersicht zum Springen, Ausschnitt zum Mixen — mit Beatgrid, Hot-Cue-Marken und Abspielkopf. Gezeichnet wird aus den vorberechneten Spitzen der Analyse; über dreizehn Millionen Rohwerte je Deck und Bild zu laufen wäre bei 60 Bildern pro Sekunde aussichtslos. Der Mixer hat vier Kanalzüge samt AUX, jeder mit Trim, Dreiband-EQ, DJ-Filter, Cue und Linefader, dazu Crossfader mit Kurve, Master, Kopfhörer und Cue-Mix. Die Plattenkiste durchsucht die Sammlung und legt Treffer auf ein Deck; „Mischbar zu A" filtert auf Tempo-Nachbarschaft. Drei Dinge, die beim Bauen erst sichtbar wurden: - Ein `Frame` erbt das Layout seines Elternteils. Im Mixer ist das waagerecht, also standen die Regler eines Kanalzuges nebeneinander statt übereinander — jeder Rahmen sagt jetzt ausdrücklich `vertical`. - Die Zoom-Ansicht ist um die Abspielposition zentriert und ragt am Anfang eines Tracks nach links über ihn hinaus. Dort wiederholte der Zeichner die erste Spitze für jede Spalte: ein solider Balken, den es nicht gibt. Auch das Beatgrid hörte vor Frame 0 nicht auf. - Die mitgelieferte Schrift hat keine Pfeilzeichen. „→ A" wurde zum leeren Kästchen, ebenso ◄ ► und ⟳. Ohne Audiogerät startet die Oberfläche trotzdem: der Mixer läuft dann von einem Taktgeber im Leerlauf. Sonst wäre sie in genau der Umgebung ein Standbild, in der man sie am ehesten prüfen möchte. Ein geladener Track startet gestoppt mit geschlossenem Fader. Ein Deck, das beim Laden auf die Anlage geht, ist ein Unfall.
Mixxx ist die naheliegende Frage — zwanzig Jahre freie DJ-Software, die alles kann, was hier noch drei Phasen entfernt ist. Die Antwort ist zweigeteilt, und beide Hälften waren bisher nirgends festgehalten. Lizenz: die `LICENSE` sagt GPLv2+, ein gebautes Binary ist aber faktisch GPLv3+, weil libKeyFinder (GPLv3) mitgelinkt wird. Für uns egal — Copyleft bleibt Copyleft, und Mixxx landet damit in derselben Schublade wie Rubber Band und aubio: nutzbar, solange nichts weitergegeben wird, ausgeschlossen, sobald der VibeMind-Anschluss ernst wird. Übernehmbar ist trotzdem einiges, weil Urheberrecht die Ausdrucksform schützt und nicht die Idee: das Control-System als Vorbild für die MCP-Schnittstelle, die zwei Beatgrid-Stufen (unser Anker+BPM ist die konstante — variables Grid ist der benannte Aufrüstpfad), das Streaming von Platte, und vor allem das Mapping-Verzeichnis als Dokumentation der Geräte-Landschaft für Phase 10. Welche CC-Nummer ein Jogwheel sendet, ist eine Tatsache über die Hardware; die Mapping-Datei ist ein Werk. Was Mixxx nicht löst und was dieses Projekt eigentlich beiträgt: Steuerung von außen. Mixxx spricht OSC nur ausgehend, das Control-System ist prozessintern. Und der ehrliche Gegenpunkt, der ins Dokument gehört statt weggelassen zu werden: für rein persönlichen Gebrauch entstehen aus Mixxx' GPL null Pflichten. Wer heute Abend auflegen will, nimmt Mixxx. Der Eigenbau rechtfertigt sich über genau zwei Ziele — VibeMind und Agentensteuerung. Fallen die weg, fällt der Grund weg.
Bis hierher lag die Bedienung an drei Stellen: Transport in den Atomics des DeckState, der Mixer in der Kommandoschlange, und die Reglerstellungen als Spiegel in der Oberfläche. Wer von außen steuern wollte, hätte alle drei anfassen müssen — und die Oberfläche war die einzige Stelle, die wusste, wo ein Fader gerade steht. Das neue Crate `control` dreht das um. Jedes bedienbare Ding hat einen Namen (`deck1.play`, `channel2.eq_low`, `master.crossfader`), das Pult besitzt den Spiegel und schickt jede Änderung selbst in die Schlange. Die Oberfläche wird damit zu einem von mehreren Bedienern, gleichberechtigt mit einem Skript am Socket. Die Idee stammt von Mixxx — dort laufen Tastatur, MIDI, HID und Oberfläche über dieselben benannten Controls. Sie ist frei: geschützt ist die Ausdrucksform, nicht der Entwurf. Drei Dinge sind bewusst anders: Der Steuerraum beschreibt sich zur Laufzeit selbst. `list` liefert zu jedem Control Typ, Bereich, Einheit, Schreibbarkeit und Bedeutung. Bei Mixxx steht das im Handbuch, und ein laufendes Programm lässt sich nicht fragen. Der Gewinn ist doppelt: MCP-Werkzeugbeschreibungen lassen sich daraus erzeugen statt doppelt pflegen, und die Schieberegler im Mixer holen sich Bereich und Tooltip aus demselben Katalog — ein neues Control ist ohne eine Zeile in der Oberfläche bedienbar. Werte sind typisiert. Bei Mixxx ist alles ein double, in das jeder Tippfehler als gültiger Wert hineinpasst. Hier weist `set channel1.assign mitte` mit den erlaubten Namen zurück. Zwei Ausnahmen von der Strenge sind Absicht: Werte außerhalb des Bereichs werden begrenzt statt abgelehnt, weil ein MIDI-Regler auf Anschlag das Maximum meint und keinen Fehler; und die Bestätigung nennt den angekommenen Wert, nicht den gesendeten. Er ist von außen erreichbar. Ein Unix-Socket, kein TCP-Port — wer hineinschreibt, steuert die Anlage, und ein offener Port täte das für jeden im selben Netz. Der Socket erbt die Rechte des Dateisystems und liegt in XDG_RUNTIME_DIR, nicht in /tmp. Echtzeit bleibt unangetastet: Das Pult schreibt nie in den Audio-Callback, sondern in dieselbe lock-freie Schlange, und liest aus Atomics und Spiegel. Der Mutex wird nur von Bedienern genommen. Zwei Funde beim Bauen, beide von Tests aufgedeckt: Der Wortzerleger machte aus Leerzeichenfolgen leere Felder, weil splitn nicht zusammenfasst. Und Tempo liegt im Deck als f32 — 1.04 kommt als 1.0399999618530273 zurück. Der Test forderte exakte Gleichheit und behauptete damit eine Genauigkeit, die es dort nicht gibt. Nachgewiesen an der laufenden Anwendung: zwei Decks von außen ineinander gefahren, Deck B per `set deck2.tempo 1.032` auf Deck A gezogen (127,98 gegen 127,99 BPM). Screenshot in docs/bilder/fernsteuerung.png.
Der Steuerraum konnte Regler bewegen, aber nicht auflegen. Fünf Dinge fehlten, und zusammen waren sie der Unterschied zwischen „ein Agent kann den Mixer bedienen" und „ein Agent kann auflegen": laden, syncen, einen Hot Cue auslösen, das Ende eines Tracks bemerken, die Sammlung durchsuchen. Aktionen sind jetzt ein eigener Typ, kein Wert. Mixxx modelliert sie als Control, in das man 1.0 schreibt (`cue_gotoandplay`), und das ist dieselbe Schwäche wie beim durchgehenden double: Ein Auslöser hat keinen Zustand, den man lesen könnte, und `get` darauf müsste etwas erfinden. Hier haben sie ein eigenes Verb, und beide Richtungen weisen die Verwechslung zurück. `sync` ist Tempo UND Phase in einem Befehl. Ohne das müsste ein Agent beide BPM lesen, den Quotienten bilden und die Phase selbst rechnen — und die vergisst man, weil das Ergebnis auch ohne sie plausibel aussieht. `load` arbeitet in drei Stationen, weil Dekodieren und Analysieren Sekunden dauern und das Pult unter einem Mutex liegt, an dem die Oberfläche hängt: Der Auftrag wird angenommen, ein Arbeiter-Thread macht die langsame Arbeit ohne jedes Schloss, der UI-Thread setzt das Ergebnis ein. Gemeldet wird deshalb Annahme, nicht Erledigung; der Fortschritt steht in load_status. Ein abgelehnter Auftrag lässt den Status unberührt, statt ein Laden vorzutäuschen, das nie begonnen hat. `sub` erspart das Pollen. Ehrlich gesagt vergleicht der Server alle 50 ms, statt dass das Pult meldet — für Werte aus dem Audio-Thread wäre eine echte Benachrichtigung auch sinnlos, die Position ändert sich mit jedem Sample. Der Gewinn ist, dass der Bediener das nicht selbst baut und `finished` ohne Dauerabfrage ankommt. Damit `control` nicht von SQLite und Dekodierung abhängt, reicht der Betreiber beides über eine Schnittstelle herein. Nebeneffekt, der wichtiger ist als er klingt: Auch die Oberfläche geht jetzt durch diesen Weg. Suchen und Laden hatten einen eigenen Pfad — zwei Wege zum selben Ziel heißt zwei Stellen, an denen es schiefgehen kann, und die seltener benutzte fällt seltener auf. Beim Umbau wäre das fast passiert: Die Library wanderte in den Lader, und die Plattenkiste wäre still leer geblieben. Ein Test hat sofort zugeschlagen, als `search_mixable` dazukam: Die Gegenprobe geht jede aufgezählte Aktion durch und verlangt, dass sie auch verdrahtet ist. Eine Aktion, die sich auflistet und dann „unbekanntes Control" sagt, wäre eine Lüge. Nachgewiesen an der laufenden Anwendung: ein kompletter Übergang allein über den Socket — suchen, zwei Tracks laden, syncen (126,008 × 1,0157 = 127,986, beide Decks gleich), Cue setzen und anspringen, Bass-Swap, Beatjump, aufs Ende horchen. Screenshot in docs/bilder/agent-uebergang.png.
Phase 7. Vier statt vierzig: Delay, Gater, Flanger, Crusher. Der Filter sitzt ohnehin schon fest im Kanalzug. Reverb fehlt bewusst — ein schlechter Hall ist schlimmer als keiner, und ein guter ist ein eigenes Stück Arbeit. Sie liegen hinter dem Fader, und das ist der eigentliche Entwurf: Zieht man den Fader zu, während ein Delay klingt, soll die Fahne ausklingen statt abzureißen. Genau dafür sitzen Mixer-FX auf jedem Gerät an dieser Stelle. Damit das nicht nur auf dem Papier stimmt, musste die Stumm-Optimierung des Mixers weichen: Sie übersprang jeden Kanal mit geschlossenem Fader und hätte die Fahne im selben Moment abgeschnitten, in dem sie entsteht. Der Mixer fragt jetzt nach, ob der Effekt noch klingt, bevor er überspringt. Zwei Tests halten beides fest — dass die Fahne den zugezogenen Fader überlebt und dass ein Kanal ohne Effekt trotzdem still bleibt. Der Kopfhörer greift weiterhin davor ab. Man bereitet den nächsten Track im Kopfhörer vor; ein Effekt, den man für die Anlage eingestellt hat, gehört dort nicht hinein. Zwei Dinge, die beim Bauen wichtig wurden und ohne die es billig klänge: Das Gate läuft über eine Rampe von 1,5 ms, weil ein harter Sprung von 1 auf 0 ein Klick ist. Und die Verzögerungsleitung liest zwischen zwei Frames, weil man sonst beim Flanger jede Stufe der wandernden Verzögerung als Zwitschern hört. Echtzeit: Alle Puffer werden beim Anlegen belegt. Die Delayzeit ist deshalb nach oben begrenzt — vier Sekunden, bei 60 BPM ein ganzer Takt. `fx_sync` rechnet Beats in Sekunden um, weil ein Delay, das nicht im Takt steht, nach Fehler klingt. Die Umrechnung braucht das Beatgrid und liegt deshalb im Steuerpult, nicht im Mixer, der von Decks nichts weiß. Ohne Deck oder ohne Grid sagt es das, statt zu raten. Und die Probe aufs Exempel für den Steuerraum: Die Oberfläche hat für die Effekte keine einzige Zeile bekommen. Der Kanalzug holt sich seine Regler jetzt aus dem Katalog — vier neue Einträge, und das FX-Feld stand da. Der erste Versuch deckte prompt auf, dass damit auch die Reihenfolge aus dem Katalog kommt: Der EQ stand verkehrtherum, weil dort `eq_low` vor `eq_high` lag. Höhen oben, Bässe unten ist jetzt im Katalog festgehalten und begründet. Ein Test war zuerst falsch gebaut: Er verglich zwei verschiedene Ausschnitte derselben Schleife und maß damit das Signal statt den Effekt. Zwei identische Engines, gleich weit gerendert, sind die richtige Probe.
Phase 9. Aufgenommen wird die Summe hinter dem Begrenzer — das, was auf die Anlage geht, nicht das, was vorher da war. Der Audio-Callback darf nicht auf die Platte schreiben. Ein write ist ein Syscall, er kann Millisekunden dauern, und in dieser Zeit steht die Wiedergabe — ein Aussetzer mitten im Set, und zwar genau dann, wenn man mitschneidet. Also derselbe Weg wie beim AUX-Eingang, nur andersherum: ein lock-freier Ringpuffer aus dem Audio-Thread heraus, ein eigener Thread, der ihn leert und schreibt. Kommt der Schreiber nicht hinterher, gehen Frames verloren — blockieren wäre schlimmer. Verloren heißt aber nicht verschwiegen: Sie werden gezählt, sind über `master.record_dropped` abfragbar, und `record_stop` sagt es von sich aus dazu. Ein Mitschnitt mit Lücken, der aussieht wie einer ohne, wäre das schlechteste Ergebnis von allen. Der Zähler ist mit einem winzigen Ring ohne Leser getestet, statt auf eine überlastete Platte zu warten. Der Kopf einer WAV-Datei enthält die Länge, die man beim Anlegen nicht kennt. Also Platzhalter schreiben und am Ende zurückspringen — und deshalb prüft ein Test, dass die Länge im Kopf wirklich zur Datei passt. Geschrieben wird gerundet, nicht abgeschnitten: Abschneiden verschiebt jedes Sample in dieselbe Richtung und klingt als leise Verzerrung mit. Ein Fund beim Testen, der ein echtes Verhaltensproblem war: `create_dir_all` hat als root klaglos vier Verzeichnisebenen unter / angelegt, weil im Test ein absichtlich unsinniger Pfad stand. Jetzt wird höchstens eine Ebene angelegt — `~/sets/heute.wav` in einem noch leeren Ordner soll gehen, ein Tippfehler soll keinen Verzeichnisbaum hinterlassen. Nachgewiesen an der laufenden Anwendung: über den Socket gestartet, 6,78 s aufgenommen, null verworfene Frames — und die eigene Analyse liest die Datei mit 128,02 BPM zurück, dem Tempo des Quelltracks.
CI hat gefangen, was hier durchgerutscht ist. Der Schreiber-Thread leerte den Ringpuffer, sobald der Start-Befehl bei ihm ankam — um Reste eines früheren Mitschnitts loszuwerden. Der Aufnehmende schreibt aber schon in den Ring, sobald `starten` zurück ist, und der Schreiber bekommt den Befehl erst, wenn er das nächste Mal an der Reihe ist. Auf einem ausgelasteten Rechner liegen dann bereits Samples im Ring, und das Leeren warf genau den Anfang des Mitschnitts weg. Kamen Start und Stopp dicht hintereinander, blieb von der Aufnahme nur der WAV-Kopf übrig — genau das ist auf dem CI-Läufer passiert. Nötig war das Leeren an dieser Stelle ohnehin nicht: Nach dem Schließen ist der Ring leer, und außerhalb einer laufenden Aufnahme legt niemand etwas hinein. Also wird jetzt nach dem Schließen geleert statt vor dem Öffnen, und die Invariante steht als Kommentar dabei. Zwei Tests dazu. `ein_zweiter_mitschnitt_erbt_nichts_vom_ersten` fängt den Fehler zuverlässig, weil der Schreiber-Thread nach der ersten Aufnahme schläft und Start, Aufnehmen und Stopp deshalb gebündelt bei ihm ankommen — gegengeprüft, indem der Fehler kurz wieder eingebaut wurde. `ein_sofortiger_stopp_verliert_den_anfang_nicht` beschreibt denselben Fall direkt, hängt aber am Zeitverhalten und schlägt nicht immer an.
…ogue Der Anschluss, den docs/VIBEMIND.md seit dem ersten Tag vorsieht und den STEUERUNG.md zweimal angekündigt hat. Ein MCP-Server, der das Zeilenprotokoll in Werkzeuge übersetzt. Sechs Werkzeuge statt zweihundert. Ein Werkzeug je Control wäre die naheliegende Übersetzung und die schlechtere: Der Steuerraum hat über zweihundert Einträge, und ein Agent, der sie alle als Werkzeuge sieht, findet keins davon. `musik_set` und `musik_do` erreichen alles; `musik_status` und `musik_search` sparen die Wege, die man sonst am häufigsten doppelt ginge. Der Kern: Die Beschreibungen von `musik_set` und `musik_do` werden beim Start aus dem laufenden Programm erzeugt, nicht von Hand gepflegt. Was ein Agent liest, ist dasselbe, was im Katalog steht und in der Oberfläche als Tooltip erscheint. Ein neues Control erscheint dort ohne eine Zeile Python. Läuft die Anwendung gerade nicht, bleibt die Beschreibung allgemein und verweist auf `musik_list_controls`. Genau dabei fiel ein Loch in der Selbstbeschreibung auf: `list` gab das Argument einer Aktion nicht mit. Es stand im Katalog, verließ aber den Prozess nie — die erzeugte Beschreibung versprach „erwartetes Argument in Klammern" und zeigte überall `(-)`. Jetzt steht es in der Bereichsspalte, und `deck1.load` sagt von sich aus, dass es einen Pfad will. Python neben einem Rust-Projekt, weil der Empfänger Python spricht: VibeMind läuft auf FastMCP. Die Brücke redet nur über den Socket und weiß von Audio nichts; der Rust-Kern bleibt unangetastet, und wer keinen Agenten braucht, braucht den Ordner nicht. Sicherheit: kein Netz, nur stdio und der Unix-Socket. Jedes Argument wird auf Zeilenumbrüche geprüft — das Protokoll ist zeilenweise, ein Pfad mit `\n` wäre sonst ein zweiter Befehl, den niemand geschickt hat. `musik_do` ist als destruktiv gemeldet, weil `load` einen Track tauscht und `record` eine Datei schreibt. Geprüft über einen echten MCP-Client im selben Prozess — `tools/list` und `tools/call`, nicht an der Schnittstelle vorbei: 16 Prüfungen, darunter die erzeugten Beschreibungen, die Begrenzung von Werten, die Fehlerwege und die abgewiesene Einschleusung. Ohne laufende Anwendung meldet der Test sich mit 77 ab, statt Grün zu behaupten, wo nichts geprüft wurde.
Nach dem Tempo die zweite Hälfte der Trackauswahl. Zwei Stücke im gleichen Takt können gegeneinander klingen, wenn die Tonarten nicht zusammenpassen. Selbst gebaut, weil libKeyFinder und der QM Key Detector — den auch Mixxx mitlinkt — unter GPL stehen und das den Weg zu VibeMind (MIT) zugemacht hätte. Verfahren: Chroma über eine FFT, Vergleich gegen die Krumhansl-Kessler-Profile, Ausgabe zusätzlich als Camelot-Zahl. Der Wert (Name, Camelot, Verwandtschaft) liegt in `audio-core`, damit `library` und `control` ihn benutzen können, ohne den Analyseapparat mitzuschleppen — dasselbe Argument, aus dem `Beatgrid` dort liegt. Zwei Fehler, die erst die Demo-Oberfläche sichtbar gemacht hat: - Das Analyseband reichte bis 65 Hz hinunter. Bei 8192 Punkten ist ein Bin 5,4 Hz breit, ein Halbton bei C2 nur 3,9 Hz — unterhalb von etwa 200 Hz fielen mehrere Halbtöne in denselben Bin, ausgerechnet dort, wo Kick und Bass die meiste Energie haben. Der Kommentar behauptete das Gegenteil. - Die Teiltöne eines Sägezahn-Basses wurden für eigene Töne gehalten, sodass ein einzelner Ton wie ein Akkord las. Jetzt werden sechs Teiltöne auf ihren Grundton zurückgerechnet (HPCP mit harmonic contribution, Gómez 2006). Die Konfidenz misst jetzt die Güte der Anpassung statt den Vorsprung vor der zweitbesten Deutung: Der Vorsprung lag bei Perkussion (0,062) über dem einer echten Moll-Folge (0,088) und trennte nichts. Bass und Drums ohne Akkorde bekommen bewusst **keine** Tonart. Dort steht das Tongeschlecht nicht im Signal — der Sägezahn bringt seine eigene große Terz mit —, und „A" statt „Am" ist auf dem Camelot-Rad 11B statt 8A und damit ein Fehlgriff. Geprüft wird deshalb, ob die Terz überhaupt klingt. Beide Schwellen sind an synthetischem Material gemessen und gegen eine echte Sammlung nie geprüft, wie beim Tempo auch. Angeschlossen: - Sidecar-Format 2; alte Sidecars werden verworfen statt mit einem vorgetäuschten „keine Tonart gefunden" ergänzt - `musik-lib scan` legt die Tonart ab, `musik-lib harmonic <tonart>` sucht - `deckN.key` und `deckN.key_camelot` im Steuerraum, `master.search_harmonic` - Camelot-Spalte in jeder Trefferzeile, in der Plattenkiste und über MCP - Oberfläche: Tonart am Deck, KEY-Spalte, „Harmonisch zu A/B" - Playlists sind über `master.playlists`/`master.playlist` und die Oberfläche erreichbar; in der Sammlung gab es sie längst, erreichbar waren sie nie Die Demo-Tracks haben jetzt Akkorde über den Drums — sonst könnte die Vorführung ohne eigene Sammlung die halbe Anzeige nicht zeigen. Sie stehen in a-Moll (8A) und e-Moll (9A), also einem Paar, das sich mischen lässt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Fünf echte Tracks haben gezeigt, dass die Schwelle zu streng war: Sie wies vier davon ab. Der Grund ist die Eichung an synthetischem Material — selbstgebaute Akkordfolgen erreichen 0,92 bis 0,95, echte Aufnahmen kommen über 0,82 kaum hinaus. Dieselbe Falle wie beim Tempo, wo eine an Klick-Tracks geeichte Schwelle echte Musik verwarf. Offenbar die Standardfalle bei allem, was hier an Signalen gemessen wird: Synthetisches Material ist sauberer als alles, was je aus einem Mikrofon kommt. Gegengeprüft ohne Ground Truth über die Stabilität in 30-Sekunden- Abschnitten — eine echte Tonart bleibt über den Track stehen, eine erfundene springt. Die drei Tracks über 0,72 sind stabil (4/4, 5/7, 6/7), die beiden darunter springen (4/6, 4/8). Die Schwelle liegt jetzt zwischen beiden Gruppen. Beim Nachmessen kam heraus, dass die bisherige Begründung falsch war: Rauschen und Perkussion drücken die Güte kaum, weil sie alle zwölf Halbtonklassen gleichmäßig anheben und eine Korrelation gegen so einen Offset unempfindlich ist — selbst grobes Rauschen mit lauten Drums senkt sie nur von 0,95 auf 0,90. Was sie drückt, ist fremde Harmonik. Ein Test, der die Schwelle bewacht, ist nicht möglich und wurde deshalb wieder entfernt: Um sie zu prüfen, bräuchte es synthetisches Material im Bereich echter Musik, und selbst eine Tritonus-Modulation über ein Drittel der Länge kommt nur auf 0,864. Ein Test, der das vortäuscht, wäre schlechter als keiner. Bewacht wird stattdessen die Messtabelle im Modul. Fünf Aufnahmen sind eine dünne Grundlage — der Abstand zwischen 0,71 und 0,77 ist schmal. Das steht so in der Dokumentation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Bisher lagen Hot Cues allein in den Atomics des Decks. Acht Cues gesetzt, Track neu geladen — weg. Dabei hatte die Sammlung die Tabelle die ganze Zeit, und der Traktor-Import füllte sie sogar: Diese Cues erreichten nur nie ein Deck, weil niemand sie las. Jetzt in beide Richtungen. Beim Setzen oder Löschen schreibt das Pult sofort zurück, nicht beim Beenden — ein Cue, der nur im Speicher steht, ist nach einem Absturz weg, und abgestürzt wird beim Auflegen. Acht Zeilen SQLite dürfen den Aufrufer kurz aufhalten, anders als das Dekodieren beim Laden. Beim Laden kommen sie zurück aufs Deck, und ein Beatgrid aus der Sammlung schlägt dabei die frische Analyse: Was dort steht, kann aus Traktor stammen oder von Hand korrigiert sein, und beides weiß mehr als ein Detektor. Zwei Fallen, die beim Bauen auffielen: - `replace_cues` löscht *alle* Marker eines Tracks. In derselben Tabelle liegt der Grid-Anker aus dem Traktor-Import, und ein Deck kennt nur seine acht Tasten — der erste gesetzte Cue hätte den Anker mitgenommen. Dafür gibt es jetzt `replace_hot_cues`, das nur Zeilen mit Hotcue-Nummer anfasst. - Der Startpfad `--a datei.mp3` lädt an der Sammlung vorbei. Mit gesetztem Deck-Pfad, aber ohne gelesene Cues hätte er beim ersten Cue die gespeicherten überschrieben. Die Library wird deshalb vor der Deck-Schleife geöffnet, und beide Ladewege wenden dieselbe Regel an. Ein fehlgeschlagenes Speichern verschluckt den Cue nicht: Er steht am Deck, der Grund in `deckN.load_status`. Geprüft über den ganzen Weg — Cues über den Socket gesetzt, Anwendung neu gestartet, Cues kommen zurück; der Grid-Marker steht unberührt daneben, und ein abweichendes Grid aus der Sammlung (100 BPM) schlägt die Analyse (128). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Der Detektor liegt bei sperrigem Material daneben — bei einem der fünf Testtracks kam 180 BPM heraus, mit einer Konfidenz von 0,05. Bisher war so ein Track damit unbrauchbar: `bpm_grid` war nur lesbar, es gab kein Tap und kein Verschieben des Ankers. In docs/PLAN.md steht seit der Recherche, dass manuelle Grid-Korrektur nötig ist, egal wie gut der Detektor wird. Vier Wege, alle über den Steuerraum und alle mit Rückschreiben: - `bpm_grid` und `grid_anchor` sind schreibbar - `grid_scale <faktor>` für den Oktavfehler, den häufigsten Fall; der Anker bleibt stehen, denn bei einer Halbierung lag jede zweite Eins richtig - `grid_here` legt den Anker auf die Abspielposition In der Oberfläche als GRID ÷2 ×2 HIER neben den Hot Cues. `grid_here` nimmt die **Ziel**-Position, nicht die angezeigte. `seek_frames` setzt nur einen Wunsch, den der Audio-Thread im nächsten Block ausführt; wer springt und sofort „hier" sagt, meint die Stelle, auf die er gesprungen ist. Aufgefallen ist das an einem Test, der die Anzeige las und 0 statt 12,5 bekam — im Betrieb wäre es ein seltener, schwer zu findender Griff daneben gewesen. Dafür gibt es jetzt `DeckState::ziel_position`. Beim Bauen fiel außerdem auf, dass die Cue-Knöpfe der Oberfläche direkt ins Deck schrieben, am Pult vorbei — die seit dem letzten Commit gespeicherten Cues wären also ausgerechnet auf dem Weg, den ein Mensch nimmt, nicht angekommen. Jetzt gehen auch sie durch das Pult, wie Regler, Suche und Laden. Ohne Grid wird nichts geraten: Anker setzen und skalieren melden sich als gescheitert. Nur `bpm_grid` nimmt auch aus dem Nichts einen Wert an, sonst käme man aus einem fehlenden Grid nie wieder heraus. Geprüft über den ganzen Weg: 128,02 halbiert auf 64,01, Anker auf 2,5 s gelegt, Anwendung neu gestartet, beides kommt zurück — während die Analyse allein weiterhin 128,02 und 0,474 s erkennt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
…ehle
Gedacht für ein Team von Agenten, das die Anlage über Chat bedient. Fünf
Fragen führten alle auf dieselbe Lücke: Der Steuerraum kannte keine Zeit.
Ein Übergang ist keine Folge von Reglerstellungen, sondern eine Bewegung über
Takte. Wer das von außen nachbaut, muss in einer engen Schleife `beat_phase`
pollen und dazwischen schlafen — über eine Leitung, deren Timing dem
Scheduler ausgeliefert ist. Das eiert hörbar, und der Agent ist die ganze
Zeit blockiert.
Vier Verben:
ramp <control> <ziel> <beats> [deck]
in <beats> <befehl>
plan
cancel [id]
Gerechnet wird in Beats, nicht in Sekunden: Ein Plan in Sekunden geht schief,
sobald jemand am Tempo dreht. Steht das Deck, steht der Plan. Den Taktgeber
erbt ein Kanalzug von seinem Deck, die Summe nimmt das erste Deck mit Grid.
**Eine Rampe gibt auf, sobald jemand anders denselben Regler anfasst.** Ohne
das wäre die Automatik stärker als der Griff daneben — man zieht den Fader zu,
und einen Wimpernschlag später steht er wieder offen. Geprüft wird über den
Wert selbst statt über eine Kennung; das trifft einen Menschen an der
Oberfläche wie einen zweiten Agenten. Damit ist `plan` zugleich das
gemeinsame Blatt: Wer mitliest, sieht, was die anderen vorhaben.
Dabei kam ein schwerer Fehler in der Engine ans Licht: Der Mixer übersprang
stille Kanäle, um EQ und Filter zu sparen — und übersprang dabei auch die
Quelle. **Jedes Deck mit zugezogenem Fader stand still.** Der Track, den man
einsetzen will, kam nicht von der Stelle; zwei gesyncte Decks liefen
auseinander, sobald eines weggeblendet war; `finished` kam nie, womit jeder
wartende Agent ins Leere lief. Beatmatching wäre damit wirkungslos gewesen.
Jetzt läuft die Quelle immer weiter, nur die Filterkette bleibt aus — der
Sparbetrag steckte ohnehin dort. Aufgefallen ist es, weil ein Zeitplan-Test
keine Beats zu sehen bekam.
Der Prüfstand hat deshalb jetzt echte Abspieler statt `SilentSource`: Ohne
laufende Position gibt es keine Beats, und ohne Beats ließe sich der Zeitplan
nicht prüfen. Gerechnet wird weiterhin nur, wenn ein Test den Runner dreht.
Der Takt liegt bei 5 ms, rund ein Prozent eines Beats bei 128 BPM. Für eine
Blende unhörbar, für einen harten Schnitt gerade noch vertretbar; sample-genau
ist er ausdrücklich nicht.
Am laufenden System geprüft: Bass raus über 8 Beats, danach Deck B rein über
16 und der Crossfader mit — drei Zeilen, dann legt der Agent auf, und die
Werte wandern von selbst.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Die vier Verben des Zeitplans — ramp, in, plan, cancel — gab es nur über das Zeilenprotokoll. Ein Agent, der über MCP spricht, konnte Übergänge deshalb nur nachbauen: musik_set in einer Schleife, dazwischen schlafen — über eine Schnittstelle, deren Timing bei jedem Aufruf eine Modellantwort weit entfernt ist. Das eiert hörbar, und der Agent kann in der Zeit nichts anderes tun. - musik_ramp bewegt einen Regler über Beats, wahlweise erst nach einer Wartezeit (in_beats) und mit wählbarem Taktgeber-Deck - musik_schedule legt eine Aktion oder einen Wert auf einen späteren Beat - musik_cancel nimmt Vorgemerktes zurück - musik_status trägt den Plan mit: Wer eine Momentaufnahme nimmt, bevor er zugreift, sieht die Absichten der anderen, statt sie aus Reglerbewegungen zu erraten Der Plan ist gemeinsam, und danach richten sich zwei Entscheidungen. Er hängt an musik_status statt an einem eigenen Werkzeug, damit ihn sieht, wer ohnehin hinschaut. Und musik_cancel verlangt für „alles" einen eigenen Schalter — eine vergessene Nummer soll nicht die Arbeit der anderen leeren. Ein halber Befehl wird abgewiesen statt halb ausgeführt: musik_schedule verlangt entweder eine Aktion oder ein Control mit Wert, nie beides und nie keins. Sonst sähe eine Antwort aus, als wäre etwas vorgemerkt. Geprüft über einen echten MCP-Client gegen eine laufende Anwendung, 32 Prüfungen grün. Gegengeprüft, indem in_beats stillschweigend fallen gelassen wurde — dann steht der verzögerte Auftrag als sofortige Rampe im Plan, und der Test wird rot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Der erste vollständig automatisch gefahrene Übergang funktionierte technisch und klang trotzdem seelenlos. Das Dokument hält fest, warum — und in welcher Reihenfolge sich das ändern lässt. Die Reihenfolge ist dabei das Argument, nicht die Liste. Drei Befunde aus dem Durchlauf: Der Übergang war eine Liste aus acht unabhängigen Schwellen, keine Geste. Die Anlage kennt Tempo und Tonart, aber nicht Intro, Break, Drop und Outro — also nicht die Stellen, an denen ein Übergang überhaupt sitzen darf. Und sie hört sich nie selbst, womit jede Regel eine Behauptung bleibt. Daraus zwei Sätze, an denen alles Weitere hängt: Jede Entscheidung trägt ihren Grund, und jedes Set wird hinterher gemessen. Die Schichten stehen bewusst nicht nach Klanggewinn sortiert. Der Phrasenbezug kommt zuerst, weil er ein Verb kostet und jeden Mix betrifft; der Kritiker über den Mitschnitt danach, weil ohne ihn jede spätere Verbesserung ein Gefühl bleibt. Die Strukturanalyse verspricht mehr, kommt aber erst dritte — sie lässt sich ohne Kritiker nicht belegen. Was nicht Software ist, steht am Ende: Material, das Interface mit vier Ausgängen, und Geschmack. Der Kritiker soll dem Menschen die Handwerksfehler abnehmen, nicht das Urteil. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
S0 aus docs/DJ.md. Die Anlage konnte „in 32 Beats" und „wenn noch 32 Beats übrig
sind", aber nicht „auf der nächsten Eins" — und das ist die Einheit, in der ein
Übergang gedacht wird. `when beats_to_phrase < 1` trifft irgendwo im letzten
Beat, nicht auf den Schlag.
`in` nimmt jetzt statt einer Zahl auch `phrase` und `phrase+n`:
in phrase do deck2.sync
in phrase+16 ramp master.crossfader 1.0 32
`ramp` muss dafür nichts von Phrasen wissen. `in` trägt eine beliebige
Protokollzeile, also setzt `in phrase ramp …` die Bewegung auf die Grenze — ein
zweiter Phrasenbegriff im Rampen-Verb wäre eine zweite Stelle zum
Auseinanderlaufen. Aus demselben Grund ist die Rechnung selbst aus `lies_deck`
herausgehoben: `beats_to_phrase` und `in phrase` beantworten dieselbe Frage und
teilen sich jetzt eine Methode.
Der Bezugspunkt ist dasselbe Deck, auf dessen Takten der Auftrag dann liegt —
das erste mit Beatgrid. Sonst zählte man die Beats des einen und die Phrase des
anderen. Ohne ein solches Deck wird abgewiesen statt stillschweigend auf null zu
fallen.
Über MCP als Schalter `ab_phrase` an `musik_schedule` und `musik_ramp`, mit dem
Hinweis in der Beschreibung, dass es fast immer das Richtige ist.
Der erste automatisch gefahrene Übergang hing an acht willkürlichen Zahlen und
klang danach. Damit hängt er an einem musikalischen Zeitpunkt.
339 Tests grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
…hrieben S1 aus docs/DJ.md. Die Anlage schreibt seit Phase 9 einen Mitschnitt, und niemand hat ihn je gelesen. Damit blieb jede Regel im Mixen eine Behauptung und jede Verbesserung ein Gefühl. `musik-kritik <mitschnitt.wav>` findet Übergänge über die Änderung des Klangbilds — Chroma und nicht Spektrum, weil ein Trackwechsel die Harmonik ändert und ein Filterschwenk nicht. Zu jedem Übergang: Dauer, Pegelverlauf über die Blende, Tempo davor und danach. Am eigenen Material geprüft, dessen Antworten ich kannte: ein Mix mit Übergang ab Sekunde 40 über 16 Sekunden. Der Kritiker findet ihn, misst die Dauer exakt und bestätigt, dass das Tempo hält. Zwei Dinge kann er nicht, und beide standen zuerst fälschlich im Bericht: Den Beginn einer langen Blende findet er nicht genau. Am Anfang ist der eingehende Track per Konstruktion unhörbar — das ist, was eine Blende ausmacht. Die erste Fassung meldete 51 statt 40 Sekunden; mit Rückverfolgung des Anstiegs sind es 44. Die verbleibende Unschärfe steht jetzt im Bericht, statt verschwiegen zu werden. Und die Phrasenlage kann er gar nicht messen. Der Anker des Tempodetektors ist der erste starke Schlag im Analysefenster, nicht der Anfang einer Phrase — „6,1 Beats neben der Eins" war gegen einen willkürlichen Nullpunkt gemessen. Die Zahl stand schon im Bericht, bevor mir auffiel, dass sie keinen Boden hat. Dazu kommt, dass ein Sekundenfenster bei 126 BPM zwei Beats breit ist, also gröber als die Frage. Beides steht jetzt als Befund im Bericht statt als Urteil, und `--phrase` ist weg — eine Option, die nichts mehr tut, wäre eine Halbwahrheit. Daraus folgt eine Planänderung, in DJ.md festgehalten: Das Plan-Protokoll gehört zum Kritiker dazu. Der Mitschnitt sagt, was herauskam; der Plan, was gemeint war. Erst zusammen ergeben sie ein Urteil über die Phrasenlage. 347 Tests grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Der Mitschnitt sagt, was klang. Er sagt nicht, wann jemand den Fader angefasst hat, und genau das konnte der Kritiker deshalb nur schätzen: Am Anfang einer langen Blende ist der eingehende Track per Konstruktion unhörbar. Die Phrasenlage konnte er gar nicht messen, weil der Anker eines nachträglich geschätzten Rasters irgendein starker Schlag ist und nicht die Eins. Beides weiß die Anlage im Moment des Geschehens. Sie schreibt es jetzt mit: neben jedem Mitschnitt eine `.mitschrift` mit Frame, Beat jedes Decks, ob das Deck lief, und der Zeile selbst — `>` herein, `<` hinaus. Drin ist, was verändert; `get` und `list` blieben draußen, sonst geht die Bewegung im Nachfragen unter. `musik-kritik` findet die Datei von selbst. Am ersten Lauf danach, ein Übergang über 32 Beats: Beginn laut Klang 27,00 s (±4) Beginn laut Mitschrift 23,25 s, Beat 48,01 — 0,01 in die Phrase gefahren 32,0 von 32 bestellten Beats Die Schätzung lag 3,7 s zu spät. Das war vorher eine Vermutung; mit `--ohne-mitschrift` misst der Kritiker sie jetzt bei jedem Set nach. Und ein Befund, den vorher niemand hätte finden können: Deck 2 setzte auf seinem Beat -1 ein, fünfzehn Beats neben seiner eigenen Eins. Der ausgehende Track lag auf der Phrase, der eingehende nicht — der Auftrag für S2. Zwei Dinge fielen beim Bauen selbst auf und sind mit repariert: Ein stehendes Deck bekam eine Phrasenlage, die sich las wie „es kam neben der Eins herein", obwohl es überhaupt nicht hereinkam (jetzt `~` in der Zeile und `phrasenlage() -> Option`). Und ein Wächter für „der Rest der Zeile bleibt am Stück" war Dekoration, bis ein Test mit zwei Leerzeichen ihn wirklich rot werden ließ. 376 Tests grün (vorher 347).
Die Anlage kannte Tempo, Tonart und Wellenform. Sie kannte nicht die Stellen, an denen ein Übergang sitzen darf, und deshalb war „blende aus, während das Outro läuft" nicht ausdrückbar — das Wort Outro gab es nicht. Gerechnet wird auf dem vorhandenen Beatgrid. Je Phrase drei Zahlen: Pegel, Bass (zwei Einpoler bei 160 Hz) und Onset-Dichte. Eine Grenze liegt dort, wo sich das Klangbild über eine Phrasengrenze hinweg ändert. Die Namen kommen aus Quantilen des Tracks selbst, nie aus absoluten Zahlen. Am gebauten Material mit bekannter Gliederung — sechs Teile, Grenzen bei Sekunde 15, 30, 45, 60, 75: 0.5s intro Pegel 0.34 Bass 0.12 Dichte 0.43 15.5s aufbau Pegel 0.53 Bass 0.42 Dichte 0.72 30.5s drop Pegel 0.99 Bass 0.99 Dichte 0.98 45.5s break Pegel 0.44 Bass 0.41 Dichte 0.56 60.5s drop Pegel 0.98 Bass 0.98 Dichte 0.97 75.5s outro Pegel 0.20 Bass 0.06 Dichte 0.25 Der Versatz von einer halben Sekunde ist der Anker des Beatgrids: Das Phrasengitter hängt an der ersten Eins, nicht an Frame 0. Genau das macht den Einstiegspunkt zu einem Downbeat statt zu Sekunde 0 — der Fehler, den die Mitschrift beim ersten gemessenen Übergang aufgedeckt hat. Vier Entwürfe sind unterwegs am Material gescheitert und stehen mit ihrer Begründung im Code: - Ein Kern aus zwei Phrasen je Seite verschmiert genau die Abschnitte, die zwei Phrasen lang sind. Mit einer je Seite trennen sich die Grenzen um das Fünfundzwanzigfache vom Rest, mit zweien nur noch um das Doppelte. - Median plus Streuung als Schwelle fand gar nichts: Wenn jede zweite Phrase eine Grenze ist, sind die Ausschläge die halbe Verteilung und überdecken sich selbst. - Der z-Wert als Normierung machte aus gleichförmigem Material Struktur — Rauschen, auf Streuung 1 gestreckt, sieht aus wie ein Wechsel. - „Break heißt Bass weg" war falsch gemessen: Ein Kick allein bringt reichlich Energie unter 160 Hz. Der Pegel trennt das zuverlässiger. Dazu ein Prüfstein, der selbst untauglich war: Als „gleichförmiges Material" stand zuerst ein reiner Sinus da. Dessen spektraler Fluss driftet über das Stück um 45 %, weil die Bin-Leckage gegen den Hop schwebt — eine Forderung an den Detektor, die in Musik nicht vorkommt. Jetzt steht dort ein durchlaufender Takt. Belegt sind die Grenzen. Die Benennung ist es nicht: Ob „lauteste 25 % mit viel Bass" auf echten Produktionen der Drop ist, weiß erst, wer es an echten Produktionen nachmisst. Bis dahin stehen die Zahlen neben jedem Namen im Bericht, damit man ihm widersprechen kann. Sidecar-Format auf 3. 387 Tests grün (vorher 376).
Die Erkennung stand seit dem letzten Commit, blieb aber ein Bericht. Jetzt
kennt jedes Deck seine Gliederung, und damit wird die eigentliche Regel
sagbar:
when deck1.beats_to_outro < 0 in phrase ramp master.crossfader 1 32
Vorher hieß dieselbe Absicht „wenn noch 40 Beats übrig sind" — eine Zahl,
die man je Track neu schätzt und meistens falsch.
Neu am Deck: `section` (intro, aufbau, drop, break, outro, teil),
`section_beats_left`, `beats_to_outro` (negativ, sobald es läuft),
`intro_beats`, `entry` und die Aktion `jump_entry`. `musik_status` trägt
alles mit; im Bericht steht eine Zeile: „Abschnitt **aufbau**, noch 12
Beats · Outro in 108 Beats".
`entry` ist der Einstiegspunkt — der erste Downbeat, nicht Sekunde 0. Genau
den Fehler hat die Mitschrift beim ersten gemessenen Übergang aufgedeckt:
Deck 2 setzte fünfzehn Beats neben seiner eigenen Eins ein, weil es bei
Frame 0 anfing. `do deckN.jump_entry` räumt das in einem Griff weg.
Die Werttypen liegen jetzt in `audio-core`, die Erkennung bleibt in
`analysis` — dieselbe Aufteilung wie bei der Tonart und aus demselben
Grund: `control` soll wissen, was ein Outro ist, ohne den halben
Analysestapel mitzuziehen. Dabei ist `PHRASE_BEATS` von zwei Definitionen
auf eine zusammengefallen; die Gliederung schneidet auf Phrasengrenzen, und
`in phrase` zielt darauf, also muss es dieselbe Zahl sein.
Zwei Fehler kamen erst am laufenden Programm heraus, keiner davon in einem
Test:
- **Beim Start über `--a` kam die Gliederung nie am Deck an.** Der Vorlade-
pfad in `main.rs` setzt die Felder einzeln, und `struktur` fehlte dort.
Über `load` zur Laufzeit ging es, über die Kommandozeile nicht.
- **`list deck1.beats_to_outro` schrieb `f64::MIN` aus** — dreihundert
Stellen in der Selbstbeschreibung. Ein offenes Ende wird jetzt in beide
Richtungen als offen gemeldet, mit Wächter.
Leer heißt weiterhin leer: Im Vorlauf vor der ersten Eins gibt es keinen
Abschnitt, und ein Track ohne Outro bekommt keines angedichtet. Eine 0
hieße beides.
Nachgefahren am laufenden Programm: entry 0,466 s, intro_beats 32,
beats_to_outro zählt von 161 über 93 auf 1,7 herunter, section folgt dem
Abspielkopf. Die MCP-Prüfung gegen die laufende Anwendung ist grün.
401 Tests grün (vorher 387).
`DJ.md` plant den Weg zu einem AI-DJ und reicht bis S6. Offen ist mehr: die ungeprüften Behauptungen, das Team, das es nie gegeben hat, und die Punkte, die an Hardware, Musik und Ohren hängen. Die Diagnose, aus der die Reihenfolge folgt, hat zwei Teile. Über neun gebauten Phasen liegen inzwischen drei Schichten Messwerkzeug — Kritiker, Mitschrift, Gliederung —, und jede hat beim Bauen sofort einen Fehler gefunden. Nur ist **jede Schwelle darin an Material geeicht, das aus eigener Hand stammt**. Genau das war in diesem Projekt schon viermal die Vorstufe eines Fehlers: die Tempo-Güteschwelle an Klick-Tracks, die Tonart-Schwelle an synthetischen Akkorden, der Struktur-Prüfstein an einem reinen Sinus. Jedes Mal sah es vorher gut aus. Und der Zweck des Ganzen — ein Team von Agenten — hat nie stattgefunden. Gelaufen ist ein Bediener in mehreren Rollen. Die Schutzmechanismen dagegen, dass einer dem anderen den Regler wegzieht, sind einzeln geprüft; ihr Zusammenspiel nie. Daraus zwei Schichten, die vor den musikalischen stehen: P1, der Prüfstand — ein Programm, das Tracks und eine Angabe von Hand nebeneinanderlegt und meldet, wo die Analyse zustimmt und wo nicht. Dasselbe Argument, das den Kritiker vor die Strukturanalyse gestellt hat. Er hat am ersten Tag einen Kunden: Das gebaute Material aus S2 trägt seine Wahrheit schon. Und er verschiebt den Aufwand beim Prüfen an echter Musik vom Nachrechnen zum Hinhören — hinhören kann nur ein Mensch. P2, das Team — zwei Verbindungen an einem Pult, mit einem Aufbau, der die Zusammenstöße absichtlich herbeiführt. Erst zwei Sitzungen mit einem Modell (reproduzierbar, ohne API-Kosten), dann zwei unabhängige Modelle. Vor Bogen und Raum, weil beides Entscheidungen darüber sind, was als Nächstes kommt — für einen Bediener gebaut und später umgestellt, hieße zweimal gebaut. Danach P3 Geste und Repertoire, P4 der Bogen, P5 der Raum, P6 Stems. Getrennt davon steht, was nicht von hier aus zu erledigen ist: Musik mit Angabe der Abschnitte, die Suno-Prompts, ein Interface mit vier Ausgängen, einmal Hinhören bei der Zeitstreckung, eine echte collection.nml. Vier davon sind mit denselben fünf Tracks erledigt — und diesmal landen sie sofort im Repo statt im Container, den es dreimal zerlegt hat. Nicht drin: Termine. Jede Schicht hier hat unterwegs mindestens einen Entwurf verworfen, weil das Material widersprochen hat; die Strukturanalyse allein vier.
Über der Anlage liegen drei Schichten Messwerkzeug — Kritiker, Mitschrift,
Gliederung —, und jede Schwelle darin ist an Material geeicht, das aus
eigener Hand stammt. Genau das war hier schon viermal die Vorstufe eines
Fehlers. Ein Messwerkzeug, dem niemand widerspricht, ist ein Orakel.
Der Prüfstand legt eine Wahrheitsdatei daneben und meldet den Abstand:
Nachtschicht.mp3 bpm 124 tonart Am intro 0:00 drop 1:04 outro 5:12
Abschnitte mit ihrem Anfang, nicht als Bereich — wer zuhört, notiert „hier
fängt das Outro an". Jede Angabe einzeln freiwillig; weglassen ist besser
als raten. Zeiten als m:ss oder in Sekunden.
Gemeldet werden Unterschiede in Art, nicht nur in Größe: ein Oktavfehler
beim Tempo ist etwas anderes als 5 BPM daneben, und eine Paralleltonart tut
beim Mischen nicht weh. Rückgabewert bleibt 0, auch wenn alles daneben
liegt: Was nicht lesbar war, ist ein Fehler; was nicht übereinstimmt, ist
ein Befund.
Der erste Kunde stand schon bereit — das gebaute Material aus S2 trägt seine
Wahrheit (128 BPM, A-Dur, Grenzen bei Sekunde 15/30/45/60/75). Tempo auf
0,01 BPM, Tonart gleich, alle sechs Namen richtig.
Und der Lauf hat sofort etwas gezeigt, das die reine Tabelle verschluckt
hätte: Jede Grenze liegt um +1,0 Beats daneben, keine streut. Ein
konstanter Abstand ist eine Tatsache und keine sechs — er sagt etwas über
den Nullpunkt (der Grid-Anker sitzt auf dem ersten erkannten Schlag, die
Wahrheitsdatei zählt ab Dateianfang) und nichts über die Segmentierung. Der
Bericht trennt das jetzt: gemeinsamer Versatz oben, Rest darunter. Streuen
die Abweichungen stärker, als sie gemeinsam verschoben sind, wird nichts
als Versatz erklärt — sonst würde aus „die Segmentierung wackelt" ein
beruhigendes „nur der Nullpunkt".
Dazu docs/wahrheit-vorlage.txt zum Ausfüllen. Damit verschiebt sich der
Aufwand beim Prüfen an echter Musik vom Nachrechnen zum Hinhören.
412 Tests grün (vorher 401). P1 aus docs/FAHRPLAN.md ist damit erledigt.
P2 aus docs/FAHRPLAN.md, Stufe 1: zwei Bediener an einem Pult.
Der Fund stand vor dem ersten Test. Die Frage „was sieht ein zweiter
Bediener?" hat gereicht: `takt_starten` gab den Rückgabewert des Taktgebers
nicht weiter, sondern verwarf ihn. Damit ging jede Meldung des Plans
verloren — fertig, abgebrochen und vor allem **abgelöst**. Ein Agent, dem
ein anderer den Fader wegnahm, erfuhr es nie und plante weiter auf einer
Blende, die seit zwanzig Sekunden tot war.
Für einen einzelnen Bediener fiel das nie auf. Er war derjenige, der den
Regler angefasst hat.
Jetzt liegen die Meldungen in einem Ring mit laufender Nummer:
sub master.events
event plan 3 abgeloest channel1.fader — jemand anders hat den Regler
event plan 2 gestrichen
Kein einzelner Wert, obwohl das einfacher gewesen wäre: Der Taktgeber läuft
alle 5 ms, der Server vergleicht alle 50. Zwischen zwei Blicken passen zehn
Ereignisse, und neun davon wären weg — ausgerechnet dann, wenn viel
gleichzeitig geschieht. Wer zu langsam liest, bekommt `warnung N Ereignisse
verloren`; dieselbe Regel wie bei record_dropped und den unlesbaren Zeilen
der Mitschrift.
Auch `cancel` meldet jetzt. Streicht einer den Auftrag eines anderen,
verschwand der bisher wortlos aus dem Plan — dieselbe stille Niederlage.
Über MCP hält kein Abo, weil die Brücke je Aufruf neu verbindet. Deshalb
`master.events` und `master.event_count` zum Fragen: Springt der Zähler um
mehr als eins, sind Zeilen liegengeblieben, und auch das ist besser sichtbar
als still.
Neun Zusammenstöße stehen als Test — verlorener Regler, gewonnener Regler,
gestrichener Auftrag, zwei Aufträge auf derselben Phrasengrenze, derselbe
Track zweimal, zu langsamer Leser, Abbestellen, später Einsteigen, und dass
ohne Abo nichts ungefragt kommt. Dazu einer über den echten Socket mit
laufendem Taktgeber und zwei Verbindungen: Dort geht es nicht um die Regeln,
sondern darum, ob Mutex, Taktgeber und Abo-Thread sich gegenseitig
aushungern. Sie tun es nicht.
Die vorhandenen Schutzmechanismen hielten: Die richtige Rampe gibt auf,
derselbe Track wird nicht zweimal abgenommen, zwei Aufträge auf derselben
Eins feuern beide.
Am laufenden Programm nachgefahren, zwei echte Verbindungen: A fährt eine
Blende über 64 Beats, B greift hinein, A bekommt die Ablösung unaufgefordert.
Stufe 2 steht aus: zwei unabhängige Modelle.
428 Tests grün (vorher 412).
Eine Rampe lief bisher immer gerade. Vier gleich lange, gleich gerade
Blenden hintereinander klingen nach Maschine, auch wenn jede einzelne
sauber ist — genau das war mein erster automatisch gefahrener Übergang.
ramp master.crossfader 1 32 weich # S-Kurve
ramp channel2.fader 0.9 16 spaet deck1
linear gleichmäßig — Bass-Swaps, alles nicht als Geste Gemeinte
weich S-Kurve — langsam los, durchziehen, weich ankommen
spaet lange fast nichts, dann schnell — hält den Ausgehenden präsent
frueh sofort viel, dann auslaufen — macht den Wechsel zum Ereignis
Die Namen sagen, **wann** die Bewegung passiert, nicht welche Kurve
dahintersteckt. Danach fragt man beim Auflegen.
Deck und Form stehen hinten in beliebiger Reihenfolge und werden daran
erkannt, *was* sie sind — die Namen sind disjunkt. Eine feste Reihenfolge
wäre nur eine Falle für den, der die Form angeben will, aber kein Deck, und
das ist der häufigere Fall. Ein Tippfehler wird gemeldet und nennt die
Auswahl, statt still linear zu fahren; das hätte ja auch „funktioniert".
Drei Wächter, die keine Behauptungen sind: Jede Form bildet 0 auf 0 und 1
auf 1 ab (sonst spränge der Regler beim Losfahren oder käme nicht an),
keine läuft zwischendurch zurück (ein umkehrender Fader ist kaputt, nicht
ausdrucksstark), und auf halber Strecke stehen sie messbar woanders — am
laufenden Regler nachgefahren, nicht nur in der Rechnung.
Die Form steht im Plan und in der Antwort. Ohne das sähen zwei verschieden
gemeinte Bewegungen für einen zweiten Bediener gleich aus — und seit den
Ereignissen ist das eine Frage, die sich wirklich stellt.
`musik_ramp` reicht sie durch. Offen bleibt aus P3 das Repertoire — lange
Blende, Bass-Swap, harter Schnitt, Filter-Sweep — und die
Crossfader-Kennlinie, die es schon gibt und die im ersten Lauf ungenutzt auf
weich stand.
437 Tests grün (vorher 428).
Der langweiligste DJ im Raum ist der, der immer dasselbe macht — und genau
das war der erste automatisch gefahrene Übergang hier: eine lange Blende,
linear, jedes Mal. Sauber, messbar, seelenlos.
do master.uebergang bassswap 16
uebergang bassswap über 16 Beats, 6 Zeilen
set channel2.eq_low 0 → ok channel2.eq_low 0
in phrase set deck2.play 1 → ok plan 1 in 13.5 Beats: …
in phrase ramp master.crossfader 0 16 weich → ok plan 2 …
in phrase+16 ramp channel1.eq_low 0 8 → ok plan 3 …
in phrase+16 ramp channel2.eq_low 1 8 → ok plan 4 …
in phrase+24 ramp master.crossfader 1 16 weich → ok plan 5 …
Vier Griffe: blende (32 Beats), bassswap (16), schnitt (0), filter (16).
Zwei Entscheidungen tragen das.
**Jeder Griff ist eine Handvoll gewöhnlicher Protokollzeilen**, die durch
denselben Weg laufen wie alles andere. Damit steht er im Plan, in der
Mitschrift und bei den Ereignissen, ein zweiter Bediener sieht ihn kommen,
und `cancel` nimmt ihn zurück. Die Antwort nennt jede Zeile — es gibt nichts,
was ein Agent nicht auch selbst hätte tippen können. Der Griff spart ihm
sechs Zeilen, keine Magie.
**Die Anlage wählt nicht aus.** Welcher Griff passt, hängt daran, ob der
ausgehende Track ein Outro hat, der eingehende ein langes Intro, wie groß
der Energieunterschied ist — und vor allem daran, was vorher schon dreimal
gefahren wurde. Seit S2 stehen die Zahlen dafür im Steuerraum; die
Entscheidung gehört dem, der sie begründen kann. Nähme die Anlage sie ab,
verlöre das Set genau den Teil, um den es diesem Projekt geht.
Vorausgesetzt wird, dass genau ein Deck läuft und auf dem anderen etwas
liegt. Läuft nichts, gibt es keinen Übergang; laufen beide, ist er im Gange
— und wer dann gemeint ist, kann niemand wissen. Beides wird gesagt, statt
ein Deck zu wählen.
Wächter, die zählen: Jeder Griff beginnt auf einer Phrasengrenze, und jeder
startet das eingehende Deck. Auf Stille zu blenden ist beim ersten
selbstgefahrenen Set wirklich passiert, weil `do deckN.play` gar keine
Aktion ist. Dazu: Der Crossfader geht zur Seite des eingehenden *Kanalzugs*,
nicht der Deck-Nummer — welche Seite ein Zug bedient, ist einstellbar. Und
ein Schnitt rampt nicht, sonst wäre er keiner.
Am laufenden Programm nachgefahren: bassswap plant sechs Zeilen auf dem
Phrasenraster, `plan` zählt sie herunter, `cancel` nimmt alle fünf zurück,
und die nächste Nummer ist 6 statt wieder 1.
`musik_uebergang` reicht das durch — fünfzehn Werkzeuge statt vierzehn.
Damit ist P3 aus docs/FAHRPLAN.md erledigt, bis auf den Loop-Ausstieg.
449 Tests grün (vorher 437).
Ein einzelner guter Übergang ist Handwerk. Ein gutes Set ist Architektur.
Bisher konnte die Anlage jeden Übergang begründen und keine Reihenfolge.
set master.arc 0 0.3, 20 0.7, 45 0.95, 60 0.5
do master.arc_start
get master.arc_gap → value master.arc_gap 0.102069
get master.arc_trend → value master.arc_trend steigt
Zeiten in Minuten, Energie 0 bis 1, dazwischen geradlinig verbunden — nicht
weil das musikalisch stimmt, sondern weil alles andere eine Genauigkeit
vortäuschte, die eine von Hand gesetzte Kurve nicht hat.
Damit ist die eigentliche Frage sagbar:
when master.arc_gap > 0.3 do master.queue_next
**Die Ist-Energie kommt aus der Art des Abschnitts, nicht aus dem Pegel.**
Der Pegel der Gliederung ist auf den lautesten Abschnitt desselben Tracks
bezogen: Der Drop eines leisen Stücks steht dort genauso bei 0,99 wie der
eines lauten. Über Tracks hinweg ist das nicht vergleichbar, und ein Bogen,
der solche Zahlen addiert, rechnet mit Äpfeln. Stattdessen eine grobe Leiter
über die sechs Abschnittsarten — Intro 0,2, Break 0,35, Aufbau 0,55,
Drop 0,9. Sechs Stufen sind hier ehrlicher als eine Nachkommastelle, die
niemand einlösen kann.
Ohne `arc_start` gibt es keinen Ort auf dem Bogen, und dann wird auch keiner
behauptet: leer statt null. Eine Kurve ohne Uhr ist ein Bild, kein Maßstab.
Laufen zwei Decks, zählt das lautere — beim Übergang trägt der den Raum.
Für ein Team ist der Bogen das, worüber man sich einig sein muss. Ohne ihn
verhandeln zwei Agenten über den nächsten Track ohne gemeinsamen Maßstab:
Der eine will Druck, der andere Luft, und beide haben recht, weil es keinen
Satz gibt, gegen den sich das prüfen ließe.
Ein Fund vom Wächter über alle schreibbaren Controls: `master.arc` nimmt
Text an, aber nicht *jeden* Text — der Wächter schrieb „Probe" hinein und
bekam zu Recht einen Fehler. Ein Feld, das Text annimmt, ist nicht dasselbe
wie eines, das beliebigen Text annimmt. Ein unlesbarer Bogen wird
abgewiesen statt halb übernommen; ein Set gegen eine Kurve zu fahren, die
niemand gemeint hat, wäre schlimmer als eines ohne Kurve.
`musik_status` trägt den Bogen mit: „Bei 1 min: soll 0.32, ist 0.20, Lücke
**+0.12** — mehr Energie gesucht · steigt in den nächsten Minuten".
Offen bleibt aus P4 die Zurückhaltung — zu messen, ob immer dasselbe
gefahren wird. Das Repertoire macht Abwechslung möglich, erzwingt sie nicht.
465 Tests grün (vorher 449).
Das Repertoire aus vier Griffen macht Abwechslung möglich, erzwingt sie
aber nicht. Ein System, das viermal hintereinander dieselbe Blende wählt,
klingt weiterhin nach Automat, auch wenn jede einzelne sauber ist. Der
Vorwurf an den ersten selbstgefahrenen Übergang war nie mit einer Zahl
beantwortet; `master.repeats` und `master.transitions` sind sie.
Damit ist die Bedingung sagbar:
when master.repeats > 2 do master.uebergang filter
Gezählt wird der Seitenwechsel des Crossfaders, nicht sein Wert. Eine
Rampe schreibt bei jedem Takt in denselben Regler; auf den Wert gezählt
stünde nach einer einzigen Blende eine Wiederholung von 80 da — die Zahl,
die vor Eintönigkeit warnen soll, wäre selbst der Grund, sie zu
ignorieren. Ein Bass-Swap fährt zweimal am Crossfader, kommt aber nur
einmal an.
Der letzte Schritt einer Blende und ein Schnitt schreiben denselben Wert;
unterscheiden lassen sie sich nur über den Schreiber. Deshalb sagt der
Zeitplan dem Pult um den einen Schreibvorgang herum, dass eine Rampe
fährt. Ein über `master.uebergang` angeforderter Griff wird nur
vorgemerkt und erst beim Ankommen eingetragen: Ein abgelöster Griff hat
nicht stattgefunden.
Zwei Decks müssen laufen. Das fiel erst am laufenden Programm auf — nach
vier Zeilen Einrichten meldete die Anlage bereits einen Übergang, obwohl
nur ein Deck lief. Alle vier Griffe starten das eingehende Deck, bevor
der Fader sich bewegt.
Die Grenzen stehen im Modul: Wer nur mit den Kanalfadern mischt, taucht
nicht auf; und läuft der ausgehende Track aus, bevor die Blende ankommt,
fehlt sie. Dann sagt die Zahl nichts, statt etwas Falsches zu sagen.
Geprüft am laufenden Programm über den Socket: eine Blende zählt einmal
und heißt `blende`, ein Bass-Swap mit zwei Crossfader-Rampen zählt einmal
und heißt `bassswap`, drei Schnitte melden `repeats 3`, und `musik_status`
warnt ab dreien. Beide Wächter wurden geprüft, indem der Fehler wieder
eingebaut wurde: 4 statt 1, `schnitt` statt `weich/8`, und 1 statt 0 beim
Einrichten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Die vier Signalplätze gab es lange, aber sie waren Deko: Werte gingen
hinein, und nichts hat darauf reagiert. Ein Bogen, der von der ersten
Minute an feststeht, ist ein Abspielplan — genau das sollte hier nie
herauskommen.
Jetzt beugt der Trend eines Signals das Ziel (master.room,
master.room_bend) und damit arc_gap, und `do master.search_next` sortiert
die Sammlung danach:
weil Andrang fällt (-1.15/min), Ziel 0.75 → 0.50; es läuft 0.20
track 128.02 - .../mitte.wav mitte — Energie 0.57 (+0.07 zum Ziel 0.50)
track 128.02 - .../warmup.wav warmup — Energie 0.38 (-0.12 zum Ziel 0.50)
track 128.02 - .../peak.wav peak — Energie 0.73 (+0.23 zum Ziel 0.50)
Drei Entscheidungen tragen das. Der Raum verschiebt das Ziel, nicht die
Kurve: master.arc bleibt, was jemand aufgeschrieben hat, arc_curve zeigt
es unverändert, und am Ende des Abends lässt sich vergleichen, was
geplant war und was geschah. Gebeugt wird nach dem Trend, nicht nach dem
Wert — die Höhe eines Signals ist nichts Vergleichbares, eine Änderung
schon. Und höchstens um 0,25, damit ein hängender Sender das Set nicht
übernimmt.
Die Energie eines Tracks kommt aus seiner Gliederung, nach Länge
gewichtet, und liegt in der Analyse-Datei neben dem Track statt in der
Datenbank: Sie hängt am Inhalt, nicht am Eintrag. Ein nicht analysierter
Track steht hinten und sagt das. Was sie **nicht** misst, steht im Modul:
Lautstärke und Härte nicht, weil die Abschnittsarten je Track gegen
dessen eigene Quantile benannt werden.
Ausgewählt wird trotzdem nicht. Die Anlage sortiert und begründet; welcher
Track es wird, entscheidet, wer es begründen kann.
Zwei Funde kamen erst am laufenden Programm heraus:
Die Benennung verglich Abschnitts*mittel* mit Quantilen über *Phrasen*.
Ein Mittel liegt immer unter der lautesten Phrase darin; ist die Mehrheit
der Phrasen laut, liegt der Median fast auf der Spitze, und jeder
Abschnitt fällt darunter. Ein Stück mit 90 Sekunden Vollgas hieß deshalb
"Intro, Break, Outro" und bekam die niedrigste Energie von allen — die
Reihenfolge stand auf dem Kopf. Am gebauten Prüfmaterial fiel das nie
auf, weil dort laute und leise Teile sich abwechseln. Der erste Anlauf
des Wächters lief sogar daran vorbei: Bei exakt gleich lauten Phrasen
liegt das Mittel genau auf dem Median, und die Prüfung hält gerade noch.
Er greift jetzt mit ungleichen Phrasen, wie echtes Material sie hat.
Und `set master.room ""` war über MCP nicht erreichbar — dort verlangt
musik_set mindestens ein Zeichen. Ein Weg, den nur der Socket kennt, ist
für ein Team von Agenten keiner; `-` und `aus` gehen jetzt auch.
Geprüft am laufenden Programm über den Socket und über einen echten
MCP-Client: Ein fallender Raum beugt das Ziel von 0,75 auf 0,50, und der
Peak-Track rutscht dabei von Platz eins auf Platz drei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Zwischen zwei Stücken tut das jeder: `record_stop`, dann sofort `record naechstes.wav`. Der Schreiber-Thread holt sich seine Befehle in Häppchen ab, und wenn beide im selben Häppchen liegen, fasste er sie zu Merkern zusammen und handelte erst danach. Dabei öffnete der `Start` die neue Datei, und der `Stop` schrieb den **alten** Mitschnitt hinein und schloss sie: Die erste Datei blieb bei 44 Bytes — nur der Kopf —, die zweite bekam fremdes Material, und der zweite Mitschnitt landete nirgends. Zwei Änderungen. Befehle werden der Reihe nach ausgeführt, nicht gesammelt; ein `Start` auf eine noch offene Datei schließt sie zuerst ordentlich ab, statt sie mit ungeschriebenem Kopf fallen zu lassen. Und der `Stop` bringt mit, wie viele Frames noch in diese Datei gehören — die Zahl kommt von der aufnehmenden Seite und ist die einzige, mit der sich die Grenze zwischen zwei Mitschnitten bestimmen lässt. „Alles, was noch im Ring liegt" war die naheliegende Regel und die falsche: Genau darin steckt der Anfang des nächsten. Aufgefallen ist das nicht hier, sondern in der CI, und zwar an einem Test, der den Fehler selbst versteckte: Er wartete zwischen den beiden Aufnahmen, bis die erste Datei Daten hatte. Auf einem freien Rechner kommt der Schreiber in dieser Zeit dran, auf einem ausgelasteten nicht. Der neue Wächter wartet ausdrücklich nicht und fällt ohne die Korrektur zuverlässig um — geprüft, indem der Fehler wieder eingebaut wurde: 44 statt 4044 Bytes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
`schleife` legt den Ausgehenden in eine Schleife und wechselt darüber. Der einzige Griff, der ihm Zeit gibt: Ein Track, der in vier Beats zu Ende wäre, hält so noch eine ganze Phrase durch. Genau dafür setzt ein Mensch am Ende eines Stücks eine Schleife — nicht, um etwas zu wiederholen, sondern um nicht gehetzt wechseln zu müssen. Der Griff hat zwei Fehler aufgedeckt, die beide erst am laufenden Programm sichtbar wurden. **Ein Auftrag, dessen Taktgeber zurückspringt, lief stumm weiter.** Eine Rampe rechnet ihren Fortschritt aus dem Beat des Taktgeber-Decks; läuft das in einer Schleife, fängt sie bei jedem Durchlauf von vorn an. Der Crossfader fuhr sieben Mal hin und her, und `master.transitions` zählte sieben Übergänge. Umgekehrt kommt ein Vorgemerktes hinter dem Schleifenende nie an — auch nicht die Zeile, die die Schleife wieder lösen soll. Jetzt bricht der Auftrag ab und sagt es: „plan 3 abgebrochen — deck1 ist zurückgesprungen (Schleife oder Sprung)". Das still zu tun wäre das Schlimmste von allem: Der Bediener sieht einen Plan, der läuft, und hört etwas anderes. **Und `in` konnte seinen Taktgeber nicht wählen.** Es nahm immer das erste Deck mit Beatgrid — bei diesem Griff also das schleifende. `in deckN <zeit> <befehl>` nimmt jetzt dessen Takt, auch wenn der Befehl auf ein anderes Deck wirkt. Der Griff taktet alles nach dem eingehenden Deck; nur das Setzen der Schleife selbst liegt noch auf dem ausgehenden. Am laufenden Programm nachgesehen, alle zwei Sekunden: Die Schleife greift auf der Phrasengrenze, hält den Ausgehenden über die ganze Blende (seine Position springt einmal von 15,3 auf 10,8 zurück), wird danach gelöst, und der Crossfader geht genau einmal hinüber. `master.transitions` zeigt einen Übergang, nicht sieben. Beide Wächter wurden geprüft, indem der Fehler wieder eingebaut wurde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Zwei Stimmen gleichzeitig sind der hörbarste Mixfehler überhaupt, und ohne Trennung lässt er sich nur vermeiden, indem man gar nicht überlagert. Jetzt geht `set deck1.stem3_level 0`: Stimme weg, Instrumental steht. Gemessen am laufenden Programm, zwei Mitschnitte desselben Stücks: Energie bei 1500 Hz von 9,8e-5 auf 4,9e-9, Gesamtpegel nur von 0,061 auf 0,039. Die Stimme ist um den Faktor 20 000 weg, Drums und Bass stehen. Die Spuren liegen neben der Datei, in einem Ordner gleichen Namens mit der Endung `.stems` — kein neues Dateiformat, keine neue Abhängigkeit, und genau die Form, in der die gängigen Trennwerkzeuge ihr Ergebnis ablegen. Das Hersteller-Stem-Format wäre die Alternative gewesen: eine MP4-Datei mit fünf AAC-Spuren, die jemandem gehört; ein Ordner mit vier WAV-Dateien gehört niemandem. Getrennt wird hier nicht — das ist Arbeit für ein neuronales Netz, ein eigenes Werkzeug und eine eigene Lizenzfrage. Die schwierige Stelle war die Zeitstreckung. Sie sucht in jedem Hop die Stelle, an der die Wellenform am besten anschließt; vier Spuren getrennt zu strecken hieße vier verschiedene Entscheidungen, und was zusammen klang, klänge verwaschen. Gesucht wird deshalb auf der Summe, angewandt auf jede Spur. Gemischt wird beim Lesen: dasselbe Ergebnis, aber einer statt vier Akkumulatoren. Der Wächter dafür misst es am Ton — mit vollen Pegeln muss dasselbe herauskommen wie beim Strecken der Summe; mit dem Fehler wieder eingebaut weicht es um 0,99 ab. Im Audio-Callback wird nichts angelegt: Die Spurzeiger stehen auf dem Stapel, die Pegel sind Atomics. Ein Track ohne Stems geht durch denselben Weg mit einer einzigen Spur — es gibt nur eine Stelle, die stimmen muss. Und das Umrechnen auf die Geräterate nimmt die Spuren mit. Sie dort fallen zu lassen wäre der stillste denkbare Fehler gewesen: Der Track klänge richtig, nur ließe sich nichts mehr herausnehmen — ausgerechnet auf den Geräten, deren Rate nicht zur Datei passt. Offen bleibt das Streaming von Platte. Vier Spuren kosten das Fünffache eines Tracks, weil die Summe daneben stehen bleibt: rund 500 MB je Deck bei fünf Minuten. Für die zwei Decks, die es gibt, geht das; für vier nicht. Der Abspielpfad ist der einzige Teil mit Echtzeitauflagen, und Platten-I/O gehört dort zuletzt hinein. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Ein einzelner Übergang lief. Der zweite nicht — und das war nie das Ziel. Drei Fehler lagen dazwischen, alle drei erst am laufenden Programm sichtbar und alle drei erst beim *zweiten* Griff. **Der ausgehende Track lief nach dem Griff weiter.** Unhörbar hinter dem geschlossenen Crossfader, aber laufend — und damit wurde der nächste `uebergang` abgewiesen: „es laufen mehrere Decks", denn wer aus- und wer eingeht, kann dann niemand wissen. Jeder Griff endet jetzt damit, dass das ausgehende Deck steht. Das ist die Stelle, an der aus einem Übergang ein Set wird. **Getaktet wurde nach dem ersten Deck mit Beatgrid.** Beim zweiten Griff ist das ausgerechnet das gerade gestoppte, und ein stehendes Deck hält den Plan an: Der Übergang wurde angenommen, stand im Plan und lief nie. Sichtbar wurde er nur daran, dass der Bass des eingehenden Kanals zu blieb — die eine Zeile des Bass-Swaps, die sofort ausgeführt wird. Getaktet wird jetzt überall nach dem ausgehenden Deck, das per Definition läuft, solange der Griff dauert. Nur die Schleife hängt danach am eingehenden, weil der ausgehende gerade in einer Schleife liegt. **Und eine Rampe, die im selben Takt endet, in dem ihr Deck stehenbleibt, bekam ihren letzten Schritt nicht mehr.** Der Regler stand am Ziel, der Auftrag blieb für immer im Plan stehen — sichtbar für jeden zweiten Bediener und nicht mehr wegzubekommen, weil das Deck nicht mehr läuft. Ein Beat Nachlauf, den niemand hört. Nebenbei geht der Hochpass des Filter-Griffs am Ende wieder auf. Ein Kanalzug, der beim nächsten Track noch gefiltert dasteht, ist eine Überraschung, die man lange sucht, weil man den Regler nicht angefasst hat. Am laufenden Programm nachgefahren, drei Griffe hintereinander ohne Handgriff dazwischen außer Laden und `jump_entry`: `transitions` zeigt `blende, bassswap, schleife`, `plan` nach jedem `ok 0 vorgemerkt`, die Decks und die EQ-Stellungen stimmen. Alle drei Wächter wurden geprüft, indem der Fehler wieder eingebaut wurde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Wer von Hand auflegt, cued auf den ersten Schlag. `do deckN.load` und `do master.queue_next` ließen das Deck dagegen auf Sekunde 0 stehen, und der nächste Griff startete es dort — mitten in der Stille vor dem ersten Schlag. Das ist genau der Fehler, den die Mitschrift beim ersten gemessenen Übergang aufgedeckt hat: Deck 2 setzte fünfzehn Beats neben seiner eigenen Eins ein. Behoben war bis jetzt nur das Werkzeug dagegen (`jump_entry`), nicht die Ursache. Die Reihenfolge war dabei der eigentliche Fund. Der erste Anlauf setzte den Sprung im Lade-Arbeiter — und dort hängt am DeckState noch die **alte** Stimme. Die hat ihn verbraucht, auf ihrem eigenen Track, und die neue fing wieder bei null an. Am laufenden Programm gemessen: `beat -1.02` statt `beat -0.03`. Jetzt meldet der Arbeiter den Einstieg nur, und gesprungen wird, nachdem die neue Stimme im Mixer liegt. Der Wächter dafür steht in audio-core, wo der Mechanismus liegt: Ein vorgemerkter Sprung gehört der Stimme, die als Nächstes rendert, und überlebt einen Stimmentausch nicht. Damit fällt der letzte Handgriff aus der Kette: Laden, Griff, Laden, Griff — ohne `jump_entry` dazwischen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Gefunden beim ersten Übergang, der sich selbst auslöste — `when deck1.beats_to_outro < 0 do master.uebergang bassswap`. Der ausgehende Track lief mitten im Swap aus, und danach stand der Crossfader für immer in der Mitte, mit fünf toten Aufträgen im Plan. Ihr Taktgeber-Deck war zu Ende; sein Beat rührt sich nicht wieder, also konnte keiner von ihnen je fällig werden. Ein Bediener sieht dort einen Übergang, der läuft, und hört einen, der steht. Jetzt bricht so ein Auftrag ab und sagt, warum: „plan 9 abgebrochen — deck1 ist durchgelaufen". Der Unterschied zu einem bloß **angehaltenen** Deck ist der ganze Punkt. Dort wartet der Plan absichtlich weiter — wer pausiert, will da weitermachen, wo er aufgehört hat. Beide Wächter wurden in beide Richtungen geprüft: Zählt man auch die Pause als Ende, wird der andere rot. Am laufenden Programm nachgestellt mit einem 20-Sekunden-Track und einem Bass-Swap über 32 Beats: Der Plan räumt sich selbst, und die Meldung steht in `master.events`. Gegen den Fall selbst gibt es den Griff `schleife` — der hält den Ausgehenden so lange, wie der Wechsel dauert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Die Kette aus Liste, Laden und Griff braucht keine Hand mehr dazwischen. Was dabei zu finden war, steht im Fahrplan: vier Fehler, die erst beim zweiten Griff sichtbar wurden, und einer beim ersten selbstausgelösten Übergang. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
N2 aus dem Fahrplan: die Gliederung an schwierigen Fällen. `musik-material <ordner>` baut vier Tracks mit bekannter Gliederung und legt die Wahrheitsdatei daneben — einer ohne Outro, einer ohne Intro, einer mit zwei Breaks, einer mit Tempowechsel. Jeder bricht eine Annahme, die in der Gliederung steckt. Der erste Lauf durch `musik-pruefstand`: **13 von 23 Namen richtig**. Die Grenzen saßen dabei fast alle auf den Frame genau — die Fehler lagen sämtlich in der Benennung, und sie waren nicht verstreut, sondern ein Muster. Viermal wurde ein Drop zu „Teil". Die Ursache steckte in einer Zeile: Ein Drop war, wer über dem **75. Perzentil** des Bassbands lag. Das ist rangbasiert, und darin steckt eine Behauptung über die Musik, die niemand aufgestellt hat — höchstens ein Viertel der Abschnitte kann je ein Drop sein. Ein Stück mit vier Drops bekam einen, weil die Schwelle *zwischen* den Drops lag. Jetzt entscheidet der Pegel als Anteil am lautesten Abschnitt: eine Höhe, kein Rang. Dieselbe Umstellung trennt auch Aufbau und Break sauber, und zwar an dem, was sie musikalisch unterscheidet — ein Aufbau kommt von etwas Leiserem und führt in den Drop, ein Break fällt aus einem Drop heraus. Am Pegel allein sind die beiden nicht zu trennen (0,48 gegen 0,40). Danach: **21 von 23**. Die zwei Reste liegen beim Tempowechsel, und dort steigt schon die Segmentierung aus. Diese Grenze steht jetzt im Modul, statt still zu bleiben: **ein Tempo je Track.** Gerechnet wird auf einem Beatgrid, und ein Beatgrid hat ein Tempo. Springt es mitten im Stück, stimmen die Phrasengrenzen danach nicht mehr — drei von sechs Abschnitten gefunden, alle drei davor. Ein zweites Grid wäre eine eigene Schicht und braucht man erst, wenn solche Dateien im Ordner liegen. Zwei Wächter halten das fest: drei Drops in einem Stück heißen alle drei Drop, und aus einem Schluss auf dem Höhepunkt wird kein Outro gemacht. Beide wurden geprüft, indem die alte Regel wörtlich aus git zurückgeholt wurde — dann fallen beide um. Das ist der sechste Entwurf einer Schwelle in dieser Schicht, der am Material gescheitert ist. Gebautes Material findet solche Fehler; es ersetzt keine echte Musik mit gehörter Wahrheit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGFKXoHmTkrnmQa8nav65p
Der Schreiber holt erst die Befehle ab und leert dann den Ring — ohne Deckel. Wer in dieser Lücke stoppt, neu startet und nachlegt, dessen zweiter Mitschnitt liegt schon im Ring, während der Stop noch im Briefkasten steckt. Der offene erste nimmt ihn mit; der zweite bleibt leer. Einmal in der CI gesehen: 5244 statt 4044 Bytes, also 1000 + 300 Frames in einer Datei. Vierzig Läufe mit zwanzig Runden holten es nicht zurück, die Lücke ist ein paar Maschinenbefehle breit. Jetzt wird der Stand der aufnehmenden Seite abgelesen, *bevor* die Befehle drankommen, und der Ring nur so weit geleert. Was danach hineinkommt, wartet eine Runde — dann ist auch sein Befehl da. Nach einem frischen Start gilt der Stand nicht mehr, also gar nicht leeren. Der Test hält den Schreiber an dieser einen Stelle an. Mit dem Fehler drin meldet er wieder genau 5244 statt 4044 Bytes.
N1 sollte klären, ob das Tempo-Verfahren unter MIN_BPM trägt. Es trägt: zwischen 46 und 58 BPM findet die Autokorrelation die richtige Periode, zwei unabhängige Gegenproben stimmen auf ein halbes BPM überein. Der eigentliche Fund ist ein anderer. Material mit 66 BPM bekommt ein Grid mit 71,51 BPM, eines mit 68 eines mit 69,88 — deutlich und falsch, weil die beste Verschiebung innerhalb des abgeschnittenen Fensters sauber herausragt. Sie sitzt nur am Rand. Ein Grid, das um 8 % danebenliegt, ist schlimmer als keines. Zwei Auswege gebaut und beide zurückgenommen: Das Fenster zu öffnen lädt den Halbtempo-Fehler ein (der Demo-Track mit 124 BPM bekam gar kein Grid mehr, Snare auf zwei und vier reicht dafür). Den Rand zu erkennen geht lokal nicht — echte 71 BPM sitzen bei 0,989 näher am Rand als falsche 66 bei 0,975, und "steigt am Rand noch" trifft 92 und 128 genauso. MIN_BPM bleibt also bei 70, und der Fehler bleibt bestehen. Das steht jetzt an der Konstante, ein Test hält fest, dass 71, 75 und 92 BPM nicht auf die Hälfte kippen, und die Stellen in PLAN und FAHRPLAN, die "findet der Detektor nichts" behaupteten, sagen jetzt, was wirklich passiert.
Die CI fährt dtolnay/rust-toolchain@stable und ist damit auf 1.98 gelaufen; lokal steht 1.94. Der neue Lint chunks_exact_to_as_chunks trifft vierzehn Stellen, keine davon neu — er war vorher nur nicht da. Statt einer Scheibe unbekannter Länge kommt jetzt ein Array fester Größe heraus, und `f[0]`/`f[1]` brauchen keine Bereichsprüfung mehr. Das ist für Code, der in jedem Callback über jeden Frame läuft, ohnehin die ehrlichere Form. Geprüft mit derselben Toolchain wie die CI: fmt, clippy --all-targets -D warnings und test --all sind grün, mit 1.94 ebenfalls.
…odul Wo im Mitschnitt ein Übergang liegt, stand bisher im Kritiker selbst. Für N3 braucht es dieselbe Schätzung ein zweites Mal — gegen die Wahrheit der Anlage, über viele Sets. Zwei Kopien wären zwei Verfahren gewesen, und die Streuung hätte nichts mehr über den Kritiker gesagt. Verschoben, nicht geändert: Wechselkurve, Ähnlichkeit, Schwellen, Zurückverfolgen des Beginns und die Ruhelage. Die sieben Wächter ziehen mit um; einer kommt dazu, der bisher fehlte — eine ruhige Kurve darf keinen Übergang erfinden.
Aus einem gemessenen Wert (3,7 s zu spät) wird eine Verteilung. 24 Sets laufen durch die echte Anlage — echter Mixer, echtes Pult, echtes Repertoire, echter Mitschnitt, echte Mitschrift —, danach bekommt der Kritiker die Datei, so wie er sie sonst bekommt. Median +2,0 s, im Betrag 2,4 s, Spanne -1,4 bis +7,6 s. Der Fehler hängt an der Länge des Übergangs: ein Schnitt wird auf ein Fenster genau gefunden und eher zu früh (-1,1 s), eine Blende über 32 Beats im Mittel 4,9 s zu spät. Die Unschärfe, die der Kritiker selbst angibt, deckt den Fehler in 16 von 20 Fällen. Der Fund: Vier Übergänge blieben unbemerkt, drei davon Blenden über 64 Beats. Der Kritiker vergleicht mit dem Klangbild von vor 16 s — dauert die Blende länger, liegt der Vergleich mitten in ihr. Eine Blende über zwei Phrasen ist für ihn unsichtbar. Zwei Anker waren vorher falsch und gaben beide Zahlen aus, die nach Messwerten aussahen: erst das Einrichten des Crossfaders (Sekunde 0), dann die Bestellung des Griffs statt seiner Ausführung — dazwischen liegt die Wartezeit auf die Phrase. Drei Wächter halten den Anker jetzt fest. Und der Prüfstand hat zuerst sich selbst gemessen: Der erste Durchgang überrannte den Ring des Mitschnitts, 1,2 von 2,1 Millionen Frames verworfen. Jetzt läuft die Schleife am Zügel, und ein Set mit auch nur einem verworfenen Frame wird gemeldet statt gemessen.
Wer den Eingang mitlaufen lässt, hat hinterher eine Datei mit einer Stunde Musik darin. Die Anlage kann damit wenig anfangen: Ein Beatgrid gilt je Track, eine Tonart auch, und die Gliederung sucht Intro und Outro eines Stücks, nicht eines Abends. Geschnitten wird an den Lücken — das ist ein Ablesen, kein Verfahren. Wo keine sind, bleibt der Klangwechsel, und der ist eine Schätzung mit bekannten Grenzen (N3). Findet sich beides nicht, wird nichts geschnitten und das gesagt; ein geratener Schnitt wäre schlechter als keiner. Die Schwelle für Stille ist keine feste Zahl, sondern ein Anteil am lauten Teil dieser Aufnahme. Sonst hätte sie mehr über den Aufnahmepegel gesagt als über die Musik — derselbe Fehler, der in der Gliederung schon sechs Entwürfe gekostet hat. Ein Test hält fest, dass dieselbe Musik leise aufgenommen dieselben Lücken ergibt. Die Ränder werden von der Stille befreit. Ohne das war jedes geschnittene Stück genau eine Sekunde länger als seine Vorlage — die halbe Lücke vorn und hinten —, und eine Sekunde Stille am Anfang ist genau das, wonach die Gliederung ein Intro sucht. Mit dem Beschneiden trifft der Schnitt auf 0,00 s: an drei aneinandergehängten Stücken nachgemessen.
Der Kanalzug stand seit Langem, der Ringpuffer auch — nur hat nie jemand hineingeschrieben. Im Programm stand es wörtlich: "AUX bleibt ohne Zuspieler still." Damit war der AUX-Eingang ein Kanal ohne Eingang. Jetzt öffnet `musik-app --aux-in` ein Aufnahmegerät und schiebt, was hereinkommt, in den Ring. `--aux-gerät` wählt eines über einen Teil des Namens, `--aux-liste` zeigt, welche es gibt. Ein fehlender Eingang bricht nichts ab: Der Kanal bleibt still, alles andere läuft, und gesagt wird es. Zwei Dinge werden nicht geraten: Die Samplerate muss genau die der Engine sein. Der Ring trägt Samples, keine Zeit — 44,1 statt 48 kHz heißt 8 % zu langsam, und nichts meldet es, denn es klingt wie Musik. Umrechnen wäre ein eigener Baustein. Das Sample-Format wird nach Auflösung gewählt, nicht der Reihe nach. Das fiel erst beim Anschließen auf: Das null-Gerät von ALSA bietet i8 zuerst an, also acht Bit für Material, das analysiert werden soll. Wer die Liste abarbeitet, nimmt das und merkt nichts. Dazu `musik-eingang`: öffnet dasselbe Gerät auf demselben Weg und sagt vorher, was hinterher zu spät wäre — ob etwas ankommt, ob Luft unter dem Begrenzer ist, ob es lückenlos ankommt. Der erste Block zählt dabei nicht als Unterlauf; er ist immer leer, und jeder gesunde Eingang bekäme sonst eine Warnung. An echter Hardware ungeprüft. Der Weg ist gegen das null-Gerät von ALSA gelaufen: Auswahl, Rate, Format, Callback, Umrechnung auf Stereo.
Für einen Mitschnitt des Abends ist `do master.record` richtig: Er nimmt die Summe auf, hinter Fader, EQ und Begrenzer, so wie es auf die Anlage geht. Wer Lieder gewinnen will, will genau das nicht — der Begrenzer drückt zusammen, was er einmal zusammengedrückt hat, und die Analyse misst danach eine Dynamik, die niemand gewählt hat. `--aufnehmen <datei>` schreibt deshalb direkt vom Gerät auf die Platte, ohne Mixer dazwischen. Ohne `--sekunden` läuft es, bis Strg-C kommt; fünf Sekunden als stille Vorgabe wären genau die Art Fehler, die einem erst nach dem Abend auffällt. Der Kopf wird jede Sekunde nachgetragen. Eine WAV-Datei trägt ihre Länge vorn, und eine Aufnahme, die bis Strg-C läuft, endet nie ordentlich — ohne das Nachtragen bliebe eine Datei mit falschem Kopf zurück, die manche Programme gar nicht öffnen. Nachgemessen: nach vier Sekunden abgeschossen, lesbar bei 3,98 s. Zwei Tests halten fest, dass der Kopf auch mitten im Lauf zu den geschriebenen Daten passt. Dazu der Hinweis, der beim Anschließen zählt: Ein Gerät lässt sich meist nur von einem Programm zugleich aufnehmen. Entweder musik-app --aux-in oder dieses hier.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Der Aufbau des DJ-Werkzeugs von der Recherche bis zu einer Anwendung, die ein Set fährt: Liste, Laden, Übergang — ohne Handgriff dazwischen, von außen steuerbar, von mehreren Bedienern gleichzeitig. Samt einem Kritiker, der jeden Übergang nachmisst, einer Analyse, die weiß, wo im Stück sie ist, einem Prüfstand, der all das gegen ein menschliches Ohr hält, einem Bogen, der sagt, was das Set vorhat, einem Raum, der ihn verschiebt, und Einzelspuren, mit denen sich die Stimme herausnehmen lässt.
Alle zehn geplanten Phasen angefangen, 515 Tests, clippy sauber.
Ein Set, das sich selbst weiterträgt
Die erste Bedingung legt den nächsten Track auf — auf dem freien Deck, auf dem ersten Downbeat. Die zweite fährt den Übergang, sobald das Outro anfängt. Danach steht das ausgehende Deck, der Plan ist leer, und dieselben zwei Zeilen gelten für den nächsten Track.
Gewählt hat trotzdem ein Mensch oder ein Agent, und zwar zweimal: welcher Track in die Liste kommt — mit Notiz, warum — und welcher Griff gefahren wird. Die Anlage führt aus und begründet, was sie gerade tut; sie entscheidet nicht, was als Nächstes gut wäre.
Die einzelnen Schichten waren je für sich geprüft. Beim ersten Versuch, sie hintereinander laufen zu lassen, kamen fünf Fehler heraus, die kein Test hätte zeigen können — vier davon erst beim zweiten Griff. Sie stehen unten unter „Funde, die erst das laufende Programm zeigte".
Was drin ist
audio-coreaudio-engineanalysislibrarycontrolmusik-appmusik-climcp/Drei Entscheidungen, die die Architektur bestimmt haben
Der Cue-Ausgang erzwingt ein Gerät mit vier Ausgängen. Zwei Geräte heißt zwei Taktgeber heißt Drift. Das ist eine Anschaffungsentscheidung, keine Softwarefrage.
Atomics reichen nicht. Für Position und Tempo schon; für einen Trackwechsel nicht — eine
Boxlässt sich nicht atomar tauschen. Daher eine lock-freie SPSC-Schlange plus Rückkanal für abgelöste Quellen.Ein Beatgrid braucht Anker und Tempo. Ohne Phase hat man BPM, aber kein Grid, und Sync klingt trotz gleicher Zahl falsch.
Fernsteuerung — der Teil, den es sonst nicht gibt
Mixxx hat ein sehr ähnliches Control-System im Inneren, aber prozessintern; nach außen spricht es OSC nur ausgehend. Vier Dinge sind hier bewusst anders:
listliefert zu jedem Control Typ, Bereich, Einheit, Schreibbarkeit und Bedeutung. Die MCP-Werkzeugbeschreibungen werden daraus erzeugt statt doppelt gepflegt, und die Schieberegler der Oberfläche holen sich Bereich und Tooltip aus demselben Katalog.double. Aktionen sind ein eigener Typ mit eigenem Verb.Unix-Socket statt TCP-Port, und das ist eine Sicherheitsentscheidung: Wer hineinschreibt, steuert die Anlage.
Zeit: Übergänge statt Reglerstellungen
Gerechnet wird in Beats, nicht in Sekunden: Ein Plan in Sekunden geht schief, sobald jemand am Tempo dreht.
innimmt statt einer Zahl auchphraseundphrase+n. Ein Übergang beginnt auf der Eins einer Phrase, nicht nach einer runden Zahl Beats.Der Mensch gewinnt. Eine Rampe gibt auf, sobald jemand anders denselben Regler anfasst — geprüft über den Wert selbst, was einen zweiten Agenten genauso trifft wie die Hand an der Oberfläche.
Und ein Auftrag, dessen Grundlage wegfällt, sagt das, statt still zu warten:
Ein bloß angehaltenes Deck hält den Plan dagegen nur an. Wer pausiert, will da weitermachen, wo er aufgehört hat.
Die Form einer Bewegung
linearweichspaetfruehDeck und Form stehen hinten in beliebiger Reihenfolge und werden daran erkannt, was sie sind. Die Wächter sind keine Behauptungen: Jede Form bildet 0 auf 0 und 1 auf 1 ab, keine läuft zwischendurch zurück, und auf halber Strecke stehen sie messbar woanders — am laufenden Regler nachgefahren.
Ein Repertoire statt eines Handgriffs
Fünf Griffe:
blende(32 Beats),bassswap(16),schnitt(0),filter(16),schleife(16).Die Schleife ist der einzige Griff, der dem Ausgehenden Zeit gibt. Ein Track, der in vier Beats zu Ende wäre, hält so noch eine ganze Phrase durch — genau dafür setzt ein Mensch am Ende eines Stücks eine Schleife.
Jeder Griff endet damit, dass das ausgehende Deck steht. Das ist die Stelle, an der aus einem Übergang ein Set wird. Getaktet wird nach dem ausgehenden Deck, das per Definition läuft, solange der Griff dauert. Und keine Bewegung endet im selben Takt, in dem ihr Deck stehenbleibt — sonst bleibt ihr Auftrag für immer im Plan liegen.
Jeder Griff ist eine Handvoll gewöhnlicher Protokollzeilen, die durch denselben Weg laufen wie alles andere — sie stehen im Plan, in der Mitschrift und bei den Ereignissen, ein zweiter Bediener sieht sie kommen, und
cancelnimmt sie zurück. Es gibt nichts, was ein Agent nicht auch selbst hätte tippen können.Die Anlage wählt nicht aus. Welcher Griff passt, hängt daran, ob der ausgehende Track ein Outro hat, der eingehende ein langes Intro, wie groß der Energieunterschied ist — und vor allem daran, was vorher schon dreimal gefahren wurde.
Der Bogen: was das Set vorhat
Die Ist-Energie kommt aus der Art des Abschnitts, nicht aus dem Pegel. Der Pegel der Gliederung ist auf den lautesten Abschnitt desselben Tracks bezogen: Der Drop eines leisen Stücks steht dort genauso bei 0,99 wie der eines lauten. Über Tracks hinweg ist das nicht vergleichbar, und ein Bogen, der solche Zahlen addiert, rechnet mit Äpfeln.
Ohne
arc_startgibt es keinen Ort auf dem Bogen, und dann wird auch keiner behauptet: leer statt null.Der Raum schließt die Schleife
Die vier Signalplätze gab es lange, aber sie waren Deko: Werte gingen hinein, und nichts hat darauf reagiert. Ein Bogen, der von der ersten Minute an feststeht, ist ein Abspielplan.
Am laufenden Programm nachgefahren: Bei einem Ziel von 0,75 steht
peakoben. Ein fallender Raum beugt das Ziel auf 0,50 — und derselbe Track rutscht auf Platz drei.Der Raum verschiebt das Ziel, nicht die Kurve.
master.arcbleibt, was jemand aufgeschrieben hat,arc_curvezeigt es unverändert — am Ende des Abends lässt sich vergleichen, was geplant war und was geschah.Gebeugt wird nach dem Trend, nicht nach dem Wert. Die Höhe eines Signals ist nichts Vergleichbares: Was „0,6 Andrang" bedeutet, weiß nur, wer den Sender geschrieben hat. Eine Änderung ist dagegen eine Aussage über denselben Sender. Höchstens um 0,25, damit ein hängender Sender das Set nicht übernimmt.
Zurückhaltung: ob immer dasselbe gefahren wird
Gezählt wird der Seitenwechsel des Crossfaders, nicht sein Wert — und nur, wenn zwei Decks laufen. Ein Bass-Swap fährt zweimal am Crossfader, eine Blende in achtzig kleinen Schritten, ein Schnitt in einem Sprung; angekommen sind alle drei einmal. Auf den Wert gezählt stünde nach einer einzigen Blende eine Wiederholung von 80 da, und die Zahl, die vor Eintönigkeit warnen soll, wäre selbst der Grund, sie zu ignorieren.
Einzelspuren: die Stimme wegnehmen
Gemessen am laufenden Programm, zwei Mitschnitte desselben Stücks:
Die Stimme ist um den Faktor 20 000 weg, Drums und Bass stehen.
Die Spuren liegen neben der Datei, in einem Ordner gleichen Namens mit der Endung
.stems— kein neues Dateiformat, keine neue Abhängigkeit, und genau die Form, in der die gängigen Trennwerkzeuge ihr Ergebnis ablegen. Getrennt wird hier nicht — das ist Arbeit für ein neuronales Netz, ein eigenes Werkzeug und eine eigene Lizenzfrage.Die schwierige Stelle war die Zeitstreckung. Sie sucht in jedem Hop die Stelle, an der die Wellenform am besten anschließt; vier Spuren getrennt zu strecken hieße vier verschiedene Entscheidungen, und was zusammen klang, klänge verwaschen. Gesucht wird deshalb auf der Summe, angewandt auf jede Spur. Im Audio-Callback wird nichts angelegt.
Der Prüfstand — und was er gefunden hat
Über der Anlage liegen drei Schichten Messwerkzeug: Kritiker, Mitschrift, Gliederung. Und jede Schwelle darin war an Material geeicht, das aus eigener Hand stammt. Ein Messwerkzeug, dem niemand widerspricht, ist ein Orakel.
musik-material /tmp/schwierig # vier Fälle, jeder bricht eine Annahme musik-pruefstand /tmp/schwierig/wahrheit.txtmusik-materialbaut Tracks mit bekannter Gliederung — einen ohne Outro, einen ohne Intro, einen mit zwei Breaks, einen mit Tempowechsel — und legt die Wahrheitsdatei daneben. Der erste Lauf: 13 von 23 Namen richtig. Die Grenzen saßen dabei fast alle auf den Frame genau; die Fehler lagen sämtlich in der Benennung, und sie waren nicht verstreut, sondern ein Muster. Viermal wurde ein Drop zu „Teil".Die Ursache steckte in einer Zeile: Ein Drop war, wer über dem 75. Perzentil des Bassbands lag. Das ist rangbasiert, und darin steckt eine Behauptung über die Musik, die niemand aufgestellt hat — höchstens ein Viertel der Abschnitte kann je ein Drop sein. Jetzt entscheidet der Pegel als Anteil am lautesten Abschnitt: eine Höhe, kein Rang. Danach 21 von 23.
Dieselbe Umstellung trennt Aufbau und Break an dem, was sie musikalisch unterscheidet: Ein Aufbau kommt von etwas Leiserem und führt in den Drop, ein Break fällt aus einem Drop heraus. Am Pegel allein sind die beiden nicht zu trennen (0,48 gegen 0,40).
Gegen echte Musik mit gehörter Wahrheit steht der Prüfstand weiter aus — dafür gibt es
docs/wahrheit-vorlage.txt; es fehlen die Tracks und ein Ohr.Der Kritiker gegen die Anlage
Der Kritiker hört einen Mitschnitt und sagt, wann der Übergang begann. Die Mitschrift weiß es. Einmal gemessen ergab das 3,7 Sekunden zu spät — ein Wert, aus dem sich nichts folgern lässt.
musik-streuung /tmp/streuung # 24 Sets durch die echte AnlageEchter Mixer, echtes Pult, echtes Repertoire, echter Mitschnitt, echte Mitschrift; danach bekommt der Kritiker die Datei, so wie er sie sonst bekommt. Median +2,0 s, im Betrag 2,4 s, Spanne −1,4 bis +7,6 s. Der Einzelwert von oben stammt von einer 32-Beat-Blende und sitzt genau da, wo die Verteilung ihn erwarten lässt.
Ein harter Schnitt wird also auf ein Fenster genau gefunden, und eher zu früh als zu spät. Je länger die Blende, desto später die Schätzung — ungefähr ein Viertel der Blendendauer. Das ist kein Zufallsfehler, sondern der Preis dafür, dass der Anfang einer Blende per Konstruktion unhörbar ist. Die angegebene Unschärfe trägt: In 16 von 20 Fällen deckt sie den tatsächlichen Fehler.
Der Fund: eine lange Blende wird gar nicht bemerkt. Vier von 24 Übergängen hat der Kritiker übersehen, drei davon Blenden über 64 Beats. Der Grund steht in seiner eigenen Konstante — er vergleicht mit dem Klangbild von vor 16 Sekunden. Dauert die Blende länger, liegt der Vergleich selbst schon mitten in ihr, und der Unterschied reißt die Schwelle nie. Bei 128 BPM sind 64 Beats 30 Sekunden: eine Blende über zwei Phrasen ist für ihn unsichtbar. Mit einer größeren Zahl ist das nicht behoben — ein weiterer Rückblick verwechselt zwei aufeinanderfolgende Tracks.
Wenn einer dem anderen dazwischenkommt
Der Zweck des Projekts ist ein Team von Agenten. Gelaufen war lange ein Bediener in mehreren Rollen — und die Frage „was sieht ein zweiter?" hat vor dem ersten Test einen Fehler gezeigt: Der Taktgeber-Thread verwarf den Rückgabewert des Taktgebers. Damit ging jede Meldung des Plans verloren — fertig, abgebrochen und vor allem abgelöst. Ein Agent, dem ein anderer den Fader wegnahm, erfuhr es nie.
Ein Ring mit laufender Nummer, kein einzelner Wert: Der Taktgeber läuft alle 5 ms, der Server vergleicht alle 50. Zwischen zwei Blicken passen zehn Ereignisse, und neun wären weg — ausgerechnet dann, wenn viel gleichzeitig geschieht.
Die Mitschrift
Neben jedem Mitschnitt entsteht eine zweite Datei, gleicher Name, Endung
.mitschrift:Die Schätzung aus dem Klang lag 3,7 Sekunden zu spät. Und ein Befund, den vorher niemand hätte finden können: Deck 2 setzte auf seinem Beat −1 ein, fünfzehn Beats neben seiner eigenen Eins. Ein frisch geladenes Deck steht inzwischen von selbst auf dem ersten Downbeat — die Ursache, nicht nur das Werkzeug dagegen.
Wo im Track das Deck steht
Im Steuerraum stehen
section,section_beats_left,beats_to_outro,intro_beats,entryunddo deckN.jump_entry.Der Anschluss für Agenten
Sechzehn Werkzeuge statt zweihundert.
musik_setundmusik_doerreichen alles;musik_status,musik_searchundmusik_nextsparen die häufigsten Wege;musik_ramp(mit Form),musik_uebergang,musik_schedule,musik_whenundmusik_cancelreichen den Zeitplan durch,musik_queue/_add/_nextdie Liste,musik_signalden Raum.Geprüft über einen echten MCP-Client gegen eine laufende Anwendung —
tools/listundtools/call, nicht an der Schnittstelle vorbei.Funde, die erst das laufende Programm zeigte
Fünf davon kamen beim Versuch, die Schichten hintereinander laufen zu lassen — vier erst beim zweiten Griff:
uebergangabgewiesen: „es laufen mehrere Decks".beat -1.02gegenbeat -0.03).Und davor:
StopundStartim selben Häppchen des Schreiber-Threads, öffnete derStartdie neue Datei und derStopschrieb den alten Mitschnitt hinein. Gefunden von der CI, an einem Test, der zwischen den Aufnahmen wartete und den Fehler damit versteckte.Stopnoch im Briefkasten steckt. Wieder von der CI gesehen — 5244 statt 4044 Bytes, also 1000 + 300 Frames in einer Datei — und danach nie wieder: Vierzig Läufe mit zwanzig Runden blieben grün. Zurückgeholt hat ihn erst ein Test, der den Schreiber an genau dieser Stelle anhält; er meldet mit dem Fehler drin wieder exakt 5244 Bytes.setmeldete die Anlage einen Übergang, den niemand gefahren hatte.set master.room ""war über MCP nicht erreichbar — dort verlangtmusik_setmindestens ein Zeichen. Ein Weg, den nur der Socket kennt, ist für ein Team von Agenten keiner.--aerreichte die Gliederung nie das Deck, undlist deck1.beats_to_outroschriebf64::MINmit dreihundert Stellen aus.Funde, die Tests und Prüfstand aufgedeckt haben
master.arcnimmt Text an, aber nicht jeden Text. Der Wächter über alle schreibbaren Controls schrieb „Probe" hinein und bekam zu Recht einen Fehler.beats_leftwar durch das Tempo geteilt — schnelleres Abspielen lässt weniger Zeit übrig, nicht weniger Beats.Bekannt offen
musik-material,musik-pruefstand,wahrheit-vorlage.txt); es fehlen die Tracks.collection.nmlgelaufen.Der vollständige Plan steht in
docs/FAHRPLAN.md.Lizenz
Alles im Repo ist MIT-verträglich, auch wenn das Werkzeug vorerst nicht weitergegeben wird. Grund ist der mögliche Anschluss an VibeMind (MIT, zur Veröffentlichung vorgesehen) — jede GPL-Abhängigkeit wäre eine Tür, die dahinter zufällt. Deshalb sind WSOLA, Tempo-, Tonart- und Strukturerkennung selbst gebaut, und deshalb wird Mixxx als Referenz gelesen, aber nicht eingebettet (
docs/MIXXX.md).Dokumentation
docs/PLAN.mdist der vollständige Bauplan,docs/DJ.mdder Plan für einen AI-DJ, der nicht herzlos klingt,docs/FAHRPLAN.mdalles, was darüber hinaus aussteht. Daneben:STEUERUNG.md,mcp/README.md,MIXXX.md,VIBEMIND.md,BAUSTEINE.md,APIS.md,AGENTEN.md,TRAKTOR-REFERENZ.md.