10
$\begingroup$

This is my snoring detector again.

I've gotten pretty good at detecting a signal when there's anything there -- can track from a wall-peeling snore down to breathing you can't even hear in the recording. The problem is, I can't tell when the signal has dropped below detectable level and the app is just "hearing things". And, unfortunately, snoring/breathing is often irregular enough that a simple autocorrelation or similar interval timing scheme is unlikely to help much. (And it's actually likely that in some cases the noise is more regular than the breathing.)

So, are there any tricks I'm missing for figuring out when there is no signal? It kind of seems that I'm up against a hard place here, given the "signal" is so noise-like to begin with.

(And maybe this is related to another problem I'm having: Strangely, I can't accurately (or even approximately) measure the signal level even when fairly loud. Since I need to use rolling averages and ratios to detect the signal anyway, the level information kind of gets lost. I'm looking for some tricks to reconstitute it.)

Basic technique

(For Yoda)

The audio signal is sampled (generally at 8000Hz, for various reasons), then FFTed in 1024 blocks. (In my experiments Hamming filters and overlapping blocks seem to have little effect, though those may be revisited later.)

The FFT is divided into "bands" (currently 5, slightly skewed in size to place more detail on the low end) and the "spectral difference" and level of each band is summed. Long-term averages of the peak-limited values are used as "thresholds", and further bias adjustments are used to maintain a roughly 20% "over threshold" rate.

Each "over threshold" value is given a weight of 1 (under threshold is given a weight of 0), but then that weight is adjusted by the apparent "variability" (at roughly 2Hz) in the band, to give more weight to bands that carry more apparent signal.

The weights of the bands are summed and then the summed weights of subsequent blocks are summed over about a second to produce a running "score". This is again compared to a running average threshold (plus several heuristics) to detect snore onset/offset.

Update

It suddenly occurred to me that if my algorithm effectively maintains a constant-level signal (per my signal level problem), the way to effectively gauge SNR is by measuring the noise when there's no signal.

Conveniently, snores are intermittent, with lots of "dead air" in-between. And I'm already detecting the snore envelopes. So anything outside of the envelope (between the end of one snore and the start of the next) is presumably noise! This I can (with some modest degree of accuracy/repeatability) measure. (It took three tries to come up with a halfway decent algorithm, of course -- reality never matches the theory.)

So I don't have the full answer yet, but I've made progress.

(While the above technique gives me a fairly good proxy for SNR, I'm still having trouble estimating actual signal level. My "relative level" indications can be off the scale for a barely audible breath and so-so for a window rattler. I need some sort of proxy for absolute level.)

$\endgroup$
4
$\begingroup$

So far you seemed to identify snoring through the presence of a strong periodic pattern in the audio. If you tell me that there might be other sources with the same property, it's time to move on and focus on a property of the signal more specific of snoring ; and I would suggest to look more precisely into the timbre of the recorded sound itself. Given that a human listener can tall apart snoring from breathing, you might resort to traditional audio signal classification approach. Compute features like MFCCs and Fluctuation patterns on your audio data, and train a bayesian classifier (or more sophisticated) to discriminate snoring from breathing.

It might be time also to review the literature on the topic... For example I found this: http://web.itu.edu.tr/kamasak/pubs/pdf/pm7_8_007.pdf

$\endgroup$
  • $\begingroup$ One problem is that the nature of the sound varies so much from individual to individual and even for a single individual within the course of a night. But thanks for the clues -- I'll look into them. $\endgroup$ – Daniel R Hicks Feb 17 '12 at 18:16
  • $\begingroup$ If your training set is large enough, this might still be feasible using machine learning techniques. $\endgroup$ – pichenettes Feb 17 '12 at 19:17
  • $\begingroup$ I have in excess of 50 data sets. Unfortunately, an algorithm that works perfectly on one set fails miserably on the next. Some have TV going in the background, some have air handlers rumbling, etc. Breathing/snores can be barely audible or paint-peeling. A subject may change positions and entirely change the nature of his snoring. Some sort of self-tuning scheme is clearly required. $\endgroup$ – Daniel R Hicks Feb 18 '12 at 0:19
  • $\begingroup$ which features are you using for classification? $\endgroup$ – pichenettes Feb 18 '12 at 1:05
  • $\begingroup$ Primarily just "spectral difference" (also referred to as "spectral flux") to detect "onset" and "offset". But my current scheme divides the spectrum into bands and "weights" each band based on the apparent signal strength in that band (which is judged by the amount of variation in spectral difference occurring at about 1/2 Hz). This works well to detect breathing/snoring across the entire volume range, even with significant noise, but it effectively loses all indication of actual volume. And simply looking at simultaneous total sound level doesn't work due to the noise problem. $\endgroup$ – Daniel R Hicks Feb 19 '12 at 2:57
0
$\begingroup$

You might need to build a dynamic classifier, that can adapt to the current subject, sleep phase, and audio environment, rather than tune to a fixed training set.

$\endgroup$
  • $\begingroup$ That's what I'm thinking. $\endgroup$ – Daniel R Hicks Feb 19 '12 at 2:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.