package audio import "context" // StemSeparationRequest asks a source-separation backend (Demucs style) to // split a mix into stems. Audio is carried as bytes (never a URL), mirroring // TranscriptionRequest. Zero values mean "backend default" (ADR-0024). type StemSeparationRequest struct { // Audio is the encoded mix to separate. Audio []byte // MIME is the audio MIME type (e.g. "audio/mpeg"); "" = let the backend // sniff it. MIME string // Filename is the multipart filename hint some backends key their format // detection on; "" derives one from MIME or falls back to "audio". Filename string // Mode selects the split: "two" (vocals + accompaniment) or "four" // (vocals/drums/bass/other); "" = backend default (four). Mode string // Model selects the separator's internal network where the backend // offers several (Demucs: "htdemucs", "htdemucs_ft"); "" = backend // default. This is NOT the provider model id — that is fixed when the // StemSeparator is minted (mirrors BackgroundRemovalRequest.Net). Model string // Format is the per-stem audio container ("mp3" or "wav"); // "" = backend default. Format string } // Stem is one separated source. type Stem struct { // Name is the stem's name ("vocals", "drums", "bass", "other", // "no_vocals", ...), taken from the backend's own labelling. Name string // Audio is the encoded stem. Audio []byte // MIME is the stem's audio MIME type, e.g. "audio/mpeg". MIME string } // StemSeparationResult is the canonical separation result. type StemSeparationResult struct { // Stems are the separated sources, in the order the backend returned // them. Stems []Stem } // StemSeparationOption mutates a StemSeparationRequest before it is sent. type StemSeparationOption func(*StemSeparationRequest) // WithStemMode selects the split ("two" or "four"). func WithStemMode(m string) StemSeparationOption { return func(r *StemSeparationRequest) { r.Mode = m } } // WithStemModel selects the separator's internal network. func WithStemModel(m string) StemSeparationOption { return func(r *StemSeparationRequest) { r.Model = m } } // WithStemFormat sets the per-stem audio container ("mp3", "wav"). func WithStemFormat(f string) StemSeparationOption { return func(r *StemSeparationRequest) { r.Format = f } } // Apply returns a copy of the request with all options applied. func (r StemSeparationRequest) Apply(opts ...StemSeparationOption) StemSeparationRequest { for _, opt := range opts { opt(&r) } return r } // StemSeparator splits a mix into stems. type StemSeparator interface { // SeparateStems returns the separated sources. Separation is CPU-bound // and slow (minutes for a full song); bound the call with a context // deadline. SeparateStems(ctx context.Context, req StemSeparationRequest, opts ...StemSeparationOption) (*StemSeparationResult, error) } // StemSeparatorModelOption configures a StemSeparator at construction time. // Reserved for future per-model settings. type StemSeparatorModelOption func(*StemSeparatorModelConfig) // StemSeparatorModelConfig carries per-model construction settings. type StemSeparatorModelConfig struct{} // ApplyStemSeparatorModelOptions folds options into a config. func ApplyStemSeparatorModelOptions(opts []StemSeparatorModelOption) StemSeparatorModelConfig { var cfg StemSeparatorModelConfig for _, opt := range opts { opt(&cfg) } return cfg } // StemSeparationProvider mints StemSeparators bound to one backend. type StemSeparationProvider interface { // Name is the registry identifier for the provider. Name() string // StemSeparatorModel returns a StemSeparator bound to the given id // (passed through to the backend verbatim; no catalog validation). StemSeparatorModel(id string, opts ...StemSeparatorModelOption) (StemSeparator, error) }