Methodology · v1.0 · 2026-05-09

How the instrument works.

Pukkala turns audio into a structured record. Twelve acoustic features per event, six deterministic classes, one score. No machine-learning model. No training data. No drift. This page describes the signal-processing pipeline end to end.

01 · Capture

On iOS the recorder is configured for linear PCM, sixteen-bit, mono, sampled at 44.1 kHz, written to a local .wav file. Maximum capture is 5.00 seconds. The choice of PCM over compressed AAC is deliberate: lossy codecs introduce spectral artefacts in exactly the bands we care about (sub-300 Hz fundamentals, mid-band turbulence). Android records to AAC by default; the analyzer falls back to a synthesized feature set there until a wasm AAC decoder is wired.

02 · Pre-processing

The PCM samples are normalized to Float32 in [-1, 1]. The full sample stream is used for time-domain features. For spectral analysis, we pick the loudest 4096-sample window — chosen by maximum RMS over a hop of 1024 samples. This discards silence at the head and tail of the recording without truncating the event itself, and gives a clean basis for the FFT.

03 · Time-domain features

  1. Durationlength(samples) / sampleRate, capped at five seconds.
  2. Peak amplitude20 · log₁₀(max(|samples|)), in decibels with a -120 dB floor.
  3. RMS energy√(Σ samples² / N), the root-mean-square of the full waveform.
  4. Zero-crossing rate — count of sign changes between adjacent samples, normalized per-sample.

04 · The FFT

We use a 4096-point radix-2 Cooley-Tukey FFT, hand-written in TypeScript with no external dependencies. Before the transform, the window is multiplied by a Hann taper (0.5 · (1 - cos(2π·i/(N-1)))) to reduce spectral leakage at the boundaries. The output is real and imaginary arrays; we take the magnitude spectrum (√(re² + im²)) of the first 2048 bins, covering 0 Hz to 22.05 kHz at 10.77 Hz resolution per bin.

05 · Spectral features

  1. Spectral centroid — the magnitude-weighted mean frequency. Reads as perceptual brightness. (Σ f·M(f)) / Σ M(f).
  2. Spectral flatness — Wiener entropy. The geometric mean of the spectrum divided by its arithmetic mean. Pure tones score near 0; white noise approaches 1.
  3. Spectral rolloff — the frequency below which 85 % of the total spectral energy is contained.
  4. Dominant frequency — the frequency of the highest-magnitude bin, excluding DC. Used as a proxy for the fundamental.

06 · MFCC

Thirteen mel-frequency cepstral coefficients, computed from a 40-band triangular mel filter bank applied to the magnitude spectrum, followed by a DCT-II of the log-energies. We omit pre-emphasis and use a simplified filter-bank shape — sufficient for use as opaque features in the AI interpretation pass. MFCC[0] tracks overall energy; subsequent coefficients capture spectral envelope detail.

07 · Classification

Six deterministic classes — silent, squeaker, trumpet, wet, compound, classic — are assigned by distance to centroid in feature space. Each class is defined by a small set of feature thresholds. Trumpet, for example: spectral flatness below 0.30, dominant frequency between 120 and 250 Hz. Wet: zero-crossing rate above 0.15. Squeaker: dominant frequency above 350 Hz, duration under 800 ms. There is no learned model and no training corpus. We chose deterministic over data-driven for two reasons. First, we don't have a large labeled dataset of acoustic flatulence events with which to train responsibly. Second, the math is auditable. A user can see why their event was classified the way it was.

08 · The score

A single number from zero to one hundred. Four weighted terms:

pukkala_score = 0.25·duration
              + 0.35·intensity
              + 0.25·spectral_richness
              + 0.15·rarity

Duration is the normalized event length, capped at 5 s. Intensity is an A-weighted composite of peak amplitude and RMS energy. Spectral richness is the geometric mean of normalized centroid, rolloff, and the first three MFCCs. Rarity is the cosine distance between this event's feature vector and the centroid of your last hundred events. Critically, the rarity term is benchmarked against your own history — there is no global leaderboard. A common event for one user can be a rare event for another; the score reflects that.

The reference implementation lives in lib/audio/dsp.ts and lib/audio/analyzer.ts in the iOS app. It's pure TypeScript, no external math libraries — you can read it cover to cover in fifteen minutes. Privacy practices and data flows are documented separately in the Privacy Policy.