Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
141 commits
Select commit Hold shift + click to select a range
ef30766
Serve a practice room from a real Ninjam server in process.
chalkwalk Aug 12, 2026
2cf508a
Bring a band to the practice room, and make it easy to send home.
chalkwalk Aug 12, 2026
0dec275
Give the band a rhythm generator and something to play over.
chalkwalk Aug 12, 2026
d757ef4
Give the band three voices, and fix a socket race it uncovered.
chalkwalk Aug 12, 2026
559cca6
Stop the bass sounding like a second kick drum.
chalkwalk Aug 12, 2026
208f617
Add a lead voice, lift the bass, and fix two bugs the fourth bot expo…
chalkwalk Aug 12, 2026
b46bde2
Port the note-strength axis, and let the bass play more than the kick.
chalkwalk Aug 12, 2026
4a71e30
Choose bass pulse counts that span the interval instead of repeating …
chalkwalk Aug 12, 2026
06c79c0
Make the kick heard, and glue the kit together.
chalkwalk Aug 12, 2026
079d4f1
Read the chords players write, and let a bar hold two of them.
chalkwalk Aug 12, 2026
2f2a499
Let the keys player choose an inversion, and close the loop.
chalkwalk Aug 12, 2026
cd8215c
Ask a chord chart what key it is in.
chalkwalk Aug 12, 2026
c243fd6
Read and write chords in degrees as well as in letters.
chalkwalk Aug 12, 2026
81d530c
Show the chords along the phase bar, and offer the key they imply.
chalkwalk Aug 12, 2026
ac23eec
Write down what the room can say about its music.
chalkwalk Aug 12, 2026
c08f933
Build the instrument before the thing it measures.
chalkwalk Aug 12, 2026
907f2cb
Write down what a bot would say, and what it never would.
chalkwalk Aug 12, 2026
0cc8e60
Decide two questions about talking bots, and stop the wall.
chalkwalk Aug 12, 2026
63d3072
Write the phrases first, and give the tutor one thing to listen for.
chalkwalk Aug 12, 2026
75427a9
Decide who a message is for, and write down how to stop meandering.
chalkwalk Aug 12, 2026
7486efe
Give the band real DSP to be built from.
chalkwalk Aug 12, 2026
c42254f
Make the kit a kit: a membrane, a snare with wires, and metal.
chalkwalk Aug 13, 2026
5b49ff6
Put the kit in a room, and give it two ears to be heard with.
chalkwalk Aug 13, 2026
2dfa608
Balance the band, so a rebuilt kit can actually be heard.
chalkwalk Aug 13, 2026
1206ee3
Measure loudness properly, and check the balance in the right unit.
chalkwalk Aug 13, 2026
1fee9af
Let the lab measure files it did not render.
chalkwalk Aug 13, 2026
330b9a3
Give the bass a string to be plucked, and a hand to pluck it with.
chalkwalk Aug 13, 2026
7ba22ff
Give the keyboard player a polysynth to play.
chalkwalk Aug 13, 2026
ba41548
Tune the band by ear, and widen what was too polite to hear.
chalkwalk Aug 13, 2026
3f0cda7
Let the soloist bring one of three instruments, and be asked which.
chalkwalk Aug 13, 2026
44854ee
Make the Rhodes a Rhodes, the guitar plucked, and the interval contin…
chalkwalk Aug 13, 2026
d8aaaa7
Put a control surface on the band, and make every knob data.
chalkwalk Aug 13, 2026
6f7d4a0
Let the bots be spoken to in the room, and give them names worth saying.
chalkwalk Aug 13, 2026
1f95804
Tidy the details of arriving, leaving, and being called something.
chalkwalk Aug 13, 2026
ddf8bdb
Name the band: Mirn, Delvo, Pundo, Quado, and a tutor called Tutor.
chalkwalk Aug 13, 2026
8b30b0c
Record the sampled-instruments question, and what the licence actuall…
chalkwalk Aug 13, 2026
8f55916
Re-check the soundfont question, and correct an overstatement.
chalkwalk Aug 13, 2026
6161754
Survey the other soundfont players, and find one real alternative.
chalkwalk Aug 13, 2026
17e0fad
Sweep every corner of every character, not the middle of each.
chalkwalk Aug 13, 2026
3c31eef
Fix the race that let a bot outlive its owner.
chalkwalk Aug 13, 2026
17df129
Plan the repository split, and find a fourth layer in it.
chalkwalk Aug 13, 2026
51d257c
Capture the shared-library idea, and let the research redirect it.
chalkwalk Aug 13, 2026
95bfe81
Turn the shared-library idea into a plan with a rule in it.
chalkwalk Aug 13, 2026
e503b14
Read the other repositories, and correct two guesses.
chalkwalk Aug 13, 2026
6d58e2a
Compare the two melody generators, and find they diverged usefully.
chalkwalk Aug 13, 2026
03b1b12
Capture Lockstep's note-strength model, and unify it with ours.
chalkwalk Aug 13, 2026
f93b9b4
Name the bots, and stop them being unreachable by private message.
chalkwalk Aug 13, 2026
2b19814
Grow the name pool, and put the constraints where they belong.
chalkwalk Aug 13, 2026
c89d3a0
Build the addressing engine, and let the corpus specify it.
chalkwalk Aug 13, 2026
27b2de2
Wire the addressing engine into the bots.
chalkwalk Aug 13, 2026
e676977
Record the measured SF3 sweep, and let it move the bundling position.
chalkwalk Aug 13, 2026
912e3e7
Trim the soundfont, and find the obvious guess about it is wrong.
chalkwalk Aug 13, 2026
ad4fb6d
Measure the drum kits, and find they are nearly free.
chalkwalk Aug 13, 2026
cb64c6c
Settle the soundfont sets, and find the obvious cut saves the least.
chalkwalk Aug 13, 2026
0c5832b
Let the band introduce itself, once.
chalkwalk Aug 13, 2026
452b8dc
Replace the arrival tiebreak with the question a bot can actually ans…
chalkwalk Aug 13, 2026
0c3427a
Teach the bots to read a sentence, and measure how badly.
chalkwalk Aug 14, 2026
cab381c
Refuse to "repair" a word that is ordinary English.
chalkwalk Aug 14, 2026
3eae528
Teach the bots to read a sentence, and measure it honestly.
chalkwalk Aug 14, 2026
22184d0
Learn what the server will actually accept, by asking it.
chalkwalk Aug 14, 2026
1067f91
Give the key a form a bot can say out loud.
chalkwalk Aug 14, 2026
be0d17e
Let somebody actually meet the band.
chalkwalk Aug 14, 2026
0d1a4c6
Let the band hear you.
chalkwalk Aug 14, 2026
8d52c38
Stop the band lab writing past the end of its buffers.
chalkwalk Aug 14, 2026
555af00
Let a range say where its middle sounds.
chalkwalk Aug 14, 2026
d9b43d9
Give the fader room to move outside the range.
chalkwalk Aug 15, 2026
3c7814b
Write down FluidLite's SF3 loop bug before we hit it.
chalkwalk Aug 15, 2026
8cb6ba7
Record the first outside use, and untangle a duplicated roadmap.
chalkwalk Aug 15, 2026
e1e22ae
Give the bots' answers a home that can be tested without a room.
chalkwalk Aug 16, 2026
806eac2
Answer what key the room is in, and say who decided it.
chalkwalk Aug 16, 2026
97aa9dd
Report the chords and the tempo, and keep the chords from announcing …
chalkwalk Aug 16, 2026
df4e68b
Decline a key change, and read which key was asked for without guessing.
chalkwalk Aug 16, 2026
c88a3a1
Decline the tempo and the chart, and read a tempo number without gues…
chalkwalk Aug 16, 2026
af6e1b3
Stop an ordinary question sending the band home, and retire "part".
chalkwalk Aug 16, 2026
bdcd00f
Treat "name: something" as somebody else's, even when the name is unk…
chalkwalk Aug 16, 2026
4c90f7d
Let a player reach the bots' answers.
chalkwalk Aug 16, 2026
3206398
Settle what a key change does to a chart, before writing any of it.
chalkwalk Aug 16, 2026
e416d45
Give a chart a form that knows what it is relative to.
chalkwalk Aug 16, 2026
6626f5c
Spell each chord by where it sits, not the whole chart by one flag.
chalkwalk Aug 17, 2026
0105027
Let a chart travel with the key, and let degrees reach the band.
chalkwalk Aug 17, 2026
e3cfb47
Let a bot be told to stop talking, and to start again.
chalkwalk Aug 17, 2026
2438ee5
Let a room ask for the chords its key implies.
chalkwalk Aug 17, 2026
f6fd56f
Ship the docs with the code: 6.4 is built, and the bots answer.
chalkwalk Aug 17, 2026
4a2b09c
Let the bots speak in the first person, and ask before guessing.
chalkwalk Aug 17, 2026
c7a62d9
Design how the band stops playing without leaving.
chalkwalk Aug 17, 2026
ddb894b
Put the ending's resolution on the downbeat, where it belongs.
chalkwalk Aug 17, 2026
5a2ebda
Give the ending a wrap-up interval, so it is not a dropout.
chalkwalk Aug 17, 2026
dadcc30
Settle the ending's harmony, and make the wrap-up a taper.
chalkwalk Aug 18, 2026
b32ac64
Take "stop" away from leaving and give it to stopping.
chalkwalk Aug 18, 2026
86c80a7
Give a bot the four states it needs to stop without leaving.
chalkwalk Aug 18, 2026
6eb1712
Make an ending sound like one.
chalkwalk Aug 18, 2026
a0bb634
Let one bot speak for the band, and stop "again" meaning reroll.
chalkwalk Aug 18, 2026
05a2f39
Decide what a chat line does to the key and chart in one place.
chalkwalk Aug 18, 2026
98606f2
Plan how the band plays a song rather than a series of intervals.
chalkwalk Aug 18, 2026
ac26632
Let the band arrive silent, and say how to start it.
chalkwalk Aug 18, 2026
9c3d0a9
Stop a dropped connection from destroying the band.
chalkwalk Aug 18, 2026
cd66ef1
State the contracts the shared libraries must preserve, and fix a name.
chalkwalk Aug 18, 2026
19db0dd
Supersede the repository-splitting sections with the ecosystem plan.
chalkwalk Aug 18, 2026
413db34
Take Euclidean from the shared library instead of carrying a copy.
chalkwalk Aug 18, 2026
4d418cc
Bump chalkwalk-music: guard the warning flags for MSVC.
chalkwalk Aug 18, 2026
7bd41ec
Re-pin chalkwalk-music after an authorship rewrite upstream.
chalkwalk Aug 19, 2026
b9dee3c
Note that the lead's clash rule ignores register.
chalkwalk Aug 19, 2026
1b97e68
Put both lead models side by side, so the swap can be decided by ear.
chalkwalk Aug 19, 2026
0980a54
Count what the line does, so "it leaps oddly" becomes a number.
chalkwalk Aug 19, 2026
201f993
Give the melody a memory, and settle the lead model.
chalkwalk Aug 19, 2026
5a86dc4
Write down the two melodic terms that were held back.
chalkwalk Aug 19, 2026
fd3a794
Re-pin chalkwalk-music to a commit whose CI is green.
chalkwalk Aug 19, 2026
0997b96
Give the melody a sense of direction, and stop it standing still.
chalkwalk Aug 19, 2026
72c7060
Let the line hold a common tone, and drop the direction term.
chalkwalk Aug 19, 2026
bb5bd74
Give the lead's notes a length, not just an end.
chalkwalk Aug 19, 2026
6ef2f52
Let the band play shorter or longer, without losing its phrasing.
chalkwalk Aug 19, 2026
ed58fbf
Move noteTier into the tests, where it is now a second opinion.
chalkwalk Aug 19, 2026
bdffe02
Adopt chalkwalk-dsp; the divergence this file documented is closed.
chalkwalk Aug 19, 2026
7ee2c16
One JUCE checkout for the ecosystem, opted into rather than imposed.
chalkwalk Aug 20, 2026
faee56e
Adopt chalkwalk-ninjam for the five files whose API did not move.
chalkwalk Aug 20, 2026
abb8fe8
Adopt chalkwalk-ninjam for the protocol itself.
chalkwalk Aug 20, 2026
6966903
Adopt libebur128; stop maintaining a standard.
chalkwalk Aug 20, 2026
4d65b5a
Record what landed, and mark the active focus as stale rather than re…
chalkwalk Aug 20, 2026
7551452
Merge origin/main: the keyboard shortcuts work, which the split never…
chalkwalk Aug 20, 2026
57d6f18
Stop tracking a Finder file, and ignore its whole family.
chalkwalk Aug 20, 2026
87b7745
Stop pointing a public repository at a private document.
chalkwalk Aug 20, 2026
0889555
Keep this roadmap to this project, and drop 449 lines of ecosystem an…
chalkwalk Aug 20, 2026
6fc0f43
Make the music-theory layer JUCE-free.
chalkwalk Aug 21, 2026
d9ba470
Say where each third of MusicalKey actually belongs.
chalkwalk Aug 21, 2026
5e1aef2
Stage the bots for extraction, inside this repository first.
chalkwalk Aug 21, 2026
c129c51
Take the key and the chords from chalkwalk-music.
chalkwalk Aug 21, 2026
b484a45
Let a shared library be built from a working checkout.
chalkwalk Aug 21, 2026
5d27fc3
Take the room conventions from chalkwalk-ninjam, and empty the boundary.
chalkwalk Aug 21, 2026
0343a80
Say that the boundary is clean, since it is.
chalkwalk Aug 21, 2026
7e33584
Put an interface between the bots and the client.
chalkwalk Aug 21, 2026
0c401ad
Take JUCE out of the bots.
chalkwalk Aug 21, 2026
29af0d5
Say what the boundary check covers now, and what holds PracticeBot.
chalkwalk Aug 21, 2026
cd9344f
Move the bot and the interval loop into jambot.
chalkwalk Aug 21, 2026
d3c549c
Redraw the layout map: the bot and the loop are in jambot now.
chalkwalk Aug 21, 2026
4e4e34c
Send the instruments where the other projects can reach them.
chalkwalk Aug 21, 2026
b4e8f50
Guard the half of the extraction unit nobody was checking.
chalkwalk Aug 21, 2026
e89b380
Take the preprocessor fix from music and dsp.
chalkwalk Aug 21, 2026
37bf038
Compile the whole tree as C++20.
chalkwalk Aug 22, 2026
cd97a95
Follow the libraries back to their C++17 floor.
chalkwalk Aug 22, 2026
07a1746
Use the bots rather than keeping a second copy of them.
chalkwalk Aug 22, 2026
61735d8
Take the arbitration fix, and run clang-format over the branch.
chalkwalk Aug 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,24 @@ Testing/
.claude/
.gemini/

# Operating-system detritus.
#
# .DS_Store is Finder's per-folder metadata -- icon positions, window size,
# view mode -- written into any directory macOS opens. It is machine-local by
# definition and means nothing on anybody else's disk. One reached the tree in
# 3a1193b; the rest of these are its siblings, listed now rather than after
# each one has had its own turn.
.DS_Store
.AppleDouble
.LSOverride
._*
.Spotlight-V100
.Trashes
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/

# Editor and Python detritus
*~
.vimsupport/
Expand Down
12 changes: 12 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,15 @@
[submodule "modules/vorbis"]
path = modules/vorbis
url = https://github.com/xiph/vorbis.git
[submodule "libs/music"]
path = libs/music
url = https://github.com/chalkwalk/chalkwalk-music.git
[submodule "libs/dsp"]
path = libs/dsp
url = https://github.com/chalkwalk/chalkwalk-dsp.git
[submodule "libs/ninjam"]
path = libs/ninjam
url = https://github.com/chalkwalk/chalkwalk-ninjam.git
[submodule "libs/jambot"]
path = libs/jambot
url = https://github.com/chalkwalk/chalkwalk-jambot.git
97 changes: 91 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ Authoritative docs (read these before designing anything new):
- **`docs/PARITY.md`** -- what has been verified against the reference client,
with the measured numbers.
- **`docs/ACCESSIBILITY.md`** -- the accessibility story, honestly.
- **`libs/jambot/docs/BOT-CHAT.md`** -- what the practice room's bots would say
and what they would never say. It lives with the bots now; this repository
hosts them and does not design them.

- **`test/README.md`** -- how to run every test layer.

Ordering for any new work: **PRINCIPLES -> DESIGN -> ROADMAP**. If a proposal
Expand Down Expand Up @@ -58,7 +62,33 @@ changes. As of 2026-08-10:
## Layout map

```
CMakeLists.txt # root: JUCE patching, submodules, src/, test/
CMakeLists.txt # root: JUCE patching, submodules, libs/, src/, test/
libs/music/ # SUBMODULE: chalkwalk-music (MIT, JUCE-free).
# github.com/chalkwalk/chalkwalk-music. Euclidean
# lives there now, not in src/. Builds and tests
# standalone; its Catch2 suite runs in our ctest.
libs/dsp/ # SUBMODULE: chalkwalk-dsp (MIT, JUCE-free). Two
# targets: `chalkwalk::dsp` is header-only
# primitives (Svf, PolyBlep, SoftClip, Hermite,
# Denormal) and the plugin links it;
# `chalkwalk::dsp::measure` is the instruments --
# what `AudioMeasure` used to be -- and carries
# libebur128, so only test and tool targets link
# it.
libs/jambot/ # SUBMODULE: chalkwalk-jambot (MIT, JUCE-free). The
# BAND, and the chat they answer. Was src/jambot/
# until it earned its own repository; what stays
# here is the hosting a practice room needs and a
# command-line bot does not. Its suite runs in our
# ctest, so we verify the bots rather than assume
# them. Corpora and BotDictionary.h live there
# now, and so do scripts/make_wordlist.py and
# lexicon_gaps.py.
libs/ninjam/ # SUBMODULE: chalkwalk-ninjam (MIT, JUCE-free). The
# wire protocol, and the room conventions in
# RoomConventions.h. Vendors its own ogg/vorbis,
# guarded, so whichever project adds them first
# wins.
patches/*.patch # applied to the JUCE submodule at configure time
assets/fonts/ # Inter (OFL-1.1), embedded as binary data
src/
Expand All @@ -78,12 +108,31 @@ src/
Shortcuts.h # Ctrl+Alt shortcut mapping; matches key code, not text
AudioDeviceStartup.h # 4-state standalone device-open policy, with a budget
ChannelMix.h # mono/pan/gain: one home for three rules that drifted
MusicalKey.h # key and mode: parse, display, scale notes
MusicalKey.h # three inline functions composing the envelope
# (chalkwalk::ninjam::conventions) with the key
# (chalkwalk::music::Notation), under the name
# every call site here already uses
Harmony.h # one line: an alias to chalkwalk::music::Harmony
ClipsortLog.{h,cpp} # session archive manifest: read and write
StemRender.h # one clip into one interval, resampled and aligned
GainUtils.h # dB<->linear, fader and meter scales, formatting
IntervalProbe.h # shared test signal: plugin Test Tone and the tests
AudioMeasure.h # one line: an alias to chalkwalk::dsp::measure.
# The instruments moved -- `peak` and `rms` had
# three copies across the ecosystem and
# `fundamentalHz` two, and an uncalibrated
# detector is how measurement error passes for a
# bug. libebur128 went with them.
ChatFormat.{h,cpp} # chat rendering: vote lines, chord progressions
RoomHarmony.h # WHICH of the two a chat line is, and nothing else.
# What each MEANS is Harmony::Session in
# chalkwalk-music; how a key travels is
# chalkwalk::ninjam::conventions
# --- the practice room's HOSTING, which stays here ---
PracticeRoom.{h,cpp} # the room: seeds, band settings, the bots in it
PracticeServer.{h,cpp} # a Ninjam server on loopback, so a room needs none
NinjamBotClient.h # that interface over Antiphon's client. The whole of
# what ties the band to this plugin's transport
# --- UI ---
LocalChannelStrip.{h,cpp} # 90px vertical strip per local input channel
RemoteUserStrip.{h,cpp} # card per remote player, channels arranged horizontally
Expand All @@ -106,9 +155,13 @@ test/
fixtures/testserver.cfg # config for the local ninjamsrv
tools/
StemsMain.cpp # antiphon-stems: session archive -> WAV stems
PracticeRoomMain.cpp # antiphon-practice: hosts a practice room; join it
# with the standalone. The only way to meet the
# band today -- nothing in src/ starts a room.
scripts/
testserver.sh # fetches, builds and runs a local ninjamsrv out of tree
analyze_archive.py # measures a server session archive
trim_soundfont.py # cuts an SF2/SF3 down to the presets we would use
docs/references/ # what was read to write this, and at which revision
modules/ # ogg, vorbis, clap-juce-extensions submodules
```
Expand All @@ -119,6 +172,21 @@ The single source of truth for how this repo is built and tested.

First-time clone: `git submodule update --init --recursive`

**Testing a change to a shared library without pushing it.** Point at a working
checkout instead of the submodule; the library's own suite and Antiphon's both
run against it:

```bash
cmake -B build -DCHALKWALK_MUSIC_DIR=$HOME/Programming/chalkwalk-music
```

`CHALKWALK_DSP_DIR`, `CHALKWALK_NINJAM_DIR` and `CHALKWALK_JAMBOT_DIR` likewise, as cache variables or
environment variables. Configure prints `OVERRIDE` when one is in use, because
**the submodule SHA no longer describes what you built** -- so CI must not use
them, and neither should anything meant to be attributable, `docs/PARITY.md`
above all. Iterate with an override; bump the submodule and re-verify before
calling anything done. Same shape as Anvil's `CHALKWALK_PHYSICAL_DIR`.

```bash
# Configure (once, or after CMakeLists changes). No generator flag -- use
# whatever CMake picks.
Expand All @@ -133,8 +201,23 @@ ctest --test-dir build --output-on-failure
# count, and the report goes to stdout. Runs headless, no display needed --
# but NOT in CI, where it is excluded on every platform. See ROADMAP.md.
./build/test/AntiphonAudit_artefacts/AntiphonAudit
# Host a practice room and join it with the standalone on the port it prints.
# The band is not reachable from the plugin yet; this is how you hear it.
./build/tools/AntiphonPractice_artefacts/AntiphonPractice --key "D minor"
# Offline: turn a session archive into WAV stems.
./build/tools/AntiphonStems_artefacts/AntiphonStems <session-dir> -o stems/
# Tuning the band's synthesis: render one voice and measure it. The numbers it
# prints come from chalkwalk::dsp::measure, which is what the unit tests assert
# against, so tuning by ear and setting a threshold use one instrument.
./build/tools/AntiphonVoiceLab_artefacts/AntiphonVoiceLab kick --seconds 0.6
./build/tools/AntiphonVoiceLab_artefacts/AntiphonVoiceLab band --seed 12345
# Comparing two renders for timbre rather than for level: --lufs normalises to
# an integrated loudness. It warns when a target would clip a sparse voice.
./build/tools/AntiphonVoiceLab_artefacts/AntiphonVoiceLab hat --lufs -27
# Comparing renders from builds you can no longer reproduce: measure the WAVs,
# and write copies matched to one loudness so the A/B is about the sound.
./build/tools/AntiphonVoiceLab_artefacts/AntiphonVoiceLab file a.wav b.wav
./build/tools/AntiphonVoiceLab_artefacts/AntiphonVoiceLab file a.wav --lufs -20 -o a-matched.wav
```

Targets: `Antiphon_Standalone` (easiest for iteration), `Antiphon_VST3`. CLAP is
Expand All @@ -147,9 +230,9 @@ is the only thing that compiles it.
AU's identity in the plugin registry. JUCE's defaults are the placeholder
`'Manu'` and a `string(RANDOM)` plugin code regenerated on every configure, so
dropping them would mint a new Audio Unit per build tree and break every saved
Logic session. `Chlk` is shared with arps-euclidya, which uses plugin code
`ArpE`; a new Chalkwalk plugin needs its own plugin code, not its own
manufacturer code.
Logic session. `Chlk` is the shared Chalkwalk manufacturer code; a plugin needs its own
plugin code, not its own manufacturer code. Allocated codes are tracked in the
ecosystem plan.

Sanitiser builds -- keep them around, they are worth more than gdb here:

Expand Down Expand Up @@ -245,6 +328,7 @@ reading past a buffer. Assume your change has the same failure mode.
| Mixing, routing, playback delay | `test/AudioLoopbackTests.cpp` | Drives the real path end to end. |
| Accessibility naming rules | `test/AccessibilityAuditTests.cpp` | Synthetic node tree; the real UI cannot be compiled into the test target. |
| A new control, or a new UI state | `test/AuditMain.cpp` | The `AntiphonAudit` target links the plugin's own library and audits the **real** editor across five states. Add a state when you add a surface -- an unaudited state is how the connect dialog stayed unchecked for its whole life. |
| What a bot understands or says | **`chalkwalk-jambot`, not here** | The bots left. Corpora, suites and the generator scripts went with them; iterate there with `-DCHALKWALK_JAMBOT_DIR=...` and bump the submodule when done. |
| Server-visible behaviour | `test/RealServerTests.cpp` | Opt-in via `NINJAM_TEST_SERVER`; keep the default suite hermetic. |

### Rules that are easy to get wrong
Expand All @@ -257,7 +341,8 @@ reading past a buffer. Assume your change has the same failure mode.
(`test/TestSignal.h`). Vorbis is lossy and has codec delay; sample-by-sample
comparison against the input will never hold.
- **A new `src/*.cpp` must be added to BOTH `src/CMakeLists.txt` and
`test/CMakeLists.txt`.** The test target deliberately re-lists production
`test/CMakeLists.txt`** -- and to `tools/CMakeLists.txt` if a tool uses it,
which is a third list and the one most often forgotten.** The test target deliberately re-lists production
sources rather than sharing them -- `juce_generate_juce_header` only works on
`juce_add_*` targets, and each target needs its own `JuceHeader.h`. See the
comment at the top of `test/CMakeLists.txt`.
Expand Down
93 changes: 89 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ project(Antiphon VERSION 0.1.0)
# enable_testing() must be called at the top level for ctest to work
enable_testing()

# C++20 -- applies to every target in the tree, including the format-specific
# plugin wrappers (Antiphon_Standalone / _VST3 / _CLAP) that juce_add_plugin
# creates. A per-target PUBLIC cxx_std_20 does NOT reach those wrappers,
# because JUCE compiles their sources independently with its own cxx_std_17
# floor (see JUCEUtils.cmake).
#
# It is a single standard across the build rather than a preference: JUCE has
# inlines gated on __cpp_char8_t, so a tree that compiles some translation
# units at 17 and some at 20 is an ODR hazard, not merely an inconsistent one.
# Same reasoning and same lines as Anvil and the sequencer.
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)

include(cmake/JuceSource.cmake)

# Submodule patches, applied at configure time.
#
# Each entry is "<submodule dir>|<patch file>". Each patch is idempotent:
Expand All @@ -28,7 +44,13 @@ if(GIT_FOUND)
list(GET patch_parts 0 patch_submodule)
list(GET patch_parts 1 patch_relative)
set(patch_file "${CMAKE_CURRENT_SOURCE_DIR}/${patch_relative}")
set(patch_dir "${CMAKE_CURRENT_SOURCE_DIR}/${patch_submodule}")
# JUCE may be this repository's submodule or the shared checkout that
# CHALKWALK_JUCE_DIR names; everything else is always local.
if(patch_submodule STREQUAL "JUCE")
set(patch_dir "${CHALKWALK_JUCE_ROOT}")
else()
set(patch_dir "${CMAKE_CURRENT_SOURCE_DIR}/${patch_submodule}")
endif()

if(NOT EXISTS "${patch_file}")
continue()
Expand Down Expand Up @@ -58,8 +80,7 @@ if(GIT_FOUND)
endforeach()
endif()

# Configure JUCE
add_subdirectory(JUCE)
add_subdirectory("${CHALKWALK_JUCE_ROOT}" "${CMAKE_BINARY_DIR}/juce-build")

# Configure CLAP JUCE extension
add_subdirectory(modules/clap-juce-extensions EXCLUDE_FROM_ALL)
Expand All @@ -70,7 +91,71 @@ set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
add_subdirectory(modules/ogg EXCLUDE_FROM_ALL)
add_subdirectory(modules/vorbis EXCLUDE_FROM_ALL)

# Add the sources subdirectory
# ---------------------------------------------------------------------------
# chalkwalk-music -- shared, JUCE-free music theory.
# Submodule: https://github.com/chalkwalk/chalkwalk-music (MIT).
#
# It builds and tests standalone, with no JUCE and no parent, which is the test
# of the boundary. Its own Catch2 suite is turned on here so this project
# verifies its dependency rather than assuming it.
# ---------------------------------------------------------------------------
include(cmake/ChalkwalkLibrary.cmake)
chalkwalk_add_library(music libs/music)

# ---------------------------------------------------------------------------
# chalkwalk-dsp -- shared, JUCE-free DSP primitives.
# Submodule: https://github.com/chalkwalk/chalkwalk-dsp (MIT).
#
# Same arrangement and the same reasoning as chalkwalk-music above. The filter,
# the polyBLEP oscillators, the soft clipper and the Hermite reader lived here
# and in a sibling project, and the two copies had diverged; the shared
# versions take both halves.
#
# It also owns MEASUREMENT now -- `chalkwalk::dsp::measure`, what `AudioMeasure`
# used to be -- and with it the libebur128 dependency that used to be vendored
# in this repository. That is a second target rather than part of the first, so
# the plugin links the primitives without linking a loudness meter; only the
# test and tool targets ask for `measure`. The move was for the usual reason:
# `peak` and `rms` existed three times over across these repositories and
# `fundamentalHz` twice, and a detector nobody has calibrated is how a
# measurement error gets mistaken for a bug (`PRINCIPLES §5`).
# ---------------------------------------------------------------------------
chalkwalk_add_library(dsp libs/dsp)

# ---------------------------------------------------------------------------
# chalkwalk-ninjam -- the NINJAM wire protocol, JUCE-free.
# Submodule: https://github.com/chalkwalk/chalkwalk-ninjam (MIT).
#
# Added after modules/ogg and modules/vorbis above, and not by accident: this
# library vendors its own copies of both, guarded by `if(NOT TARGET ogg)`, so
# whichever project adds them first wins and the second reuses them. Adding
# them twice is not a version conflict -- it is a duplicate CMake target name,
# which fails the configure outright.
#
# The protocol left this repository under MIT while antiphon stays GPLv3. The
# provenance note in its README is the record of why that is defensible: the
# GPLv2 reference sources were read, never vendored, and never entered any
# published history. See PRINCIPLES.md section 6.
# ---------------------------------------------------------------------------
chalkwalk_add_library(ninjam libs/ninjam)

# ---------------------------------------------------------------------------
# chalkwalk-jambot -- the practice room's band, and the chat they answer.
# Submodule: https://github.com/chalkwalk/chalkwalk-jambot (MIT).
#
# These were `src/jambot/` here until they earned their own repository. What
# stays is the HOSTING a practice room needs and a command-line bot does not:
# the loopback server, the room that puts a server and a band together, and
# `NinjamBotClient`, which is the whole of what ties the band to this plugin's
# transport.
#
# The direction of the dependency is the point. Antiphon uses the bots; the
# bots know nothing about Antiphon, and their suite runs without it -- which is
# what the `jambot-boundary` check was guarding towards and why that check is
# gone. A boundary a build enforces does not need a test to describe it.
# ---------------------------------------------------------------------------
chalkwalk_add_library(jambot libs/jambot)

add_subdirectory(src)

# Offline tools. Kept out of src/ because nothing here is part of the plugin --
Expand Down
Loading