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.
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.
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.
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:
| Axis | What it means | What breaks if you ignore it |
|---|---|---|
| Key / pitch | Sing notes that belong to the song's scale | Off-key notes sound painfully wrong |
| Beat / rhythm | Start syllables on the song's beats | Lyrics drift, feel "off", lose groove |
| Tempo / pace | Deliver words at the song's speed | Cramming or stretching sounds robotic |
| Timbre / placement | Sit where a voice belongs in the mix | Two 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.
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.
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.
Everything downstream is the realization of that insight. We will build each box from scratch:
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.
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:
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.
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.
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.
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.
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).
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.
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.
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.
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.
# 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
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
Now add the sung notification to the (vocal-suppressed) accompaniment. The mix at each instant in the window is:
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.
| Step | In | Out |
|---|---|---|
| Source separation | stereo mix [2, T] | vocals [2, T], accomp [2, T] |
| Window select | accomp + notification span [t0, t1] | accomp with [t0,t1] vocal-free |
| Mix + crossfade | accomp[t0:t1] + g·sung | blended output [2, T] |
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.
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.
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:
(key, beat_times).(text, key, beat_times) → score = list of (pitch, start, syllable).(phonemes, F0, durations) from the score → sung waveform.(vocals, accompaniment).accomp + g·sung in the window → blended output.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.
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.
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.
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.
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.
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.
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.
| Stage | Field / lineage |
|---|---|
| Beat tracking, key detection | Music Information Retrieval (onset detection, chroma, autocorrelation tempo) |
| Singing voice synthesis | Neural audio synthesis — sibling of WaveNet/TTS, with explicit F0 control |
| Source separation | Demucs / Spleeter-style stem separation (vocals vs accompaniment) |
| The interaction | HCI: context-aware, ambient, non-intrusive notification design |
| Symbol / term | Meaning |
|---|---|
| f(n) = 440·2(n−69)/12 | MIDI 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 / PCP | 12-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 contour | Per-frame target pitch the singer must follow — SVS's extra input over TTS. |
| y = accomp + g·sung | The mix inside the notification window after the vocal is removed. |
| replace ≠ overlay | The key insight: swap out the original vocal so only one voice plays at a time. |