CARGO ?= cargo
FILE ?=
SECONDS ?=
v ?=
PROFILE ?= --release
.DEFAULT_GOAL := help
.PHONY: help examples example1 example2 example3 example4 \
example-play example-play-all example-effects example-thru example-mix \
build build-all test check doc clean release \
require-cargo require-manifest require-import require-native require-fixture \
require-version
require-cargo:
@command -v $(CARGO) >/dev/null 2>&1 || { \
echo "error: $(CARGO) not found. Install Rust from https://rustup.rs"; \
exit 1; }
require-manifest: require-cargo
@$(CARGO) metadata --no-deps --format-version 1 >/dev/null 2>&1 || { \
echo "error: Cargo.toml does not parse. Run '$(CARGO) metadata' to see why."; \
exit 1; }
require-import: require-manifest
@$(CARGO) metadata --no-deps --format-version 1 \
| grep -q '"import"' || { \
echo "error: no 'import' feature in Cargo.toml — the decoders live behind it."; \
exit 1; }
require-native: require-manifest
@command -v cc >/dev/null 2>&1 || command -v gcc >/dev/null 2>&1 || command -v clang >/dev/null 2>&1 || { \
echo "error: no C compiler. import-he-aac and import-opus build C libraries from source."; \
exit 1; }
@command -v cmake >/dev/null 2>&1 || { \
echo "error: cmake not found, and libfdk-aac needs it. Try 'brew install cmake'."; \
exit 1; }
require-fixture:
@test -n "$(FILE)" -o -f tests/test_data/test415hz.mp3 || { \
echo "error: tests/test_data/test415hz.mp3 is missing."; \
echo " Pass one instead: make example-play FILE=/path/to/audio.flac"; \
exit 1; }
require-version:
@test -n "$(v)" || { \
echo "error: no version. Usage: make release v=0.9.0"; \
exit 1; }
@printf '%s' '$(v)' \
| grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$$' || { \
echo "error: '$(v)' is not a semver version (x.y.z)"; \
exit 1; }
@test -f CHANGELOG.md || { \
echo "error: no CHANGELOG.md to cut a release section in."; \
exit 1; }
@grep -Fqx '## Release $(v)' CHANGELOG.md && { \
echo "error: CHANGELOG.md already has a 'Release $(v)' section."; \
exit 1; } || true
@ @ @ @awk ' \
state == "pending" && /^## Release / { state = "tail" } \
state == "pending" && NF { found = 1 } \
state == "" && /^\*\*Done\*\*[[:space:]]*$$/ { state = "pending" } \
END { if (state == "") exit 2; if (!found) exit 3 } \
' CHANGELOG.md; \
case $$? in \
0) ;; \
2) echo "error: CHANGELOG.md has no '**Done**' line to move items out of."; exit 1 ;; \
3) echo "error: nothing under '**Done**' in CHANGELOG.md — no release to cut."; exit 1 ;; \
*) echo "error: could not read CHANGELOG.md"; exit 1 ;; \
esac
example1: require-manifest
@echo "==> audio_engine1: random input -> random output"
$(CARGO) run $(PROFILE) --example audio_engine1
example2: require-manifest
@echo "==> audio_engine2: input tied to a named output"
$(CARGO) run $(PROFILE) --example audio_engine2
example3: require-manifest
@echo "==> audio_engine3: [2, 5] channel outputs, one input routed, one not"
$(CARGO) run $(PROFILE) --example audio_engine3
example4: require-manifest
@echo "==> audio_engine4: plugin chains on input, engine, and output"
$(CARGO) run $(PROFILE) --example audio_engine4
example-play: require-import require-fixture
@echo "==> play_file: decode and play (this one you can hear)"
$(CARGO) run $(PROFILE) --features import --example play_file -- $(FILE)
example-play-all: require-import require-native require-fixture
@echo "==> play_file with every decoder (builds libopus and libfdk-aac)"
$(CARGO) run $(PROFILE) --features import-all --example play_file -- $(FILE)
example-effects: require-import require-fixture
@echo "==> play_effects: decode, run a plugin chain over it, play it"
$(CARGO) run $(PROFILE) --features import --example play_effects -- $(FILE)
example-thru: require-manifest
@echo "==> live_thru: monitor a microphone through all three plugin levels"
@echo " wear headphones — this feeds a microphone into a speaker"
$(CARGO) run $(PROFILE) --example live_thru -- $(SECONDS)
example-mix: require-import require-fixture
@echo "==> mix_sources: a file and a microphone summed by the mixer"
@echo " wear headphones — this feeds a microphone into a speaker"
$(CARGO) run $(PROFILE) --features import --example mix_sources -- $(FILE)
examples: example1 example2 example3 example4
build: require-manifest
$(CARGO) build $(PROFILE)
build-all: require-import
$(CARGO) build $(PROFILE) --examples
$(CARGO) build $(PROFILE) --features import --examples
test: require-import
$(CARGO) test --features import
check: require-import
$(CARGO) check
$(CARGO) check --features import
$(CARGO) check --all-targets --features import
doc: require-manifest
$(CARGO) doc --no-deps --features import
clean:
$(CARGO) clean
release: require-version require-manifest
@ @ @awk -v ver='$(v)' ' \
/^\[/ { in_pkg = ($$0 == "[package]") } \
in_pkg && !done && /^version[[:space:]]*=/ { \
print "version = \"" ver "\""; done = 1; next } \
{ print } \
' Cargo.toml > Cargo.toml.tmp && mv Cargo.toml.tmp Cargo.toml
@grep -Fqx 'version = "$(v)"' Cargo.toml || { \
echo "error: Cargo.toml still does not say $(v) — is there a [package] version line?"; \
exit 1; }
@ @ @sed -i.bak -E \
-e 's/^atome = "[^"]*"/atome = "$(v)"/' \
-e 's/^atome = \{ version = "[^"]*"/atome = { version = "$(v)"/' \
README.md && rm -f README.md.bak
@ @$(CARGO) metadata --format-version 1 >/dev/null
@ @ @ @awk -v ver='$(v)' ' \
BEGIN { state = "head" } \
state == "head" { \
print; \
if ($$0 ~ /^\*\*Done\*\*[[:space:]]*$$/) state = "pending"; \
next } \
state == "pending" && /^## Release / { state = "tail" } \
state == "pending" { pending = pending $$0 "\n"; next } \
{ tail = tail $$0 "\n" } \
END { \
if (state == "head") exit 2; \
sub(/^\n+/, "", pending); sub(/\n+$$/, "", pending); \
if (pending == "") exit 3; \
print ""; print "## Release " ver; print ""; print pending; \
if (tail != "") { sub(/\n+$$/, "", tail); print ""; print tail } \
} \
' CHANGELOG.md > CHANGELOG.md.tmp; \
code=$$?; \
if [ $$code -ne 0 ]; then rm -f CHANGELOG.md.tmp; fi; \
case $$code in \
0) mv CHANGELOG.md.tmp CHANGELOG.md ;; \
2) echo "error: CHANGELOG.md has no '**Done**' line to move items out of."; exit 1 ;; \
3) echo "error: nothing under '**Done**' in CHANGELOG.md — no release to cut."; exit 1 ;; \
*) exit $$code ;; \
esac
@echo "==> $(v): Cargo.toml, Cargo.lock, README.md, CHANGELOG.md"
@echo ""
@echo " Review the diff, then:"
@echo " git checkout -b rel-$(v) && git commit -am 'release $(v)' && git push -u origin rel-$(v)"
@echo ""
@echo " The push publishes to crates.io and opens the GitHub release."
help:
@echo "atome examples"
@echo ""
@grep -E '^## ' $(MAKEFILE_LIST) \
| sed -e 's/^## //' -e 's/:/:|/' \
| awk -F'|' '{ printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 }'
@echo ""
@echo " The 'example-*' targets make sound; 'example1'..'example4' do not —"
@echo " they build real streams and print how they are wired."
@echo ""
@echo " 'example-thru' and 'example-mix' open a microphone and play it back."
@echo " Wear headphones: through a speaker that is a feedback loop."
@echo ""
@echo " The engine does not yet carry captured audio from an input to its"
@echo " outputs (planning/TODO.md 2.3); the live examples do that forwarding"
@echo " themselves, and say so."