feat(spf): multi-track audio + skills building features and behaviors (#1605)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Pillsbury
2026-06-04 09:53:13 -07:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 24a78d253a
commit 057f32573e
30 changed files with 2257 additions and 742 deletions
@@ -21,7 +21,18 @@ export type AppendSegmentMeta = Pick<Segment, 'id' | 'startTime' | 'duration'> &
export type { AppendData };
export type AppendInitMessage = { type: 'append-init'; data: AppendData; meta: { trackId: Track['id'] } };
export type AppendInitMessage = {
type: 'append-init';
data: AppendData;
/**
* `language` is captured alongside `trackId` so downstream loaders can
* compare the buffered track's language to the newly-selected track's
* language and decide whether ahead-buffer flush is warranted on track
* switch (see `segment-loader`'s `planTasks`). Undefined for video and
* for audio without explicit `LANGUAGE` attribute.
*/
meta: { trackId: Track['id']; language?: string };
};
export type AppendSegmentMessage = { type: 'append-segment'; data: AppendData; meta: AppendSegmentMeta };
export type RemoveMessage = { type: 'remove'; start: number; end: number };
export type IndividualSourceBufferMessage = AppendInitMessage | AppendSegmentMessage | RemoveMessage;
@@ -37,6 +48,13 @@ export type SourceBufferActorState = 'idle' | 'updating' | 'destroyed';
/** Non-finite (extended) data managed by the actor — the XState "context". */
export interface SourceBufferActorContext {
initTrackId?: string | undefined;
/**
* Language of the most recently appended init segment's track (when
* present on the playlist). Used by the segment-loader's `planTasks`
* to detect cross-language switches and schedule ahead-buffer flush.
* Undefined for video and for language-less audio.
*/
initTrackLanguage?: string | undefined;
segments: Array<
Pick<Segment, 'id' | 'startTime' | 'duration'> & {
trackId: Track['id'];
@@ -91,7 +109,7 @@ function appendInitTask(
await appendSegment(sourceBuffer, message.data);
// No abort check here: the physical SourceBuffer has been modified, so
// the model must be updated to match regardless of signal state.
return { ...ctx, initTrackId: message.meta.trackId };
return { ...ctx, initTrackId: message.meta.trackId, initTrackLanguage: message.meta.language };
});
}