diff --git a/README.md b/README.md index 76becaa4..aa7686c0 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,7 @@ device_id: '' # Spotify device ID (auto-generated) device_name: '' # Spotify device name device_type: computer # Spotify device type (icon) audio_backend: alsa # Audio backend to use (alsa, pipe, pulseaudio) +audio_backend_runtime_socket: '' # Audio backends' runtime socket to use, if backend is pulseaudio audio_device: default # ALSA audio device to use for playback mixer_device: '' # ALSA mixer device for volume synchronization mixer_control_name: Master # ALSA mixer control name for volume synchronization @@ -232,4 +233,4 @@ or using Go: ```shell go generate ./... -``` \ No newline at end of file +``` diff --git a/cmd/daemon/main.go b/cmd/daemon/main.go index 048cbccb..f6e3eb33 100644 --- a/cmd/daemon/main.go +++ b/cmd/daemon/main.go @@ -161,10 +161,11 @@ func (app *App) newAppPlayer(ctx context.Context, creds any) (_ *AppPlayer, err CountryCode: appPlayer.countryCode, - AudioBackend: app.cfg.AudioBackend, - AudioDevice: app.cfg.AudioDevice, - MixerDevice: app.cfg.MixerDevice, - MixerControlName: app.cfg.MixerControlName, + AudioBackend: app.cfg.AudioBackend, + AudioBackendRuntimeSocket: app.cfg.AudioBackendRuntimeSocket, + AudioDevice: app.cfg.AudioDevice, + MixerDevice: app.cfg.MixerDevice, + MixerControlName: app.cfg.MixerControlName, AudioBufferTime: app.cfg.AudioBufferTime, AudioPeriodCount: app.cfg.AudioPeriodCount, @@ -393,6 +394,7 @@ type Config struct { DeviceType string `koanf:"device_type"` ClientToken string `koanf:"client_token"` AudioBackend string `koanf:"audio_backend"` + AudioBackendRuntimeSocket string `koanf:"audio_backend_runtime_socket"` AudioDevice string `koanf:"audio_device"` MixerDevice string `koanf:"mixer_device"` MixerControlName string `koanf:"mixer_control_name"` diff --git a/config_schema.json b/config_schema.json index a0159434..603d2206 100644 --- a/config_schema.json +++ b/config_schema.json @@ -75,6 +75,11 @@ ], "default": "alsa" }, + "audio_backend_runtime_socket": { + "type": "string", + "description": "The audio backend runtime socket, useful only with pulseaudio as a backend", + "default": "" + }, "audio_device": { "type": "string", "description": "Which audio device should be used for playback, leave empty for default", diff --git a/output/driver-pulseaudio.go b/output/driver-pulseaudio.go index e7e95c2b..1ce57bba 100644 --- a/output/driver-pulseaudio.go +++ b/output/driver-pulseaudio.go @@ -28,7 +28,13 @@ func newPulseAudioOutput(opts *NewOutputOptions) (*pulseAudioOutput, error) { // The device name is shown by PulseAudio volume controls (usually built // into a desktop environment), so we might want to use device_name here. // We could also maybe change the application icon name by device_type. - client, err := pulse.NewClient(pulse.ClientApplicationName("go-librespot"), pulse.ClientApplicationIconName("speaker")) + clientopts := []pulse.ClientOption{pulse.ClientApplicationName("go-librespot"), pulse.ClientApplicationIconName("speaker")} + + if opts.RuntimeSocket != "" { + clientopts = append(clientopts, pulse.ClientServerString(opts.RuntimeSocket)) + } + + client, err := pulse.NewClient(clientopts...) if err != nil { return nil, err } @@ -43,11 +49,12 @@ func newPulseAudioOutput(opts *NewOutputOptions) (*pulseAudioOutput, error) { // Create a new playback. var channelOpt pulse.PlaybackOption - if opts.ChannelCount == 1 { + switch opts.ChannelCount { + case 1: channelOpt = pulse.PlaybackMono - } else if opts.ChannelCount == 2 { + case 2: channelOpt = pulse.PlaybackStereo - } else { + default: return nil, fmt.Errorf("cannot play %d channels, pulse only supports mono and stereo", opts.ChannelCount) } volumeUpdates := make(chan proto.ChannelVolumes, 1) diff --git a/output/output.go b/output/output.go index 6a368257..7661cb52 100644 --- a/output/output.go +++ b/output/output.go @@ -57,8 +57,13 @@ type NewOutputOptions struct { // Device specifies the audio device name. // - // This feature is support only for the alsa backend. + // This feature is support only for the alsa and pulseaudio backend. Device string + // RuntimeSocket specifies a prefixed with protocol (e.g. `unix:` or `tcp:`) path + // to a runtime socket of audio backend. + // + // This feature is support only for pulseaudio backend. + RuntimeSocket string // Mixer specifies the audio mixer name. // diff --git a/player/player.go b/player/player.go index 8af38587..163696a0 100644 --- a/player/player.go +++ b/player/player.go @@ -114,9 +114,14 @@ type Options struct { // 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 backend. + // This feature is support only for the alsa and pulseaudio backend. AudioDevice string // MixerDevice specifies the audio mixer name. // @@ -126,7 +131,6 @@ type Options struct { // // This only works in combination with Mixer. MixerControlName string - // AudioBufferTime is the buffer time in microseconds. // // This is only supported on the alsa backend. @@ -178,6 +182,7 @@ func NewPlayer(opts *Options) (*Player, error) { SampleRate: SampleRate, ChannelCount: Channels, Device: opts.AudioDevice, + RuntimeSocket: opts.AudioBackendRuntimeSocket, Mixer: opts.MixerDevice, Control: opts.MixerControlName, InitialVolume: volume,