Documentation
¶
Overview ¶
Package aac implements an AAC-LC encoder ported from FFmpeg's native encoder, pinned at FFmpeg commit d09d5afc3a. See PROVENANCE.md for the origin and licensing of the ported code; architecture, porting and validation documentation is maintained in the internal planning repository.
The library has two layers, mirroring go-flac (flac + pcm) and go-opus (opus + oggopus):
- This package is the low-level codec: planar float32 frames in, raw AAC access units out (Encoder, EncodeFrame), plus the framing helpers raw-AU consumers need (AppendADTSHeader, Encoder.AudioSpecificConfig). Raw access units are not self-framing; see EncodeFrame.
- Package pcm is the streaming layer and the right entry point for almost all callers: interleaved little-endian integer PCM in via io.Writer, a self-framing ADTS stream out.
An Encoder is not safe for concurrent use; use one per goroutine. The package has no mutable global state, so any number of encoders run in parallel.
Example (LowLevel) ¶
Example_lowLevel encodes planar float32 frames with the low-level Encoder and frames each raw access unit in ADTS by hand. Callers with interleaved integer PCM should use the pcm package instead, which does all of this internally.
package main
import (
"fmt"
"log"
aac "github.com/tphakala/go-aac"
)
func main() {
enc, err := aac.NewEncoder(aac.EncoderConfig{
SampleRate: 48000,
Channels: 1,
Bitrate: 96000,
})
if err != nil {
log.Fatal(err)
}
frame := make([]float32, aac.FrameSize) // silence; real callers fill [-1, 1] samples
var stream, au []byte
writeAU := func() {
if len(au) == 0 {
return // encoder priming: the first call emits nothing
}
// A raw access unit is NOT self-framing: it must be wrapped in an
// ADTS header (or muxed into MP4 using AudioSpecificConfig).
stream, err = aac.AppendADTSHeader(stream, 48000, 1, len(au))
if err != nil {
log.Fatal(err)
}
stream = append(stream, au...)
}
for range 10 {
if au, err = enc.EncodeFrame(au[:0], [][]float32{frame}); err != nil {
log.Fatal(err)
}
writeAU()
}
for !enc.Drained() {
if au, err = enc.EncodeFrame(au[:0], nil); err != nil {
log.Fatal(err)
}
writeAU()
}
fmt.Printf("ASC % x, stream starts % x\n", enc.AudioSpecificConfig(), stream[:2])
}
Output: ASC 11 88, stream starts ff f1
Index ¶
Examples ¶
Constants ¶
const EncoderDelay = FrameSize
EncoderDelay is the encoder priming delay: the number of leading samples, per channel and at the output sample rate, that the decoded output carries ahead of the first input sample. A muxer must trim exactly this many leading samples from the decoded audio to reproduce the source gaplessly. For an MP4 edit list whose media timescale is the sample rate (the usual audio convention) set the entry's media_time to EncoderDelay; with a different timescale scale it accordingly, since media_time is in timescale units while EncoderDelay is in samples. For an iTunSMPB tag the priming (encoder delay) field is EncoderDelay, always in samples.
The delay is structurally one full frame, because the encoder buffers the first frame before emitting any access unit (see EncodeFrame). It is a property of go-aac's AAC-LC encoding path, not of AudioSpecificConfig, and it describes only the samples this encoder prepends; it makes no claim about any additional latency a particular decoder may add to its own output.
const FrameSize = 1024
FrameSize is the number of samples per channel consumed by one Encoder.EncodeFrame call and covered by one AAC access unit.
Variables ¶
var ( // ErrInvalidAudio reports input samples containing NaN or Inf. It maps // the C encoder's spectral coefficient guard (aacenc.c:1119-1124 // @ d09d5afc3a) to a testable sentinel. ErrInvalidAudio = errors.New("go-aac: input contains NaN or Inf") // ErrEncoderClosed is returned by pcm.Encoder.Write and Close after Close // has been called, and by the aac.Encoder and pcm.Encoder methods when the // encoder is uninitialized: a zero value, or one left unusable by a failed // Reset. ErrEncoderClosed = errors.New("go-aac: encoder is closed") )
Sentinel errors returned by the aac and pcm packages, testable with errors.Is. They live in the root package, mirroring go-flac's layout (flac.ErrEncoderClosed is used by go-flac/pcm the same way).
var SampleRates = [13]int{96000, 88200, 64000, 48000, 44100, 32000,
24000, 22050, 16000, 12000, 11025, 8000, 7350}
SampleRates is the MPEG-4 audio sample rate table; the position of a rate is its 4-bit index in ADTS and AudioSpecificConfig. Mirrors ff_mpeg4audio_sample_rates (libavcodec/mpeg4audio.c @ d09d5afc3a).
Functions ¶
func AppendADTSHeader ¶
AppendADTSHeader appends the 7-byte ADTS header framing one raw AAC-LC access unit of payloadLen bytes, returning the extended slice. Callers of the low-level Encoder use it to build a streamable ADTS stream: for each access unit, append the header, then the access unit itself (the pcm package does exactly this). channels is the ADTS channel configuration (1..7). Byte-identical to FFmpeg's ADTS muxer output (libavformat/adtsenc.c:adts_write_frame_header @ d09d5afc3a).
Types ¶
type Coder ¶
type Coder int
Coder selects the quantizer search strategy. The zero value is CoderNMR, upstream's default. Mirrors enum AACCoder (libavcodec/aacenc.h @ d09d5afc3a).
const ( // CoderNMR is the noise-to-mask-ratio trellis search, the upstream // default and the best quality per bit. CoderNMR Coder = iota // CoderTwoLoop is the ISO 13818-7 Appendix C two-loop search. CoderTwoLoop // CoderFast is the constrained two-loop heuristic, the fastest. CoderFast )
Quantizer search strategies, from best quality to fastest.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder is the low-level AAC-LC encoder: planar float32 frames in, raw access units out, one frame per call. The output is NOT self-framing; see EncodeFrame. For interleaved integer PCM in and a streamable ADTS stream out, use the pcm package instead.
An Encoder is stateful (MDCT overlap, psychoacoustic history, bit reservoir all carry across frames) and is not safe for concurrent use; use one Encoder per goroutine.
func NewEncoder ¶
func NewEncoder(cfg EncoderConfig) (*Encoder, error)
NewEncoder returns an Encoder for cfg. Mirrors aacenc.c:aac_encode_init @ d09d5afc3a: bitrate clamping, coding bandwidth selection and psychoacoustic model setup all happen here, fixed for the stream.
func (*Encoder) AudioSpecificConfig ¶
AudioSpecificConfig returns the MPEG-4 AudioSpecificConfig for this stream (2 bytes for AAC-LC), as carried in an MP4 esds box; raw-access- unit consumers hand it to their muxer or decoder. It returns a fresh copy on every call; callers may retain or mutate it freely. Mirrors aacenc.c:put_audio_specific_config @ d09d5afc3a.
func (*Encoder) Delay ¶
Delay reports this stream's encoder priming delay in samples per channel: the number of leading samples a muxer must trim from the decoded output. It feeds an MP4 edit list media_time (scaled to the track timescale, which for audio is usually the sample rate) or an iTunSMPB priming field, in samples. It always equals EncoderDelay for the current encoder, and is exposed as a method so a muxer holding an *Encoder can read the delay from the encoder itself. Should a future configuration ever make the delay depend on the stream, this accessor would keep reporting the correct value without any API change.
func (*Encoder) Drained ¶
Drained reports whether a drain (EncodeFrame with nil samples) has flushed all buffered audio; once true, further nil calls append nothing.
func (*Encoder) EncodeFrame ¶
EncodeFrame encodes the next frame of planar float32 PCM in [-1, 1] (one slice per channel, up to FrameSize samples each; only the final frame of a stream may be shorter, and it is zero-padded) and appends the encoded access unit to dst, returning the extended slice. It is append-style like strconv.AppendInt: allocation-free when dst has capacity.
The returned bytes are one RAW access unit, not a self-framing stream: concatenating raw access units produces a byte stream no decoder accepts. Wrap each access unit in an ADTS header (AppendADTSHeader), or mux the access units into a container (MP4/M4A) using AudioSpecificConfig for the decoder configuration. The pcm package does the ADTS framing automatically and is the right layer for almost all callers.
The encoder delays output by one frame (encoder priming): the first call appends nothing. This shifts the decoded output later by EncoderDelay samples per channel, which a muxer trims (see EncoderDelay). Pass nil samples to drain; each nil call appends one remaining access unit until Drained reports true:
for !e.Drained() {
dst, err = e.EncodeFrame(dst, nil)
}
Input containing NaN or Inf returns an error satisfying errors.Is(err, ErrInvalidAudio) and appends nothing. The samples are checked at ingest, so the error surfaces on the same call that carried the bad samples. After ErrInvalidAudio the stream is unusable; Reset the encoder.
func (*Encoder) Reset ¶
func (e *Encoder) Reset(cfg EncoderConfig) error
Reset re-arms the encoder for a new, independent stream with cfg, reusing all internal buffers (the encoder state is about 650 KiB, so pooled reuse matters; this is what backs pcm.EncodeInterleaved). Encoding after Reset produces the same bytes as a fresh NewEncoder. On error the encoder must not be used.
type EncoderConfig ¶
type EncoderConfig struct {
// SampleRate is the input sample rate in Hz: 44100 or 48000 in v1.
// Required; there is no zero default.
SampleRate int
// Channels is 1 (mono SCE) or 2 (stereo CPE). Required; there is no
// zero default.
Channels int
// Bitrate is the ABR target in bits per second for the whole stream
// (all channels), e.g. 128000. Zero selects FFmpeg's default of
// 200 kb/s total. Targets above the AAC buffer model ceiling (6144
// bits per channel per 1024-sample frame) are clamped, exactly as the
// C encoder clamps them; negative targets are rejected.
Bitrate int
// Cutoff, when > 0, overrides the automatic coding bandwidth in Hz
// (FFmpeg -cutoff equivalent). It must not exceed SampleRate/2. Leave
// it 0 for the tuned rate-dependent default.
Cutoff int
// Coder selects the quantizer search. The zero value is CoderNMR,
// upstream's default and the recommended choice.
Coder Coder
// Tool switches are negative (Disable*) so the zero value enables
// every tool, matching FFmpeg's defaults: TNS, PNS, M/S and I/S on.
DisableTNS bool // disable temporal noise shaping
DisablePNS bool // disable perceptual noise substitution
DisableMS bool // disable the mid/side stereo search
DisableIS bool // disable intensity stereo
}
EncoderConfig configures a low-level Encoder. It is a flat struct, mirroring opus.EncoderConfig in go-opus: every field's zero value is documented, so a literal with only SampleRate and Channels set is a complete, valid configuration.
type Stats ¶
type Stats struct {
Frames int64 // access units produced
ChannelFrames int64 // coded channel-frames (Frames x channels)
ShortFrames int64 // channel-frames coded with eight short windows
TNSLongFrames int64 // TNS-active channel-frames among long blocks
TNSShortFrames int64 // TNS-active channel-frames among short blocks
Bands int64 // coded channel-bands
PNSBands int64 // of which perceptual noise substitution
PairBands int64 // coded channel-pair bands (stereo only)
MSBands int64 // of which mid/side coded
ISBands int64 // of which intensity coded
MeanLambda float64 // mean per-frame operating lambda (the C's Qavg)
}
Stats reports encoder tool usage, accumulated per encoded frame over the final per-band decisions. It mirrors the counters FFmpeg's encoder prints once at uninit (libavcodec/aacenc.h:232-239, aacenc.c:1352-1386 @ d09d5afc3a); this library never logs, so the report is exposed as data instead. All counters reset on Encoder.Reset.
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
bits
Package bits implements an MSB-first bit writer with the semantics of FFmpeg's PutBitContext (libavcodec/put_bits.h @ d09d5afc3a).
|
Package bits implements an MSB-first bit writer with the semantics of FFmpeg's PutBitContext (libavcodec/put_bits.h @ d09d5afc3a). |
|
coder
NMR (noise-to-mask ratio) scalefactor coder: an optimal Viterbi search over scalefactors with self rate control.
|
NMR (noise-to-mask ratio) scalefactor coder: an optimal Viterbi search over scalefactors with self rate control. |
|
dec
Package dec implements the AAC-LC decoder, ported from the fixed-point flavor of FFmpeg's AAC decoder (libavcodec/aac/ @ d09d5afc3a).
|
Package dec implements the AAC-LC decoder, ported from the fixed-point flavor of FFmpeg's AAC decoder (libavcodec/aac/ @ d09d5afc3a). |
|
dsp
Package dsp provides the scalar DSP kernels of the AAC encoder.
|
Package dsp provides the scalar DSP kernels of the AAC encoder. |
|
enc
Package enc implements the AAC encoder frame pipeline: input buffering, window switching, MDCT, psychoacoustic analysis, rate control and raw_data_block bitstream writing.
|
Package enc implements the AAC encoder frame pipeline: input buffering, window switching, MDCT, psychoacoustic analysis, rate control and raw_data_block bitstream writing. |
|
fdsp
Package fdsp ports the int32 primitives of libavutil/fixed_dsp.c that the fixed-point AAC decoder uses (avpriv_alloc_fixed_dsp @ d09d5afc3a).
|
Package fdsp ports the int32 primitives of libavutil/fixed_dsp.c that the fixed-point AAC decoder uses (avpriv_alloc_fixed_dsp @ d09d5afc3a). |
|
fmath
Package fmath centralizes the math primitives used by encoder code paths (docs/go-design.md: only this package imports math in per-frame code).
|
Package fmath centralizes the math primitives used by encoder code paths (docs/go-design.md: only this package imports math in per-frame code). |
|
mdct
Package mdct implements the forward MDCT used by the AAC encoder, numerically equivalent to FFmpeg's AV_TX_FLOAT_MDCT (libavutil/tx @ d09d5afc3a).
|
Package mdct implements the forward MDCT used by the AAC encoder, numerically equivalent to FFmpeg's AV_TX_FLOAT_MDCT (libavutil/tx @ d09d5afc3a). |
|
psy
Package psy implements the AAC encoder psychoacoustic model: the LAME block-switching window decision and the 3GPP TS26.403-inspired threshold analysis with its bit reservoir.
|
Package psy implements the AAC encoder psychoacoustic model: the LAME block-switching window decision and the 3GPP TS26.403-inspired threshold analysis with its bit reservoir. |
|
tables
SPDX-License-Identifier: LGPL-2.1-or-later
|
SPDX-License-Identifier: LGPL-2.1-or-later |
|
tx
Package tx ports the int32 flavor of FFmpeg's libavutil/tx transform framework, exactly as instantiated for AV_TX_INT32_MDCT by the fixed-point AAC decoder: the split-radix power-of-two FFT codelets and their fixed-point twiddle tables.
|
Package tx ports the int32 flavor of FFmpeg's libavutil/tx transform framework, exactly as instantiated for AV_TX_INT32_MDCT by the fixed-point AAC decoder: the split-radix power-of-two FFT codelets and their fixed-point twiddle tables. |
|
vlc
Package vlc implements a table-driven decoder for the variable-length codes of the AAC bitstream.
|
Package vlc implements a table-driven decoder for the variable-length codes of the AAC bitstream. |
|
window
Package window generates the AAC analysis half-windows at init time.
|
Package window generates the AAC analysis half-windows at init time. |
|
Package pcm is the high-level PCM streaming API for go-aac: interleaved little-endian integer PCM in via io.Writer, an AAC-LC ADTS stream out.
|
Package pcm is the high-level PCM streaming API for go-aac: interleaved little-endian integer PCM in via io.Writer, an AAC-LC ADTS stream out. |
|
tools
|
|
|
cdec/gencorpus
command
Generates D0 rehearsal corpus streams from the go-aac encoder itself: an ADTS stream and a raw-AU stream (2-byte BE length prefix per AU).
|
Generates D0 rehearsal corpus streams from the go-aac encoder itself: an ADTS stream and a raw-AU stream (2-byte BE length prefix per AU). |
|
cdec/gencraft
command
Crafts corpus streams for decode paths no encoder emits:
|
Crafts corpus streams for decode paths no encoder emits: |
|
wav2aac
command
wav2aac encodes a canonical RIFF/WAVE file to an ADTS AAC stream with go-aac.
|
wav2aac encodes a canonical RIFF/WAVE file to an ADTS AAC stream with go-aac. |