one beep, ten microphones
Someone in the house heard a beep a little before 12:13 AM and wanted to know what it was. I handed the question to an agent with access to the camera recorder. It came back with the sound, the source and a short list of reasons.
The answer is a smoke alarm. It sounded once, at 3417 Hz for 0.87 seconds, at 00:11:05, about two metres from the Room A camera. It was not a fire, not carbon monoxide and not a dying battery. It was a horn that went off once and stopped.
I didn’t write any of the code that found it. I gave the agent one rule at the start and one correction in the middle. Everything else it worked out, built, ran and cleaned up on its own.
The brief
The recorder is a Shinobi NVR in a virtual machine. Ten cameras, all recording audio, AAC at 16 kHz mono, in 15-minute segments.
All processing happens on the recorder. Only finished clips leave it. Raw footage never does.
That was the whole brief. The agent ran its commands through the hypervisor’s guest agent and pulled the camera list out of Shinobi’s own database. It made a scratch directory on the box, installed numpy and scipy into a private Python environment rather than the system’s, and cut the time window out of every camera’s segments with ffmpeg into mono WAV files. The recordings themselves were only ever read.
When it was done it deleted the scratch directory. What came off the machine was a few candidate reels, a 15-second clip, and the blurred four-room video above, level meters burned in.
Teaching a script to hear
A beep is a narrow, steady tone. Speech, footsteps and dishes smear energy across the spectrum. So the agent wrote a detector around that one idea. It runs a spectrogram at two resolutions, 64 ms frames for sustained tones and 16 ms for short chirps. In each frame it keeps the strongest bin between 0.7 and 7.8 kHz only if that bin stands 15–18 dB above both its own usual level and the rest of the frame. Then the tone has to hold the same pitch for several frames running.
The first pass found plenty of tones. They were birds.
Outdoor cameras are full of birdsong, and birdsong is a narrow, steady tone. The detector did exactly what it was built to do. That’s where I came in, with the only question that needed a person: indoors or out? Indoors, and widen the window to 00:03–00:13.
The agent’s next move was the clever one. A real household beep is one sound heard in several rooms at once. It grouped same-pitch hits within 80 Hz and 0.6 seconds across cameras and ranked them by how many rooms heard each one. The event at 00:11:05 fell straight out: all six indoor cameras, about 30 dB above anything else.
What it is
Then it zoomed in. A narrow filter at 3417 Hz, 10 ms resolution, with a purity check against 2.9 and 3.95 kHz on either side. One continuous tone, 0.87 seconds, the same length on every camera. An FFT put the pitch at 3416–3417 Hz with no real overtones.
An earlier, looser pass had flagged two repeats at 00:13. The purity check showed they were broadband noise that happened to have energy in the band, not tones. The agent went back two days on two rooms and found no other long, pure beep at that pitch. One beep, once.
Residential smoke and CO horns are piezos in the 3–4 kHz range, loud enough to cross a house. Appliance beepers can’t reach five rooms. The pattern does the rest. A low-battery chirp comes back every 30–60 seconds for days. A fire alarm runs Temporal-3, a CO alarm runs Temporal-4, and both repeat for as long as the smoke or gas is there.
Where it is
Arrival-time triangulation was out. Segment start times are only good to about a second, and the camera clocks drift by up to five. So the agent triangulated on loudness.
Same camera model, same stream profile, noise floors all between −87 and −92 dBFS in that band, so it treated the mics as equal. Sound falls about 6 dB per doubling of distance, so a level gap is a distance ratio: B is 3.1 times farther from the alarm than A is, and C is 4.1 times. Each ratio puts the source on a small circle around camera A, an Apollonius circle, with a radius of about a third of the camera spacing. With cameras five or six metres apart, that’s about two metres from camera A, just outside its field of view.
D, E and F are down 26 to 48 dB, which is mostly walls and doors. That rules those rooms out and adds no geometry. The first version measured each camera against its own room’s background. The final one used absolute dBFS, which is the only way turning decibels into metres means anything.
Why it beeped
A smoke alarm that senses smoke or gas doesn’t play one note. It runs its full pattern for as long as the condition lasts. A single short note comes from the unit’s electronics, not its sensor. So the agent went looking for an electrical reason, and read the house’s home-automation history from 00:00 to 00:20, read-only.
UPS input sat at 123.3–123.9 V with no transfer to battery. All 572 entities kept reporting through 00:10:30–00:11:40, so nothing dropped long enough to reset a smart plug. No events at all between midnight and 00:12. Whatever happened was too small for the house to notice.
That leaves a short list. Score each one against the three things the data can test.
Power-restore beep. Hardwired alarms with battery backup beep once when AC power comes back after a drop. A sag of a few cycles is enough, from a compressor kicking on or a grid blip. That’s far too short for a UPS sampling once a minute or any Wi-Fi device to notice. It fits all three.
Self-check blip. The unit’s own fault check hiccups once. More common in older alarms near the end of their ten years. Also fits all three.
Interconnect glitch. A spike on the shared signal wire can make interconnected units chirp once. But then every unit chirps, and cameras near the other alarms would each have heard a loud one of their own. Room D, the only other room likely to have one, is 26 dB down. One loud source, not a chorus.
Shower steam. The bathroom humidity was high at the time, which is the only thing going for it. Steam that trips an alarm sets off the full cycle, and it doesn’t get as far as Room A.
Why 00:11:05 exactly? Nothing logged says, and the agent said so instead of guessing. A sub-second sag on one branch circuit is invisible to every sensor in the house.
Automagically
This used to be a weekend. Pull the files, write the throwaway script, fight the DSP library, squint at a spectrogram, redo it when the first idea is wrong. This time the agent read the recorder’s database, built its own sandbox, wrote an STFT detector, a cross-room clusterer, a purity filter and the geometry, read a second system’s logs, rendered a video with meters on it, and tidied up after itself. It never broke the one rule.
Its write-up even lists its own mistakes. Relative levels where it needed absolute ones. Repeats that a purity check turned back into noise. A coincidence in the humidity log that matched the time and nothing else. The one thing it couldn’t work out was a fact about the house, and that took one sentence from a person.
The requester listened to the clip and said yes, that’s the one.
The last step is a step stool. Press test on the alarm nearest Room A, listen for 3.4 kHz, and read the date on the back.