audio

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package audio provides the PCM ingest and FFT analysis that feed the TUI's visualizers. PipeReader consumes raw little-endian s16le stereo samples from librespot's stdout; the FFT layer produces FrequencyData with log-spaced bands plus bass/mid/high convenience averages.

FrequencyData also exposes LeftLevel and RightLevel, time-domain per-channel peak amplitudes shared-AGC normalized to 0–1, for visualizers (e.g. the VU meter) that need true stereo loudness rather than the post-mix spectral peak. ProgressMs is derived from the running sample count so visualizers can display playback progress without a separate Spotify poll. PipeReader is safe to call Start/Stop from any goroutine; reads are internally synchronized.

Index

Constants

View Source
const ChunkBytes = WindowSize * 2 * 2

ChunkBytes is the number of raw bytes per FFT frame (stereo, 16-bit). 2048 samples × 2 channels × 2 bytes = 8192.

View Source
const NumBands = 64

NumBands is the number of frequency bands in FFT output.

View Source
const WindowSize = 2048

WindowSize is the number of mono samples per FFT frame. 2048 at 44100 Hz = ~46 ms, a good latency/resolution tradeoff.

Variables

View Source
var DefaultFormat = PCMFormat{SampleRate: 44100, Channels: 2, BitDepth: 16}

DefaultFormat is librespot's default output: 44100 Hz, stereo, 16-bit signed LE.

Functions

This section is empty.

Types

type Analyzer

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

Analyzer performs FFT analysis on PCM audio chunks and produces FrequencyData.

func NewAnalyzer

func NewAnalyzer(windowSize int) *Analyzer

NewAnalyzer creates an Analyzer with a precomputed Hann window of the given size.

func (*Analyzer) Analyze

func (a *Analyzer) Analyze(samples []int16) FrequencyData

Analyze takes interleaved stereo int16 PCM samples and returns FrequencyData. The samples slice must contain at least WindowSize*2 values (stereo pairs).

type FrequencyData

type FrequencyData struct {
	Bands      [NumBands]float32 // log-spaced frequency bands, normalized 0.0–1.0
	Peak       float32           // overall spectral peak this frame, 0.0–1.0
	Bass       float32           // average of bands 0–7
	Mid        float32           // average of bands 8–31
	High       float32           // average of bands 32–63
	LeftLevel  float32           // time-domain per-channel peak, AGC-normalized 0.0–1.0
	RightLevel float32           // time-domain per-channel peak, AGC-normalized 0.0–1.0
	ProgressMs int32             // playback progress derived from PCM sample count
}

FrequencyData holds FFT output mapped to visualization-friendly bands.

func (*FrequencyData) ComputeConvenienceFields

func (fd *FrequencyData) ComputeConvenienceFields()

ComputeConvenienceFields fills Bass, Mid, and High from Bands.

type PCMFormat

type PCMFormat struct {
	SampleRate int
	Channels   int
	BitDepth   int
}

PCMFormat describes the expected audio format from librespot.

type PipeReader added in v0.2.0

type PipeReader struct {

	// NewPlayer creates an audio player. Defaults to oto. Override in tests.
	NewPlayer playerFactory
	// contains filtered or unexported fields
}

PipeReader reads raw PCM from librespot's stdout pipe, plays it via oto, runs FFT analysis, and stores the latest FrequencyData atomically.

func NewPipeReader added in v0.2.0

func NewPipeReader() *PipeReader

NewPipeReader creates a PipeReader ready to accept pipes via Start().

func (*PipeReader) Latest added in v0.2.0

func (pr *PipeReader) Latest() *FrequencyData

Latest returns the most recent FrequencyData, or nil if no fresh data. Returns nil if the last frame is older than 150ms (e.g., paused or between restarts). Thread-safe; called from the Bubble Tea goroutine.

Bands are compensated by the inverse of the current device volume so visualizers stay bright at low playback volume (librespot's softvol scales the PCM before piping). Compensation is capped at maxVolumeGain.

func (*PipeReader) SetVolumePercent added in v0.3.0

func (pr *PipeReader) SetVolumePercent(v int)

SetVolumePercent records the current Spotify device volume (0–100) so Latest() can compensate the FFT bands for it. Safe to call from any goroutine; applies to the next Latest() read. Out-of-range values are clamped and logged so upstream data quality issues surface in the log.

func (*PipeReader) Start added in v0.2.0

func (pr *PipeReader) Start(pipe io.ReadCloser)

Start begins reading PCM from pipe, playing audio and running FFT analysis. Safe to call multiple times — each call cancels the previous read loop. This handles librespot restarts which create a new stdout pipe each time.

func (*PipeReader) Stop added in v0.2.0

func (pr *PipeReader) Stop()

Stop cancels any active read loop. Safe to call multiple times.

Jump to

Keyboard shortcuts

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