summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlawl <github@dumbinter.net>2021-05-19 18:59:47 +0200
committerlawl <github@dumbinter.net>2021-05-19 18:59:47 +0200
commitfad3e1d4f4e76a74c6e32cf11f892b6ba3022c69 (patch)
treef0350383ffc9a711828db13d5bacdff50f28036a
parent9b651eb701f2c2423166022d8fe9b9df067fc387 (diff)
Add support for PipeWire0.11.0
Warning: Alpha quality. Please manage your expectations.
-rw-r--r--c/ladspa/Makefile2
-rw-r--r--c/ladspa/module.c10
-rw-r--r--cli.go8
-rw-r--r--main.go80
-rw-r--r--module.go325
-rw-r--r--ui.go50
6 files changed, 351 insertions, 124 deletions
diff --git a/c/ladspa/Makefile b/c/ladspa/Makefile
index f61aded..ee7b64d 100644
--- a/c/ladspa/Makefile
+++ b/c/ladspa/Makefile
@@ -1,3 +1,3 @@
default:
$(CC) -Wall -Werror -O2 -c -fPIC ../ringbuf.c ../rnnoise/*.c module.c
- $(CC) -shared -Wl,--version-script=export.txt -o rnnoise_ladspa.so *.o
+ $(CC) -shared -lm -Wl,--version-script=export.txt -o rnnoise_ladspa.so *.o
diff --git a/c/ladspa/module.c b/c/ladspa/module.c
index dc4a37a..bc3886d 100644
--- a/c/ladspa/module.c
+++ b/c/ladspa/module.c
@@ -14,9 +14,9 @@
#include "../ringbuf.h"
#include "../rnnoise/rnnoise.h"
-#define SF_VAD 0
-#define SF_INPUT 1
-#define SF_OUTPUT 2
+#define SF_INPUT 0
+#define SF_OUTPUT 1
+#define SF_VAD 2
#define FRAMESIZE_NSAMPLES 480
#define FRAMESIZE_BYTES (480 * sizeof(float))
@@ -187,9 +187,7 @@ ON_LOAD_ROUTINE {
g_psDescriptor->PortRangeHints =
(const LADSPA_PortRangeHint *)psPortRangeHints;
psPortRangeHints[SF_VAD].HintDescriptor =
- (LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE |
- LADSPA_HINT_SAMPLE_RATE | LADSPA_HINT_LOGARITHMIC |
- LADSPA_HINT_DEFAULT_440);
+ (LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE);
psPortRangeHints[SF_VAD].LowerBound = 0;
psPortRangeHints[SF_VAD].UpperBound = 95;
psPortRangeHints[SF_INPUT].HintDescriptor = 0;
diff --git a/cli.go b/cli.go
index 887927e..d8b09ee 100644
--- a/cli.go
+++ b/cli.go
@@ -45,7 +45,6 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
}
paClient, err := pulseaudio.NewClient()
-
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't create pulseaudio client: %v\n", err)
os.Exit(1)
@@ -53,6 +52,13 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
defer paClient.Close()
ctx := ntcontext{}
+
+ info, err := serverInfo(paClient)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Couldn't fetch audio server info: %s\n", err)
+ }
+ ctx.serverInfo = info
+
ctx.config = config
ctx.librnnoise = librnnoise
diff --git a/main.go b/main.go
index c072bea..0ed7d2f 100644
--- a/main.go
+++ b/main.go
@@ -6,6 +6,8 @@ import (
"io/ioutil"
"log"
"os"
+ "regexp"
+ "strconv"
"strings"
"time"
@@ -123,12 +125,10 @@ func getSources(client *pulseaudio.Client) []device {
outputs := make([]device, 0)
for i := range sources {
- if strings.Contains(sources[i].Name, "nui_") {
+ if strings.Contains(sources[i].Name, "nui_") || strings.Contains(sources[i].Name, "NoiseTorch") {
continue
}
- log.Printf("Input %s, %+v\n", sources[i].Name, sources[i])
-
var inp device
inp.ID = sources[i].Name
@@ -153,7 +153,7 @@ func getSinks(client *pulseaudio.Client) []device {
inputs := make([]device, 0)
for i := range sources {
- if strings.Contains(sources[i].Name, "nui_") {
+ if strings.Contains(sources[i].Name, "nui_") || strings.Contains(sources[i].Name, "NoiseTorch") {
continue
}
@@ -190,6 +190,14 @@ func paConnectionWatchdog(ctx *ntcontext) {
fmt.Fprintf(os.Stderr, "Couldn't create pulseaudio client: %v\n", err)
}
+ info, err := serverInfo(paClient)
+ if err != nil {
+ log.Printf("Couldn't fetch audio server info: %s\n", err)
+ }
+ ctx.serverInfo = info
+
+ log.Printf("Connected to audio server. Server name '%s'\n", info.name)
+
ctx.paClient = paClient
go updateNoiseSupressorLoaded(ctx)
@@ -203,6 +211,70 @@ func paConnectionWatchdog(ctx *ntcontext) {
}
}
+func serverInfo(paClient *pulseaudio.Client) (audioserverinfo, error) {
+ info, err := paClient.ServerInfo()
+ if err != nil {
+ log.Printf("Couldn't fetch pulse server info: %v\n", err)
+ fmt.Fprintf(os.Stderr, "Couldn't fetch pulse server info: %v\n", err)
+ }
+
+ pkgname := info.PackageName
+ log.Printf("Audioserver package name: %s\n", pkgname)
+ log.Printf("Audioserver package version: %s\n", info.PackageVersion)
+ isPipewire := strings.Contains(pkgname, "PipeWire")
+
+ var servername string
+ var servertype uint
+ var major, minor, patch int
+ var versionRegex *regexp.Regexp
+ var versionString string
+
+ var outdatedPipeWire bool
+
+ if isPipewire {
+ servername = "PipeWire"
+ servertype = servertype_pipewire
+ versionRegex = regexp.MustCompile(`.*?on PipeWire (\d+)\.(\d+)\.(\d+).*?`)
+ versionString = pkgname
+ log.Printf("Detected PipeWire\n")
+ } else {
+ servername = "PulseAudio"
+ servertype = servertype_pulse
+ versionRegex = regexp.MustCompile(`.*?(\d+)\.(\d+)\.(\d+).*?`)
+ versionString = info.PackageVersion
+ log.Printf("Detected PulseAudio\n")
+ }
+
+ res := versionRegex.FindStringSubmatch(versionString)
+ if len(res) != 4 {
+ return audioserverinfo{}, fmt.Errorf("couldn't parse server version, regexp didn't match.")
+ }
+ major, err = strconv.Atoi(res[1])
+ if err != nil {
+ return audioserverinfo{}, err
+ }
+ minor, err = strconv.Atoi(res[2])
+ if err != nil {
+ return audioserverinfo{}, err
+ }
+ patch, err = strconv.Atoi(res[3])
+ if err != nil {
+ return audioserverinfo{}, err
+ }
+ if isPipewire && major <= 0 && minor <= 3 && patch < 28 {
+ log.Printf("pipewire version %d.%d.%d too old.\n", major, minor, patch)
+ outdatedPipeWire = true
+ }
+
+ return audioserverinfo{
+ servertype: servertype,
+ name: servername,
+ major: major,
+ minor: minor,
+ patch: patch,
+ outdatedPipeWire: outdatedPipeWire}, nil
+}
+
func preselectDevice(ctx *ntcontext, devices []device, preselectID string,
fallbackFunc func(client *pulseaudio.Client) (string, error)) []device {
diff --git a/module.go b/module.go
index 25bd45a..6947537 100644
--- a/module.go
+++ b/module.go
@@ -39,56 +39,74 @@ func supressorState(ctx *ntcontext) int {
c := ctx.paClient
var inpLoaded, outLoaded, inputInc, outputInc bool
if ctx.config.FilterInput {
- _, nullsink, err := findModule(c, "module-null-sink", "sink_name=nui_mic_denoised_out")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for module-null-sink: %v\n", err)
- }
- _, ladspasink, err := findModule(c, "module-ladspa-sink", "sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for module-ladspa-sink: %v\n", err)
- }
- _, loopback, err := findModule(c, "module-loopback", "sink=nui_mic_raw_in")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for module-loopback: %v\n", err)
- }
- _, remap, err := findModule(c, "module-remap-source", "master=nui_mic_denoised_out.monitor source_name=nui_mic_remap")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for module-remap-source: %v\n", err)
- }
+ if ctx.serverInfo.servertype == servertype_pipewire {
+ _, ladspasource, err := findModule(c, "module-ladspa-source", "source_name='NoiseTorch Microphone'")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for module-ladspa-source: %v\n", err)
+ }
+ inpLoaded = ladspasource
+ inputInc = false
+ } else {
+ _, nullsink, err := findModule(c, "module-null-sink", "sink_name=nui_mic_denoised_out")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for module-null-sink: %v\n", err)
+ }
+ _, ladspasink, err := findModule(c, "module-ladspa-sink", "sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for module-ladspa-sink: %v\n", err)
+ }
+ _, loopback, err := findModule(c, "module-loopback", "sink=nui_mic_raw_in")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for module-loopback: %v\n", err)
+ }
+ _, remap, err := findModule(c, "module-remap-source", "master=nui_mic_denoised_out.monitor source_name=nui_mic_remap")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for module-remap-source: %v\n", err)
+ }
- if nullsink && ladspasink && loopback && remap {
- inpLoaded = true
- } else if nullsink || ladspasink || loopback || remap {
- inputInc = true
+ if nullsink && ladspasink && loopback && remap {
+ inpLoaded = true
+ } else if nullsink || ladspasink || loopback || remap {
+ inputInc = true
+ }
}
} else {
inpLoaded = true
}
if ctx.config.FilterOutput {
- _, out, err := findModule(c, "module-null-sink", "sink_name=nui_out_out_sink")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
- }
- _, lad, err := findModule(c, "module-ladspa-sink", "sink_name=nui_out_ladspa")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
- }
- _, loop, err := findModule(c, "module-loopback", "source=nui_out_out_sink.monitor")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
- }
- _, outin, err := findModule(c, "module-null-sink", "sink_name=nui_out_in_sink")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
- }
- _, loop2, err := findModule(c, "module-loopback", "source=nui_out_in_sink.monitor")
- if err != nil {
- log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
- }
+ if ctx.serverInfo.servertype == servertype_pipewire {
+ _, ladspasink, err := findModule(c, "module-ladspa-sink", "sink_name='NoiseTorch Headphones'")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for module-ladspa-sink: %v\n", err)
+ }
+ outLoaded = ladspasink
+ outputInc = false
+ } else {
+ _, out, err := findModule(c, "module-null-sink", "sink_name=nui_out_out_sink")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
+ }
+ _, lad, err := findModule(c, "module-ladspa-sink", "sink_name=nui_out_ladspa")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
+ }
+ _, loop, err := findModule(c, "module-loopback", "source=nui_out_out_sink.monitor")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
+ }
+ _, outin, err := findModule(c, "module-null-sink", "sink_name=nui_out_in_sink")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
+ }
+ _, loop2, err := findModule(c, "module-loopback", "source=nui_out_in_sink.monitor")
+ if err != nil {
+ log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err)
+ }
- outLoaded = out && lad && loop && outin && loop2
- outputInc = out || lad || loop || outin || loop2
+ outLoaded = out && lad && loop && outin && loop2
+ outputInc = out || lad || loop || outin || loop2
+ }
} else {
outLoaded = true
}
@@ -105,110 +123,200 @@ func supressorState(ctx *ntcontext) int {
}
func loadSupressor(ctx *ntcontext, inp *device, out *device) error {
+ if ctx.serverInfo.servertype == servertype_pulse {
+ log.Printf("Querying pulse rlimit\n")
+ pid, err := getPulsePid()
+ if err != nil {
+ return err
+ }
+
+ lim, err := getRlimit(pid)
+ if err != nil {
+ return err
+ }
+ log.Printf("Rlimit: %+v. Trying to remove.\n", lim)
+
+ removeRlimit(pid)
- log.Printf("Querying pulse rlimit\n")
+ defer setRlimit(pid, &lim) // lowering RLIMIT doesn't require root
+
+ newLim, err := getRlimit(pid)
+ if err != nil {
+ return err
+ }
+ log.Printf("Rlimit: %+v\n", newLim)
+ }
+ if inp.checked {
+ var err error
+ if ctx.serverInfo.servertype == servertype_pipewire {
+ err = loadPipeWireInput(ctx, inp)
+ } else {
+ err = loadPulseInput(ctx, inp)
+ }
+ if err != nil {
+ log.Printf("Error loading input: %v\n", err)
+ return err
+ }
+ }
+
+ if out.checked {
+ var err error
+ if ctx.serverInfo.servertype == servertype_pipewire {
+ err = loadPipeWireOutput(ctx, out)
+ } else {
+ err = loadPulseOutput(ctx, out)
+ }
+ if err != nil {
+ log.Printf("Error loading output: %v\n", err)
+ return err
+ }
+ }
+
+ return nil
+}
+
+func loadPipeWireInput(ctx *ntcontext, inp *device) error {
c := ctx.paClient
+ log.Printf("Loading supressor for pipewire\n")
+ idx, err := c.LoadModule("module-ladspa-source",
+ fmt.Sprintf("source_name='NoiseTorch Microphone' master=%s "+
+ "rate=48000 channels=1 "+
+ "label=noisetorch plugin=%s control=%d", inp.ID, ctx.librnnoise, ctx.config.Threshold))
- pid, err := getPulsePid()
if err != nil {
return err
}
+ log.Printf("Loaded ladspa source as idx: %d\n", idx)
+ return nil
+}
+
+func loadPipeWireOutput(ctx *ntcontext, out *device) error {
+ c := ctx.paClient
+ log.Printf("Loading supressor for pipewire\n")
+ idx, err := c.LoadModule("module-ladspa-sink",
+ fmt.Sprintf("sink_name='NoiseTorch Headphones' master=%s "+
+ "rate=48000 channels=1 "+
+ "label=noisetorch plugin=%s control=%d", out.ID, ctx.librnnoise, ctx.config.Threshold))
- lim, err := getRlimit(pid)
if err != nil {
return err
}
- log.Printf("Rlimit: %+v. Trying to remove.\n", lim)
-
- removeRlimit(pid)
+ log.Printf("Loaded ladspa source as idx: %d\n", idx)
+ return nil
+}
- defer setRlimit(pid, &lim) // lowering RLIMIT doesn't require root
+func loadPulseInput(ctx *ntcontext, inp *device) error {
+ c := ctx.paClient
+ log.Printf("Loading supressor for pulse\n")
+ idx, err := c.LoadModule("module-null-sink", "sink_name=nui_mic_denoised_out rate=48000")
+ if err != nil {
+ return err
+ }
+ log.Printf("Loaded null sink as idx: %d\n", idx)
- newLim, err := getRlimit(pid)
+ idx, err = c.LoadModule("module-ladspa-sink",
+ fmt.Sprintf("sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out "+
+ "label=noisetorch plugin=%s control=%d", ctx.librnnoise, ctx.config.Threshold))
if err != nil {
return err
}
- log.Printf("Rlimit: %+v\n", newLim)
+ log.Printf("Loaded ladspa sink as idx: %d\n", idx)
- if inp.checked {
- log.Printf("Loading supressor\n")
- idx, err := c.LoadModule("module-null-sink", "sink_name=nui_mic_denoised_out rate=48000")
+ if inp.dynamicLatency {
+ idx, err = c.LoadModule("module-loopback",
+ fmt.Sprintf("source=%s sink=nui_mic_raw_in channels=1 latency_msec=1 source_dont_move=true sink_dont_move=true", inp.ID))
if err != nil {
return err
}
- log.Printf("Loaded null sink as idx: %d\n", idx)
-
- idx, err = c.LoadModule("module-ladspa-sink",
- fmt.Sprintf("sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out "+
- "label=noisetorch plugin=%s control=%d", ctx.librnnoise, ctx.config.Threshold))
+ log.Printf("Loaded loopback as idx: %d\n", idx)
+ } else {
+ idx, err = c.LoadModule("module-loopback",
+ fmt.Sprintf("source=%s sink=nui_mic_raw_in channels=1 latency_msec=50 source_dont_move=true sink_dont_move=true adjust_time=1", inp.ID))
if err != nil {
return err
}
- log.Printf("Loaded ladspa sink as idx: %d\n", idx)
+ log.Printf("Loaded fixed latency loopback as idx: %d\n", idx)
+ }
- if inp.dynamicLatency {
- idx, err = c.LoadModule("module-loopback",
- fmt.Sprintf("source=%s sink=nui_mic_raw_in channels=1 latency_msec=1 source_dont_move=true sink_dont_move=true", inp.ID))
- if err != nil {
- return err
- }
- log.Printf("Loaded loopback as idx: %d\n", idx)
- } else {
- idx, err = c.LoadModule("module-loopback",
- fmt.Sprintf("source=%s sink=nui_mic_raw_in channels=1 latency_msec=50 source_dont_move=true sink_dont_move=true adjust_time=1", inp.ID))
- if err != nil {
- return err
- }
- log.Printf("Loaded fixed latency loopback as idx: %d\n", idx)
- }
+ idx, err = c.LoadModule("module-remap-source", `master=nui_mic_denoised_out.monitor `+
+ `source_name=nui_mic_remap source_properties="device.description='NoiseTorch Microphone'"`)
+ if err != nil {
+ return err
+ }
+ log.Printf("Loaded remap source as idx: %d\n", idx)
+ return nil
+}
- idx, err = c.LoadModule("module-remap-source", `master=nui_mic_denoised_out.monitor `+
- `source_name=nui_mic_remap source_properties="device.description='NoiseTorch Microphone'"`)
- if err != nil {
- return err
- }
- log.Printf("Loaded remap source as idx: %d\n", idx)
+func loadPulseOutput(ctx *ntcontext, out *device) error {
+ c := ctx.paClient
+ _, err := c.LoadModule("module-null-sink", `sink_name=nui_out_out_sink`)
+ if err != nil {
+ return err
}
- if out.checked {
+ _, err = c.LoadModule("module-null-sink", `sink_name=nui_out_in_sink sink_properties="device.description='NoiseTorch Headphones'"`)
+ if err != nil {
+ return err
+ }
- _, err := c.LoadModule("module-null-sink", `sink_name=nui_out_out_sink`)
- if err != nil {
- return err
- }
+ _, err = c.LoadModule("module-ladspa-sink", fmt.Sprintf(`sink_name=nui_out_ladspa sink_master=nui_out_out_sink `+
+ `label=noisetorch channels=1 plugin=%s control=%d rate=%d`,
+ ctx.librnnoise, ctx.config.Threshold, 48000))
+ if err != nil {
+ return err
+ }
- _, err = c.LoadModule("module-null-sink", `sink_name=nui_out_in_sink sink_properties="device.description='NoiseTorch Headphones'"`)
- if err != nil {
- return err
- }
+ _, err = c.LoadModule("module-loopback",
+ fmt.Sprintf("source=nui_out_out_sink.monitor sink=%s channels=2 latency_msec=50 source_dont_move=true sink_dont_move=true", out.ID))
+ if err != nil {
+ return err
+ }
- _, err = c.LoadModule("module-ladspa-sink", fmt.Sprintf(`sink_name=nui_out_ladspa sink_master=nui_out_out_sink `+
- `label=noisetorch channels=1 plugin=%s control=%d rate=%d`,
- ctx.librnnoise, ctx.config.Threshold, 48000))
- if err != nil {
- return err
- }
+ _, err = c.LoadModule("module-loopback",
+ fmt.Sprintf("source=nui_out_in_sink.monitor sink=nui_out_ladspa channels=1 latency_msec=50 source_dont_move=true sink_dont_move=true"))
+ if err != nil {
+ return err
+ }
+ return nil
+}
- _, err = c.LoadModule("module-loopback",
- fmt.Sprintf("source=nui_out_out_sink.monitor sink=%s channels=2 latency_msec=50 source_dont_move=true sink_dont_move=true", out.ID))
- if err != nil {
- return err
- }
+func unloadSupressor(ctx *ntcontext) error {
+ if ctx.serverInfo.servertype == servertype_pipewire {
+ return unloadSupressorPipeWire(ctx)
+ } else {
+ return unloadSupressorPulse(ctx)
+ }
+}
- _, err = c.LoadModule("module-loopback",
- fmt.Sprintf("source=nui_out_in_sink.monitor sink=nui_out_ladspa channels=1 latency_msec=50 source_dont_move=true sink_dont_move=true"))
- if err != nil {
- return err
- }
+func unloadSupressorPipeWire(ctx *ntcontext) error {
+ log.Printf("Unloading modules for pipewire\n")
+ log.Printf("Searching for module-ladspa-source\n")
+ c := ctx.paClient
+ m, found, err := findModule(c, "module-ladspa-source", "source_name='NoiseTorch Microphone'")
+ if err != nil {
+ return err
+ }
+ if found {
+ log.Printf("Found module-ladspa-source at id [%d], sending unload command\n", m.Index)
+ c.UnloadModule(m.Index)
}
+ log.Printf("Searching for module-ladspa-sink\n")
+ m, found, err = findModule(c, "module-ladspa-sink", "sink_name='NoiseTorch Headphones'")
+ if err != nil {
+ return err
+ }
+ if found {
+ log.Printf("Found module-ladspa-sink at id [%d], sending unload command\n", m.Index)
+ c.UnloadModule(m.Index)
+ }
return nil
}
-func unloadSupressor(ctx *ntcontext) error {
- log.Printf("Unloading pulseaudio modules\n")
+func unloadSupressorPulse(ctx *ntcontext) error {
+ log.Printf("Unloading modules for pulseaudio\n")
if pid, err := getPulsePid(); err == nil {
if lim, err := getRlimit(pid); err == nil {
@@ -318,6 +426,7 @@ func unloadSupressor(ctx *ntcontext) error {
// Finds a module by exactly matching the module name, and checking if the second string is a substring of the argument
func findModule(c *pulseaudio.Client, name string, argMatch string) (module pulseaudio.Module, found bool, err error) {
lst, err := c.ModuleList()
+
if err != nil {
return pulseaudio.Module{}, false, err
}
diff --git a/ui.go b/ui.go
index bbab17f..56aefa5 100644
--- a/ui.go
+++ b/ui.go
@@ -33,8 +33,24 @@ type ntcontext struct {
haveCapabilities bool
capsMismatch bool
views *ViewStack
+ serverInfo audioserverinfo
}
+//TODO pull some of these strucs out of UI, they don't belong here
+type audioserverinfo struct {
+ servertype uint
+ name string
+ major int
+ minor int
+ patch int
+ outdatedPipeWire bool
+}
+
+const (
+ servertype_pulse = iota
+ servertype_pipewire
+)
+
var green = color.RGBA{34, 187, 69, 255}
var red = color.RGBA{255, 70, 70, 255}
var orange = color.RGBA{255, 140, 0, 255}
@@ -84,6 +100,11 @@ func mainView(ctx *ntcontext, w *nucular.Window) {
w.LabelColored("Inconsistent state, please unload first.", "RC", orange)
}
+ if ctx.serverInfo.servertype == servertype_pipewire {
+ w.Row(20).Dynamic(1)
+ w.Label("Running in PipeWire mode. PipeWire support is currently alpha quality. Please report bugs.", "LC")
+ }
+
if ctx.update.available && !ctx.update.triggered {
w.Row(20).Ratio(0.9, 0.1)
w.LabelColored("Update available! Click to install version: "+ctx.update.serverVersion, "LC", green)
@@ -376,23 +397,23 @@ func capabilitiesView(ctx *ntcontext, w *nucular.Window) {
if w.ButtonText("Grant capability (requires root)") {
err := pkexecSetcapSelf()
if err != nil {
- ctx.views.Push(makeErrorView(ctx, w, err.Error()))
+ ctx.views.Push(makeErrorView(ctx, err.Error()))
return
}
self, err := os.Executable()
if err != nil {
- ctx.views.Push(makeErrorView(ctx, w, err.Error()))
+ ctx.views.Push(makeErrorView(ctx, err.Error()))
return
}
err = syscall.Exec(self, []string{""}, os.Environ())
if err != nil {
- ctx.views.Push(makeErrorView(ctx, w, err.Error()))
+ ctx.views.Push(makeErrorView(ctx, err.Error()))
return
}
}
}
-func makeErrorView(ctx *ntcontext, w *nucular.Window, errorMsg string) ViewFunc {
+func makeErrorView(ctx *ntcontext, errorMsg string) ViewFunc {
return func(ctx *ntcontext, w *nucular.Window) {
w.Row(15).Dynamic(1)
w.Label("Error", "CB")
@@ -407,6 +428,21 @@ func makeErrorView(ctx *ntcontext, w *nucular.Window, errorMsg string) ViewFunc
}
}
+func makeFatalErrorView(ctx *ntcontext, errorMsg string) ViewFunc {
+ return func(ctx *ntcontext, w *nucular.Window) {
+ w.Row(15).Dynamic(1)
+ w.Label("Fatal Error", "CB")
+ w.Row(15).Dynamic(1)
+ w.Label(errorMsg, "CB")
+ w.Row(40).Dynamic(1)
+ w.Row(25).Dynamic(1)
+ if w.ButtonText("Quit") {
+ os.Exit(1)
+ return
+ }
+ }
+}
+
func resetUI(ctx *ntcontext) {
ctx.views = NewViewStack()
ctx.views.Push(mainView)
@@ -414,6 +450,12 @@ func resetUI(ctx *ntcontext) {
if !ctx.haveCapabilities {
ctx.views.Push(capabilitiesView)
}
+
+ if ctx.serverInfo.outdatedPipeWire {
+ ctx.views.Push(makeFatalErrorView(ctx,
+ fmt.Sprintf("Your PipeWire version is too old. Detected %d.%d.%d. Require at least 0.3.28.",
+ ctx.serverInfo.major, ctx.serverInfo.minor, ctx.serverInfo.patch)))
+ }
}
func loadPatreonImg() *image.RGBA {