aac

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: LGPL-2.1 Imports: 3 Imported by: 0

README

go-aac

CI Go Reference Go Version Sponsor

Pure-Go AAC-LC encoder and decoder, ported from FFmpeg's native AAC encoder and fixed-point decoder. No cgo and no external libraries in the published module.

Status

Every layer is validated against the C reference before it lands (see Approach), so the pieces marked done are done in the strong sense.

  • Encoder: complete for AAC-LC. All three FFmpeg coders (NMR, twoloop, fast), all four coding tools (TNS, PNS, M/S, I/S), mono and stereo, 44.1 and 48 kHz, ADTS output. The NMR coder is the default, as it is upstream at the pin (aac_coder is AAC_CODER_NMR, and the commit that made it so says the old coders will soon be removed). FFmpeg's own released docs still describe the older set, in which twoloop is the default and anmr is a different, experimental coder; that text is stale, not a contradiction.
  • Decoder: usable for AAC-LC. Pure fixed point, producing output identical to ffmpeg -c:a aac_fixed at the sample level. The public pcm.NewDecoder streams an ADTS (or raw plus ASC) AAC-LC stream to interleaved little-endian S16 PCM, matching the oracle byte for byte across the test corpus and on Apple afconvert output, with no cgo. Mono and stereo, 44.1 and 48 kHz. Not yet covered: HE-AAC (SBR/PS), 960-sample frames, and channel configs above stereo.

Quality tracks the C encoder closely. At 96/128/192 kbps stereo with the NMR coder on both sides, decoded PSNR is within +-0.04 dB of FFmpeg's own output and stream sizes within 0.22%.

On real field recordings the port slightly exceeds the C encoder at the same bitrate:

Recording (48 kHz mono, 128 kbps) go-aac FFmpeg (same coder)
120 s dawn chorus 85.44 dB 85.42 dB
15 s distant owl call 63.90 dB 63.87 dB

Not implemented: HE-AAC (SBR/PS), xHE-AAC, LATM, ER/LD/ELD profiles, multichannel beyond stereo, VBR (global_quality), MP4 muxing (the pure-Go go-m4a is the container companion).

Approach

go-aac is a faithful port of FFmpeg's AAC encoder and fixed-point decoder at a pinned commit (d09d5afc3a), kept honest by differential testing against the real C.

For each subsystem, a C harness links the pinned FFmpeg libraries, runs the actual FFmpeg function on identical input, and dumps its internals; the Go port must then reproduce them. That is a far sharper instrument than PSNR:

Harness What it pins Result
tools/cdump MDCT, KBD windows, LPC 1.17e-07 relative / bit-exact / 0
tools/gentables 31 codec tables byte-identical
tools/cquant quantizer search, codebook trellis, band encoding 128/128 band decisions, byte-identical bitstreams
tools/cpsy the 3GPP psychoacoustic model window decisions identical, bit reservoir exact
tools/cnmr the NMR Viterbi trellis and rate control bit-exact, tie-breaking included
tools/ctns, tools/ctwoloop TNS and the twoloop coder bit-exact
decoder gates LC symbol decode, int32 IMDCT, full reconstruction, s16 PCM 1,999,224 symbols + 12,969,984 reconstructed values byte-identical; s16 PCM identical to ffmpeg -c:a aac_fixed

PSNR cannot tell you that a psychoacoustic constant was misported, that a bit reservoir is drifting, or that a Viterbi path was suboptimal. These harnesses can, and they caught real bugs that would otherwise have shipped silently.

The internal packages are deliberately written in a C-shaped style, so they stay diffable against upstream FFmpeg, and every ported function carries a provenance comment naming its C origin. That constraint is temporary: with the AAC-LC port complete and the differential gates green, the idiomatic-Go rewrite happens alongside the optimization work. The public API is idiomatic Go today.

Install

go get github.com/tphakala/go-aac

Usage

The library has two layers, mirroring go-flac (flac + pcm) and go-opus (opus + oggopus).

pcm: the streaming layer

Interleaved little-endian integer PCM in, a self-framing ADTS stream out via io.Writer, or raw access units out through a callback for muxing. This is the right entry point for almost all callers.

import aacpcm "github.com/tphakala/go-aac/pcm"

cfg := aacpcm.Config{SampleRate: 48000, BitDepth: 16, Channels: 1, Bitrate: 96000}

// One shot (encoder drawn from a pool, safe for concurrent use):
err := aacpcm.EncodeInterleaved(w, cfg, pcmBytes)

// Or streaming, accepting any chunk size:
e, err := aacpcm.NewEncoder(w, cfg)
_, err = io.Copy(e, src)
err = e.Close()

Write accepts arbitrary chunk sizes and buffers partial samples internally, so io.Copy works with any buffer, including sizes that do not divide the sample stride.

The package name deliberately collides with go-flac/pcm; import it with an alias (aacpcm), which is ordinary Go practice and lets a consumer switch between the two encoders with the same call shape.

Muxing into MP4 or fragmented MP4 (CMAF) needs the opposite of ADTS: raw access units, boundaries reported out of band. FrameEncoder is that path, and it is the same pipeline, so the units are byte-identical to the ADTS stream's payloads.

fe, err := aacpcm.NewFrameEncoder(cfg)
asc := fe.AudioSpecificConfig() // esds DecoderSpecificInfo, valid before any audio
_ = asc                         // goes in the init segment's esds box
emit := func(au []byte, samples int) error {
    segment = append(segment, au...) // au is borrowed; copy or append
    return nil
}
err = fe.EncodeInterleaved(pcm, emit)
err = fe.Flush(emit) // drains the priming frame; fe.Delay() gives the elst media_time

Delay() and the per-unit samples count are both PCM samples per channel, so a muxer whose track timescale is not the sample rate scales them into media-timescale ticks first.

The emit callback has the same shape as go-flac's pcm.FrameEncoder, so a muxer's per-unit path is shared between the two codecs; the lifecycle differs, since AAC-LC needs a Flush to drain the priming frame where the FLAC frame encoder is one-shot.

Decoding mirrors go-flac's pcm.Decoder: an AAC-LC stream in via io.Reader, interleaved little-endian S16 PCM out.

d, err := aacpcm.NewDecoder(r) // ADTS by default, resynced past leading garbage
if err != nil {                // classify with errors.Is against aacpcm.ErrCorruptStream or aacpcm.ErrUnsupported
    return err
}
info := d.Info()       // SampleRate, Channels, Profile, valid immediately
_, err = io.Copy(w, d) // WriteTo drains the whole decode; Read fills any buffer

The decoded PCM is byte-identical to ffmpeg -c:a aac_fixed -f s16le on every LC stream tested, including Apple afconvert output. The decoder never panics on malformed input (it returns wrapped ErrCorruptStream or ErrUnsupported sentinels) and runs at zero allocations per frame in steady state. Raw access units plus an AudioSpecificConfig are opt in via aacpcm.WithRawStream(asc).

aac: the low-level codec

Planar float32 frames in, raw AAC access units out, append-style and allocation-free in steady state.

import "github.com/tphakala/go-aac"

e, err := aac.NewEncoder(aac.EncoderConfig{SampleRate: 48000, Channels: 1, Bitrate: 128000})
au, err := e.EncodeFrame(au[:0], [][]float32{frame}) // up to aac.FrameSize (1024) samples

Raw access units are not self-framing. Use aac.AppendADTSHeader to build a streamable ADTS stream, or Encoder.AudioSpecificConfig to mux them elsewhere. Most muxing callers want pcm.FrameEncoder instead, which does the conversion, framing and priming for them.

Gapless playback

ADTS cannot signal encoder delay. Decoders emit roughly 1024 extra leading samples, and every AAC-in-ADTS stream behaves this way. Compute clip durations from the source PCM, not from the decoded AAC length.

For gapless, sample-accurate output, mux into a container that carries an edit list, feeding the muxer from pcm.FrameEncoder. go-m4a is the pure-Go MP4/M4A muxer and demuxer that pairs with go-aac for exactly this: it writes the encoder priming (aac.EncoderDelay, also pcm.FrameEncoder.Delay) into an elst edit list so playback is gapless, and reads .m4a files back into access units. Its aacm4a subpackage is a one-call bridge over go-aac, PCM to .m4a and back. No cgo and no external binaries.

Benchmarking

scripts/bench-encoders.sh compares go-aac against FFmpeg's native AAC encoder, the C this library is ported from, on the same input (encode single-threaded, one process, file in and file out), reporting wall time, CPU seconds, peak RSS and stream size. GOAAC_FFMPEG must point at the pinned oracle build; a distro FFmpeg is refused, because 7.x and earlier ship a different coder set whose anmr is not the nmr trellis this library ports.

GOAAC_FFMPEG=/path/to/pinned/ffmpeg scripts/bench-encoders.sh          # generated reproducible input
GOAAC_FFMPEG=/path/to/pinned/ffmpeg scripts/bench-encoders.sh my.wav   # your own WAV

Results on a 120 s 48 kHz mono recording at 128 kbps, single-threaded. The ratio is CPU seconds, go-aac over FFmpeg; the FFmpeg CLI spawns helper threads, so CPU time compares more honestly than wall time.

Coder Platform go-aac FFmpeg go/C
NMR (default) Raspberry Pi 5 19x realtime 40x 2.09x
NMR (default) x86_64 (i7-1260P) 43x 98x 2.09x
NMR (default) Apple M4 Pro 68x 179x 2.64x
twoloop x86_64 78x 156x 1.74x
fast x86_64 143x 308x 1.87x

go-aac runs at roughly twice the CPU time of the C and in about a third of the memory (4.1 MB peak RSS against 10.4 to 13.3 MB). Stream sizes match FFmpeg to within 0.001% for the NMR and fast coders at the same bitrate.

The gap is not FFmpeg's hand-written assembly: disabling it (-cpuflags 0) changes AAC encoding by about 1%. It is compiler auto-vectorization. GCC emits 631 packed floating-point arithmetic instructions in aaccoder.o from plain C, concentrated in the NMR quantizer search; Go's compiler emits none anywhere in the equivalent package. Closing the gap therefore means hand-writing in Go what C compilers generate for free. The optional SIMD kernels below take the first steps; the scalar port remains the canonical reference.

Steady-state encoding is allocation-free (0 allocs/frame) for every coder, mono and stereo. Decoding is far cheaper, roughly 3000x real time for mono and 1500x for stereo at 48 kHz, and is likewise allocation-free per frame in steady state.

Optional SIMD kernels (-tags goaac_simd)

Building with -tags goaac_simd swaps two of the encoder's hottest kernels for SIMD implementations built on github.com/tphakala/simd: the NMR Viterbi trellis search and the AbsPow34 magnitude transform (|x|^(3/4)), with AVX2 on x86_64, NEON on arm64, and a portable Go fallback everywhere else. Every backend is bit-identical, so the tagged build produces byte-identical output to the scalar default and passes the same differential oracle gate, not a relaxed PSNR tier.

Measured full-encode NMR speedups over the scalar default (128 kbps, single recording, benchstat over interleaved rounds):

Platform SIMD trellis Both kernels
Raspberry Pi 5 (Cortex-A76, NEON) 14% faster pending
x86_64 i7-1260P (AVX2) 22% faster about 24% faster

The trellis search is the larger lever; the AbsPow34 kernel adds roughly a further 2.7% on the i7-1260P (4.6% with the psychoacoustic tools disabled). The Raspberry Pi 5 figure for AbsPow34 is pending. Both kernels are byte-identical to the scalar port, so the tag is a pure speed knob with no effect on output.

The default build stays pure Go, with no assembly in the binary and the simd dependency linked only under the tag. The tag is opt-in and the scalar kernel remains canonical.

License

LGPL-2.1-or-later. go-aac is a derivative work of FFmpeg's LGPL-licensed AAC encoder and cannot be relicensed permissively. See LICENSE and PROVENANCE.md.

Sponsor

If go-aac is useful to you, consider sponsoring its development.

Sponsor on GitHub

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, or raw access units through a callback for muxing (pcm.FrameEncoder).

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

View Source
const DefaultBitrate = 200_000

DefaultBitrate is the ABR target selected when a config leaves Bitrate zero: 200 kb/s for the whole stream, regardless of channel count. Mirrors AV_CODEC_DEFAULT_BITRATE, the bit_rate FFmpeg's encoder receives when the caller sets none (libavcodec/options_table.h:47 @ d09d5afc3a).

It is exported so a caller that has to predict the encoded size, such as a muxer pre-sizing a buffer, can read the value that will actually be used instead of restating it and hoping it does not drift.

View Source
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.

View Source
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

View Source
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 after Close and by
	// pcm.FrameEncoder.EncodeInterleaved after Flush; Close and Flush are
	// themselves idempotent and re-report the outcome of the flush they
	// performed, so neither returns this error on a stream that ended
	// cleanly. It is also returned by the aac.Encoder, pcm.Encoder and
	// pcm.FrameEncoder 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).

View Source
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

func AppendADTSHeader(dst []byte, sampleRate, channels, payloadLen int) ([]byte, error)

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 at the pinned commit. Mirrors enum AACCoder (libavcodec/aacenc.h @ d09d5afc3a).

The constants are not ordered by speed, because speed is content dependent: CoderTwoLoop is faster than CoderNMR on broadband material but several times slower on tonal material. See each constant for the measurements.

The coders also code to different bandwidths when Cutoff is 0, and neither is consistently the wider one. CoderNMR takes FFmpeg's tuned rate-to-bandwidth table above 32 kb/s per channel, while CoderTwoLoop and CoderFast derive the cutoff from the bitrate alone (aacenc.c:1592-1614 @ d09d5afc3a). At 48 kHz mono that yields, in Hz:

per channel      32k     64k    128k    192k
CoderNMR       14000   16000   18666   20000
TwoLoop/Fast    9500   16000   20000   22000

So a twoloop stream keeps more highs than an NMR stream at high bitrates but distinctly fewer at low ones, which is visible on a spectrogram as a different shelf. Set Cutoff explicitly if the coding bandwidth has to stay stable across coders.

const (
	// CoderNMR is the noise-to-mask-ratio scalefactor trellis, and the
	// default here because it is the default upstream: aac_coder is
	// {.i64 = AAC_CODER_NMR} at aacenc.c:1651 @ d09d5afc3a. Upstream also
	// intends it to be the only coder, as the commit that made it the
	// default (0efac66e7e) states the old coders will soon be removed.
	//
	// FFmpeg's released documentation describes an older coder set in which
	// twoloop is the default and anmr is an experimental, lower quality
	// coder. That text is stale relative to the code at the pin (doc/
	// encoders.texi does not mention the nmr option at all), and anmr is a
	// different coder from the nmr trellis ported here.
	CoderNMR Coder = iota
	// CoderTwoLoop is the ISO 13818-7 Appendix C two-loop search. It is
	// faster than CoderNMR on broadband material but several times slower
	// on tonal material, where the rate and distortion loops fight each
	// other and the search runs its full iteration budget every frame.
	// Encoding 20 s of 48 kHz mono at the default bitrate took 244 ms
	// against CoderNMR's 314 ms on broadband noise, but 1262 ms against
	// 336 ms on a 1400 Hz tone.
	CoderTwoLoop
	// CoderFast is the constrained two-loop heuristic, which skips the
	// more expensive adjustments. It was the fastest of the three on both
	// broadband and tonal material in the measurements above (126 ms and
	// 640 ms respectively).
	CoderFast
)

Quantizer search strategies.

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

func (e *Encoder) AudioSpecificConfig() []byte

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

func (e *Encoder) Delay() int

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

func (e *Encoder) Drained() bool

Drained reports whether a drain (EncodeFrame with nil samples) has flushed all buffered audio; once true, further nil calls append nothing.

func (*Encoder) EncodeFrame

func (e *Encoder) EncodeFrame(dst []byte, samples [][]float32) ([]byte, error)

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 is the right layer for almost all callers either way: pcm.Encoder does the ADTS framing automatically, and pcm.FrameEncoder reports raw access units for a muxer, both from interleaved integer PCM.

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.

func (*Encoder) Stats

func (e *Encoder) Stats() Stats

Stats returns a snapshot of the encoder's tool-usage counters, accumulated since NewEncoder or Reset. Call it after draining for whole-stream numbers.

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 DefaultBitrate. 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 at the pin and the recommended choice. See Coder
	// for how the alternatives differ in speed and coding bandwidth; note
	// that speed is content dependent rather than a fixed ordering.
	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.

func (Stats) String

func (s Stats) String() string

String formats the stats like the report FFmpeg's encoder logs at uninit (aacenc.c:1437-1445 @ d09d5afc3a).

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, an AAC-LC ADTS stream out via io.Writer (Encoder), or raw access units out through a callback for muxing into MP4 (FrameEncoder).
Package pcm is the high-level PCM streaming API for go-aac: interleaved little-endian integer PCM in, an AAC-LC ADTS stream out via io.Writer (Encoder), or raw access units out through a callback for muxing into MP4 (FrameEncoder).
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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL