App · Android · submitted to Google Play

Something is beeping. But where?

A charger, a smoke alarm with a flat battery, a headphone case, an uninterruptible power supply in the basement. It can be heard but not found. BeepHunt listens in, remembers exactly this one sound and, while the user walks around, says whether it is getting warmer or colder.

Finished · submitted to Google Play for review, not yet in the store
BeepHunt while searching: strength on a scale up to 100, the trend “warmer”, the time until the next beep and the probable direction.
The app

Listen, choose, find

The list of sounds heard: recurring tones with duration, interval, frequency and stars for how evenly they recur.
Candidates. Sorted by how evenly the rhythm is kept, not by loudness.
The finder: strength up to 100, the trend “warmer”, the best value and the prediction of the next beep.
Finder. Strength 0–100 with trend, best value and “next beep in … s”.
The radar sweep, marked as experimental: a circular chart after one turn on the spot, with the probable area and its confidence.
Radar sweep. Turn once on the spot, then a circle shows the probable area with a confidence value – not an exact bearing. Experimental.
  • Second phone. Can optionally be paired to determine the direction.
  • Display that turns with you. During a search it turns automatically when the phone is held flat and pointing away from the body. Can be switched off in the settings.
  • Help in the app. Three-step introduction, questions and answers, feedback by e-mail.
  • German and English. Android 9 and later, about 2.5 MB.
  • Offline. No recordings, no upload, no account, no ads. The microphone only runs during a search.
Starting point

Why the ear alone is not enough

A beep every two seconds is loud enough to drive anyone mad and too short to follow. Before anyone has turned their head, it is over. If it comes from a drawer, from behind a cupboard or from the ceiling, it sounds much the same everywhere, because the room reflects it off every wall.

The obvious tools do not help. A sound level meter measures everything at once: fridge, street, one's own breathing and the beep add up to a single number. A spectrum analyser does show frequencies, but leaves all the work to the viewer.

Everything adds up

What arrives at the microphone is the sum of all sound sources. Following one of them means separating it out of this sum first.

Louder does not mean closer

Standing waves form in a furnished room. With high tones, two centimetres can decide between loud and quiet.

A phone has one ear

Hearing direction needs at least two separate recording channels. Many devices report two and in reality deliver one.

The process

Listen, select, go there

The app asks one question and offers one button. Everything else happens in the background; what it can do beyond that depends on the device, but the path to the find works with a single microphone.

01

Listen

The app listens in and keeps the last 15 seconds in memory. This lead time is necessary: by the time the button is pressed, the beep is long gone.

02

Select

What was heard becomes a short list, sorted by how precisely a device keeps its rhythm – not by what is loudest. Each entry is described so that it can be recognised: “88 ms, every 3.00 s, 3.10 kHz, heard seven times”.

03

Track

From now on only this one sound counts. The rest of the room is ignored. While the user walks around, the app reports warmer or colder and remembers the best spot.

04

Direction, where possible

Where the hardware allows it: a turn on the spot for a rough sector, two real microphones for a bearing, a second phone for locating within the room.

The physics behind it

What happens between microphone and arrow

Eight questions that had to be answered while building it – what can and cannot be determined from a variation in air pressure. All numerical values are the ones the app actually calculates with.

Time versus frequency: not both at once

To know which frequency is sounding at the moment, a piece of signal has to be examined. The longer this piece, the more precise the frequency and the less precise the point in time. This is not a question of effort but a property of the Fourier transform: for a window of length N the frequency resolution is fixed.

Δf = fs / N At a 48 kHz sampling rate and a window of 2048 samples, that is 23.4 Hz, and at the same time 42.7 ms over which it is smeared.

23.4 Hz is more than the difference between two units of the same device. So one window is not enough. BeepHunt therefore calculates three resolutions at once, each for the task it is suited to.

StreamWindowFrequency gridTime resolutionTask
Fast102446.9 Hz5.3 msexactly when a tone starts
Main204823.4 Hz10.7 msdetection and recognition
Fine409611.7 Hz21.3 msdetermining the frequency precisely

Even the finest grid would still be too coarse. The actual frequency almost never lies exactly on a grid point, but on a logarithmic scale the main lobe of a sine tone is almost a parabola. Fitting a parabola through the largest point and its two neighbours, its vertex gives the frequency to about one per cent of a grid step.

The same window length also distorts the measured duration: a window already sees the tone as soon as it reaches in, and still sees it when it is almost out. Every event therefore measures too long, and by a constant amount, regardless of how long it really was. For the window used, that is 19.1% of its length. Subtracting it turns a measured 108 ms back into the 100 ms that the device actually beeps.

When is a spike an event?

A fixed threshold in decibels does not work: in a quiet flat it is too high, next to a street too low. BeepHunt therefore never measures in absolute terms, but always relative to the background at the same point. The method comes from radar technology, where it is called CFAR, after the constant false alarm rate it guarantees.

Background from the neighbours, not from the past
Cell under test REFERENCE REFERENCE GUARD GUARD Level Frequency → pink background falls off towards high frequencies

The guard gap prevents the tone being measured from raising its own background.

Three details carry the whole method:

DecisionWhy
Average power, not decibels Averaging decibels actually forms the geometric mean, and that lies below the true background. Every signal-to-noise ratio therefore comes out too good. This one detail was the difference between zero and 1014 false alarms over twelve seconds of noise.
Take the larger side Room background is pink: it falls off towards high frequencies. A symmetric window over a sloping curve averages the two sides and sits too low on the loud side. In the lower two octaves, that permanently invents 12 to 13 dB of signal-to-noise ratio that does not exist.
Across frequency, not across time Estimating the background from the past of the same frequency loses every continuous tone: after one minute, the past is that tone, and it is measured against itself. Only estimating from the neighbouring frequencies lets a coil whine stand out permanently.

The power of a noise cell is exponentially distributed. This makes it possible to calculate how often pure chance breaks the threshold:

P(exceeding by T dB) = exp(−10T/10) At 12 dB that is 1.3·10⁻⁷ per cell. Over 800 cells and 94 frames per second, that gives roughly one false alarm every two hours. At 9 dB it would be several per second.
A feature that does not shift with volume

The sound pressure of a point source decreases with distance. Moving in from five metres to half a metre changes the level by around 20 dB. A recognition feature that shifts with volume would therefore stop matching precisely when one is closest.

The solution lies in a rule of arithmetic: a gain is a multiplication in amplitude terms and an addition in decibel terms. Subtracting its own mean from the spectrum in decibels removes exactly this constant term, and with it the volume, without losing the shape.

10·log(a·x) = 10·log(a) + 10·log(x) The first term is the distance. Subtract the mean, and it is gone.

Every other feature is made independent in the same way: the envelope by its own maximum, the frequency curve by its median, the partials relative to the strongest. Duration and harmonicity are level-independent anyway.

When comparing two recordings, frequency carries the most weight, because the oscillator that sets the pitch of a beeper changes neither with distance nor with viewing direction. What is compared is the relative deviation: 30 Hz is a rounding error at 12 kHz and a different tone at 400 Hz. Envelope and duration weigh less, because the room's reverberation smears the end of every event. On top of that, cheap beepers are no clocks: the same alarm clock delivers 92 ms and, minutes later, 104 ms once its oscillator has warmed up.

Recognising rhythm when beats are missing

A device beeps every two seconds. When a lorry drives past, one beep is drowned out, and the intervals become 2.0 · 2.0 · 4.0 · 2.0. The median is still 2.0, but the 4.0 pushes the spread up so far that the beat counts as irregular and the countdown switches off – precisely when one is standing in a noisy environment and needs it most.

That is why intervals are folded: if an interval fits an integer multiple of the fundamental period, it is divided by that multiple and admitted again. 4.0 becomes two beats of 2.0. The fundamental period itself is estimated from the shorter half of the intervals, because the average of all intervals would be pulled upwards by every gap, and a fundamental period that is too long no longer folds anything correctly.

Why the level does not give a distance

In a free field it would be simple: the level falls with distance, so the distance could be calculated from the level. In a room, that does not hold. The direct sound overlaps with the reflections from the walls, and with a pure tone this creates a fixed pattern of maxima and minima in the room. Their spacing is half a wavelength.

λ/2 = c / (2f) ≈ 343 / (2 · 8000) ≈ 2.1 cm At 8 kHz, loud and quiet are therefore a good two centimetres apart. Two steps to the side can reverse the level without getting any closer to the source.

The app therefore never calculates a distance from volume. It only says warmer or colder, uses a dead zone of 1.5 dB for this, and asks for several small steps instead of one big one. In addition, it remembers the best value reached so far, so walking past the spot shows up.

Direction from a time difference

Two microphones a distance d apart hear the same sound wave one after the other. Because the source is far enough away for the wavefront to be treated as plane, the time difference depends only on the angle.

θ = arcsin( c·τ / d ) c is the speed of sound, τ the measured time difference, d the microphone spacing. Zero degrees is perpendicular to the connecting line.

At a 48 kHz sampling rate, a single sample corresponds to 7.1 mm of path difference. The peak of the cross-correlation is therefore interpolated between samples; the result is better than 0.4 samples.

Two limits are hard here. First: |c·τ| ≤ d must hold. A larger difference cannot be a direct path, only a reflection. It is discarded rather than clipped to ±90°, which would otherwise produce a confident bearing straight ahead or straight behind whenever in doubt.

Plane wave arriving at two microphones
A B d c·τ θ Wavefront

Second: a pure phase measurement is only unambiguous as long as the microphone spacing is at most half a wavelength. Beyond that, the pattern repeats.

funambiguous = c / (2d) ≈ 343 / (2 · 0.13) ≈ 1.3 kHz A phone with 13 cm between its microphones is therefore only unambiguous up to about 1.3 kHz. Almost every beep being searched for lies above that.

A narrow-band tone therefore produces a correlation that oscillates at its own frequency and every 1/f has peaks of almost equal height. Taking the highest means taking an arbitrary one. That produces bearings that are off by dozens of degrees while looking very convincing. BeepHunt therefore also forms the envelope of the correlation. It does not oscillate along, its width depends on the bandwidth rather than on the centre frequency, and it shows which peak is the right one. Only within this peak does the actual correlation provide the fine detail.

What even that does not resolve: two omnidirectional microphones measure exactly one number, and one number cannot fix a direction in space. Every bearing is really a cone around the connecting line; in the plane, this leaves an ambiguity between front and back. The app names both possibilities and suggests two steps to the side instead of picking one and being wrong half the time.

And finally, everything depends on a quantity that no sensor measures:

c(T) = 331.3 + 0.606 · T [m/s, T in °C] Between a garage at 5 °C and a room at 25 °C, that is 3.5%, and it feeds straight into every angle. That is why there is a setting for it.
Two phones: first the clock, then the location

Two phones hear the same beep at different moments, and the difference could be used to determine the location. Before that, however, there is a problem bigger than the effect being measured: the audio clocks of two devices have nothing to do with each other. As long as their relationship has not been measured, a time difference between them is not imprecise but meaningless.

Two nominally identical 48 kHz crystals typically differ by a few tens of millionths. 25 ppm amounts to 3 ms after two minutes, which converts to a good metre of path difference – in a measurement whose entire useful signal is a few hundred microseconds.

tB = a · tA + b b is the offset at the start, a the rate difference of the crystals. Both have to be measured before anything is compared.

The measurement uses sound, not the network: Wi-Fi latencies fluctuate in the millisecond range and differ between the two directions. They would shift the result, not merely scatter it. The room, on the other hand, is the only clock both devices truly share. Each phone plays a short sweep, and both hear both. The two directions give two equations:

Offset = (D₁ − D₂) / 2 Travel time = (D₁ + D₂) / 2 The clock offset and the distance between the devices come out of the same measurement. Because each device picks up its own tone by actually listening, instead of relying on the playback command, the device-specific output latency cancels out as well.

The calibration tone is a logarithmic sweep from 2 to 10 kHz in 60 ms. A click would have the sharpest autocorrelation, but would need a high peak amplitude for it. A tone burst would be quiet and unobtrusive, but correlates with itself when shifted by whole periods – every 0.25 ms at 4 kHz, i.e. every 8 cm of apparent distance. A sweep spreads its energy over time and therefore stays quiet, but has the bandwidth that a sharp response needs.

Only then does the actual localisation begin. The time difference between two receivers places the source on a hyperbola: all points whose difference in distance stays the same. With two devices, that is the honest result, and the app draws the curve instead of claiming a point. Only a third microphone provides a second hyperbola and thus an intersection.

Two devices give a curve, three give an area
TWO DEVICES A B all locations with the same difference in distance THREE DEVICES Uncertainty as an area, not a point

The uncertainty does not come from a formula but from randomly perturbing each measurement 160 times within its own scatter and solving again. If the devices stand almost in a line, this produces a long, thin crescent, and that shape is at the same time the instruction to move a phone.

What the app explicitly does not claim

The most valuable part of a measurement is stating where it ends. This list also appears in the project documentation.

Not claimedBecause
Distance from volume Standing waves mean the level in the room does not change monotonically with distance.
Front or back from two microphones A time difference is one number and cannot fix a direction in space.
A point from two devices A time difference gives a hyperbola, not a location.
A location from devices in a row The arrangement is poorly conditioned; the error slides away along the line.
A bearing from half a turn Sectors not visited stay empty instead of being filled in between.
Which device is beeping A tone is a tone. Deriving a make from it would be guesswork. The app describes the waveform, for example “short high electronic tone”.
That a signal is harmless Never. If a sound resembles a warning device, the app points this out neutrally and says nothing else. False reassurance about a smoke or CO alarm would hurt the user, not me.
Expansion stages

What the device can do decides

Android audio hardware varies widely, and no data sheet describes it reliably. Instead of demanding minimum equipment, the app is built as a ladder: the lowest rung needs one microphone and works on its own. Each further rung is added when its prerequisite is really met.

1 Search and track detect, recognise, warmer and colder, best value, remember sounds one microphone
2 Radar sweep turn on the spot, get the probable sector gyroscope
3 Bearing from the device microphones direction from the time difference two truly separate channels
4 Bearing via headphones the same with stereo microphones at the ear LE Audio with stereo input
5 Direction with two phones curve instead of point, plus a direction band second Android device on the network
6 Locating in the plane intersection with uncertainty ellipse three distributed microphone positions
7 Third position by moving a phone calculation in place, guided operation still missing two phones, one of them is moved

The rule above all of this: No stage may make a lower one worse. If the stereo signal turns out to be a mix of a single microphone, warmer and colder stays exactly as good as before. If the connection to the second phone drops in the middle of a search, the single-device search carries on without any window popping up.

This check is not a formality. Manufacturers ship stereo recordings in which both channels come from the same processing. Such a signal correlates perfectly at zero delay on every event, and a naive evaluation turns it into a rock-solid bearing of 0°, always straight ahead, no matter where the source is. What matters is therefore not the height of the correlation, but the spread of the measured delays: two real microphones in a real room see values that move. Two channels from one source do not.

Architecture

Where the computation lives

Kotlin, Compose for the interface, otherwise nothing third-party in the computing core. The signal processing deliberately sits in a module of its own without any Android dependency.

ModuleContent
Computing core Pure Java module: Fourier transform, background estimation, fingerprints, correlation, geometry, least-squares fitting. No Android, no Compose. That makes it testable in milliseconds, and the layering rule is enforced by the build tool rather than by discipline.
Recording Microphone access, ring buffer, check of the device's capabilities.
Platform Sensors, distance measurement, connection to the second phone via network service discovery and a simple connection on the local network. No third-party service, so that the function does not depend on a service that not every device has.
Flow The search lives in the application, not in a screen: a hunt for a beep that comes once a minute has to survive the device being rotated.

The recording thread only writes values into the ring buffer and never blocks. The analysis thread reads at its own pace, and if it cannot keep up, it skips ahead and reports this, instead of holding up the writer or reading stitched-together audio. A gap costs one event; corrupted audio produces confident nonsense.

98 tests

All on the Java side, run time around 14 seconds. They test against artificially generated signals: beeps at 1 and 8 kHz, 25 dB level difference, two sources at once, speech and music as interference. And silence when there is only noise.

The test tone comes from the app

The signal generator is not in the test folder but in the program itself. The diagnostics view uses it to send the same reference tone that the tests run against.

Android 9 and later

Only from this version onwards does the system report where a device's microphones are located. Without this information, no angle can be derived from a time difference.

Microphone

What is listened to and what happens to it

An app that listens has to be able to say something about it. The audio is analysed exclusively on the device.

  • The microphone only runs during a search that the user has started.
  • The audio sits in a ring buffer in memory and is discarded at the end of the search. It is not written to storage.
  • Only abstract features are stored: frequencies, durations, repetition intervals, spectral shapes.
  • Nothing is uploaded. No account, no analytics tools, no advertising components.
  • In a search with two phones, short snippets of about 0.25 seconds around a confirmed detection go over the local network. No continuous audio stream, and no third-party server.
  • No camera, no contacts, no location.
Status

Where the project stands

Started on 16 August 2026. The app is finished and is under review at Google Play. As soon as the review is complete, it will appear in the store.

Done

Core and direction finding

Event detection, fingerprints, rhythm, target tracking, radar sweep and the calculation for a second phone.

Done

User interface

Candidate list, finder, radar, introduction, help and questions, feedback by e-mail. German and English.

Done

Closed test

No crashes and no bugs reported, only suggestions for improvement. All of them have been implemented. 126 automated tests pass.

In progress

Review at Google Play

Version 1.0.1 has been submitted for release.

After that

In the Play Store

As soon as the review is complete, the app will appear in the store automatically.

Done, coming next

Version 1.0.2: filter for high and low tones

Above the list there are three buttons: All, high peep, low hum. The boundary is 1 kHz. So the fridge, router and heating drop out when a detector is beeping. Will be released once 1.0.1 has been approved.

Contact

Questions about the construction, the decisions behind it or how it transfers to another case.

Arithmetic check Arithmetic task, shown as an image

No Google captcha, no cookies, nothing passed to third parties. Prefer e-mail? info@timobritz.de.