audio

package
v3.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: AGPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package audio is the playback engine: it streams, decrypts and decodes Deezer audio (MP3 + FLAC) into a PCM ring that an output device drains. Supports seek, per-track ReplayGain, gapless transitions and (experimental) crossfade. In-memory by default; SetStreamCache attaches an opt-in on-disk cache of the raw (still-encrypted) CDN bytes for full tracks — decrypted audio is never written to disk.

The output device is abstracted behind the `output` interface so the backend is build-tag-selected: malgo/miniaudio by default (adds output-device selection), or oto under the `otosink` tag — used for the macOS GUI, where malgo's CoreAudio callback runs unreliably inside the c-archive.

Index

Constants

This section is empty.

Variables

View Source
var EQPresetNames = []string{
	"flat", "bass-boost", "bass-reducer", "treble-boost", "vocal",
	"rock", "pop", "jazz", "classical", "electronic",
}

EQPresetNames is the preset order every client shows (core-owned so the UIs stay identical). "custom" is implicit: any manual band edit switches to it.

View Source
var ErrOffline = errors.New("offline: track not cached and no CDN URL available")

ErrOffline is returned when attempting to play a track offline (empty CDN URL) but the track is not present in the local StreamCache.

Functions

This section is empty.

Types

type Device

type Device struct {
	ID        string
	Name      string
	IsDefault bool
}

Device is an output device the user can pick (empty ID = system default).

type Player

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

Player owns the malgo context + one output device and plays a current source, optionally with a preloaded next source for gapless/crossfade.

func NewPlayer

func NewPlayer() (*Player, error)

NewPlayer initializes the audio output (backend chosen by build tag).

func (*Player) AddVolume

func (p *Player) AddVolume(delta float64) float64

func (*Player) CancelSleepTimer

func (p *Player) CancelSleepTimer()

CancelSleepTimer disarms the sleep timer and restores full volume.

func (*Player) ClearPreload

func (p *Player) ClearPreload()

ClearPreload discards any preloaded next source. Call when the upcoming track is no longer determined (e.g. shuffle/repeat was toggled after a linear-next was preloaded) so a stale preload can't be gaplessly swapped in.

func (*Player) Close

func (p *Player) Close()

Close tears down sources and the output device.

func (*Player) CrossfadeMS

func (p *Player) CrossfadeMS() int

func (*Player) CurrentDevice

func (p *Player) CurrentDevice() string

CurrentDevice returns the selected device id ("" = default).

func (*Player) Devices

func (p *Player) Devices() ([]Device, error)

Devices lists available output devices.

func (*Player) DurationMS

func (p *Player) DurationMS() int64

func (*Player) EQBands

func (p *Player) EQBands() []float64

EQBands returns the band center frequencies in Hz.

func (*Player) EQEnabled

func (p *Player) EQEnabled() bool

EQEnabled reports whether the equalizer is on.

func (*Player) EQGains

func (p *Player) EQGains() []float64

EQGains returns the band gains in dB.

func (*Player) EQPreampDB

func (p *Player) EQPreampDB() float64

EQPreampDB returns the preamp in dB.

func (*Player) EQPreset

func (p *Player) EQPreset() string

EQPreset returns the current preset name ("custom" after manual edits).

func (*Player) Format

func (p *Player) Format() string

func (*Player) Gapless

func (p *Player) Gapless() bool

Gapless reports whether gapless transitions are enabled.

func (*Player) IsPreview

func (p *Player) IsPreview() bool

IsPreview reports whether the current track is Deezer's 30-second preview (the free-account fallback) rather than the full, entitled stream.

func (*Player) LastError

func (p *Player) LastError() string

func (*Player) MonoDownmix

func (p *Player) MonoDownmix() bool

MonoDownmix reports whether mono downmix is on.

func (*Player) Pause

func (p *Player) Pause()

func (*Player) Play

func (p *Player) Play(plan *deezer.StreamPlan, durationMS int64) error

Play starts a track immediately, replacing anything current.

func (*Player) PositionMS

func (p *Player) PositionMS() int64

func (*Player) Preload

func (p *Player) Preload(plan *deezer.StreamPlan, durationMS int64)

Preload prepares the next track so the transition is gapless/crossfaded. It is a no-op if gapless is disabled.

func (*Player) ReplayGain

func (p *Player) ReplayGain() bool

func (*Player) Resume

func (p *Player) Resume()

func (*Player) SeekMS

func (p *Player) SeekMS(ms int64)

SeekMS jumps to an absolute position in the current track.

func (*Player) SetCrossfadeMS

func (p *Player) SetCrossfadeMS(ms int)

func (*Player) SetDevice

func (p *Player) SetDevice(id string) error

SetDevice switches output to the given device id ("" = system default).

func (*Player) SetEQEnabled

func (p *Player) SetEQEnabled(on bool)

SetEQEnabled turns the equalizer on/off (mono downmix is independent).

func (*Player) SetEQGain

func (p *Player) SetEQGain(band int, db float64) error

SetEQGain sets one band's gain in dB (clamped to ±12). Preset becomes "custom".

func (*Player) SetEQGains

func (p *Player) SetEQGains(db []float64) error

SetEQGains sets all band gains in dB (clamped). Preset becomes "custom".

func (*Player) SetEQPreamp

func (p *Player) SetEQPreamp(db float64)

SetEQPreamp sets the output preamp in dB (clamped to ±12), for taming clipping when several bands are boosted.

func (*Player) SetEQPreset

func (p *Player) SetEQPreset(name string) error

SetEQPreset applies a named preset's band gains.

func (*Player) SetGapless

func (p *Player) SetGapless(on bool)

SetGapless enables/disables gapless transitions between tracks.

func (*Player) SetMonoDownmix

func (p *Player) SetMonoDownmix(on bool)

SetMonoDownmix folds stereo to mono (accessibility / single-speaker setups).

func (*Player) SetOnFinish

func (p *Player) SetOnFinish(fn func())

func (*Player) SetOutputSuspended

func (p *Player) SetOutputSuspended(on bool) error

SetOutputSuspended releases (on=true) or restores (on=false) the OS audio output without tearing down playback state (decoder, ring, position). Use it on audio-focus loss/interruption (e.g. iOS): playback resumes where it left off. Safe to call from any goroutine.

func (*Player) SetReplayGain

func (p *Player) SetReplayGain(on bool)

func (*Player) SetSleepTimer

func (p *Player) SetSleepTimer(d time.Duration, endOfTrack bool)

SetSleepTimer arms the sleep timer. When endOfTrack is true the player pauses once the current track finishes (d is ignored); otherwise it fades out over the last few seconds and pauses after d elapses. d <= 0 with endOfTrack false cancels any armed timer.

func (*Player) SetStreamCache

func (p *Player) SetStreamCache(c StreamCache)

SetStreamCache attaches an optional raw-stream cache used by every source created afterwards (Play/Preload). nil — the default — disables caching. Only full encrypted track streams are cached; previews and plain passthrough streams (podcasts) never touch the cache.

Call it once at startup, before any playback: the field is read without synchronization when sources are created, so SetStreamCache is NOT safe to call concurrently with Play/Preload or while a track is playing.

func (*Player) SetVolume

func (p *Player) SetVolume(v float64)

SetVolume sets the absolute volume (clamped to 0..1).

func (*Player) SleepActive

func (p *Player) SleepActive() bool

SleepActive reports whether a sleep timer is armed.

func (*Player) SleepEndOfTrack

func (p *Player) SleepEndOfTrack() bool

SleepEndOfTrack reports whether the armed timer is in end-of-track mode.

func (*Player) SleepRemainingMS

func (p *Player) SleepRemainingMS() int64

SleepRemainingMS returns the milliseconds until the timer fires: for duration mode the wall-clock remainder, for end-of-track mode the current track's remaining time. Returns 0 when no timer is armed.

func (*Player) State

func (p *Player) State() State

func (*Player) Stop

func (p *Player) Stop()

Stop halts playback and releases sources.

func (*Player) TogglePause

func (p *Player) TogglePause()

func (*Player) Volume

func (p *Player) Volume() float64

type State

type State int

State is the player's lifecycle state.

const (
	Stopped State = iota
	Loading
	Playing
	Paused
	Errored
)

func (State) String

func (s State) String() string

type StreamCache

type StreamCache interface {
	// Get returns a reader over the cached body and its size when key is
	// present. The caller must close the reader.
	Get(key string) (io.ReadCloser, int64, bool)
	// TeeReader returns a reader that consumes src while mirroring the bytes
	// into the cache under key, committing the entry on clean EOF and
	// discarding the partial write on any read/write error. Implementations
	// return src unchanged when they cannot start the write (e.g. one is
	// already in progress for key).
	TeeReader(key string, src io.Reader) io.Reader
}

StreamCache is an optional on-disk cache of raw CDN stream bodies, keyed by "trackID.format" (e.g. "12345.MP3_320"). The stored bytes are exactly what the CDN served — still stripe-encrypted ciphertext for tracks — so nothing decrypted ever lands on disk; decryption still happens in the normal playback pipeline. *mediacache.Cache satisfies this interface; the audio package depends only on the interface so the cache stays a pluggable opt-in.

Jump to

Keyboard shortcuts

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