Real-time musical key detection. Mono audio in, a key out (e.g. F# minor).
A C99 engine: FFT, spectral peaks, HPCP chroma, key-profile matching, plus a streaming mode with hysteresis for live input. No heap, no file I/O in the core, depends only on libm.
make# whole file
./build/jamkey file song.wav
./build/jamkey file song.wav --json
# live from the microphone (Ctrl-C to stop)
./build/jamkey mic --window 24 --hop 2
# rolling from a file
./build/jamkey stream song.wav --window 24 --hop 2
# rolling from raw PCM on stdin
some-source | ./build/jamkey stream - --rate 44100 --channels 1 --format s16$ ./build/jamkey file song.wav
F# minor confidence 0.33 alt: A major, C# minor
mic and stream integrate over --window seconds and print every --hop
seconds. --hysteresis sets how readily the shown key changes.
pw-record --target <sink-id> -P stream.capture.sink=true \
--rate 44100 --channels 1 --format s16 - \
| ./build/jamkey stream -Find the sink id with wpctl status (the * under Sinks). The --format on both
sides must match.
The engine is core/jamkey.h and core/jamkey.c.
#include "jamkey.h"
JamKeyConfig cfg = jamkey_default_config(44100);
JamKeyResult r;
if (jamkey_detect(samples, sample_count, &cfg, &r) == JAMKEY_OK) {
printf("%s\n", r.label); /* "F# minor" */
}Streaming:
JamKeyStream s;
jamkey_stream_init(&s, &cfg, 24.0f /*window*/, 0.05f /*hysteresis*/);
for (;;) {
if (jamkey_stream_push(&s, hop_samples, hop_count, &r) == JAMKEY_OK) {
show(r.label);
}
}core/ the engine (jamkey.h, jamkey.c)
apps/cli/ the jamkey binary
test/ fixtures and `make test`
MIT. Uses the Albrecht and Sha'ath key profiles.