Documentation
¶
Index ¶
- Constants
- Variables
- func GetFormatBitrate(format metadatapb.AudioFile_Format) int
- func GetFormatCodec(format metadatapb.AudioFile_Format) string
- func NarrationLoudness(metadata map[string]string, prefix string) (loudnessDb, truePeakDb float32, ok bool)
- type AudioFormat
- type Event
- type EventManager
- type EventType
- type NarratedSource
- type NarrationSource
- type Options
- type Player
- func (p *Player) Close()
- func (p *Player) HasBeenPlayingFor() time.Duration
- func (p *Player) NewStream(ctx context.Context, client *http.Client, spotId librespot.SpotifyId, ...) (*Stream, error)
- func (p *Player) Pause() error
- func (p *Player) Play() error
- func (p *Player) PositionMs() int64
- func (p *Player) Receive() <-chan Event
- func (p *Player) ReopenOutput(device string) error
- func (p *Player) SeekMs(pos int64) error
- func (p *Player) SetPrimaryStream(source librespot.AudioSource, paused, drop bool) error
- func (p *Player) SetSecondaryStream(source librespot.AudioSource)
- func (p *Player) SetVolume(val uint32)
- func (p *Player) Stop()
- type Stream
- type SwitchingAudioSource
- func (s *SwitchingAudioSource) Close() error
- func (s *SwitchingAudioSource) Done() <-chan struct{}
- func (s *SwitchingAudioSource) PositionMs() int64
- func (s *SwitchingAudioSource) Read(p []float32) (n int, err error)
- func (s *SwitchingAudioSource) SetPositionMs(pos int64) error
- func (s *SwitchingAudioSource) SetPrimary(source librespot.AudioSource)
- func (s *SwitchingAudioSource) SetSecondary(source librespot.AudioSource)
Constants ¶
const ( SampleRate = 44100 Channels = 2 )
const CdnUrlQuarantineDuration = 15 * time.Minute
const MaxStateVolume = 65535
Variables ¶
var ErrPlayerClosed = errors.New("player is closed")
Functions ¶
func GetFormatBitrate ¶ added in v0.3.0
func GetFormatBitrate(format metadatapb.AudioFile_Format) int
func GetFormatCodec ¶ added in v0.9.0
func GetFormatCodec(format metadatapb.AudioFile_Format) string
GetFormatCodec returns the codec name of a format, for reporting through the API. Note that a format may be named here without being decodable.
func NarrationLoudness ¶ added in v0.9.0
func NarrationLoudness(metadata map[string]string, prefix string) (loudnessDb, truePeakDb float32, ok bool)
NarrationLoudness reads the loudness and true peak a track's narration metadata declares. Reporting absence matters: taking a missing value as zero would mean normalising against 0 LUFS and attenuating the clip to nothing.
Types ¶
type AudioFormat ¶ added in v0.5.1
type AudioFormat int
const ( AudioFormatUnknown AudioFormat = iota AudioFormatFLAC AudioFormatOGGVorbis AudioFormatMP3 AudioFormatAAC )
func GetAudioFileFormatAudioFormat ¶ added in v0.5.1
func GetAudioFileFormatAudioFormat(format metadatapb.AudioFile_Format) AudioFormat
GetAudioFileFormatAudioFormat maps metadatapb.AudioFile_Format to AudioFormat
type EventManager ¶ added in v0.3.0
type EventManager interface {
PreStreamLoadNew(playbackId []byte, spotId librespot.SpotifyId, mediaPosition int64)
PostStreamResolveAudioFile(playbackId []byte, targetBitrate int32, media *librespot.Media, file *metadata.AudioFile)
PostStreamRequestAudioKey(playbackId []byte)
PostStreamResolveStorage(playbackId []byte)
PostStreamInitHttpChunkReader(playbackId []byte, reader *audio.HttpChunkedReader)
OnPrimaryStreamUnload(stream *Stream, pos int64)
PostPrimaryStreamLoad(stream *Stream, paused bool)
OnPlayerPlay(stream *Stream, ctxUri string, shuffle bool, playOrigin *connectpb.PlayOrigin, track *connectpb.ProvidedTrack, pos int64)
OnPlayerResume(stream *Stream, pos int64)
OnPlayerPause(stream *Stream, ctxUri string, shuffle bool, playOrigin *connectpb.PlayOrigin, track *connectpb.ProvidedTrack, pos int64)
OnPlayerSeek(stream *Stream, oldPos, newPos int64)
OnPlayerSkipForward(stream *Stream, pos int64, skipTo bool)
OnPlayerSkipBackward(stream *Stream, pos int64)
OnPlayerEnd(stream *Stream, pos int64)
Close()
}
type NarratedSource ¶ added in v0.9.0
type NarratedSource struct {
// contains filtered or unexported fields
}
NarratedSource plays a track with the DJ talking around it: an introduction before it and a closing remark after it, either of which may be absent.
Playback is sequential rather than mixed, matching the ms_narration_overlapping of 0 the official client reports. Position stays the track's throughout, so it reads as not yet started while the DJ introduces it.
func NewNarratedSource ¶ added in v0.9.0
func NewNarratedSource(log librespot.Logger, intro, main, outro librespot.AudioSource) *NarratedSource
NewNarratedSource wraps a track with its narration. Either clip may be nil.
func (*NarratedSource) Close ¶ added in v0.9.0
func (s *NarratedSource) Close() error
func (*NarratedSource) NoCrossfade ¶ added in v0.9.0
func (s *NarratedSource) NoCrossfade() bool
NoCrossfade keeps a closing remark out of the crossfade reserve, which would otherwise spend the whole of it fading out. A source with only an introduction ends in ordinary music and still crossfades.
func (*NarratedSource) PositionMs ¶ added in v0.9.0
func (s *NarratedSource) PositionMs() int64
func (*NarratedSource) Read ¶ added in v0.9.0
func (s *NarratedSource) Read(p []float32) (int, error)
func (*NarratedSource) SetPositionMs ¶ added in v0.9.0
func (s *NarratedSource) SetPositionMs(pos int64) error
SetPositionMs seeks the track, abandoning an introduction still playing: a listener who scrubs wants the music. The closing remark still follows, since it belongs to the end of the track.
type NarrationSource ¶ added in v0.9.0
NarrationSource plays a DJ narration clip: MP3 fetched from a CDN with a plain unauthenticated GET, with no Spotify id, audio key or PlayPlay involved.
The whole clip is buffered. It is small, and the decoder needs to seek.
func NewNarrationSource ¶ added in v0.9.0
func NewNarrationSource(ctx context.Context, log librespot.Logger, client *http.Client, url string, loudnessDb, truePeakDb, pregain float32) (*NarrationSource, error)
NewNarrationSource fetches a synthesized narration clip and decodes it.
The url from Spclient.NarrationUrl is already signed, so it is fetched without the Authorization header the rest of the client sends. The levels come from the track's narration.*.loudness and narration.*.true_peak metadata.
type Options ¶ added in v0.2.0
type Options struct {
Spclient *spclient.Spclient
AudioKey *audio.KeyProvider
Events EventManager
Log librespot.Logger
// Cache, when non-nil, is used to store and retrieve encrypted audio files
// on disk, avoiding a CDN download when a track is played again.
Cache *cache.Cache
// FlacEnabled specifies if FLAC files should be preferred when available.
// When setting this to true, it is assumed that the PlayPlay plugin is provided.
FlacEnabled bool
// NormalisationEnabled specifies if the volume should be normalised according
// to Spotify parameters. Only track normalization is supported.
NormalisationEnabled bool
// NormalisationUseAlbumGain specifies whether album gain instead of track gain
// should be used for normalisation
NormalisationUseAlbumGain bool
// NormalisationPregain specifies the pre-gain to apply when normalising the volume
// in dB. Use negative values to avoid clipping.
NormalisationPregain float32
// CrossfadeDuration specifies for how long tracks should overlap during
// a track change. Zero disables crossfading.
CrossfadeDuration time.Duration
// CountryCode specifies the country code to use for media restrictions.
CountryCode *string
// AudioBackend specifies the audio backend to use (alsa, pulseaudio, etc).
AudioBackend string
// AudioBackendRuntimeSocket specifies a prefixed with protocol (e.g. `unix:` or `tcp:`) path
// to a runtime socket of audio backend.
//
// This feature is support only for the pulseaudio backend.
AudioBackendRuntimeSocket string
// AudioDevice specifies the audio device name.
//
// This feature is support only for the alsa and pulseaudio backend.
AudioDevice string
// MixerDevice specifies the audio mixer name.
//
// This feature is support only for the alsa backend.
MixerDevice string
// MixerControlName specifies the mixer control name.
//
// This only works in combination with Mixer.
MixerControlName string
// AudioBufferTime is the buffer time in microseconds.
//
// This is only supported on the alsa backend.
AudioBufferTime int
// AudioPeriodCount is the number of periods to request.
//
// This is only supported on the alsa backend.
AudioPeriodCount int
// ExternalVolume specifies, if the volume is controlled outside the app.
//
// This is only supported on the alsa and pipe backends.
// The PulseAudio backend always uses external volume.
ExternalVolume bool
// VolumeUpdate is a channel on which volume updates will be sent back to
// Spotify. This must be a buffered channel.
VolumeUpdate chan float32
// AudioOutputPipe is the path to the output pipe.
//
// This is only supported on the pipe backend.
AudioOutputPipe string
// AudioOutputPipeFormat is the format of the output pipe.
// Available formats are: "s16le", "s32le", "f32le". Default is "s16le".
//
// This is only supported on the pipe backend.
AudioOutputPipeFormat string
// AudioOutputPipeWaitForReader makes the pipe backend wait for a reader to
// appear when opening the FIFO, instead of failing if none is present at the
// time playback starts. This is useful for readers (e.g. snapcast with
// dryout) that only connect to the FIFO when data is expected.
//
// This is only supported on the pipe backend.
AudioOutputPipeWaitForReader bool
}
type Player ¶
type Player struct {
// contains filtered or unexported fields
}
func (*Player) Close ¶
func (p *Player) Close()
Close stops the player. It is safe to call more than once and safe to call while other goroutines are issuing commands: those get ErrPlayerClosed.
func (*Player) HasBeenPlayingFor ¶
func (*Player) PositionMs ¶
PositionMs returns the current playback position, or zero if the player has been closed.
func (*Player) ReopenOutput ¶ added in v0.8.0
ReopenOutput reopens the audio output on the given device without touching the Spotify session, preserving the current playback position, paused state and volume. If playback is currently active it switches devices live (with a brief audio gap); if nothing is playing it just records the device for the next output open. It returns an error if the new device fails to open, in which case playback is left stopped.
func (*Player) SetPrimaryStream ¶
func (p *Player) SetPrimaryStream(source librespot.AudioSource, paused, drop bool) error
func (*Player) SetSecondaryStream ¶
func (p *Player) SetSecondaryStream(source librespot.AudioSource)
type Stream ¶
type Stream struct {
PlaybackId []byte
// RequestedId is the id this stream was created for, before any relinking
// to an alternative track. Callers keep referring to the stream by the id
// they requested, which may differ from the media's own id.
RequestedId librespot.SpotifyId
Source librespot.AudioSource
Media *librespot.Media
File *metadatapb.AudioFile
// SampleRate of the decoded audio, in Hz.
SampleRate int32
// BitDepth of the source audio, in bits per sample. Only lossless formats
// have one: the lossy codecs decode to floats with no meaningful source
// depth, and report zero.
BitDepth int32
}
type SwitchingAudioSource ¶
type SwitchingAudioSource struct {
// contains filtered or unexported fields
}
func NewSwitchingAudioSource ¶
func NewSwitchingAudioSource(crossfadeSamples int) *SwitchingAudioSource
NewSwitchingAudioSource creates an audio source that switches seamlessly between a primary and a prefetched secondary source.
If crossfadeSamples is greater than zero, track transitions overlap by up to that many samples (interleaved, so frames times channels) with an equal-power crossfade. A value of zero disables crossfading and preserves gapless behavior.
func (*SwitchingAudioSource) Close ¶
func (s *SwitchingAudioSource) Close() error
func (*SwitchingAudioSource) Done ¶
func (s *SwitchingAudioSource) Done() <-chan struct{}
func (*SwitchingAudioSource) PositionMs ¶
func (s *SwitchingAudioSource) PositionMs() int64
func (*SwitchingAudioSource) Read ¶
func (s *SwitchingAudioSource) Read(p []float32) (n int, err error)
func (*SwitchingAudioSource) SetPositionMs ¶
func (s *SwitchingAudioSource) SetPositionMs(pos int64) error
func (*SwitchingAudioSource) SetPrimary ¶
func (s *SwitchingAudioSource) SetPrimary(source librespot.AudioSource)
func (*SwitchingAudioSource) SetSecondary ¶
func (s *SwitchingAudioSource) SetSecondary(source librespot.AudioSource)