dither

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package dither provides bit-depth quantization with configurable dither PDFs and noise-shaping filters.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func SharpPresetForSampleRate

func SharpPresetForSampleRate(sampleRate float64) []float64

SharpPresetForSampleRate returns the sharp 15 kHz noise-shaping coefficients optimized for the given sample rate. The selection logic matches the legacy TDitherSharpNoiseShaper32.ChooseNoiseshaper implementation.

Types

type DitherType

type DitherType int

DitherType selects the probability distribution used for dither noise.

const (
	// DitherNone applies no dither (plain rounding/truncation).
	DitherNone DitherType = iota
	// DitherRectangular uses a uniform (rectangular) PDF.
	DitherRectangular
	// DitherTriangular uses a triangular PDF (TPDF), the most common choice.
	DitherTriangular
	// DitherGaussian uses an exact Gaussian PDF.
	DitherGaussian
	// DitherFastGaussian uses an approximated Gaussian PDF (sum of uniform draws).
	DitherFastGaussian
)

func (DitherType) String

func (dt DitherType) String() string

String returns the name of the dither type.

func (DitherType) Valid

func (dt DitherType) Valid() bool

Valid reports whether dt is a known dither type.

type FIRShaper

type FIRShaper struct {
	// contains filtered or unexported fields
}

FIRShaper implements error-feedback noise shaping with FIR coefficients and a circular buffer for quantization error history.

func NewFIRShaper

func NewFIRShaper(coeffs []float64) *FIRShaper

NewFIRShaper creates a new FIR noise shaper with the given coefficients. A nil or empty slice creates a pass-through (no shaping).

func (*FIRShaper) RecordError

func (s *FIRShaper) RecordError(quantizationError float64)

RecordError stores the quantization error for the current sample. Must be called once after each Shape call.

func (*FIRShaper) Reset

func (s *FIRShaper) Reset()

Reset clears the error history and resets the ring buffer position.

func (*FIRShaper) Shape

func (s *FIRShaper) Shape(input float64) float64

Shape applies FIR error-feedback filtering. The filter subtracts weighted past quantization errors from the input.

type IIRShelfShaper

type IIRShelfShaper struct {
	// contains filtered or unexported fields
}

IIRShelfShaper implements noise shaping using a biquad low-shelf filter applied to the quantization error signal. This provides a lightweight alternative to FIR noise shaping with less precise spectral control but lower CPU cost.

func NewIIRShelfShaper

func NewIIRShelfShaper(freq, sampleRate float64) (*IIRShelfShaper, error)

NewIIRShelfShaper creates an IIR shelf noise shaper with the given corner frequency and sample rate. The shelf applies -5 dB of low-frequency de-emphasis to the error signal, pushing quantization noise above the shelf frequency where human hearing is less sensitive.

func (*IIRShelfShaper) RecordError

func (s *IIRShelfShaper) RecordError(quantizationError float64)

RecordError stores the quantization error for the next Shape call.

func (*IIRShelfShaper) Reset

func (s *IIRShelfShaper) Reset()

Reset clears the biquad filter state and stored error.

func (*IIRShelfShaper) Shape

func (s *IIRShelfShaper) Shape(input float64) float64

Shape applies the IIR shelf filter to the previous error and subtracts it from the input.

type NoiseShaper

type NoiseShaper interface {
	// Shape applies the noise-shaping filter to the input sample using
	// previously recorded quantization errors.
	Shape(input float64) float64

	// RecordError stores the quantization error from the current sample
	// for use in subsequent Shape calls.
	RecordError(quantizationError float64)

	// Reset clears all internal state (error history, filter state).
	Reset()
}

NoiseShaper applies spectral shaping to quantization error via feedback filtering. The typical usage cycle per sample is:

  1. shaped := shaper.Shape(scaledInput)
  2. quantized := round(shaped + dither)
  3. shaper.RecordError(float64(quantized) - shaped)

type Option

type Option func(*config) error

Option configures a Quantizer.

func WithBitDepth

func WithBitDepth(bits int) Option

WithBitDepth sets the target bit depth for quantization (1–32, default 16).

func WithDitherAmplitude

func WithDitherAmplitude(amp float64) Option

WithDitherAmplitude sets the dither noise amplitude (default 1.0, must be >= 0).

func WithDitherType

func WithDitherType(dt DitherType) Option

WithDitherType sets the dither noise PDF (default DitherTriangular).

func WithFIRPreset

func WithFIRPreset(p Preset) Option

WithFIRPreset creates an FIRShaper from a predefined coefficient Preset.

func WithIIRShelf

func WithIIRShelf(freq float64) Option

WithIIRShelf creates an IIRShelfShaper with the given corner frequency. The sample rate is taken from the Quantizer constructor.

func WithLimit

func WithLimit(enabled bool) Option

WithLimit enables or disables output limiting to the bit-depth range (default true).

func WithNoiseShaper

func WithNoiseShaper(ns NoiseShaper) Option

WithNoiseShaper sets a custom NoiseShaper implementation.

func WithRNG

func WithRNG(rng *rand.Rand) Option

WithRNG sets a deterministic random number generator for reproducible output.

func WithSharpPreset

func WithSharpPreset() Option

WithSharpPreset enables sample-rate-adaptive sharp noise shaping. The coefficient set is selected automatically based on the Quantizer's sample rate.

type Preset

type Preset int

Preset identifies a predefined FIR noise-shaping coefficient set.

const (
	PresetNone         Preset = iota // No shaping
	PresetEFB                        // Simple error feedback, 1st order
	Preset2SC                        // Simple 2nd-order highpass
	Preset2MEC                       // Modified E-weighted, 2nd order
	Preset3MEC                       // Modified E-weighted, 3rd order
	Preset9MEC                       // Modified E-weighted, 9th order
	Preset5IEC                       // Improved E-weighted, 5th order
	Preset9IEC                       // Improved E-weighted, 9th order
	Preset3FC                        // F-weighted, 3rd order
	Preset9FC                        // F-weighted, 9th order (default)
	PresetSBM                        // Sony Super Bit Mapping, 12th order
	PresetSBMReduced                 // Reduced Super Bit Mapping, 10th order
	PresetSharp14k                   // Sharp 14 kHz rolloff, 7th order (44.1 kHz)
	PresetSharp15k                   // Sharp 15 kHz rolloff, 8th order (44.1 kHz)
	PresetSharp16k                   // Sharp 16 kHz rolloff, 9th order (44.1 kHz)
	PresetExperimental               // Experimental, 9th order

)

func (Preset) Coefficients

func (p Preset) Coefficients() []float64

Coefficients returns a copy of the FIR noise-shaping coefficients for this preset. Returns nil for PresetNone.

func (Preset) String

func (p Preset) String() string

String returns the name of the preset.

func (Preset) Valid

func (p Preset) Valid() bool

Valid reports whether p is a known preset.

type Quantizer

type Quantizer struct {
	// contains filtered or unexported fields
}

Quantizer performs bit-depth quantization with optional dither noise and noise shaping.

func NewQuantizer

func NewQuantizer(sampleRate float64, opts ...Option) (*Quantizer, error)

NewQuantizer creates a new Quantizer. The default configuration is: 16-bit, triangular dither, amplitude 1.0, limiting enabled, F-weighted 9th-order FIR noise shaper (Preset9FC).

Example
package main

import (
	"fmt"
	"math"
	"math/rand/v2"

	"github.com/cwbudde/algo-dsp/dsp/dither"
)

func main() {
	quant, err := dither.NewQuantizer(
		44100,
		dither.WithBitDepth(16),
		dither.WithDitherType(dither.DitherTriangular),
		dither.WithRNG(rand.New(rand.NewPCG(42, 0))),
	)
	if err != nil {
		panic(err)
	}

	// Quantize a sine wave sample.
	input := 0.5 * math.Sin(2*math.Pi*1000/44100)
	output := quant.ProcessSample(input)

	fmt.Printf("quantized: %.6f\n", output)
}
Output:
quantized: 0.071000
Example (SharpPreset)
package main

import (
	"fmt"
	"math/rand/v2"

	"github.com/cwbudde/algo-dsp/dsp/dither"
)

func main() {
	// The sharp preset automatically selects coefficients
	// optimized for the given sample rate.
	quant, err := dither.NewQuantizer(
		48000,
		dither.WithSharpPreset(),
		dither.WithBitDepth(16),
		dither.WithRNG(rand.New(rand.NewPCG(42, 0))),
	)
	if err != nil {
		panic(err)
	}

	output := quant.ProcessSample(0.5)

	fmt.Printf("sharp: %.6f\n", output)
}
Output:
sharp: 0.499992

func (*Quantizer) BitDepth

func (q *Quantizer) BitDepth() int

BitDepth returns the current target bit depth.

func (*Quantizer) DitherAmplitude

func (q *Quantizer) DitherAmplitude() float64

DitherAmplitude returns the current dither noise amplitude.

func (*Quantizer) DitherType

func (q *Quantizer) DitherType() DitherType

DitherType returns the current dither noise type.

func (*Quantizer) Limit

func (q *Quantizer) Limit() bool

Limit returns whether output limiting is enabled.

func (*Quantizer) ProcessInPlace

func (q *Quantizer) ProcessInPlace(buf []float64)

ProcessInPlace quantizes each sample in buf in-place.

Example
package main

import (
	"fmt"

	"github.com/cwbudde/algo-dsp/dsp/dither"
)

func main() {
	quant, err := dither.NewQuantizer(
		44100,
		dither.WithBitDepth(16),
		dither.WithDitherType(dither.DitherNone),
		dither.WithFIRPreset(dither.PresetNone),
	)
	if err != nil {
		panic(err)
	}

	buf := []float64{0.0, 0.25, 0.5, 0.75}
	quant.ProcessInPlace(buf)

	for _, val := range buf {
		fmt.Printf("%.6f ", val)
	}

	fmt.Println()
}
Output:
0.000015 0.249989 0.499992 0.749996

func (*Quantizer) ProcessInteger

func (q *Quantizer) ProcessInteger(input float64) int

ProcessInteger quantizes the input (expected in [-1, +1]) to an integer in the bit-depth range.

func (*Quantizer) ProcessSample

func (q *Quantizer) ProcessSample(input float64) float64

ProcessSample quantizes the input and returns a normalized float64 in approximately [-1, +1].

func (*Quantizer) Reset

func (q *Quantizer) Reset()

Reset clears all internal state (noise shaper history).

func (*Quantizer) SampleRate

func (q *Quantizer) SampleRate() float64

SampleRate returns the configured sample rate.

func (*Quantizer) SetBitDepth

func (q *Quantizer) SetBitDepth(bits int) error

SetBitDepth changes the target bit depth (1–32).

func (*Quantizer) SetDitherAmplitude

func (q *Quantizer) SetDitherAmplitude(amp float64) error

SetDitherAmplitude changes the dither noise amplitude.

func (*Quantizer) SetDitherType

func (q *Quantizer) SetDitherType(dt DitherType) error

SetDitherType changes the dither noise PDF.

func (*Quantizer) SetLimit

func (q *Quantizer) SetLimit(enabled bool)

SetLimit enables or disables output limiting.

Directories

Path Synopsis
Package design provides a stochastic coefficient optimizer for psychoacoustically weighted noise-shaping filters.
Package design provides a stochastic coefficient optimizer for psychoacoustically weighted noise-shaping filters.

Jump to

Keyboard shortcuts

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