Surround Sound

Finds the sounds around the words: non-verbal vocal sounds (breath, cough, laugh, sigh, …) and background sounds (music, traffic, birds, crowd, …) with start and end times, and writes them into the transcript: [cough] where the speaker makes a sound and <music> … </music> around the words a background sound overlaps.

54 sound tagsone prediction every 40 ms5.8M parametersruns on CPU Model card ↗
A 59-minute documentary, analysed offline. The video is embedded from YouTube (Documentary Central); no audio is hosted here. Transcript: Whisper large-v3 (English and Hindi, detected per chunk) with word times from MMS forced alignment. Press play, then click any word, tag, lane, highlight or event to jump there.
Loading the viewer…

How it works

features16 kHz mono audio → 64-band log-mel spectrogram
modelCNN front end (5 blocks) + Transformer (6 layers, d=256, 8 heads), 5.8M parameters; one probability per tag every 40 ms
long audio8 s windows every 2 s; overlapping predictions are averaged, so times are absolute
events5-frame median filter, per-tag thresholds with hysteresis (an event continues while the score stays above 60% of the threshold), gap joining, 0.15 s minimum length
thresholdssensitive: tuned for recall (F2), catches more sounds and adds more false tags. balanced: best F1 per tag
transcriptWhisper on chunks of up to 28 s cut at pauses (a sentence that runs into a cut moves to the next chunk), language detected per chunk, Whisper's stock phrases over music and repeated phrases removed; each sentence is then force-aligned inside its Whisper time window with the MMS aligner (non-Latin scripts romanized with uroman)

In the viewer each lane shows a tag's probability (line), its threshold (red dashes) and the detected events (shaded), so you can see why a tag fired or did not.

20 speaker sounds, written inline where they start:

breath, sigh, sniff, laugh, cough, throat_clear, cry, gasp, yawn, snore, sneeze, nose_blow, lip_smack, groan, scream, whisper, shout, chew, burp, hiccup

34 background sounds, wrapped around the words they overlap (a lone <tag> covers no words):

nature: bird, insect, dog_bark, animal, wind, rain, thunder, water
people: child_voice, baby_cry, applause, cheer, crowd, footsteps
traffic & machines: horn, vehicle, siren, machine, fan, construction, gunshot, explosion
media & devices: music, tv_speaker, phone_ring, phone_vibrate, beep, bell, whistle, buzzer
household: knock, door, dishes, glass_break

Training data. Synthetic mixtures (event clips placed on speech beds, with noise, babble, reverb, phone-band and gain augmentation) plus real annotated windows from NonverbalSpeech-38K, the Vaani Noise Event Dataset (ARTPARK-IISc), NonverbalTTS, AudioSet-Strong, OpenSLR 99 (Deeply Nonverbal Vocalization), DNC (TU Berlin) and DEMAND. Trained for 10 epochs on one T4; the checkpoint of epoch 6 was kept (best mean macro AP over the validation sets: four real-audio sets and one noisy set).

Limits. Thresholds were tuned on held-out clips from the training sources. On produced media (documentaries, music under narration) the sensitive profile in particular adds false tags, often breath, water or animal under music; the balanced profile is stricter. Some tags have little real training data and are weak (the model card lists per-tag scores). Whisper can still miss or mistranscribe speech, especially short non-English passages and speech over music.

Run it on your audio

Download the model and its inference code, then run it on any file ffmpeg can read:

pip install torch torchaudio numpy scipy safetensors huggingface_hub   # plus the ffmpeg binary
pip install openai-whisper                                              # optional, for transcripts
hf download theboringai-work/surround-sound --local-dir surround-sound
cd surround-sound
python inference.py my_audio.mp3                  # transcript with tags + event list
python inference.py my_audio.mp3 --asr none       # events only
python inference.py my_audio.mp3 --profile balanced --times

From Python, inside the downloaded folder:

from sed import Detector, rich_transcript, clean

det = Detector.from_pretrained("theboringai-work/surround-sound", profile="sensitive")
events = det.detect("my_audio.mp3")
# [{'label': 'cough', 'start': 0.68, 'end': 2.0, 'prob': 0.92}, ...]

# with any ASR that gives word timestamps: words = [(word, start_s, end_s), ...]
print(rich_transcript(words, clean(words, events)))