-
Notifications
You must be signed in to change notification settings - Fork 60
Expand file tree
/
Copy pathMakefile
More file actions
226 lines (181 loc) · 9.6 KB
/
Copy pathMakefile
File metadata and controls
226 lines (181 loc) · 9.6 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
# Makefile for AMY , including an example
TARGET = amy-example amy-message amy-piano
LIBS = -lm -pthread
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
SOURCES = src/macos_midi.m
CFLAGS += -DMACOS
LIBS = -framework AudioUnit -framework CoreAudio -framework CoreFoundation -framework CoreMIDI -framework Cocoa -lstdc++
# Needed for brew's python3.12+ on MacOS
EXTRA_PIP_ENV = PIP_BREAK_SYSTEM_PACKAGES=1
CC = clang
else
CC = gcc
endif
# Linux MIDI is ALSA raw MIDI (src/linux_midi.c), the counterpart of
# src/macos_midi.m -- and it is OPTIONAL, detected rather than assumed.
# A synth library may not make itself unbuildable over a device layer:
# plenty of Linux boxes (CI runners, containers, a headless Pi) have no
# libasound2-dev, and before this they built amy perfectly well. Without
# it AMY_ALSA is unset, linux_midi.c compiles to nothing, and amy_midi.c
# keeps the no-op run_midi/stop_midi it always had there.
# apt install libasound2-dev
ifeq ($(UNAME_S),Linux)
HAVE_ALSA := $(shell echo 'int main(){return 0;}' | $(CC) -x c - -include alsa/asoundlib.h -lasound -o /dev/null >/dev/null 2>&1 && echo 1)
ifeq ($(HAVE_ALSA),1)
SOURCES += src/linux_midi.c
CFLAGS += -DAMY_ALSA
LIBS += -lasound
else
$(info amy: no libasound2-dev, building without linux MIDI)
endif
endif
# on Raspberry Pi, at least under 32-bit mode, libatomic and libdl are needed.
ifeq ($(shell uname -m), armv7l)
LIBS += -ldl -latomic
endif
ifeq ($(shell uname -m), armv6l)
LIBS += -ldl -latomic
endif
CFLAGS += -O3 -Wall -Wno-strict-aliasing -Wextra -Wno-unused-parameter -Wpointer-arith -Wno-float-conversion -Wno-missing-declarations -DAMY_WAVETABLE
#CFLAGS += -DAMY_DEBUG
# -Wdouble-promotion
# Table-driven C API exports (AMY_C_API_EXPORTED_FUNCTIONS), generated by
# scripts/gen_amy_c_api.py so a table entry can never miss the export list.
include src/amy_c_api_exports.mk
EMSCRIPTEN_OPTIONS = -s WASM=1 --bind \
-DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 \
-sSTACK_SIZE=128000\
-sMODULARIZE -s 'EXPORT_NAME="amyModule"' \
-s EXPORTED_RUNTIME_METHODS="['cwrap','ccall', 'HEAPU8', 'wasmMemory']" \
-s EXPORTED_FUNCTIONS="['_amy_add_event', \
'_amy_live_stop', '_amy_set_external_input_buffer', '_amy_live_start_web', '_amy_live_start_web_audioin',\
'_amy_start_web', '_amy_start_web_no_synths', '_malloc', '_free', '_size_of_amy_event', '_cos_lut', \
'_amy_set_external_hook_context', '_amy_get_external_hook_context', $(AMY_C_API_EXPORTED_FUNCTIONS)]" \
-s SUPPORT_LONGJMP=emscripten \
-s INITIAL_MEMORY=128mb -s TOTAL_STACK=64mb -s ALLOW_MEMORY_GROWTH=1 \
-s ASYNCIFY -s ASYNCIFY_STACK_SIZE=128000
PYTHON = python3
.PHONY: default all clean amy-module test ctest web deploy-web godot-api c-api check-c-api
default: $(TARGET)
all: default
# Regenerate the table-driven cross-platform C API bindings (pyamy/micropython/
# web/godot .inc + .js + exports fragment + the amy/__init__.py block).
c-api:
$(PYTHON) scripts/gen_amy_c_api.py
$(PYTHON) scripts/gen_amy_js_api.py
$(PYTHON) scripts/gen_patches_js.py
$(PYTHON) scripts/gen_pcm_presets_js.py
check-c-api:
$(PYTHON) scripts/gen_amy_c_api.py --check
$(PYTHON) scripts/gen_amy_js_api.py --check
$(PYTHON) scripts/gen_patches_js.py --check
$(PYTHON) scripts/gen_pcm_presets_js.py --check
SOURCES += src/algorithms.c src/amy.c src/envelope.c src/examples.c src/parse.c \
src/filters.c src/oscillators.c src/pcm.c src/interp_partials.c src/custom.c \
src/delay.c src/log2_exp2.c src/patches.c src/transfer.c src/sequencer.c \
src/libminiaudio-audio.c src/instrument.c src/amy_midi.c src/api.c src/midi_mappings.c \
src/cv_trigger.c
OBJECTS = $(patsubst %.c, %.o, $(SOURCES))
HEADERS = $(wildcard src/*.h)
HEADERS_BUILD := $(filter-out src/patches.h,$(HEADERS))
PYTHONS = $(wildcard *.py)
# The grep below takes every NUMERIC #define out of amy.h. AMY_BLOCK_SIZE is
# the one derived define -- (1 << BLOCK_SIZE_BITS), since the block has to be
# a power of two and the bits are the knob -- so it is spelt out afterwards
# from the BLOCK_SIZE_BITS that landed, or amy.render() and the generated JS
# API would lose it.
src/patches.h: $(PYTHONS) $(HEADERS_BUILD)
cat src/amy.h | sed -e 's@^//.*@@' | tr '\t' ' ' | egrep 'define +[^ ]+ +[.0-9-]+' | sed -e 's/\([-0-9][0-9]*\.[0-9]*\)f.*/\1/' | awk '{print $$2 "=" $$3}' > amy/constants.py
echo "AMY_BLOCK_SIZE=$$((1 << $$(sed -n 's/^BLOCK_SIZE_BITS=//p' amy/constants.py | tail -1)))" >> amy/constants.py
${PYTHON} -m amy.headers
%.o: %.c $(HEADERS) src/patches.h
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.mm $(HEADERS)
clang $(CFLAGS) -c $< -o $@
%.o: %.m
clang -I$(INC) $(CFLAGS) -c -o $@ $<
.PRECIOUS: $(TARGET) $(OBJECTS)
amy-example: $(OBJECTS) src/amy-example.o
$(CC) $(CFLAGS) $(OBJECTS) src/amy-example.o -Wall $(LIBS) -o $@
amy-piano: $(OBJECTS) src/amy-piano.o
$(CC) $(CFLAGS) $(OBJECTS) src/amy-piano.o -Wall $(LIBS) -o $@
amy-message: $(OBJECTS) src/amy-message.o
$(CC) $(CFLAGS) $(OBJECTS) src/amy-message.o -Wall $(LIBS) -o $@
# Plain C tests for things the audio-rendering suite can't reach -- e.g. clock
# rollovers 50 days out, which you can only hit by fast-forwarding the counters.
CTESTS = tests/test_clock_wrap tests/test_sequencer_active tests/test_sequencer_bounds \
tests/test_sequencer_cumulate \
tests/test_bus_config tests/test_patch_slots \
tests/test_synth_readout tests/test_log2_lut tests/test_clone_on_grow \
tests/test_timebase_reset tests/test_osc_free_on_release \
tests/test_voice_osc_range tests/test_dist_coefs tests/test_dist_scope
# Static pattern rules, so these win over the generic %.o: %.c above (which
# would compile without -Isrc and fail to find amy.h).
$(addsuffix .o,$(CTESTS)): %.o: %.c $(HEADERS) src/patches.h
$(CC) $(CFLAGS) -Isrc -c $< -o $@
$(CTESTS): %: %.o $(OBJECTS)
$(CC) $(CFLAGS) $(OBJECTS) $< -Wall $(LIBS) -o $@
ctest: $(CTESTS)
@for t in $(CTESTS); do echo "== $$t"; ./$$t || exit 1; done
amy-module: amy-example
${EXTRA_PIP_ENV} ${PYTHON} -m pip install -r requirements.txt; touch src/amy.c; ${EXTRA_PIP_ENV} ${PYTHON} -m pip install . --force-reinstall --no-deps; cd ..
test: amy-module
${PYTHON} -m amy.test
qtest: amy-module
${PYTHON} -m amy.test quiet
playfailed: qtest
for x in `cat failed_tests.txt`; do echo $$x "ref"; sleep 0.2; afplay tests/ref/$$x.wav; echo $$x "tst"; sleep 0.2; afplay tests/tst/$$x.wav; done
updateref: qtest
for x in `cat failed_tests.txt`; do cp tests/tst/$$x.wav tests/ref/; done
# Report the median FILTER_PROCESS timing over 50 runs.
timing: amy-module
for a in 0 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; do ${PYTHON} -m amy.timing 2>&1 ; done > /tmp/timings.txt
cat /tmp/timings.txt | grep AMY_RENDER: | sed -e 's/us//' | sort -n | awk ' { a[i++]=$$4; } END { print a[int(i/2)]; }'
cat /tmp/timings.txt | grep FILTER_PROCESS: | sed -e 's/us//' | sort -n | awk ' { a[i++]=$$4; } END { print a[int(i/2)]; }'
cat /tmp/timings.txt | grep PARAMETRIC_EQ_PROCESS: | sed -e 's/us//' | sort -n | awk ' { a[i++]=$$4; } END { print a[int(i/2)]; }'
#USBSERIAL=/dev/cu.usbserial-0001
USBSERIAL=/dev/cu.usbserial-A5069RR4
speedtest:
echo "Compiling LoadTestChord.ino..."
arduino-cli compile --fqbn esp32:esp32:amyboard --build-path ./build --build-property "compiler.c.extra_flags=-DARDUINO_SPEEDTEST" tools/arduino_loadsweep/LoadTestChord
echo 'Running measure.py. Press RESET on board after seeing "[flash] ok (attempt 1)"'
${PYTHON} tools/arduino_loadsweep/measure.py --out ./load --port ${USBSERIAL} ./build
speedtest-piano:
echo "Compiling LoadTestChord.ino..."
arduino-cli compile --fqbn esp32:esp32:amyboard --build-path ./build --build-property "compiler.c.extra_flags=-DARDUINO_SPEEDTEST" --build-property "compiler.cpp.extra_flags=-DSPEEDTEST_PATCH=256 -DSPEEDTEST_NUM_NOTES=6" tools/arduino_loadsweep/LoadTestChord
echo 'Running measure.py. Press RESET on board after seeing "[flash] ok (attempt 1)"'
${PYTHON} tools/arduino_loadsweep/measure.py --out ./load --port ${USBSERIAL} ./build
valgrind: amy-example
valgrind --leak-check=full --show-reachable=yes --suppressions=valgrind.suppressions ./amy-example
# The web build carries the Gamma9001 drum banks: drums.bin is generated from
# sounds/gamma9001/ and linked straight into the wasm as gamma9001_pcm_data.
build/drums_bin.c: sounds/gamma9001/manifest.json amy/headers.py
$(PYTHON) -m amy.headers gamma9001
build/amy.js: $(TARGET) build/drums_bin.c
mkdir -p build
@# The linux MIDI layer is native ALSA and has no business in a wasm
@# build -- and SOURCES/CFLAGS pick it up on any linux box that has
@# libasound, so building web THERE would hand emcc alsa/asoundlib.h.
emcc $(filter-out src/linux_midi.c,$(SOURCES)) build/drums_bin.c $(filter-out -DAMY_ALSA,$(CFLAGS)) -DGAMMA9001 $(EMSCRIPTEN_OPTIONS) -O3 -o $@
# The JS API / patches / pcm-preset files are committed outputs in src/,
# regenerated by `make c-api` and verified fresh in CI by `make check-c-api`
# (they used to be built into build/ here, which let downstream copies drift).
web: build/amy.js
# emscripten (>=4.x) inlines the AudioWorklet/WasmWorker glue into amy.js, so it
# no longer emits separate amy.aw.js / amy.ww.js files (older builds did).
deploy-web: web
cat build/amy.js src/amy_connector.js src/amy_api.generated.js src/amy_c_api.generated.js > docs/amy.js
cp build/amy.wasm docs/
# Regenerate the Godot GDScript send() kwarg map from amy/__init__.py (the
# source of truth). Run on release and verified in CI so godot/amy.gd can't drift.
godot-api:
$(PYTHON) scripts/gen_amy_gd_api.py
js-api:
$(PYTHON) scripts/gen_amy_js_api.py
clean:
-rm -f src/*.o
-rm -r src/patches.h
-rm -f amy/constants.py
-rm -f $(TARGET)
-rm -f tests/*.o $(CTESTS)