Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
399 lines (327 loc) · 21.1 KB
/
Copy pathMakefile
File metadata and controls
399 lines (327 loc) · 21.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# SPDX-License-Identifier: MIT
# squatch-dsp: the core library, its tests and benchmarks. System clang++, no fetched dependencies.
# clang++ where it exists (this Mac), g++ otherwise (the Pi). CXX=... on the command line wins.
ifeq ($(origin CXX),default)
CXX := $(if $(shell command -v clang++ 2>/dev/null),clang++,g++)
endif
ifeq ($(origin CC),default)
CC := $(if $(shell command -v clang 2>/dev/null),clang,gcc)
endif
BUILD := build
UNAME := $(shell uname -s)
WARN := -Wall -Wextra -Wpedantic -Wshadow -Werror
CORE := -std=c++17 -fno-exceptions -fno-rtti $(WARN) -Iinclude -Iblocks/tuner/include
RELEASE := $(CORE) -O3
SANITIZE := $(CORE) -O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all
TSAN := $(CORE) -O1 -g -fsanitize=thread
HEADERS := $(shell find include -name '*.hpp')
SRC := $(wildcard src/*.cpp)
OBJ := $(SRC:src/%.cpp=$(BUILD)/obj/%.o)
TEST_SRC := $(filter-out tests/test_queue_threads.cpp tests/test_model_swap_threads.cpp,$(wildcard tests/*.cpp))
TEST_DEPS := $(wildcard tests/*.hpp) $(wildcard tests/golden/*.inc) bench/protocol.hpp
# Locks taken inside process() are counted by an interposing dylib (dyld honours __interpose in
# a linked library). Elsewhere the lock check is reported as unmeasured, never as passed.
ifeq ($(UNAME),Darwin)
LOCKS := $(BUILD)/liblockcount.dylib
LOCK_LINK := -L$(BUILD) -llockcount -Wl,-rpath,@loader_path
BENCH_TAG ?= mac
else
TEST_DEFS := -DSQUATCH_NO_LOCK_COUNT
BENCH_TAG ?= $(shell uname -m)
endif
# BENCH_TAG=pi5 names the files results/bench-pi5.md and results/bench-long-pi5.md. BENCH_NOTES=text adds a line
# (governor, temperature) under the header of both.
BENCH_OUT ?= results/bench-$(BENCH_TAG).md
.PHONY: all lib test test-tsan test-tsan-negative lint headers bench bench-long golden ci live rig wasm wasm-test wasm-shots record-session-test clean FORCE \
contracts contracts-check tuner-csv plugins plugins-install plugins-check export-check release-scan-test lv2 lv2-linux
all: lib $(BUILD)/tests $(BUILD)/bench
# The core as the targets build it: optimised, no exceptions, no RTTI.
lib: $(BUILD)/libsquatch-dsp.a
$(BUILD)/obj/%.o: src/%.cpp $(HEADERS) $(wildcard blocks/tuner/include/squatch/tuner/*.hpp) | $(BUILD)/obj
$(CXX) $(RELEASE) -c -o $@ $<
$(BUILD)/libsquatch-dsp.a: $(OBJ)
ar rcs $@ $^
$(BUILD)/liblockcount.dylib: tests/lockcount.c | $(BUILD)
$(CC) -O1 -Wall -Wextra -Werror -dynamiclib -o $@ $<
# Tests compile the sources again with ASan and UBSan.
# The tuner's headers live under blocks/, outside $(HEADERS); without them here an edit to tuner.hpp left the
# test binary stale and every tuner test passed against the old code (found 2026-10-02).
$(BUILD)/tests: $(TEST_SRC) $(SRC) $(HEADERS) $(wildcard blocks/tuner/include/squatch/tuner/*.hpp) $(TEST_DEPS) $(LOCKS) | $(BUILD)
$(CXX) $(SANITIZE) $(TEST_DEFS) -o $@ $(TEST_SRC) $(SRC) $(LOCK_LINK)
# The Seed3 firmware's measurement code and its overdrive block, on this machine, with ASan, UBSan
# and the allocation watch. The firmware itself is `make -C hosts/daisy` (arm-none-eabi-gcc;
# hosts/daisy/README.md). `make test` also runs the Python tools' guard tests there.
DAISY := $(wildcard hosts/daisy/tests)
ifneq ($(DAISY),)
DAISY_TEST_SRC := $(wildcard hosts/daisy/tests/*.cpp) tests/runner.cpp tests/rt_watch.cpp
DAISY_HEADERS := $(wildcard hosts/daisy/common/*.hpp hosts/daisy/faust/*.hpp)
$(BUILD)/test_daisy: $(DAISY_TEST_SRC) $(DAISY_HEADERS) $(HEADERS) $(LOCKS) | $(BUILD)
$(CXX) $(SANITIZE) $(TEST_DEFS) -o $@ $(DAISY_TEST_SRC) $(LOCK_LINK)
endif
# The parameter queue is tested across threads, so it gets ThreadSanitizer instead.
$(BUILD)/test_queue_threads: tests/test_queue_threads.cpp $(HEADERS) | $(BUILD)
$(CXX) $(TSAN) -o $@ $< -lpthread
# ModelSwap's handoff across a control and an audio thread. test-tsan runs the stress under
# ThreadSanitizer and again under ASan and UBSan. test-tsan-negative rebuilds it three times with
# one memory ordering of the handoff weakened (SQUATCH_BREAK_HANDOFF=1,2,3) and requires
# ThreadSanitizer to report a data race each time: a guard only counts if breaking it fails.
# TSan refuses the Pi 5's 47-bit address space (see HOST_SAN below), so this is a Mac/x86 target.
SWAP_THREADS := tests/test_model_swap_threads.cpp src/model_swap.cpp src/cabinet.cpp src/crossfade.cpp src/tail_convolver.cpp src/fft.cpp
$(BUILD)/test_swap_tsan: $(SWAP_THREADS) $(HEADERS) tests/signals.hpp | $(BUILD)
$(CXX) $(TSAN) -o $@ $(SWAP_THREADS) -lpthread
$(BUILD)/test_swap_asan: $(SWAP_THREADS) $(HEADERS) tests/signals.hpp | $(BUILD)
$(CXX) $(SANITIZE) -o $@ $(SWAP_THREADS) -lpthread
$(BUILD)/test_swap_broken%: $(SWAP_THREADS) $(HEADERS) tests/signals.hpp | $(BUILD)
$(CXX) $(TSAN) -DSQUATCH_BREAK_HANDOFF=$* -o $@ $(SWAP_THREADS) -lpthread
test-tsan: $(BUILD)/test_swap_tsan $(BUILD)/test_swap_asan test-tsan-negative
./$(BUILD)/test_swap_tsan
./$(BUILD)/test_swap_asan
test-tsan-negative: $(BUILD)/test_swap_broken1 $(BUILD)/test_swap_broken2 $(BUILD)/test_swap_broken3
@for v in 1 2 3; do \
if ./$(BUILD)/test_swap_broken$$v > $(BUILD)/swap_broken$$v.log 2>&1; then \
echo "test-tsan-negative: handoff variant $$v ran clean, so the stress test would not notice it"; exit 1; fi; \
grep -q 'WARNING: ThreadSanitizer: data race' $(BUILD)/swap_broken$$v.log || { echo "variant $$v failed without a race report"; exit 1; }; \
echo "test-tsan-negative: variant $$v caught ($$(grep -c 'WARNING: ThreadSanitizer: data race' $(BUILD)/swap_broken$$v.log) race reports)"; \
done
# Hosts (hosts/): not core, so exceptions are allowed, but the same warnings. The host layer's
# handoff is tested for data races with TSan, which refuses the Pi 5's 47-bit address space:
# there, `make test HOST_SAN=`.
HOST := -std=c++17 -O3 $(WARN) -Iinclude -Iblocks/tuner/include
HOST_SAN ?= -fsanitize=thread
HOST_HEADERS := $(wildcard hosts/common/*.hpp) $(wildcard hosts/jack/*.hpp) $(wildcard hosts/coreaudio/*.hpp) $(HEADERS)
TUNER_HEADERS := $(wildcard blocks/tuner/include/squatch/tuner/*.hpp)
$(BUILD)/test_hosts: hosts/tests/test_hosts.cpp $(HOST_HEADERS) $(TUNER_HEADERS) | $(BUILD)
$(CXX) $(HOST) -O1 -g $(HOST_SAN) -o $@ $< -lpthread
# The CoreAudio host's audio path, offline (no device, no CoreAudio types: runs anywhere). ASan and UBSan.
$(BUILD)/test_host_audio: hosts/tests/test_host_audio.cpp $(HOST_HEADERS) $(TUNER_HEADERS) | $(BUILD)
$(CXX) $(HOST) -O1 -g -fsanitize=address,undefined -o $@ $< -lpthread
# The live tuner and its test-tone generator. On macOS they use CoreAudio directly (hosts/coreaudio): only
# system frameworks, nothing to install. Elsewhere they use JACK (hosts/jack; needs libjack-jackd2-dev).
# `make live LIVE_BACKEND=jack` forces JACK on a Mac that has it. Built on the Pi bench, not part of
# `all`. hosts/pi/ expects them in build/.
ifeq ($(shell uname -s),Darwin)
LIVE_BACKEND ?= coreaudio
else
LIVE_BACKEND ?= jack
endif
ifeq ($(LIVE_BACKEND),coreaudio)
LIVE_LIBS := -framework CoreAudio -framework AudioToolbox -framework CoreFoundation -lpthread
# The tuner reads a microphone: its Info.plist (embedded in the binary) carries the text macOS shows when
# it asks, so a launch from anywhere but Terminal is asked instead of being killed.
TUNER_LINK := -Wl,-sectcreate,__TEXT,__info_plist,hosts/coreaudio/Info.plist
else
LIVE_LIBS := -ljack -lpthread
TUNER_LINK :=
endif
live: $(BUILD)/squatch-tuner-live $(BUILD)/squatch-tone
$(BUILD)/squatch-tuner-live: hosts/$(LIVE_BACKEND)/tuner_live.cpp $(HOST_HEADERS) $(TUNER_HEADERS) | $(BUILD)
$(CXX) $(HOST) -o $@ $< $(LIVE_LIBS) $(TUNER_LINK)
$(BUILD)/squatch-tone: hosts/$(LIVE_BACKEND)/tone.cpp $(HOST_HEADERS) | $(BUILD)
$(CXX) $(HOST) -o $@ $< $(LIVE_LIBS)
# The live rig (gate, tuner, amp, cabinet, EQ, limiter as one JACK client) and the loopback
# latency probe. JACK headers as for `live`; not part of `all`. The amp stage runs NAM Core,
# which is optional and never part of the library: name a checkout with NAM_CORE=DIR (its NAM/
# sources; Eigen and nlohmann/json from NAM_EIGEN and NAM_JSON, by default its Dependencies/).
# Without NAM_CORE the rig builds and runs with the amp bypassed.
NAM_CORE ?=
NAM_EIGEN ?= $(NAM_CORE)/Dependencies/eigen
NAM_JSON ?= $(NAM_CORE)/Dependencies/nlohmann
NAM_ARCH ?= $(if $(filter Linux-aarch64,$(UNAME)-$(shell uname -m)),-mcpu=native,)
NAM_INC := -isystem $(NAM_CORE) -isystem $(NAM_EIGEN) -isystem $(NAM_JSON)
NAM_FLAGS := -std=c++17 -O3 -DNDEBUG $(NAM_ARCH) -DNAM_SAMPLE_FLOAT -DNAM_ENABLE_A2_FAST -DEIGEN_DONT_PARALLELIZE $(NAM_INC)
NAM_SRC := $(if $(NAM_CORE),$(wildcard $(NAM_CORE)/NAM/*.cpp $(NAM_CORE)/NAM/wavenet/*.cpp))
NAM_OBJ := $(patsubst $(NAM_CORE)/NAM/%.cpp,$(BUILD)/nam/%.o,$(NAM_SRC))
RIG_NAM := $(if $(NAM_CORE),-DSQUATCH_WITH_NAM -DNDEBUG -DNAM_SAMPLE_FLOAT -DNAM_ENABLE_A2_FAST -DEIGEN_DONT_PARALLELIZE $(NAM_INC))
RIG_HEADERS := $(wildcard hosts/jack/rig_*.hpp) hosts/jack/nam_amp.hpp hosts/common/ir_file.hpp
rig: $(BUILD)/squatch-rig-live $(BUILD)/squatch-latency
# Rebuilds the rig when NAM_CORE changes (with or without the amp).
$(BUILD)/rig.cfg: FORCE | $(BUILD)
@echo '$(NAM_CORE)' | cmp -s - $@ || echo '$(NAM_CORE)' > $@
$(BUILD)/nam/%.o: $(NAM_CORE)/NAM/%.cpp
@mkdir -p $(@D)
$(CXX) $(NAM_FLAGS) -c -o $@ $<
$(BUILD)/squatch-rig-live: hosts/jack/rig_live.cpp $(RIG_HEADERS) $(HOST_HEADERS) $(TUNER_HEADERS) $(BUILD)/libsquatch-dsp.a \
$(NAM_OBJ) $(BUILD)/rig.cfg | $(BUILD)
$(CXX) $(HOST) $(RIG_NAM) -o $@ $< $(NAM_OBJ) $(BUILD)/libsquatch-dsp.a -ljack -lpthread
$(BUILD)/squatch-latency: hosts/jack/latency_probe.cpp $(HOST_HEADERS) | $(BUILD)
$(CXX) $(HOST) -o $@ $< -ljack -lpthread
# The rig's chain over a recording, no JACK: any machine (squatch-rig-render IN.wav OUT.wav ...).
render: $(BUILD)/squatch-rig-render
$(BUILD)/squatch-rig-render: hosts/render/rig_render.cpp $(RIG_HEADERS) $(TUNER_HEADERS) $(BUILD)/libsquatch-dsp.a \
$(NAM_OBJ) $(BUILD)/rig.cfg | $(BUILD)
$(CXX) $(HOST) $(RIG_NAM) -o $@ $< $(NAM_OBJ) $(BUILD)/libsquatch-dsp.a -lpthread
# The tuner over a recording as display-frame CSV rows (squatch-tuner-csv IN.wav > rows.csv), and
# the ESP32-S3 tuner probe's own code built for this machine (hosts/esp32/tuner-probe): any machine.
# The probe is development-tree only (tools/private-paths): without it only the CSV tool builds.
PROBE_HEADERS := $(wildcard hosts/esp32/tuner-probe/main/*.hpp)
PROBE := $(wildcard hosts/esp32/tuner-probe/main/probe_session.hpp)
tuner-csv: $(BUILD)/squatch-tuner-csv $(if $(PROBE),$(BUILD)/squatch-tuner-probe-host)
$(BUILD)/squatch-tuner-csv: hosts/render/tuner_csv.cpp $(HOST_HEADERS) $(TUNER_HEADERS) $(BUILD)/libsquatch-dsp.a | $(BUILD)
$(CXX) $(HOST) -o $@ $< $(BUILD)/libsquatch-dsp.a
$(BUILD)/squatch-tuner-probe-host: hosts/esp32/tuner-probe/host/probe_host.cpp $(PROBE_HEADERS) $(HOST_HEADERS) \
$(TUNER_HEADERS) $(BUILD)/libsquatch-dsp.a | $(BUILD)
$(CXX) $(HOST) -o $@ $< $(BUILD)/libsquatch-dsp.a
# The display frames, the hold and the S3 probe's session and USB feed, with ASan and UBSan.
$(BUILD)/test_probe: hosts/tests/test_probe.cpp src/tuner.cpp $(PROBE_HEADERS) $(HOST_HEADERS) $(TUNER_HEADERS) | $(BUILD)
$(CXX) $(HOST) -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -o $@ $< src/tuner.cpp
# The rig's host code that needs no JACK (WAV reading, resampling, options), with ASan and UBSan.
$(BUILD)/test_rig: hosts/tests/test_rig.cpp hosts/common/ir_file.hpp hosts/jack/rig_options.hpp hosts/jack/rig_stages.hpp | $(BUILD)
$(CXX) $(HOST) -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -o $@ $<
# CabSwap (hosts/common/cab_swap.hpp) keeps its own copy of the impulse response it is given: the test frees
# the caller's buffer first, so ASan flags a read of it. Exceptions on (a host), the library sources built in.
$(BUILD)/test_cab_swap_owns_taps: hosts/tests/test_cab_swap_owns_taps.cpp hosts/common/cab_swap.hpp $(HEADERS) $(SRC) tests/runner.cpp | $(BUILD)
$(CXX) $(HOST) -O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all -Wno-unused-parameter \
-o $@ hosts/tests/test_cab_swap_owns_taps.cpp tests/runner.cpp $(SRC)
# NamAmp's live model swap against real models. NAM Core is optional and never part of the library, so these
# are not part of `make test`: name a checkout with NAM_CORE=DIR (its example_models are the models).
# ASan for the malloc hooks the real-time watch uses.
.PHONY: test-nam test-nam-tsan
test-nam: $(BUILD)/test_nam_swap
./$(BUILD)/test_nam_swap
$(BUILD)/test_nam_swap: hosts/tests/test_nam_swap.cpp hosts/jack/nam_amp.hpp $(HEADERS) tests/glitch.hpp tests/rt_watch.cpp tests/runner.cpp \
$(SRC) $(NAM_OBJ) $(LOCKS) | $(BUILD)
@test -n '$(NAM_CORE)' || { echo 'test-nam: name a NAM Core checkout with NAM_CORE=DIR'; exit 2; }
$(CXX) -std=c++17 -O1 -g -fsanitize=address $(WARN) -Wno-unused-parameter -Iinclude -Iblocks/tuner/include $(RIG_NAM) \
-DNAM_MODELS_DIR='"$(NAM_CORE)/example_models"' $(TEST_DEFS) -o $@ hosts/tests/test_nam_swap.cpp tests/rt_watch.cpp \
tests/runner.cpp $(SRC) $(NAM_OBJ) $(LOCK_LINK)
# The same file under ThreadSanitizer, NAM Core built with it as well, running only the thread stress.
$(BUILD)/namtsan/%.o: $(NAM_CORE)/NAM/%.cpp
@mkdir -p $(@D)
$(CXX) $(NAM_FLAGS) -O1 -g -fsanitize=thread -c -o $@ $<
NAM_TSAN_OBJ := $(patsubst $(NAM_CORE)/NAM/%.cpp,$(BUILD)/namtsan/%.o,$(NAM_SRC))
test-nam-tsan: $(NAM_TSAN_OBJ) hosts/tests/test_nam_swap.cpp hosts/jack/nam_amp.hpp $(HEADERS) $(SRC) tests/glitch.hpp tests/rt_watch.cpp tests/runner.cpp | $(BUILD)
@test -n '$(NAM_CORE)' || { echo 'test-nam-tsan: name a NAM Core checkout with NAM_CORE=DIR'; exit 2; }
$(CXX) -std=c++17 -O1 -g -fsanitize=thread $(WARN) -Wno-unused-parameter -Iinclude -Iblocks/tuner/include $(RIG_NAM) \
-DNAM_MODELS_DIR='"$(NAM_CORE)/example_models"' $(TEST_DEFS) -o $(BUILD)/test_nam_swap_tsan hosts/tests/test_nam_swap.cpp \
tests/rt_watch.cpp tests/runner.cpp $(SRC) $(NAM_TSAN_OBJ) $(LOCK_LINK)
./$(BUILD)/test_nam_swap_tsan namSwapUnderAControlThreadStress
# The browser demo (hosts/wasm): the tuner as WebAssembly in an AudioWorklet, a static folder
# for any HTTPS host. Needs Emscripten on PATH (source <emsdk>/emsdk_env.sh); not part of `all`.
# wasm-test scores the module in Node, then the page in headless Chrome against test tones.
DEMO := $(BUILD)/web-demo
wasm:
python3 hosts/wasm/build.py $(DEMO)
wasm-test: wasm
node hosts/web/hold.test.mjs
node hosts/wasm/test/core.test.mjs $(DEMO)/tuner.wasm
node hosts/wasm/test/browser.test.mjs --dir $(DEMO)
# The Pi bench's recording session page (hosts/pi/record-session: the standard tuner view with the
# session's controls) in headless Chrome, against a stand-in for the Pi's tuner. Needs node and Chrome.
record-session-test:
node hosts/web/hold.test.mjs
node hosts/pi/test/record-session.test.mjs
# Rewrites the demo screenshots in blocks/tuner/docs/screens/ (desktop and phone, light and dark).
wasm-shots: wasm
node hosts/wasm/test/browser.test.mjs --dir $(DEMO) --only a2,a2-plus7 --shots blocks/tuner/docs/screens
# LV2 bundles for PiPedal (hosts/lv2): the macOS build and its smoke host (LV2 headers from a first
# `make lv2-linux`), and arm64 Linux in a Debian 12 container with its symbol, TTL and lv2lint checks.
lv2:
$(MAKE) -C hosts/lv2 test
lv2-linux:
hosts/lv2/build-linux.sh --lint
$(BUILD)/bench: bench/bench.cpp bench/protocol.hpp $(BUILD)/libsquatch-dsp.a $(HEADERS) | $(BUILD)
$(CXX) $(RELEASE) -o $@ $< $(BUILD)/libsquatch-dsp.a
# Benches of their own (bench/bench_NAME.cpp): make build/bench_NAME
$(BUILD)/bench_%: bench/bench_%.cpp $(BUILD)/libsquatch-dsp.a $(HEADERS) | $(BUILD)
$(CXX) $(RELEASE) -o $@ $< $(BUILD)/libsquatch-dsp.a
$(BUILD) $(BUILD)/obj:
mkdir -p $@
test: $(BUILD)/tests $(BUILD)/test_queue_threads $(BUILD)/test_hosts $(BUILD)/test_host_audio $(BUILD)/test_rig $(BUILD)/test_cab_swap_owns_taps $(if $(DAISY),$(BUILD)/test_daisy) $(if $(PROBE),$(BUILD)/test_probe)
./$(BUILD)/tests
./$(BUILD)/test_queue_threads
./$(BUILD)/test_hosts
./$(BUILD)/test_host_audio
./$(BUILD)/test_rig
./$(BUILD)/test_cab_swap_owns_taps
$(if $(PROBE),./$(BUILD)/test_probe)
$(if $(DAISY),./$(BUILD)/test_daisy)
$(if $(DAISY),python3 hosts/daisy/tests/test_tools.py)
$(MAKE) -C blocks/tuner test CXX=$(CXX)
# The tuner quality suite (blocks/tuner/bench/quality/README.md): every case through the display
# path the apps use (TunerBlock, 16- and 64-frame blocks, read 30 times a second, the page's hold),
# scored against its truth. Data stays outside the repository: name the DI corpus folder with
# TUNER_CORPUS=DIR, here or in an untracked local.mk; without it only the synthetic set runs.
-include local.mk
.PHONY: tuner-quality tuner-quality-test
QUALITY_DIR := blocks/tuner/bench/quality
QUALITY_HEADERS := $(wildcard $(QUALITY_DIR)/*.hpp) $(wildcard blocks/tuner/bench/*.hpp) hosts/common/display.hpp \
hosts/common/hold.hpp hosts/common/tuner_frames.hpp
TUNER_CORPUS ?=
QUALITY_OUT ?= $(BUILD)/tuner-quality
SCORECARD ?= $(QUALITY_OUT)/scorecard
$(BUILD)/tuner_quality: $(QUALITY_DIR)/quality.cpp $(QUALITY_HEADERS) $(TUNER_HEADERS) $(BUILD)/libsquatch-dsp.a | $(BUILD)
$(CXX) $(HOST) -I. -o $@ $< $(BUILD)/libsquatch-dsp.a -lpthread
$(BUILD)/test_quality: $(QUALITY_DIR)/test_quality.cpp $(QUALITY_HEADERS) $(TUNER_HEADERS) src/tuner.cpp | $(BUILD)
$(CXX) $(HOST) -I. -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -o $@ $< src/tuner.cpp -lpthread
$(BUILD)/hold_parity: $(QUALITY_DIR)/hold_parity.cpp hosts/common/hold.hpp | $(BUILD)
$(CXX) $(HOST) -I. -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -o $@ $<
# The suite's own tests (reference estimator, display model, truth, MIDI) and hold.hpp against
# hold.js (needs node).
tuner-quality-test: $(BUILD)/test_quality $(BUILD)/hold_parity
./$(BUILD)/test_quality
node $(QUALITY_DIR)/hold_parity.mjs $(BUILD)/hold_parity
tuner-quality: $(BUILD)/tuner_quality
./$(BUILD)/tuner_quality --out $(QUALITY_OUT) $(if $(TUNER_CORPUS),--corpus $(TUNER_CORPUS))
python3 $(QUALITY_DIR)/score.py scorecard --run $(QUALITY_OUT) --out $(SCORECARD)
# Rewrites tests/golden/*.inc from the current code. Only after a deliberate change of sound.
golden: $(BUILD)/tests
SQUATCH_WRITE_GOLDEN=tests/golden ./$(BUILD)/tests Golden
$(MAKE) test
bench: $(BUILD)/bench
mkdir -p results
./$(BUILD)/bench "$$(sysctl -n machdep.cpu.brand_string 2>/dev/null || uname -m)" \
"$$($(CXX) --version | head -1)" > $(BENCH_OUT)
cat $(BENCH_OUT)
# Cabinet over 4k to 240k taps (results/bench-long-*.md); minutes, not seconds.
bench-long: $(BUILD)/bench
mkdir -p results
./$(BUILD)/bench "$$(sysctl -n machdep.cpu.brand_string 2>/dev/null || uname -m)" \
"$$($(CXX) --version | head -1)" long > results/bench-long-$(BENCH_TAG).md
cat results/bench-long-$(BENCH_TAG).md
# Each public header compiles on its own, with the core's flags.
headers:
@for h in $(HEADERS); do \
printf '#include <%s>\n' "$${h#include/}" | $(CXX) $(CORE) -x c++ -fsyntax-only - || exit 1; \
done; echo "headers: $(words $(HEADERS)) compile standalone"
# Complexity limits of the Squatch project kit: CCN 10, 60 lines, 5 parameters per function.
lint:
@command -v lizard >/dev/null || { echo "lizard not found: this repo installs nothing"; exit 2; }
lizard -C 10 -L 60 -a 5 -w -x 'hosts/clap/generated/*' -x 'hosts/daisy/vendor/*' -x 'tests/reference/*_faust.h' include src tests bench hosts tools/build-contracts blocks/tuner/include blocks/tuner/bench blocks/tuner/tests
ci: lint headers lib test contracts-check release-scan-test
# Control contracts (contracts/*.params.json) -> ParamDesc tables and the plugin list. `contracts`
# rewrites the generated files; `contracts-check` fails if they are stale.
contracts:
tools/build-contracts --formats $(BUILD)/contracts
contracts-check:
tools/build-contracts --check
# The desktop plugins (hosts/clap): CLAP, and VST3 and AUv2 through clap-wrapper, from the pinned
# dependencies in hosts/clap/deps.lock (fetched into build/deps; nothing downloads during a build).
PLUGIN_BUILD := $(BUILD)/plugins
plugins:
tools/fetch-plugin-deps
cmake -S hosts/clap -B $(PLUGIN_BUILD) -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build $(PLUGIN_BUILD)
# Copies the bundles into $HOME/Library/Audio/Plug-Ins/{CLAP,VST3,Components} (macOS).
plugins-install: plugins
for pair in clap:CLAP vst3:VST3 component:Components; do \
mkdir -p "$$HOME/Library/Audio/Plug-Ins/$${pair#*:}"; \
for b in $(PLUGIN_BUILD)/bundles/*."$${pair%%:*}"; do \
rm -rf "$$HOME/Library/Audio/Plug-Ins/$${pair#*:}/$$(basename "$$b")"; \
ditto "$$b" "$$HOME/Library/Audio/Plug-Ins/$${pair#*:}/$$(basename "$$b")"; \
done; \
done
-killall -9 AudioComponentRegistrar 2>/dev/null
# The whole plugin matrix: test host (linked and over the bundles) under ASan+UBSan, TSan and
# RTSan, clap-validator, the VST3 validator, pluginval and auval. See tools/plugins-check.
plugins-check:
tools/plugins-check
# The development tree keeps some work out of the public export (tools/private-paths). This
# proves the export leaves it out and that the exported tree builds and passes its tests.
# The release scanner's own tests, each guard with its negative control.
release-scan-test:
tools/test-release-scan
tools/test-release-signing
export-check:
@if [ -x tools/check-private-export ]; then tools/check-private-export; else echo "export-check: nothing private here"; fi
clean:
rm -rf $(BUILD)