Documentation
¶
Overview ¶
Package media is WaxTap's in-process audio engine: it probes, transcodes, remuxes, and cuts local audio files by calling the pure-Go WaxFlow library rather than shelling out to ffmpeg.
Callers pass file paths; the package opens them as WaxFlow sources, bounds the number of concurrent operations, and stages output atomically next to the destination. A Codec selects the target format; Spec adds an optional bitrate, a downmix, and a normalization gain, all fused into one WaxFlow pass. CodecCopy is the no-re-encode mode, served by a container-level remux.
Index ¶
- Constants
- func CheckOutputContainer(codec Codec, output string) error
- func ContainerAccepts(ext, codecName string) bool
- func ContainersFor(codecName string) []string
- func IsAIFFExt(ext string) bool
- func OutputFormats() []string
- func ValidateCrossfade(keeps []cutrange.Range, d time.Duration) error
- type Codec
- type CutResult
- type CutSpec
- type Levels
- type Mode
- type ProbeFormat
- type ProbeResult
- type ProbeStream
- type Result
- type Runner
- func (r *Runner) AnalyzeFile(ctx context.Context, input string, channels int) (*waxflow.AnalyzeResult, error)
- func (r *Runner) AnalyzeMedia(ctx context.Context, med format.Media, channels int) (*waxflow.AnalyzeResult, error)
- func (r *Runner) Engine() *waxflow.Engine
- func (r *Runner) OpenAlbumConcat(inputs []string) (format.Media, func() error, error)
- func (r *Runner) OpenComposed(input string, keeps []cutrange.Range, total, crossfade time.Duration) (format.Media, func() error, error)
- func (r *Runner) Probe(ctx context.Context, input string) (ProbeResult, error)
- func (r *Runner) RemuxContainer(ctx context.Context, input, output, container string) error
- func (r *Runner) Render(ctx context.Context, input, output string, spec CutSpec) (CutResult, error)
- func (r *Runner) Transcode(ctx context.Context, input, output string, spec Spec) (Result, error)
- type RunnerConfig
- type Spec
Constants ¶
const ContainerProgressive = waxflow.ContainerProgressive
ContainerProgressive is WaxFlow's flat (moov+mdat) MP4 container override, re-exported for callers that remux through RemuxContainer.
Variables ¶
This section is empty.
Functions ¶
func CheckOutputContainer ¶
CheckOutputContainer reports whether output's extension can hold codec. Copy passes (its container follows the source), as does an extensionless or codec-named output (force-muxed, so the extension does not constrain it). A recognized extension that cannot hold codec returns waxerr.ErrIncompatibleSpec with suggested containers.
func ContainerAccepts ¶
ContainerAccepts reports whether the container named by ext can hold the given codec unchanged (a copy/remux). Some extensions support several codecs, so this consults a compatibility table rather than comparing names; unknown extensions are permissive. codecName may be a codecName-mapped value ("opus", "aac", "pcm", "wav", "aiff").
PCM has two container-defining names: "wav" and "aiff" are Codec.String() values naming a container, while "pcm" is what a probe reports for a source. isPCM covers RIFF and probed sources only; AIFF has its own branch, and Matroska takes PCM through WaxFlow's wav row.
func ContainersFor ¶
ContainersFor returns a short list of conventional container extensions, each with a leading dot, that can hold codecName unchanged. The result is a subset of the extensions ContainerAccepts allows. Unknown codecs return nil.
func IsAIFFExt ¶
IsAIFFExt reports whether ext names an AIFF container. WaxFlow's aiff row registers four spellings for one container; .aifc/.afc name the compressed-capable variant, which the muxer selects from the sample format rather than the filename.
The list lives here rather than in each table because the output extension is what picks between PCM's two containers, so a spelling missed by one table gets RIFF bytes in an AIFF file. ext must be lowercased and undotted, which every caller already guarantees.
func OutputFormats ¶
func OutputFormats() []string
OutputFormats lists the audio formats the in-process engine can produce. The doctor command reports it as WaxTap's capability set.
func ValidateCrossfade ¶
ValidateCrossfade checks whether the retained spans can supply the requested overlap. A crossfade consumes d from both sides of each join, so an interior span must be at least 2*d. Rejecting short spans up front avoids an encode that emits no audio.
Types ¶
type Codec ¶
type Codec uint8
Codec identifies an output audio encoding. CodecCopy rewrites the source packets into a new container without re-encoding (a remux). The lossless codecs (FLAC, ALAC, WAV, AIFF) decode and encode; they are not stream copies.
const ( CodecCopy Codec = iota // container rewrite / remux (the only no-re-encode path) CodecFLAC // lossless re-encode (.flac) CodecALAC // lossless re-encode (Apple Lossless in .m4a) CodecWAV // lossless PCM (.wav) CodecMP3 // MP3, CBR 320 by default CodecAAC // AAC-LC in .m4a CodecOpus // Opus (.opus) CodecVorbis // Vorbis (.ogg) CodecAIFF // lossless PCM (.aiff) )
func (Codec) Extension ¶
Extension returns the canonical file extension (without a dot) for c, or "" for CodecCopy (whose container follows the source).
func (Codec) IsLossless ¶
IsLossless reports whether c is a remux or a lossless encoder (FLAC, ALAC, WAV, AIFF). They keep the source bit depth unless Spec.BitDepth asks for a specific one, so by default they never narrow a higher-depth source.
type CutResult ¶
type CutResult struct {
Output string
Removed time.Duration
Mode Mode
Applied bool
// Levels is WaxFlow's level measurement of the cut re-encode; see
// Result.Levels. It is always zero for a lossless cut-remux.
Levels Levels
}
CutResult reports a completed cut.
type CutSpec ¶
type CutSpec struct {
Keeps []cutrange.Range
Total time.Duration
Crossfade time.Duration
CopyCut bool
// RequireCopy fails the cut rather than re-encoding when WaxFlow declines the
// lossless cut-remux. It expresses an explicit copy request (--format copy or
// --cut-mode copy): silently re-encoding would break the caller's promise.
RequireCopy bool
Encode Spec
}
CutSpec describes a resolved cut. Keeps are the spans to retain, in order, on the source timeline; Total is the source duration. CopyCut asks for a lossless cut-remux (kept codec, byte-identical packets), which WaxTap tries first and falls back from to Encode when WaxFlow declines the source codec. Encode names the re-encode used for the re-encode path (or the CopyCut fallback).
type Levels ¶ added in v3.1.3
type Levels struct {
ClippedSamples int64 // channel samples the integer output clamped
Samples int64 // output frames (per channel)
Channels int // output channel count
TruePeak float64 // output true peak, linear, 1.0 = full scale
Quantized bool // a quantizer ran (float cut to integer)
}
Levels carries WaxFlow's level measurement of one encode as numbers, so the caller sets the warning policy (thresholds, per-cause remedies, aggregation) instead of receiving a pre-rendered sentence. See waxflow.TranscodeResult for the field semantics. The zero value reports nothing.
func (Levels) Note ¶ added in v3.1.3
Note renders WaxFlow's one-line level warning for these levels, or "" when they warrant none: the clipped-sample count, else a true peak past full scale on a quantized output. The wording is WaxFlow's own, reconstructed through its LevelNote so the two never drift; callers may append remedies.
type Mode ¶
type Mode uint8
Mode selects how a cut is rendered. The facade maps waxtap.CutMode onto these.
const ( // ModeSmart copies (lossless cut-remux) when the cut keeps the source codec, // and re-encodes when a transcode, downmix, gain, or crossfade is involved. ModeSmart Mode = iota // ModeCopy forces a lossless cut-remux. It cannot transcode, downmix, or // crossfade, and it fails when WaxFlow cannot cut-remux the source codec. ModeCopy // ModeAccurate forces a re-encode. ModeAccurate )
type ProbeFormat ¶
type ProbeFormat struct {
Container string // identified container name ("webm", "mka", "flac", ...)
Duration time.Duration // container duration, or 0 when unknown
Size int64 // bytes, or 0 when unknown
// BitRate is always 0: WaxFlow does not report a container bit rate, so
// callers fall back to a size/duration estimate (mapping.applyProbe).
BitRate int
}
ProbeFormat describes the container.
type ProbeResult ¶
type ProbeResult struct {
Format ProbeFormat // container metadata
Streams []ProbeStream // audio tracks
}
ProbeResult is a probe of a local audio file: container metadata plus the audio track's codec details.
func (ProbeResult) AudioStream ¶
func (p ProbeResult) AudioStream() (ProbeStream, bool)
AudioStream returns the first audio track and true, or a zero stream and false when the result carries none. Probe rejects no-audio inputs, so a result obtained from Probe always has one.
type ProbeStream ¶
type ProbeStream struct {
CodecType string // always "audio"
CodecName string // codecName-mapped: "opus", "aac", "flac", "pcm", ...
SampleRate int // Hz
Channels int // channel count
BitRate int // always 0 (WaxFlow reports none)
Duration time.Duration // track duration, or 0 when unknown
}
ProbeStream describes one audio track.
type Result ¶
type Result struct {
Output string // final output path
Size int64 // output size in bytes (0 if it could not be stat'd)
Codec Codec // codec the output was encoded with
// Levels is WaxFlow's level measurement of the encode. It is zero for a
// container copy, which never re-derives samples.
Levels Levels
}
Result reports a completed transcode.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner drives WaxFlow's engine for local audio files. It bounds concurrency, and it is safe for concurrent use.
func NewRunner ¶
func NewRunner(cfg RunnerConfig) *Runner
NewRunner builds a Runner. WaxFlow's engine construction cannot fail, so there is no error to return.
func (*Runner) AnalyzeFile ¶
func (r *Runner) AnalyzeFile(ctx context.Context, input string, channels int) (*waxflow.AnalyzeResult, error)
AnalyzeFile measures the loudness of a whole local file. channels, when 1 or 2, measures after folding to that channel count so a two-pass gain matches a later downmixing encode; 0 keeps the source layout. The loudness package maps the result to its own type; this keeps the WaxFlow engine and the concurrency bound in one place.
func (*Runner) AnalyzeMedia ¶
func (r *Runner) AnalyzeMedia(ctx context.Context, med format.Media, channels int) (*waxflow.AnalyzeResult, error)
AnalyzeMedia measures the loudness of an already-open Media, so a cut/downmix composition is measured as it will be encoded. channels folds the measurement to a downmix target (0 keeps the source layout). The caller owns med.
func (*Runner) Engine ¶
Engine returns the underlying WaxFlow engine, for callers (loudness) that measure through it directly.
func (*Runner) OpenAlbumConcat ¶
OpenAlbumConcat opens the gapless concatenation of inputs as one Media, for a group loudness measurement. WaxFlow conforms members whose rate differs from the envelope's, and opens each member lazily: only the track headers are read up front (one descriptor at a time), and Concat opens and closes each member's file as the timeline reaches it, so a large album holds one descriptor open rather than one per track. The caller closes the returned Media via closer.
func (*Runner) OpenComposed ¶
func (r *Runner) OpenComposed(input string, keeps []cutrange.Range, total, crossfade time.Duration) (format.Media, func() error, error)
OpenComposed opens the cut-composed Media for measurement (loudness), without re-encoding. The caller closes it.
func (*Runner) Probe ¶
Probe inspects a local input and returns the parsed result. Undecodable media and media with no audio track are reported as waxerr.ErrUnsupportedInput.
func (*Runner) RemuxContainer ¶
RemuxContainer remuxes input to output using an explicit WaxFlow container override, e.g. "progressive" to flatten a fragmented MP4 into a tag-writable progressive one. It is a packet copy (no re-encode). input and output may be the same path; the source is closed before the atomic rename.
func (*Runner) Render ¶
Render applies spec's cut to input and writes the result to output. Output is staged and atomically renamed on success. When CopyCut is set and no downmix, gain, or crossfade is requested, Render tries a lossless cut-remux first and re-encodes only if WaxFlow declines the source codec.
func (*Runner) Transcode ¶
Transcode reads input, applies spec, and writes the result to output. The output is staged in a temp file in output's directory and atomically renamed into place on success; on failure or cancellation the temp is removed and any existing file at output is left untouched.
CodecCopy is a whole-file container remux (no re-encode); it rejects a channel or gain change, which require decoding.
type RunnerConfig ¶
type RunnerConfig struct {
// MaxProcs bounds concurrent WaxFlow operations (0 = unlimited). Each
// operation runs one goroutine, so this maps operation count onto cores and
// caps peak memory from pooled decode/DSP/encode buffers.
MaxProcs int
// Logger receives debug logs. Nil discards them.
Logger *slog.Logger
}
RunnerConfig configures a Runner.
type Spec ¶
type Spec struct {
Codec Codec
Bitrate int
BitDepth int // forced output depth (16 or 24); 0 follows the decoded stream
Channels int // output channel count (downmix); 0 keeps the source layout
GainDB float64 // scalar normalization gain in dB; 0 is a no-op
}
Spec describes a transcode. Codec selects the output codec; Bitrate overrides lossy preset defaults in bits per second; BitDepth forces integer output for the codecs that hold integer PCM; Channels downmixes (1 or 2) when positive; GainDB applies a normalization gain (0 is a no-op). The zero value is a container remux (CodecCopy) with no processing.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package loudness measures EBU R128 loudness with WaxFlow's analyzer and derives the scalar gain that normalizes a track to a target.
|
Package loudness measures EBU R128 loudness with WaxFlow's analyzer and derives the scalar gain that normalizes a track to a target. |