Skip to content

Games stage 3: clip endpoint and name that tune (MAD-783) - #61

Merged
madeofpendletonwool merged 2 commits into
mainfrom
mad-783-games-stage-3
Oct 9, 2026
Merged

madeofpendletonwool merged 2 commits into
mainfrom
mad-783-games-stage-3

Conversation

@madeofpendletonwool

Copy link
Copy Markdown
Owner

Stage 3 of Phase 10 (MAD-783): the server cuts short song clips, and name that tune runs on them. ADR 0015 has a new "Stage 3" section.

MAD-792 Clip endpoint

  • New clips package: clips are cut on the server with ffmpeg from the same stream the speaker plays, using a new transcode.Clipper.
    • One ffmpeg pass per song makes all of a round's clips: the audio is split, then each copy is trimmed.
    • Each clip fades in and out over 50 ms and comes out as MP3 with tags stripped (-map_metadata -1, no ID3, bitexact).
  • GET /rooms/{id}/games/clips/{clipId} serves a clip to its room only. IDs are random, so the URL, the file and its tags never name the song. Clips stay in memory for 20 minutes; paired screens can fetch them too.
  • quiz.ClipStart picks where a clip starts:
    • the beat map's loudest section (usually a chorus), snapped to a nearby bar
    • a third of the way in when there's no beat map
    • with the intro or outro option: the first bar, or ending 4 s before the song does
  • Lengths are 1, 2 and 4 s, plus an 8 s reveal clip from the same spot. Clips can be up to 30 s.
  • Only songs that stream are clipped. A remote service's song (Spotify Connect) is left out unless it played through a streaming stand-in.
  • The next tune's clips are cut while the current song plays, so a round never waits on a stream.

MAD-793 Name that tune

  • Game night only. It stops the music (Break) and plays a clip of a different song: 1 s, then 2 s, then 4 s. Each clip's ID goes out only when it's time to play it. Every device playing the room (speaker or listener) plays each clip as it appears.
  • One guess per person, no changes (already_answered, 409). A right answer scores 1000 on the first clip, 800 on the second and 600 after that.
  • Answers: multiple choice, with wrong answers drawn from similar artists' songs. Hard mode is typed: the title is fuzzy-matched, and the artist alone scores half (new Question.Half).
  • Reveal: shows the tune's cover (the round artwork endpoint now covers tunes) and plays 8 s more from the same spot. Then the music resumes where it stopped, if it was playing.
  • Where tunes come from (new games.tune room settings: from, typed, clip): tonight's songs, the room's favourites (played through, with a heart counting as two plays), or new DJ picks through suggest. If the chosen source has none, it tries the others. Recently used tunes don't come back.
  • Sets: a host can start a set of 5 or 10 from the room menu (POST …/games/rounds with set). Tunes run back to back with the music stopped, and the set's board shows after each reveal.
    • The last reveal crowns the winner, then the music comes back.
    • A set counts once toward breaks per hour (migration 00026 adds game_rounds.set_id).
    • It ends early if the song changes, or if the next tune isn't ready within 20 s.
  • A tune needs clips that have finished cutting, ffmpeg on the server, and a speaker in the room. Until then it's no_question.
  • Web:
    • Phones show which clip is playing and lock after one guess.
    • The big screen gets a clip meter, the tune's cover and the set board.
    • Settings gain a Name that tune block.

Also

  • Engine.Start now returns a copy of the round. It used to return the live round, which the HTTP handler could read while timers were changing it (the race detector caught this).

Not done here

  • No clip of the sampled song at sample detective's reveal yet. MusicBrainz names the sampled song, but finding a streamable copy on the room's services needs its own matching step. The ADR records this.
  • Clip playback is best-effort on iOS: a browser that hasn't had a tap may block the clip's audio element.

Testing

  • go test -race ./... passes and golangci-lint reports 0 issues. The clip tests run against real ffmpeg on the fake provider's WAV audio.
  • New tests:
    • full tune round: clips released in turn, one answer each, stage scoring, Break, then Resume after the reveal clip
    • a 5-tune set: board, no resume until the end, counted once in the budget
    • needs a speaker; set validation
    • ClipStart, NameTune with half credit, the clips service, tune settings, and the HTTP clip endpoint (404 for other rooms)
  • Web: pnpm test, typecheck, lint and build all pass. I haven't played a tune end to end in a browser.

🤖 Generated with Claude Code

madeofpendletonwool and others added 2 commits October 8, 2026 20:34
Clips are cut on the server with ffmpeg from the stream the speaker
plays: one pass per song, faded in and out, untagged MP3 under opaque
IDs, kept briefly in memory and served at GET
/rooms/{id}/games/clips/{clipId}. They start on the beat map's loudest
section, snapped to a bar, or a third in without one; only songs that
stream are clipped (MAD-792).

Name that tune stops the music and plays a clip of another song, 1s,
then 2s, then 4s, each released only when it plays. One answer each,
scored by which clip it came in on; choices from similar artists' songs,
or typed in hard mode with half for the artist. The reveal plays more
from the same spot, then the music resumes where it stopped. Tunes come
from tonight, the room's favorites or the DJ's new picks, and are cut a
song ahead. A host can start a set of 5 or 10 tunes with a board between,
counted once toward breaks per hour (MAD-793). ADR 0015 updated.

Start now returns a copy of the round rather than the live one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every device playing the room (speaker or listener) plays each clip as
the round lists it, newest only, never late (MAD-792). Phones show which
clip is playing, take one guess, and show the tune's cover at the reveal;
the big screen has a clip meter, the tune's cover, and a set's board with
the winner crowned. Settings pick where tunes come from, where clips are
cut and hard mode, and hosts can start a set of 5 or 10 from the room
menu (MAD-793).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@madeofpendletonwool
madeofpendletonwool merged commit 5cb96f5 into main Oct 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant