aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlawl <github@dumbinter.net>2021-06-04 13:12:35 +0200
committerlawl <github@dumbinter.net>2021-06-04 13:12:35 +0200
commita2a45bc88bd549eb35a652941f0a26c5c4da09f0 (patch)
tree681b5a903fcd0e0dc22dc7299aa731df7b27dc0c
parentcb2ee053b622750356c4f8b717acb6b50fbdb937 (diff)
downloadnoisetorch-a2a45bc88bd549eb35a652941f0a26c5c4da09f0.tar.gz
noisetorch-a2a45bc88bd549eb35a652941f0a26c5c4da09f0.zip
UI: Add confirm screen for unload/reload when device in use
Reloading/Unloading destroys the virtual device that an application is currently using. Add a warning that this may cause strange behaviour.
-rw-r--r--module.go25
-rw-r--r--ui.go125
2 files changed, 102 insertions, 48 deletions
diff --git a/module.go b/module.go
index 12783e8..8e6b87a 100644
--- a/module.go
+++ b/module.go
@@ -24,8 +24,7 @@ func updateNoiseSupressorLoaded(ctx *ntcontext) {
}
for {
- ctx.noiseSupressorState = supressorState(ctx)
-
+ ctx.noiseSupressorState, ctx.virtualDeviceInUse = supressorState(ctx)
if !c.Connected() {
break
}
@@ -34,16 +33,18 @@ func updateNoiseSupressorLoaded(ctx *ntcontext) {
}
}
-func supressorState(ctx *ntcontext) int {
+func supressorState(ctx *ntcontext) (int, bool) {
//perform some checks to see if it looks like the noise supressor is loaded
c := ctx.paClient
var inpLoaded, outLoaded, inputInc, outputInc bool
+ var virtualDeviceInUse bool = false
if ctx.config.FilterInput {
if ctx.serverInfo.servertype == servertype_pipewire {
- _, ladspasource, err := findModule(c, "module-ladspa-source", "source_name='NoiseTorch Microphone'")
+ module, 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)
}
+ virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
inpLoaded = ladspasource
inputInc = false
} else {
@@ -59,11 +60,13 @@ func supressorState(ctx *ntcontext) int {
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")
+ module, 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)
}
+ virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
+
if nullsink && ladspasink && loopback && remap {
inpLoaded = true
} else if nullsink || ladspasink || loopback || remap {
@@ -76,10 +79,11 @@ func supressorState(ctx *ntcontext) int {
if ctx.config.FilterOutput {
if ctx.serverInfo.servertype == servertype_pipewire {
- _, ladspasink, err := findModule(c, "module-ladspa-sink", "sink_name='NoiseTorch Headphones'")
+ module, 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)
}
+ virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
outLoaded = ladspasink
outputInc = false
} else {
@@ -95,10 +99,11 @@ func supressorState(ctx *ntcontext) int {
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")
+ module, 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)
}
+ virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
_, 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)
@@ -112,14 +117,14 @@ func supressorState(ctx *ntcontext) int {
}
if (inpLoaded || !ctx.config.FilterInput) && (outLoaded || !ctx.config.FilterOutput) && !inputInc {
- return loaded
+ return loaded, virtualDeviceInUse
}
if (inpLoaded && ctx.config.FilterInput) || (outLoaded && ctx.config.FilterOutput) || inputInc || outputInc {
- return inconsistent
+ return inconsistent, virtualDeviceInUse
}
- return unloaded
+ return unloaded, virtualDeviceInUse
}
func loadSupressor(ctx *ntcontext, inp *device, out *device) error {
diff --git a/ui.go b/ui.go
index 56aefa5..327f35f 100644
--- a/ui.go
+++ b/ui.go
@@ -34,6 +34,7 @@ type ntcontext struct {
capsMismatch bool
views *ViewStack
serverInfo audioserverinfo
+ virtualDeviceInUse bool
}
//TODO pull some of these strucs out of UI, they don't belong here
@@ -149,13 +150,13 @@ func mainView(ctx *ntcontext, w *nucular.Window) {
if w.CheckboxText("Filter Microphone", &ctx.config.FilterInput) {
ctx.sourceListColdWidthIndex++ //recompute the with because of new elements
go writeConfig(ctx.config)
- go (func() { ctx.noiseSupressorState = supressorState(ctx) })()
+ go (func() { ctx.noiseSupressorState, _ = supressorState(ctx) })()
}
if w.CheckboxText("Filter Headphones", &ctx.config.FilterOutput) {
ctx.sourceListColdWidthIndex++ //recompute the with because of new elements
go writeConfig(ctx.config)
- go (func() { ctx.noiseSupressorState = supressorState(ctx) })()
+ go (func() { ctx.noiseSupressorState, _ = supressorState(ctx) })()
}
w.TreePop()
@@ -239,21 +240,19 @@ func mainView(ctx *ntcontext, w *nucular.Window) {
w.Row(25).Dynamic(2)
if ctx.noiseSupressorState != unloaded {
if w.ButtonText("Unload NoiseTorch") {
- ctx.views.Push(loadingView)
ctx.reloadRequired = false
- go func() { // don't block the UI thread, just display a working screen so user can't run multiple loads/unloads
- if err := unloadSupressor(ctx); err != nil {
- log.Println(err)
- }
- //wait until PA reports it has actually loaded it, timeout at 10s
- for i := 0; i < 20; i++ {
- if supressorState(ctx) != unloaded {
- time.Sleep(time.Millisecond * 500)
- }
- }
- ctx.views.Pop()
- (*ctx.masterWindow).Changed()
- }()
+ if ctx.virtualDeviceInUse {
+ confirm := makeConfirmView(ctx,
+ "Virtual Device in Use",
+ "Some applications may behave weirdly when you remove a device they're currently using",
+ "Unload",
+ "Go back",
+ func() { uiUnloadNoisetorch(ctx) },
+ func() {})
+ ctx.views.Push(confirm)
+ } else {
+ go uiUnloadNoisetorch(ctx)
+ }
}
} else {
w.Spacing(1)
@@ -271,30 +270,20 @@ func mainView(ctx *ntcontext, w *nucular.Window) {
((ctx.config.FilterOutput && ctx.config.GuiltTripped) || !ctx.config.FilterOutput) &&
ctx.noiseSupressorState != inconsistent {
if w.ButtonText(txt) {
- ctx.views.Push(loadingView)
ctx.reloadRequired = false
- go func() { // don't block the UI thread, just display a working screen so user can't run multiple loads/unloads
- if ctx.noiseSupressorState == loaded {
- if err := unloadSupressor(ctx); err != nil {
- log.Println(err)
- }
- }
- if err := loadSupressor(ctx, &inp, &out); err != nil {
- log.Println(err)
- }
- //wait until PA reports it has actually loaded it, timeout at 10s
- for i := 0; i < 20; i++ {
- if supressorState(ctx) != loaded {
- time.Sleep(time.Millisecond * 500)
- }
- }
- ctx.config.LastUsedInput = inp.ID
- ctx.config.LastUsedOutput = out.ID
- go writeConfig(ctx.config)
- ctx.views.Pop()
- (*ctx.masterWindow).Changed()
- }()
+ if ctx.virtualDeviceInUse {
+ confirm := makeConfirmView(ctx,
+ "Virtual Device in Use",
+ "Some applications may behave weirdly when you reload a device they're currently using",
+ "Reload",
+ "Go back",
+ func() { uiReloadNoisetorch(ctx, inp, out) },
+ func() {})
+ ctx.views.Push(confirm)
+ } else {
+ go uiReloadNoisetorch(ctx, inp, out)
+ }
}
} else {
w.Spacing(1)
@@ -302,6 +291,45 @@ func mainView(ctx *ntcontext, w *nucular.Window) {
}
+func uiUnloadNoisetorch(ctx *ntcontext) {
+ ctx.views.Push(loadingView)
+ if err := unloadSupressor(ctx); err != nil {
+ log.Println(err)
+ }
+ //wait until PA reports it has actually loaded it, timeout at 10s
+ for i := 0; i < 20; i++ {
+ if state, _ := supressorState(ctx); state != unloaded {
+ time.Sleep(time.Millisecond * 500)
+ }
+ }
+ ctx.views.Pop()
+ (*ctx.masterWindow).Changed()
+}
+
+func uiReloadNoisetorch(ctx *ntcontext, inp, out device) {
+ ctx.views.Push(loadingView)
+ if ctx.noiseSupressorState == loaded {
+ if err := unloadSupressor(ctx); err != nil {
+ log.Println(err)
+ }
+ }
+ if err := loadSupressor(ctx, &inp, &out); err != nil {
+ log.Println(err)
+ }
+
+ //wait until PA reports it has actually loaded it, timeout at 10s
+ for i := 0; i < 20; i++ {
+ if state, _ := supressorState(ctx); state != loaded {
+ time.Sleep(time.Millisecond * 500)
+ }
+ }
+ ctx.config.LastUsedInput = inp.ID
+ ctx.config.LastUsedOutput = out.ID
+ go writeConfig(ctx.config)
+ ctx.views.Pop()
+ (*ctx.masterWindow).Changed()
+}
+
func ensureOnlyOneInputSelected(inps *[]device, current *device) {
if current.checked != true {
return
@@ -443,6 +471,27 @@ func makeFatalErrorView(ctx *ntcontext, errorMsg string) ViewFunc {
}
}
+func makeConfirmView(ctx *ntcontext, title, text, confirmText, denyText string, confirmfunc, denyfunc func()) ViewFunc {
+ return func(ctx *ntcontext, w *nucular.Window) {
+ w.Row(15).Dynamic(1)
+ w.Label(title, "CB")
+ w.Row(15).Dynamic(1)
+ w.Label(text, "CB")
+ w.Row(40).Dynamic(1)
+ w.Row(25).Dynamic(2)
+ if w.ButtonText(denyText) {
+ ctx.views.Pop()
+ go denyfunc()
+ return
+ }
+ if w.ButtonText(confirmText) {
+ ctx.views.Pop()
+ go confirmfunc()
+ return
+ }
+ }
+}
+
func resetUI(ctx *ntcontext) {
ctx.views = NewViewStack()
ctx.views.Push(mainView)