Wang, Lindlbauer & Donahue (CMU), UIST 2024

Music-Aware Virtual Assistants
Notifications that Sing

You are driving with music on and your phone needs to tell you something. Instead of stabbing the volume down to bark "Meeting in ten minutes," what if the assistant sang it — in your song's key, on its beat, in place of the chorus? This paper makes that real.

Prerequisites: Basic audio + a little music intuition
11
Chapters
7
Simulations

Chapter 0: The Problem

People listen to music in exactly the situations where a screen is useless: driving, running, cooking, walking. These are also the situations where a virtual assistant earns its keep — it is the only channel available when your eyes and hands are busy. So the assistant must reach you through your ears. The trouble is that your ears are already occupied.

Today's fix is brutal. When a notification arrives, the system ducks the music — it slams the volume down (or pauses entirely), reads the message in a flat synthetic voice, then fades the song back in. The paper calls this the conventional baseline, and it is jarring for a reason: the spoken voice has no relationship to the music. Different pitch, different rhythm, different emotional register. It is an interruption, full stop.

The core question of the paper: Can we deliver the same information a spoken notification carries, but in a form that fits inside the music the listener already chose — so it feels less like an interruption and more like a moment of the song?

Why "just say it" is the wrong default

Notice the implicit assumption in every assistant ever shipped: a notification is text, and text wants to be spoken. But that is a UI convention, not a law. The information ("you have a meeting in 10 minutes") is independent of its delivery modality. You could flash it, vibrate it, speak it — or, as this paper proposes, sing it.

The authors are HCI researchers, so their unit of success is not a benchmark number — it is whether a human prefers the experience. Their thesis: a notification that matches the music in rhythm and pitch and slips in where the vocals would be will feel delightful rather than disruptive. The whole paper is the machinery to test that thesis.

What has to be true for this to work

Singing a notification "in harmony" is a deceptively large ask. To not sound terrible, the sung notification must agree with the song along several axes at once:

AxisWhat it meansWhat breaks if you ignore it
Key / pitchSing notes that belong to the song's scaleOff-key notes sound painfully wrong
Beat / rhythmStart syllables on the song's beatsLyrics drift, feel "off", lose groove
Tempo / paceDeliver words at the song's speedCramming or stretching sounds robotic
Timbre / placementSit where a voice belongs in the mixTwo voices clash; muddy or doubled

That table is the spine of this lesson. Each axis becomes a stage in the system, and each stage is a small classic of music information retrieval (MIR) or audio synthesis bent to a new purpose.

What is the central problem the paper attacks?

Chapter 1: The Key Insight

The idea in one sentence: turn the notification text into a sung voice whose melody and timing agree with the song, then temporarily swap that sung voice in for the song's original vocals.

Read it again, because two distinct moves are hiding in there. First, generation: you must produce a singing voice that says the notification words but obeys the song's musical rules. Second, substitution: you must place that voice into the music where a voice already lives — the vocal track — so the listener hears one coherent performance, not a duet of strangers.

The insight: the song's existing vocal line is a perfect "slot." A pop song's mix is built around a lead vocal — there is room carved out for it in pitch and timing. If you remove the original vocal for a few seconds and drop your sung notification into that same slot, the notification inherits the song's musical context "for free." You are not adding a voice on top; you are replacing one.

Why this beats the alternatives the authors considered

Imagine the design space. You could (a) play a spoken notification over the music — but then two unrelated audio streams fight, and the speech is hard to parse. You could (b) duck the music and speak — the conventional baseline, jarring. Or you could (c) generate a sung notification and layer it on top — but now you have two voices, the original singer and the notification, talking over each other.

Option (d), the paper's choice, is to generate a sung notification and remove the original vocal during it. One voice at a time, musically consistent. That single decision — replace, don't overlay — is what makes the result feel like part of the song.

The pipeline in one breath

Everything downstream is the realization of that insight. We will build each box from scratch:

analyze the music
Estimate the song's beat positions and musical key from the audio (Ch 3).
compose a melody
Assign the notification's syllables to in-key notes landing on beats (Ch 4).
synthesize singing
Render the text+melody as an actual singing-voice waveform (Ch 5).
swap & mix
Suppress the original vocal in that window, mix the sung notification into the slot (Ch 6).
What is the single design decision that distinguishes the paper's approach from simply layering a sung notification on top of the song?

Chapter 2: Music 101 — Just Enough Theory

Before we can analyze a song or compose a melody, we need a precise vocabulary for three things the system manipulates: pitch, scale/key, and beat. If you already know music, skim; if you don't, this is all you need.

Pitch is log-frequency

A musical note is a frequency in Hz, but our ears hear pitch logarithmically: doubling the frequency raises the pitch by one octave, and that feels like an equal step whether you start low or high. So music uses a log scale. The standard mapping from a MIDI note number n to frequency is:

f(n) = 440 · 2(n − 69) / 12

Symbol by symbol: n is an integer note number (60 = middle C, 69 = A4), 440 Hz is the reference pitch A4, and 12 is the number of equal half-steps (semitones) per octave. The 21/12 factor is one semitone — the smallest step on a piano. Move up 12 of them and frequency doubles.

A key is a 7-note menu carved from the 12

There are 12 pitch classes per octave (C, C#, D, ... B). A key (say "C major") picks 7 of them as "in-bounds" — the notes that sound consonant together. The C-major scale is the white keys: C D E F G A B. Sing any of those over a C-major song and it fits; sing a black key and most people wince. This is the single most important constraint on our melody.

Why keys matter for us: the notification's melody must use only notes in the song's key. Getting the key wrong by even one step (say C major vs C# major) makes every note sound sour. So "detect the key" (Ch 3) is not optional polish — it is the gate that decides whether the whole thing is listenable.

Beat is the clock you tap your foot to

Underneath a song is a steady pulse — the beat. Its rate is the tempo, measured in beats per minute (BPM). At 120 BPM there is a beat every 0.5 s. Lyrics in real songs almost always start syllables on or near beats; that alignment is what makes singing feel "in the pocket." Our notification must do the same: each syllable starts on a beat.

Pitch & Scale Explorer — build a key

A piano octave. Highlighted keys are in the chosen scale (singable); dim keys are out. Change the root and watch the 7-note menu slide. The frequency under each highlighted key is computed from the formula above.

A key (e.g. C major) is best described as:

Chapter 3: Music Analysis — Reading the Song

The system receives only the audio of whatever the user is playing — no score, no metadata. So the first job is to listen: recover the beat grid and the key from the raw waveform. These are two of the oldest tasks in MIR, and the system uses well-established estimators (the paper builds on standard music-analysis tooling rather than inventing new detectors).

Beat tracking, from intuition to algorithm

Where are the beats? The classic recipe has three steps. (1) Compute an onset strength signal — at each instant, how much new energy just appeared (a drum hit, a strummed chord). You get this from the frame-to-frame increase in spectral energy. (2) Find the dominant period of that onset signal — its autocorrelation peaks at the beat period, giving tempo. (3) Lay down a grid at that period and slide it to line up with the strongest onsets.

onset(t) = ∑f max(0,  |X(t,f)| − |X(t−1,f)|)

Here |X(t,f)| is the magnitude of the spectrogram at time frame t and frequency bin f. The max(0, ·) keeps only increases in energy (note onsets), discarding decays. Sum over all frequencies and you get one number per frame: "how much started right now." Peaks in this curve are candidate beats.

Key detection via pitch-class profiles

To find the key, count how much energy each of the 12 pitch classes receives across the song — a 12-bin histogram called a chroma or pitch-class profile. A song in C major spends most of its energy on C, E, G and the other white keys. Then correlate that 12-bin profile against a stored template for each of the 24 possible keys (12 roots × major/minor); the best-matching template wins.

Why this is robust: you are not trying to transcribe every note (hard, error-prone). You are asking a coarse statistical question — "which 7 notes get the most energy overall?" — which is far more forgiving of noise, percussion, and polyphony. The output we actually need from this stage is tiny: one key label (e.g. "A minor") and a list of beat timestamps. Everything else about the song is discarded.
Beat Tracker — onset strength → beat grid

The orange curve is a synthetic onset-strength signal — spikes are note onsets. Drag Tempo to change the underlying pulse; drag Noise to muddy the signal (weak onsets, distractor peaks). Toggle the beat grid (teal lines) to see where the tracker would place beats. Watch how high noise makes the grid drift off the true onsets — the failure mode of beat tracking.

python
# Beat + key from raw audio with librosa (the standard MIR toolkit)
import librosa, numpy as np

y, sr = librosa.load("user_song.wav")            # waveform [T], sample rate

# --- beats ---
onset = librosa.onset.onset_strength(y=y, sr=sr)    # [frames]: energy increases
tempo, beat_frames = librosa.beat.beat_track(onset_envelope=onset, sr=sr)
beat_times = librosa.frames_to_time(beat_frames, sr=sr)  # [N] timestamps (s)

# --- key (via chroma / pitch-class profile) ---
chroma = librosa.feature.chroma_cqt(y=y, sr=sr)     # [12, frames]
profile = chroma.mean(axis=1)                     # [12] avg energy per pitch class
# correlate `profile` against 24 Krumhansl key templates -> argmax = key
key = best_key(profile)                             # e.g. "A minor"
print(tempo, beat_times[:4], key)               # 120.0 [0.46 0.95 1.44 ...] A minor
Why detect the key with a 12-bin pitch-class profile rather than transcribing every note?

Chapter 4: Melody Fitting (the showcase)

Now the creative heart. We have the song's key (a 7-note menu) and its beat grid (timestamps). We have notification text — say "Meeting in ten minutes". We must produce a melody: an assignment of each syllable to a pitch (from the key) and a start time (on a beat) and a duration. This melody must agree with the song and be singable for the words.

Step 1 — text becomes syllables

You sing syllables, not letters. So first split the text into syllables: "Mee-ting in ten mi-nutes" → 6 syllables. Each syllable will get one note (held notes can span more, but one-note-per-syllable is the clean baseline). The count of syllables tells you how many notes — and beats — you need.

Step 2 — syllables snap to beats

Lay the 6 syllables onto consecutive beats from the grid. If the song is 120 BPM, beats are 0.5 s apart, so the notification spans 6 × 0.5 = 3 seconds. The tempo of the song sets the pace of the notification automatically — no separate "speech rate" knob. This is the elegant part: by snapping to beats, pace and rhythm both come for free from Ch 3's grid.

Step 3 — choose in-key pitches

Each note's pitch must come from the song's key (the 7 in-bounds notes). A natural, melodic choice is to take small steps — mostly stay on a note or move to a neighbor in the scale, occasionally leap — because human melodies are locally smooth. A purely random in-key walk already sounds plausibly musical; biasing toward small intervals sounds intentional. The constraint "stay in key" does the heavy lifting; the smoothness bias adds polish.

The whole trick, stated plainly: a melody that (1) uses only the song's key, (2) places one syllable per beat, and (3) prefers small pitch steps will sound like it belongs to the song — even though it was generated, not composed by a human. The three constraints are doing what a songwriter's ear would do. Drop any one and it falls apart: off-key (sour), off-beat (sloppy), wild leaps (random/atonal).
Melody Composer — fit a notification to the song

A piano-roll. The x-axis is time (vertical lines are beats); the y-axis is pitch. Each block is one syllable of "Mee-ting in ten mi-nutes". Rows that are part of the key are tinted. Turn In-key OFF to allow out-of-scale notes (watch blocks land on red rows — that is the "sour" failure). Drag Smoothness toward 0 to allow wild leaps (random/atonal); toward 100 for a smooth, intentional line. Recompose rolls a new melody under the current constraints.

The core insight as runnable code

python
import random

MAJOR = [0,2,4,5,7,9,11]        # semitone offsets of a major scale

def compose(text, root_midi, beat_times, smoothness=0.8):
    """Map each syllable to (pitch, start_time) obeying key + beat + smoothness."""
    syllables = syllabify(text)                 # ["Mee","ting","in","ten","mi","nutes"]
    scale = [root_midi + s for s in MAJOR + [12+x for x in MAJOR]]
    notes, idx = [], scale.index(root_midi)        # start on the tonic
    for i, syl in enumerate(syllables):
        notes.append((scale[idx], beat_times[i], syl))   # pitch ON a beat
        # next pitch: small step (smooth) vs occasional leap
        step = random.choice([-1,0,1]) if random.random()else random.choice([-3,-2,2,3])
        idx = max(0, min(len(scale)-1, idx+step))   # stay inside the scale
    return notes   # [(pitch, start_s, syllable), ...] -> feed to the singer

Trace the data: in is (text, key, beat_times); out is a list of (pitch, start_time, syllable) triples — exactly the score the singing synthesizer (Ch 5) consumes. Notice idx is clamped to the scale array, so we cannot leave the key; and beat_times[i] guarantees the i-th syllable starts on the i-th beat. The two hard constraints are structurally enforced; only the pitch contour is random.

In the composer, which two properties are structurally guaranteed (cannot be violated), and which is left to chance?

Chapter 5: Singing Synthesis — From Score to Voice

We have a score: (pitch, start, syllable) triples. Now we need an actual singing voice waveform that says those words at those pitches and times. This is singing voice synthesis (SVS) — text-to-speech's musical cousin, with an extra input the TTS world ignores: a target pitch (F0) contour the voice must follow.

Why you cannot reuse plain TTS

A normal TTS engine speaks. Its prosody — the natural rise and fall of pitch — is whatever the model learned for speech, and you cannot dictate "hold this exact note for this exact duration." But our whole premise is pitch control: the voice must sit on the scale notes from Ch 4. So the synthesizer must accept three aligned inputs: the phonemes (what to say), the F0 contour (what pitch to say it at), and the durations (how long each note lasts, set by the beat grid).

The data contract (with shapes): the synthesizer takes phonemes p ∈ {1..P}L (a length-L sequence of phoneme IDs), a per-frame target pitch F0 ∈ RT (one frequency per ~5 ms frame, T frames), and durations mapping phonemes to frame spans. It emits a waveform ∈ R16000·sec. The melody from Ch 4 is what fills in F0 and durations — that is the bridge between "composition" and "voice".

The simplest realization: synthesize, then pitch-correct

A practical route — and the easiest to reason about — is two stages. (1) Run ordinary TTS on the notification text to get a spoken waveform. (2) Pitch-shift each syllable to the target note using a phase vocoder or PSOLA, snapping the speech's natural pitch to the melody. Stretch or compress each syllable's duration to fill its beat. The result is "spoken words, sung pitches" — robotic if done crudely, but the principle is clear and it isolates pitch control as the new ingredient over TTS.

More modern SVS models (neural, end-to-end) take the phoneme + F0 + duration inputs directly and render a far more natural singing voice in one pass — but the interface is the same. What matters for understanding the paper is the contract: score in, sung waveform out, with pitch dictated by us.

Pitch-Shift a Voice onto the Melody

The gray line is the natural speech pitch (TTS prosody) for each syllable. The teal dashes are the target melody notes from Ch 4. Drag Shift strength: at 0 you hear plain speech (pitch ignores the melody); at 100 each syllable is fully snapped to its target note (singing). Step through to advance syllable by syllable and watch the correction.

What single input does singing voice synthesis require that ordinary text-to-speech does not?

Chapter 6: Vocal Swap & Mixing

We have a sung notification waveform that matches the song's key, beat, and pace. The final move from Chapter 1: replace the song's original vocal during the notification window and mix our voice into that slot — so the listener hears one coherent performance.

Step 1 — suppress the original vocal

To swap voices we must first remove the original singer for those few seconds. This is music source separation: split the mix into stems — typically vocals, drums, bass, other. Modern separators (e.g. Demucs / Spleeter-style models) take the stereo mix and output a vocals stem and an accompaniment stem. We keep the accompaniment (the instrumental bed) and discard the vocal stem only within the notification window; outside it, the original song plays untouched.

Why separate instead of just ducking everything? Ducking the whole song (the baseline) removes the music too — you lose the groove that makes the notification feel musical. Removing only the vocal keeps the drums, bass, and chords playing, so your sung notification lands on a live instrumental backing in the right key and tempo. The accompaniment is the harmony your notification sings over.

Step 2 — mix into the slot

Now add the sung notification to the (vocal-suppressed) accompaniment. The mix at each instant in the window is:

y(t) = accomp(t) + g · sung(t)

where accomp(t) is the instrumental bed, sung(t) is our notification voice, and g is a gain that places the voice at a believable lead-vocal level. To avoid clicks, the edges of the window are crossfaded: the original vocal fades out as the notification fades in, and back at the end.

Data flow, end to end of this stage

StepInOut
Source separationstereo mix [2, T]vocals [2, T], accomp [2, T]
Window selectaccomp + notification span [t0, t1]accomp with [t0,t1] vocal-free
Mix + crossfadeaccomp[t0:t1] + g·sungblended output [2, T]
Vocal Swap Mixer — build the blended output

Stacked tracks over time: original vocal, accompaniment, and the notification. The shaded band is the notification window. Toggle Remove original vocal to see the vocal carved out inside the band (the paper's approach) vs left in (clashing duet). Hit baseline duck to see the conventional alternative — the entire song's level collapses and speech plays over silence. Drag gain to set the notification's level in the mix.

Why does the system remove only the vocal stem rather than ducking the whole song?

Chapter 7: The Full Pipeline

Time to assemble. Click each box to trace exactly what tensor or object flows between stages. This is the entire system the paper builds — every box is a chapter you have already seen.

End-to-End System — click a stage to trace the data
Click any stage to see what flows in and out.

The two inputs and the one output

The system has exactly two inputs — the user's music (a waveform) and the notification text (a string) — and produces one output: a blended mix where the music briefly sings the notification. Everything in between is the realization. Walking the data:

  1. Music analysis (Ch 3): waveform → (key, beat_times).
  2. Melody fitting (Ch 4): (text, key, beat_times) → score = list of (pitch, start, syllable).
  3. Singing synthesis (Ch 5): (phonemes, F0, durations) from the score → sung waveform.
  4. Source separation (Ch 6): mix → (vocals, accompaniment).
  5. Mix + crossfade (Ch 6): accomp + g·sung in the window → blended output.
The architecture lesson: notice this is a pipeline of frozen, off-the-shelf components, not one trained end-to-end network. Beat/key estimators, an SVS model, a source separator — each is a mature system used as-is. The paper's contribution is not a new model; it is the idea and the composition — recognizing that these existing pieces, wired together with the "replace the vocal" insight, produce a genuinely new and preferable interaction. That is the shape of a lot of great HCI work.
What are the system's two inputs and one output?

Chapter 8: The User Study

An HCI paper lives or dies by its evaluation, and here the "metric" is human preference. The authors ran a user study comparing their music-aware (sung) notification against the conventional approach — ducking the music and speaking the notification.

What they measured

Participants experienced notifications delivered both ways over real music and reported their reactions. The dimensions that mattered were intrusiveness (how much it disrupted the listening experience), blending (how well the notification fit with the music), and overall preference / delight.

The headline finding: most participants found the sung, music-aware notification blended better with the music and was less intrusive than the conventional spoken-over-ducked-music baseline — and they found it more delightful. The thesis from Chapter 0 held: matching the music in rhythm and pitch turns an interruption into a moment of the song.

Why "delight" is a legitimate result, not fluff

It is tempting to dismiss preference data as soft. But the entire premise of a notification UI is that humans tolerate it. A notification that users find less annoying is one they are more likely to leave enabled and actually heed — which is the real-world success criterion. The study also surfaces the tension every notification designer faces: noticeability vs. intrusiveness. A sung notification is gentler, but does it still reliably grab attention? That trade-off is the design space the next chapter maps.

Music-Aware vs. Baseline — preference comparison

Illustrative comparison of the two conditions across the study's dimensions (directional, reconstructing the paper's qualitative finding — the sung condition wins on blending and delight, and is less intrusive). Toggle the dimension. For Intrusiveness, lower is better.

What was the central finding of the user study?

Chapter 9: Failure Modes & the Design Space

A system this composite has many places to fail, and naming them sharpens your understanding of what each stage is really for. Honest engineering means knowing where the cracks are.

Where it breaks

The fundamental tension: noticeability vs. pleasantness. The conventional duck-and-speak is maximally noticeable and maximally annoying. The sung notification is pleasant and blends in — but blending in is the opposite of grabbing attention. The right design likely depends on the message: sing the ambient stuff ("song almost over"), speak the urgent stuff ("turn left now"). The paper opens this design space rather than claiming one answer for all notifications.

The deeper point: notifications are a modality choice

Step back. The lasting idea here is not "singing assistants" specifically — it is that how information reaches you should be sensitive to what you are already doing. You are in a musical context; the notification should respect it. That reframing — context-aware delivery, not one-size-fits-all speech — is the seed the paper plants for future "ambient" assistants.

Which failure is the most catastrophic for listenability, and why?

Chapter 10: Connections & Cheat Sheet

This paper sits at the crossroads of music information retrieval, audio synthesis, and HCI. Here is where its pieces connect to the rest of the field.

What each component descends from

StageField / lineage
Beat tracking, key detectionMusic Information Retrieval (onset detection, chroma, autocorrelation tempo)
Singing voice synthesisNeural audio synthesis — sibling of WaveNet/TTS, with explicit F0 control
Source separationDemucs / Spleeter-style stem separation (vocals vs accompaniment)
The interactionHCI: context-aware, ambient, non-intrusive notification design

The cheat sheet

Symbol / termMeaning
f(n) = 440·2(n−69)/12MIDI note number → frequency (Hz). 12 semitones per octave.
onset(t)Half-wave-rectified spectral flux: how much new energy appeared at frame t (drives beat tracking).
chroma / PCP12-bin pitch-class energy profile; correlated against key templates to find the key.
score = (pitch, start, syllable)The composed melody; the interface between melody fitting and singing synthesis.
F0 contourPer-frame target pitch the singer must follow — SVS's extra input over TTS.
y = accomp + g·sungThe mix inside the notification window after the vocal is removed.
replace ≠ overlayThe key insight: swap out the original vocal so only one voice plays at a time.

Related lessons on Engineermaxxing

The mastery test: you should now be able to sketch the full pipeline on a whiteboard, explain why "replace the vocal" beats "overlay" and "duck", name the input/output of every stage, derive why detecting the wrong key ruins everything, and articulate the noticeability-vs-pleasantness tension that makes this a design space, not a solved problem.
What is the paper's enduring conceptual contribution, beyond the specific singing system?