-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoptions.go
More file actions
271 lines (259 loc) · 12.4 KB
/
Copy pathoptions.go
File metadata and controls
271 lines (259 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
package waxflow
import (
"log/slog"
"github.com/colespringer/waxflow/codec/ape"
"github.com/colespringer/waxflow/codec/wavpack"
"github.com/colespringer/waxflow/container"
"github.com/colespringer/waxflow/dsp/dither"
"github.com/colespringer/waxflow/dsp/gain"
"github.com/colespringer/waxflow/dsp/resample"
)
// Option configures an Engine.
type Option func(*Engine)
// WithLogger sets the Engine's logger. Nil (and the default) discards.
func WithLogger(l *slog.Logger) Option {
return func(e *Engine) {
if l != nil {
e.log = l
}
}
}
// IndexCache persists demuxer-built source indexes across sessions (the
// cacheDir/idx sidecar): MP3 frame tables today, seek tables for later
// formats. The engine restores a cached index when it opens a source
// whose demuxer can use one, and saves fresh snapshots on close. Keying
// blobs by source identity is the implementation's job (the server keys
// by ref plus size plus mtime); the engine stays identity-agnostic.
type IndexCache interface {
// Load returns the saved index blob for src, or nil.
Load(src container.Source) []byte
// Save persists a fresh snapshot for src. Best effort: failures are
// the implementation's to swallow (a lost sidecar only costs a
// rebuild).
Save(src container.Source, blob []byte)
// Drop removes src's saved blob. The engine calls it when a demuxer
// rejects a loaded blob, so an invalid one stops being served (and
// LRU-refreshed) forever.
Drop(src container.Source)
}
// WithIndexCache wires an index sidecar cache into the Engine.
func WithIndexCache(c IndexCache) Option {
return func(e *Engine) {
e.idx = c
}
}
// TranscodeOptions selects the Transcode output, with the DSP chain
// (resample, mix, gain, dither) between decode and encode. Zero values keep
// the source's properties, so the zero options add no DSP stage and the
// decoder's samples reach the encoder unaltered: a bit-exact container
// rewrite for a lossless source to a lossless output. A lossy source is
// decoded and re-encoded even so, which costs a generation.
//
// Remux is what removes that generation, and the options it accepts are exactly
// the ones described above: zero everywhere but Format and Container. It moves
// the source's own packets rather than decoding them, so it is a bit-exact
// container rewrite for a lossy source too, but only where the codec survives
// the trip (the output format's codec must already be the source's). PlanRemux
// answers whether a given request is one of those, and the server's ladder asks
// it before reaching for a transcode.
type TranscodeOptions struct {
// Format is the output format name: "wav", "aiff", "flac", "mp3",
// "alac", "aac", "opus", or "vorbis".
Format string
// Container overrides the format's default container where the
// format defines an alternative; empty selects the default. Today
// only aac has one: "adts" replaces the progressive fragmented MP4
// with the raw ADTS elementary stream, a legacy opt-out that
// sacrifices gapless signaling (ADTS has none).
Container string
// Rate resamples to this sample rate in Hz; 0 keeps the source rate.
Rate int
// Channels converts the channel count (downmix to 1 or 2, or mono
// duplication to stereo); 0 keeps the source layout.
Channels int
// BitDepth forces integer output at this depth, dithered when
// reducing; 0 keeps the source domain and depth.
BitDepth int
// GainDB applies a scalar gain, finite within +-120 dB. Positive
// gain engages the true-peak limiter; tighter policy clamps (the
// HTTP +12 dB bound) live at the API boundary, not here.
GainDB float64
// Dynamics applies a dynamics-processing preset to the post-gain
// signal: gain.PresetOff (the zero value) applies none, gain.PresetVoice
// the spoken-word leveller. It is a closed vocabulary rather than raw
// compressor parameters; see gain.Preset for why.
//
// It composes with GainDB rather than replacing it, and the order is
// load-bearing: the preset's curve has a fixed threshold, so the caller
// levels the signal to a known point with GainDB first and the preset
// then shapes it. A caller with a measured loudness (an analyze job's)
// sends the exact dB alongside the preset. WaxFlow cannot measure a
// live stream, so it cannot do this for the caller: two-pass is
// jobs-only.
//
// A preset always engages the true-peak limiter.
Dynamics gain.Preset
// FromSample starts output at this source-timeline sample, seeking
// sample-exact before the first chunk. Seconds convert to samples at
// the API boundary (ADR-0006); 0 starts at the beginning.
FromSample int64
// FLACLevel selects the FLAC compression level for flac output: 1
// through 8 literally, FLACLevelDefault (the zero value) for the
// encoder default, and FLACLevelFastest for level 0, which needs a
// sentinel because the zero value cannot mean it without stealing
// the default. Levels trade encode speed for size and never affect
// decoded audio.
FLACLevel int
// WavPackLevel selects the WavPack compression level for wavpack
// output: WavPackLevelFast through WavPackLevelVeryHigh literally, and
// WavPackLevelDefault (the zero value) for the encoder default, which
// is normal. Levels choose how deep a decorrelation cascade each block
// runs: they trade encode speed for size and never affect decoded
// audio.
WavPackLevel int
// APELevel selects the Monkey's Audio compression level for ape
// output: APELevelFast, APELevelNormal, or APELevelHigh literally, and
// APELevelDefault (the zero value) for the encoder default, which is
// normal. Levels choose the filter cascade each frame runs through:
// they trade encode and decode speed for size and never affect decoded
// audio. The format's two deeper levels decode here but are not
// written; see ape.MaxEncodeLevel.
APELevel int
// MP3Bitrate selects the constant bit rate in bits per second for mp3
// output; the zero value uses the encoder default (128000). It must be
// a legal Layer III CBR rate for the output sample rate. Under MP3VBR
// it anchors the quality level instead.
MP3Bitrate int
// MP3VBR selects variable bit rate for mp3 output: each frame carries
// the smallest legal bit-rate index that holds its psychoacoustic
// demand, anchored at MP3Bitrate. The zero value is constant bit rate.
MP3VBR bool
// OpusBitrate selects the target bit rate in bits per second for opus
// output; the zero value uses the encoder default (96000).
OpusBitrate int
// AACBitrate selects the target bit rate in bits per second for aac
// output; the zero value uses the encoder default (128000). AAC
// frames are variable-size, so the encoder holds the long-term mean
// at the target with a bit reservoir.
AACBitrate int
// HEAACv2 selects HE-AAC v2 (parametric stereo over a mono SBR core,
// an AOT-29 stream) for he-aac output, and drops the zero-AACBitrate
// default to 32000. Selection is explicit rather than
// bitrate-automatic: an auto threshold would silently switch stereo
// coding technology across a bitrate boundary. Stereo sources only;
// a mono source is refused at plan time (encode v1 instead). Other
// formats ignore it.
HEAACv2 bool
// OpusComplexity gates the Opus encoder's analysis depth: 1 through 10
// literally, OpusComplexityDefault (the zero value) for the encoder
// default (5), and OpusComplexityLowest for complexity 0, which needs a
// sentinel because the zero value cannot mean it without stealing the
// default. Higher is slower and higher quality.
OpusComplexity int
// OpusVBR selects variable bit rate for opus output, sizing each frame to its
// content around OpusBitrate. The zero value is constant bit rate.
OpusVBR bool
// OpusSignal hints the opus encoder about the content type: "voice"
// biases the speech/music mode decision toward SILK/hybrid (audiobooks,
// podcasts), "music" toward CELT. The zero value ("" or "auto") lets the
// encoder's analyser decide per frame.
OpusSignal string
// VorbisQuality selects VBR quality for vorbis output on libvorbis's -q
// scale (-1..10); higher is larger and better. The zero value uses the
// encoder default (3.0). Vorbis is natively quality-driven, so this is the
// primary knob; a small nonzero value near 0 reaches the lowest qualities
// (the zero value cannot, matching the "0 means default" idiom).
VorbisQuality float64
// VorbisBitrate is a reserved ABR target in bits per second for vorbis
// output. ABR rate control is not implemented, so a nonzero value is
// rejected at plan time rather than silently ignored; leave it 0 for
// quality-driven VBR.
VorbisBitrate int
// Shaping selects the dither strategy for quantization; the default
// is flat TPDF.
Shaping dither.Shaping
// ResampleProfile selects resampler quality; empty means resample.HQ.
ResampleProfile resample.Profile
// Tags embeds canonical metadata fields (TITLE, ARTIST, ...) in the
// output where the muxer can represent them in its stream form: Ogg
// OpusTags, a FLAC VORBIS_COMMENT block, an MP3 ID3v2 tag, MP4 ilst
// atoms. Formats without stream-form tagging (WAV, AIFF, ADTS)
// ignore them; a finished file gets full metadata from the mapping
// post-pass instead. Tags never change the plan: callers keying
// cached bytes must fold the tag values into their own key.
// WavPack and Monkey's Audio take them too, as the APEv2 block they
// write after the audio.
//
// Every muxer that bounds its tag block refuses a value that does not
// fit, failing the transcode with waxerr.CodeUnsupportedFormat naming
// the key, rather than writing an output that quietly lacks it. The
// refusal comes before the encode, so it costs nothing to retry with
// the value trimmed. Those bounds sit at each format's own read or
// field limit, so tags read off a source can always be written back;
// Ogg is the exception, capping its comment header at 48 KiB to keep
// the pre-audio headers small. MP4 sets no bound at all.
Tags []container.Tag
// Chapters embeds chapter markers. Only the MP4 muxer represents
// them (Nero chpl); the mapping post-pass covers finished files of
// the other formats.
Chapters []container.Chapter
// Art embeds cover art. Only the MP4 muxer represents it (the ilst
// covr atom); art inflates the pre-audio init header, so live
// streams should leave it nil.
Art *container.Picture
// Progress, when non-nil, is called after each encoded chunk with
// the encoder-input samples consumed so far and the projected total
// (-1 unknown). It runs on the transcoding goroutine, so blocking it
// pauses the pipeline; the job runner's yield-to-live-streams check
// rides on exactly that.
Progress func(done, total int64)
}
// FLACLevel spellings whose meaning the zero value cannot carry.
const (
// FLACLevelDefault keeps the encoder's default compression level.
FLACLevelDefault = 0
// FLACLevelFastest selects FLAC level 0.
FLACLevelFastest = -1
)
// WavPackLevel spellings. WavPack's levels are named modes rather than a
// numeric scale, and they are numbered from one, so the zero value means the
// default with no sentinel needed.
const (
// WavPackLevelDefault keeps the encoder's default level (normal).
WavPackLevelDefault = 0
// WavPackLevelFast is the shallowest cascade: fastest, largest.
WavPackLevelFast = wavpack.LevelFast
// WavPackLevelNormal is the default cascade.
WavPackLevelNormal = wavpack.LevelNormal
// WavPackLevelHigh spends more passes for a smaller file.
WavPackLevelHigh = wavpack.LevelHigh
// WavPackLevelVeryHigh is the deepest cascade: smallest, slowest.
WavPackLevelVeryHigh = wavpack.LevelVeryHigh
)
// APELevel spellings. Monkey's Audio names its levels in thousands, which is
// the vocabulary the format itself uses in the file header, so the option
// carries those numbers rather than a scale of its own; zero is not one of
// them, so the default needs no sentinel.
const (
// APELevelDefault keeps the encoder's default level (normal).
APELevelDefault = 0
// APELevelFast is the shallowest cascade: no filter at all, fastest,
// largest.
APELevelFast = ape.LevelFast
// APELevelNormal is the default cascade, a 16-tap filter.
APELevelNormal = ape.LevelNormal
// APELevelHigh runs a 64-tap filter for a smaller file.
APELevelHigh = ape.LevelHigh
)
// OpusComplexity spellings whose meaning the zero value cannot carry.
const (
// OpusComplexityDefault keeps the encoder's default complexity.
OpusComplexityDefault = 0
// OpusComplexityLowest selects complexity 0.
OpusComplexityLowest = -1
)
// ProbeOptions configures Engine.Probe.
type ProbeOptions struct {
// Strict turns tolerated input damage into errors.
Strict bool
}