Post

Automating Notification Sounds with Audio Fingerprints

Using worldveil/dejavu audio fingerprints to verify a notification sound while separating capture failure, sound occurrence, false matches, and business completion.

A notification sound leaves no trace on screen, so it is tempting to end verification with “the sound matched.” That single result actually hides four different questions: did the recording capture audio at all, did the sound occur, was it the expected sound, and did the user receive the notification? This article describes verifying notification sounds with audio fingerprints, and what I would add if rebuilding it today.

First, a correction. The original post introduced appbaseio/dejavu from Docker Hub as an audio fingerprint tool. That project is an Elasticsearch data viewer. Audio fingerprinting uses worldveil/dejavu.

Four branches: audio stream present, sound present, false-match check, and business completion

Figure 1. The four questions need different evidence. If the first one fails, the rest prove nothing.

What a fingerprint answers

An audio fingerprint does not compare waveforms. It extracts prominent peaks in time and frequency, turns them into hashes, and counts how many hashes overlap between a short recording and a reference. That is why it tolerates some compression and background noise.

worldveil/dejavu provides this in Python. The original environment ran PostgreSQL through Docker Compose and used the songs and fingerprints tables. Installation and run commands should come from the current README.

1
2
3
4
5
6
7
8
from dejavu import Dejavu

config = {
    "database": {"host": "127.0.0.1", "user": "dejavu", "password": "change-me", "database": "dejavu"},
    "database_type": "postgres",
}
djv = Dejavu(config)
djv.fingerprint_directory("reference_sounds/", [".mp3", ".wav"])

This is an abbreviated example of the shape. Configuration keys and supported databases vary by library version.

First check: does the recording contain audio?

Android’s adb shell screenrecord records video only and includes no audio (official documentation). Appium’s Android screen recording often uses the same command, so check the resulting file rather than assuming. The original states only that the device’s Screen Recording was used; whether that file contained audio cannot be verified here.

Run this only after confirming the file exists:

1
ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of csv=p=0 notification.mp4

Empty output means there is no audio stream. That is not “the notification did not play”; it is “the capture path did not record sound.” Extract only when a stream exists.

1
ffmpeg -i notification.mp4 -vn -acodec pcm_s16le notification.wav

Then check whether the extracted file is silent. This command is meaningful only when an audio stream exists, and it does not exercise the matching algorithm.

1
ffmpeg -i notification.wav -af volumedetect -f null -

If mean_volume and max_volume are extremely low, look at the recorder, permissions, and volume settings first. Matching a silent file returns “no match,” and reading that as “no notification” points at the wrong cause.

Choose a threshold with positive and negative controls

Dejavu returns a matched reference and a score. The hard question is the cutoff. Do not copy one from a tutorial: the distribution depends on the device, microphone position, volume, ambient noise, and OS audio processing.

Collect four kinds of samples in your own environment instead.

KindExampleExpectation
PositiveThe expected notification actually playsHigh score against the reference
Negative AOnly other system soundsLow score
Negative BThe notification does not playLow score
ConfuserA different notification from the same appDistinguishable

If the lowest positive score sits clearly above the highest negative score, put the threshold between them. If the distributions overlap, no threshold on those samples separates them without error; a policy must trade false positives against false negatives, add a UI or API check, or change the recording conditions and signal. This procedure does not promise any accuracy figure.

Keep the samples used to choose the threshold separate from the samples used to validate it. Choosing and validating on the same samples cannot support a generalization claim. The original observation — matching despite a TV and family conversation — is one environment’s result, not performance under all noise.

Once a threshold exists, log the score and the version of the reference audio. A score alone cannot explain why a run passed after the reference changed.

A diagnostic table

SymptomLook at firstMeaning
Always “no match”ffprobe output, signal presence, reference and time window“No match” alone does not prove capture failure; check stream, signal, and reference in order
Mostly silenceVolume settings, microphone permission, recording start timeCannot judge whether the sound occurred
Low positive and negative scoresWhether the reference is the actual notificationReference mismatch or conversion loss
High negative scoreScore gap against the confuserFalse match; separate references or change the checkpoint
Match passes but the user sees nothingUI screenshot, API stateSound and notification display are separate

“The right event happened but a different sound played” and “no sound played” are different failures. Both can score poorly against a single reference, so that score alone cannot distinguish them. Registering other notification sounds as references lets you check for a match to a different sound. Use the scenario step and time window to narrow down whether the cause lies in configuration, the sound asset, or audio acquisition.

Sound is not business completion

The sound occurring does not prove the notification was displayed correctly or the order was processed. Combine audio with UI or API checks. On failure, keep the original and extracted audio, the reference used, the score, and the scenario step. Keeping only the recording loses the comparison basis.

Verification boundary

The command patterns were checked only on synthetic files. With FFmpeg 8.1, a video-only MP4 produced empty ffprobe -select_streams a:0 output, a silent WAV reported pcm_s16le with mean/max -91.0dB, and a 440Hz tone reported -21.1/-18.1dB. That is a narrow check that stream selection and volumedetect behave as documented. Real device recording, Dejavu recognition, threshold accuracy, processing time, and business completion were not tested, and no recommended thresholds are given.

Three things are immediately actionable: check for an audio stream before matching, set the threshold from positive and negative controls, and retain both the recording and the reference on failure. With those, “did it make a sound” and “did the check fail” stop being the same question.

Discuss an automation or AX problem with Kitae at KFlowLabs

This post is licensed under CC BY 4.0 by the author.