feat(playout): configure device for alsa and pulseaudio system outputs (#2654)

### Description

Add hardware configuration to liquidsoap so that users may
set hardware output in config.yml.

---------

Co-authored-by: jo <ljonas@riseup.net>
This commit is contained in:
maxtim 2023-12-29 09:22:43 -05:00 committed by GitHub
parent 9d6061e42e
commit 06af18b84e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 149 additions and 11 deletions

View File

@ -326,3 +326,7 @@ stream:
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
# > default is pulseaudio
kind: pulseaudio
# System output device.
# > only available for kind=(alsa, pulseaudio)
device:

View File

@ -326,3 +326,7 @@ stream:
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
# > default is pulseaudio
kind: pulseaudio
# System output device.
# > only available for kind=(alsa, pulseaudio)
device:

View File

@ -326,3 +326,7 @@ stream:
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
# > default is pulseaudio
kind: pulseaudio
# System output device.
# > only available for kind=(alsa, pulseaudio)
device:

View File

@ -526,11 +526,15 @@ stream:
system:
- # Whether the output is enabled.
# > default is false
enabled: false
enabled: true
# System output kind.
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
# > default is pulseaudio
kind: "pulseaudio"
# System output device.
# > only available for kind=(alsa, pulseaudio)
device: "alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp__sink"
```
## LDAP

View File

@ -326,3 +326,7 @@ stream:
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
# > default is pulseaudio
kind: pulseaudio
# System output device.
# > only available for kind=(alsa, pulseaudio)
device:

View File

@ -211,6 +211,7 @@ class Schema implements ConfigurationInterface
/* */->validate()->ifNotInArray(["alsa", "ao", "oss", "portaudio", "pulseaudio"])
/* */->thenInvalid('invalid stream.outputs.system.kind %s')
/* */->end()->end()
/* */->scalarNode('device')->end()
/**/->end()->end()->end()
->end()->end()

View File

@ -117,20 +117,41 @@ output.shoutcast(
)
{%- endmacro -%}
{% for output in config.stream.outputs.system -%}
{% if output.enabled -%}
# {{ output.kind.value }}:{{ loop.index }}
{#-
Build a system output configuration.
#}
{%- macro output_system(output_id, output) -%}
# {{ output.kind.value }}:{{ output_id }}
%ifndef output.{{ output.kind.value }}
log("output.{{ output.kind.value }} is not defined!")
%endif
%ifdef output.{{ output.kind.value }}
output.{{ output.kind.value }}(id="{{ output.kind.value }}:{{ loop.index }}", s)
output.{{ output.kind.value }}(
id="{{ output.kind.value }}:{{ output_id }}",
{%- if output.kind.value == "alsa" %}
{%- if output.device is not none %}
device="{{ output.device }}",
{%- endif %}
{%- elif output.kind.value == "pulseaudio" %}
{%- if output.device is not none %}
device="{{ output.device }}",
{%- endif %}
{%- endif %}
s
)
%endif
{%- endmacro -%}
{# ############################### #}
{%- for output in config.stream.outputs.system -%}
{% if output.enabled -%}
{{ output_system(loop.index, output) }}
{% endif -%}
{% endfor -%}
{% for output in config.stream.outputs.icecast -%}
{%- for output in config.stream.outputs.icecast -%}
{% if output.enabled -%}
{{ output_icecast(loop.index, output) }}

View File

@ -315,7 +315,10 @@
log("output.pulseaudio is not defined!")
%endif
%ifdef output.pulseaudio
output.pulseaudio(id="pulseaudio:1", s)
output.pulseaudio(
id="pulseaudio:1",
s
)
%endif
@ -355,6 +358,55 @@
%include "/fake/1.4/ls_script.liq"
# pulseaudio:1
%ifndef output.pulseaudio
log("output.pulseaudio is not defined!")
%endif
%ifdef output.pulseaudio
output.pulseaudio(
id="pulseaudio:1",
device="alsa_output.pci-0000_00_sink",
s
)
%endif
gateway("started")
'''
# ---
# name: test_generate_entrypoint[stream_config7-1.4]
'''
# THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT!
###########################################################
# The ignore() lines are to squash unused variable warnings
# Inputs
input_main_mount = "main"
input_main_port = 8001
input_main_secure = false
input_show_mount = "show"
input_show_port = 8002
input_show_secure = false
# Settings
set("log.file.path", "/var/log/radio.log")
set("server.telnet", true)
set("server.telnet.bind_addr", "127.0.0.1")
set("server.telnet.port", 1234)
set("harbor.bind_addrs", ["0.0.0.0"])
station_name = interactive.string("station_name", "LibreTime")
message_offline = interactive.string("message_offline", "LibreTime - offline")
message_format = interactive.string("message_format", "0")
input_fade_transition = interactive.float("input_fade_transition", 0.0)
%include "/fake/1.4/ls_script.liq"
gateway("started")

View File

@ -92,6 +92,17 @@ TEST_STREAM_CONFIGS: List[Config] = [
"system": [{"enabled": True, "kind": "pulseaudio"}],
}
),
make_config_with_stream(
outputs={
"system": [
{
"enabled": True,
"kind": "pulseaudio",
"device": "alsa_output.pci-0000_00_sink",
}
],
}
),
make_config_with_stream(
outputs={
"system": [{"enabled": False, "kind": "alsa"}],

View File

@ -216,7 +216,7 @@ class ShoutcastOutput(BaseModel):
mobile: bool = False
class SystemOutputKind(str, Enum):
class SystemOutput(str, Enum):
ALSA = "alsa"
AO = "ao"
OSS = "oss"
@ -224,16 +224,49 @@ class SystemOutputKind(str, Enum):
PULSEAUDIO = "pulseaudio"
class SystemOutput(BaseModel):
class BaseSystemOutput(BaseModel):
enabled: bool = False
kind: SystemOutputKind = SystemOutputKind.PULSEAUDIO
class ALSASystemOutput(BaseSystemOutput):
kind: Literal[SystemOutput.ALSA] = SystemOutput.ALSA
device: Optional[str] = None
class AOSystemOutput(BaseSystemOutput):
kind: Literal[SystemOutput.AO] = SystemOutput.AO
class OSSSystemOutput(BaseSystemOutput):
kind: Literal[SystemOutput.OSS] = SystemOutput.OSS
class PortAudioSystemOutput(BaseSystemOutput):
kind: Literal[SystemOutput.PORTAUDIO] = SystemOutput.PORTAUDIO
class PulseAudioSystemOutput(BaseSystemOutput):
kind: Literal[SystemOutput.PULSEAUDIO] = SystemOutput.PULSEAUDIO
device: Optional[str] = None
AnySystemOutput = Annotated[
Union[
ALSASystemOutput,
AOSystemOutput,
OSSSystemOutput,
PortAudioSystemOutput,
PulseAudioSystemOutput,
],
Field(discriminator="kind", default=SystemOutput.PULSEAUDIO),
]
# pylint: disable=too-few-public-methods
class Outputs(BaseModel):
icecast: List[IcecastOutput] = Field([], max_length=3)
shoutcast: List[ShoutcastOutput] = Field([], max_length=1)
system: List[SystemOutput] = Field([], max_length=1)
system: List[AnySystemOutput] = Field([], max_length=1)
@property
def merged(self) -> List[Union[IcecastOutput, ShoutcastOutput]]: