Repository navigation
The DJ's leftovers: outro cues, what aired, and voices from the console #13
Replies: 3 comments
|
Status check, 2026-09-22 (main at 23b983e) Done: break ratings ( Still open:
|
|
Status, 2026-10-23. The aired-at log is covered by the as-run log (#765). |
|
Status, 2026-10-24. Three more are done in #774.
Still open: voice uploads from the console, the Kokoro blend helper, and a live mic (#727). |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
The DJ that actually says something
As of: 2026-08-08, when the station first played audio it was not given by a music provider.
Cut down: 2026-10-10, to what is still open. The sections describing the two pieces as designed
and as shipped were removed; the file as it stood before is in this discussion's edit history.
Status (2026-10-10)
Both pieces this discussion was written for are built, and so is most of what it left behind:
what to say (2026-08-12):
BreakWriterRegistrykeyed bysegments.kind, the model writer withthe deterministic
TalkBreakWriteras its floor, and the forward-claim check(
segments.claims_item_id).come from synced lyrics (
lyrics/vocal.ranges.ts,lyrics/vocal.runway.ts, LRCLIB as the firstsource), an operator's correction wins (
deadair.track_vocal_overrides, migration0064), thewriter is told the runway and the words that fit (
director/talk.up.ts,breaks.talkUp, off bydefault), and the link becomes a talk-over on the order. The first sung word replaced the "first
sustained vocal" this file asked source separation for.
length into
segments.duration_ms, which was null on 160 of 161 rows.0022_script_ratings.sql,script.ratings.repository.ts,PUT /scripts/{id}/rating.exaggeration,cfg_weight) per break andper voice in
chatterbox.delivery.ts(chatterbox: a break can be read hushed or frantic, and a voice carries its own exaggeration #80), onPOST /tts.markSpokenOverinplayout/live.order.ts.components/segments/segments.page.tsx),with upload (
/segments/upload) and an inbox scan. Adding one to the running order isPOST /director/air/segments, owned by the page that owns the order.personas.
What is left is six smaller things: outro cues, an aired-at record of what the station said, a
setting for writer order, re-writing a failed break, getting a clip or voicepack onto a speech server
from the console (with a Kokoro blend helper beside it), and a live microphone.
Outro cues
"Finish two seconds before the record ends" is the other half of what makes a station sound like a
station rather than a playlist with announcements: a break over a long fade. The talk-up limit (#412)
is the front of a record; this is the back.
Both inputs now exist.
outro_startandcue_outare measured per track (playout/rundown.tscarries
outroStartMs, andplayout/crossfade.tssizes the blend from it), and since #434 a spokensegment has a real
duration_ms. The original note here said Liquidsoap could measure the length(
request.durationon the resolved request) as a better answer than trusting a tag or a header; theanalyzer's measurement of the whole rendered file is that answer, and it is already stored.
What is not built is everything between: a writer told how long the outro runs (the same
constraint-on-the-request the talk-up uses, since it is cheaper to constrain the text than to trim
rendered speech), a cue that arms itself against
outro_startrather than being handed a time, andthe decision about how an outro talk-over and the blend into the next record share the same seconds.
The fallback is the talk-up's: a record with no measured outro gets no outro cue, and the break airs
in the gap as it does today.
Play history for what the station SAID
Segments are deliberately excluded from
deadair.play_history, because the repeat window and artistcooldown are reads of it and an ident has no artist.
deadair.script_history(the "What it said" tab)records what was WRITTEN, not what aired, so it answers "what has the station been saying" and cannot
answer "what did it say at nine o'clock".
markSpokenOverrecords whether a talk-over fired, whichis one fact about one kind of break. An aired-at stamp is still its own decision, and it still wants
its own table rather than a relaxation of
play_history.This is the same gap as #449 (an as-run log the operator can trust) seen from the break side. Whoever
builds one should build it as the other.
Which writer wins is registration order, not a setting
director.module.tsranks the writers andllm.breakWriteris the only switch. That is right for onemodel and one floor; an operator who wants the templates ABOVE the model on some shows wants a
setting, and the registry would take one without changing shape. A setting is a string: read it
through the registry and
settingIsOn, perapps/api/CLAUDE.md.A break still cannot be re-written
A
failedsegment is not re-claimable for writing, on the grounds that the usual reason nothing couldbe written is that there was nothing true to say. An operator who edits their phrasings and wants last
night's failures retried has no button for it. The Segments page says so in its own header: it cannot
re-render a
failedsegment because there is no route that does, and a client-side retry would meandeleting and re-creating the row, which is a different thing wearing the same button.
The one retry that does exist is narrower and should not be mistaken for this: a render that failed
because its engine was
unavailableis released for retry (releaseForRetryinsegment.repository.ts, from #29). That is a render retry with the words kept, not a re-write.Getting a clip or a voicepack onto the server from the console
Every engine wants this and the station has none of it. Chatterbox reads reference WAVs out of a
directory and Kokoro reads
.ptvoicepacks out of one, and in both cases the operator's only routeis a file copy on the host. (Rhapsode's server takes an upload through its own API, which is the
server's feature, not the station's.) The previous station had it as a
PUTwith the raw bytes asthe body and the name in the query string, hand-written rather than generated because a binary upload
does not fit the JSON-only contract codegen.
/segments/uploadhas since shown the station can carryan upload route, so that reason is weaker than it was. The compose file already mounts
./.docvol/voicesfor exactly this and seeds the image's own voicepacks into it, so the deploymenthalf is done and only the route is missing.
Chatterbox's
clonevoice mode is the related idea: the plugin asks forpredefinedand says areference clip the operator pushed is "a second way to name a voice", which belongs with the mapping
table if it is ever wanted. An upload route is what would make it wanted.
A blend helper
Kokoro takes
af_bella(2)+af_sky(1)as a voice and also has a/v1/audio/voices/combinethat mints apersisted one. The expression is already typeable in the voice table's engine cell
(
kokoro.manifest.tskeeps that cell astringrather than aselectfor this reason), so what ismissing is only the affordance for building and hearing one before committing it. That is a console
feature over an engine capability that works today.
The harbor mount
input.harbor("dj", ...)was removed when the voice queue replaced it, andradio.liqkeeps theharbor port only for the
/control/*endpoints. Bring it back as a second arm of the voice source ifsomething genuinely needs to stream live audio in; a real microphone is the honest case.
Related
director-and-lineups for the rest of the
director's deferred half, and from-v1 for what
the previous station did here.
Moved from
docs/todo/dj-voice.md. The file as it last stood: https://github.com/robert-dean/deadair/blob/71e431d45ce7b35a189c7d226832ec4a6247e9dc/docs/todo/dj-voice.mdAll reactions