Compare commits

...

7 Commits

Author SHA1 Message Date
lawl 1aa277446b Add PipeWire support to README 3 years ago
lawl a2a45bc88b 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.
3 years ago
lawl cb2ee053b6 Handle non-semver versions in audio client gracefully-ish 3 years ago
lawl d1aa44c3c7 pulseaudio: update module with fixed cookie lookup 3 years ago
lawl 077b4763fe CLI: Cleanup temp files on exit
Fixes #135
3 years ago
Andreas Schneider 809f184e87 c:ladspa: Fix linking with libm
Fixes #140
3 years ago
Andreas Schneider a32f73509f Revert "Temporarily force clang if available"
This reverts commit c2bfe0202f.
3 years ago

@ -2,9 +2,6 @@ UPDATE_URL=https://noisetorch.epicgamer.org
UPDATE_PUBKEY=3mL+rBi4yBZ1wGimQ/oSQCjxELzgTh+673H4JdzQBOk= UPDATE_PUBKEY=3mL+rBi4yBZ1wGimQ/oSQCjxELzgTh+673H4JdzQBOk=
VERSION := $(shell git describe --tags) VERSION := $(shell git describe --tags)
CLANG := $(shell which clang)
dev: rnnoise dev: rnnoise
mkdir -p bin/ mkdir -p bin/
go generate go generate
@ -30,14 +27,6 @@ release: rnnoise
go run scripts/signer.go -s go run scripts/signer.go -s
git describe --tags > bin/version.txt git describe --tags > bin/version.txt
rnnoise: rnnoise:
# For some reason gcc10 refuses to link libm
# gcc11 seems to work. Temporarily force clang
# if available until i can fix this properly
ifdef CLANG
cd c/ladspa; \
CC=clang make
else
cd c/ladspa; \ cd c/ladspa; \
make make
endif

@ -5,7 +5,7 @@
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Last Release](https://img.shields.io/github/v/release/lawl/NoiseTorch?label=latest&style=flat-square)](https://github.com/lawl/NoiseTorch/releases) [![Last Release](https://img.shields.io/github/v/release/lawl/NoiseTorch?label=latest&style=flat-square)](https://github.com/lawl/NoiseTorch/releases)
NoiseTorch is an easy to use open source application for Linux with PulseAudio. It creates a virtual microphone that suppresses noise, in any application. Use whichever conferencing or VOIP application you like and simply select the NoiseTorch Virtual Microphone as input to torch the sound of your mechanical keyboard, computer fans, trains and the likes. NoiseTorch is an easy to use open source application for Linux with PulseAudio or PipeWire. It creates a virtual microphone that suppresses noise, in any application. Use whichever conferencing or VOIP application you like and simply select the NoiseTorch Virtual Microphone as input to torch the sound of your mechanical keyboard, computer fans, trains and the likes.
Don't forget to ~~like, comment and subscribe~~ leave a star ⭐ if this sounds useful to you! Don't forget to ~~like, comment and subscribe~~ leave a star ⭐ if this sounds useful to you!

@ -1,3 +1,3 @@
default: default:
$(CC) -Wall -O2 -c -fPIC ../ringbuf.c ../rnnoise/*.c module.c $(CC) -Wall -Werror -O2 -c -fPIC ../ringbuf.c ../rnnoise/*.c module.c
$(CC) -shared -lm -Wl,--version-script=export.txt -o rnnoise_ladspa.so *.o $(CC) -o rnnoise_ladspa.so *.o -shared -Wl,--version-script=export.txt -lm

@ -39,15 +39,15 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
if opt.setcap { if opt.setcap {
err := makeBinarySetcapped() err := makeBinarySetcapped()
if err != nil { if err != nil {
os.Exit(1) cleanupExit(librnnoise, 1)
} }
os.Exit(0) cleanupExit(librnnoise, 0)
} }
paClient, err := pulseaudio.NewClient() paClient, err := pulseaudio.NewClient()
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't create pulseaudio client: %v\n", err) fmt.Fprintf(os.Stderr, "Couldn't create pulseaudio client: %v\n", err)
os.Exit(1) cleanupExit(librnnoise, 1)
} }
defer paClient.Close() defer paClient.Close()
@ -77,7 +77,7 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
fmt.Printf("\tDevice Name: %s\n\tDevice ID: %s\n\n", sinks[i].Name, sinks[i].ID) fmt.Printf("\tDevice Name: %s\n\tDevice ID: %s\n\n", sinks[i].Name, sinks[i].ID)
} }
os.Exit(0) cleanupExit(librnnoise, 0)
} }
if opt.threshold > 0 { if opt.threshold > 0 {
@ -93,9 +93,9 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
err := unloadSupressor(&ctx) err := unloadSupressor(&ctx)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error unloading PulseAudio Module: %+v\n", err) fmt.Fprintf(os.Stderr, "Error unloading PulseAudio Module: %+v\n", err)
os.Exit(1) cleanupExit(librnnoise, 1)
} }
os.Exit(0) cleanupExit(librnnoise, 0)
} }
if opt.loadInput { if opt.loadInput {
@ -105,7 +105,7 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
defaultSource, err := getDefaultSourceID(paClient) defaultSource, err := getDefaultSourceID(paClient)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "No source specified to load and failed to load default source: %+v\n", err) fmt.Fprintf(os.Stderr, "No source specified to load and failed to load default source: %+v\n", err)
os.Exit(1) cleanupExit(librnnoise, 1)
} }
opt.sinkName = defaultSource opt.sinkName = defaultSource
} }
@ -115,13 +115,13 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
err := loadSupressor(&ctx, &sources[i], &device{}) err := loadSupressor(&ctx, &sources[i], &device{})
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err) fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err)
os.Exit(1) cleanupExit(librnnoise, 1)
} }
os.Exit(0) cleanupExit(librnnoise, 0)
} }
} }
fmt.Fprintf(os.Stderr, "PulseAudio source not found: %s\n", opt.sinkName) fmt.Fprintf(os.Stderr, "PulseAudio source not found: %s\n", opt.sinkName)
os.Exit(1) cleanupExit(librnnoise, 1)
} }
if opt.loadOutput { if opt.loadOutput {
@ -131,7 +131,7 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
defaultSink, err := getDefaultSinkID(paClient) defaultSink, err := getDefaultSinkID(paClient)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "No sink specified to load and failed to load default sink: %+v\n", err) fmt.Fprintf(os.Stderr, "No sink specified to load and failed to load default sink: %+v\n", err)
os.Exit(1) cleanupExit(librnnoise, 1)
} }
opt.sinkName = defaultSink opt.sinkName = defaultSink
} }
@ -141,13 +141,18 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) {
err := loadSupressor(&ctx, &device{}, &sinks[i]) err := loadSupressor(&ctx, &device{}, &sinks[i])
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err) fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err)
os.Exit(1) cleanupExit(librnnoise, 1)
} }
os.Exit(0) cleanupExit(librnnoise, 0)
} }
} }
fmt.Fprintf(os.Stderr, "PulseAudio sink not found: %s\n", opt.sinkName) fmt.Fprintf(os.Stderr, "PulseAudio sink not found: %s\n", opt.sinkName)
os.Exit(1) cleanupExit(librnnoise, 1)
} }
} }
func cleanupExit(librnnoise string, exitCode int) {
removeLib(librnnoise)
os.Exit(exitCode)
}

@ -7,7 +7,7 @@ require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046
github.com/aarzilli/nucular v0.0.0-20200615134801-81910c722bba github.com/aarzilli/nucular v0.0.0-20200615134801-81910c722bba
github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0 github.com/lawl/pulseaudio v0.0.0-20210604102109-cb2596d6a8ef
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
) )

@ -21,6 +21,8 @@ github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad h1:eMxs9EL0Pv
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0 h1:JrvOwrr1teFiqsp0EQxgEPJsm0pet+YLTL+HdYmnMx0= github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0 h1:JrvOwrr1teFiqsp0EQxgEPJsm0pet+YLTL+HdYmnMx0=
github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0/go.mod h1:9h36x4KH7r2V8DOCKoPMt87IXZ++X90y8D5nnuwq290= github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0/go.mod h1:9h36x4KH7r2V8DOCKoPMt87IXZ++X90y8D5nnuwq290=
github.com/lawl/pulseaudio v0.0.0-20210604102109-cb2596d6a8ef h1:mpCJg3O6C+B8mh5xoO147NG3Z70GBcFNgqgz2DH/rLQ=
github.com/lawl/pulseaudio v0.0.0-20210604102109-cb2596d6a8ef/go.mod h1:9h36x4KH7r2V8DOCKoPMt87IXZ++X90y8D5nnuwq290=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

@ -247,19 +247,20 @@ func serverInfo(paClient *pulseaudio.Client) (audioserverinfo, error) {
res := versionRegex.FindStringSubmatch(versionString) res := versionRegex.FindStringSubmatch(versionString)
if len(res) != 4 { if len(res) != 4 {
return audioserverinfo{}, fmt.Errorf("couldn't parse server version, regexp didn't match.") log.Printf("couldn't parse server version, regexp didn't match version: %s\n", versionString)
return audioserverinfo{servertype: servertype}, nil
} }
major, err = strconv.Atoi(res[1]) major, err = strconv.Atoi(res[1])
if err != nil { if err != nil {
return audioserverinfo{}, err return audioserverinfo{servertype: servertype}, err
} }
minor, err = strconv.Atoi(res[2]) minor, err = strconv.Atoi(res[2])
if err != nil { if err != nil {
return audioserverinfo{}, err return audioserverinfo{servertype: servertype}, err
} }
patch, err = strconv.Atoi(res[3]) patch, err = strconv.Atoi(res[3])
if err != nil { if err != nil {
return audioserverinfo{}, err return audioserverinfo{servertype: servertype}, err
} }
if isPipewire && major <= 0 && minor <= 3 && patch < 28 { if isPipewire && major <= 0 && minor <= 3 && patch < 28 {
log.Printf("pipewire version %d.%d.%d too old.\n", major, minor, patch) log.Printf("pipewire version %d.%d.%d too old.\n", major, minor, patch)

@ -24,8 +24,7 @@ func updateNoiseSupressorLoaded(ctx *ntcontext) {
} }
for { for {
ctx.noiseSupressorState = supressorState(ctx) ctx.noiseSupressorState, ctx.virtualDeviceInUse = supressorState(ctx)
if !c.Connected() { if !c.Connected() {
break 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 //perform some checks to see if it looks like the noise supressor is loaded
c := ctx.paClient c := ctx.paClient
var inpLoaded, outLoaded, inputInc, outputInc bool var inpLoaded, outLoaded, inputInc, outputInc bool
var virtualDeviceInUse bool = false
if ctx.config.FilterInput { if ctx.config.FilterInput {
if ctx.serverInfo.servertype == servertype_pipewire { 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 { if err != nil {
log.Printf("Couldn't fetch module list to check for module-ladspa-source: %v\n", err) log.Printf("Couldn't fetch module list to check for module-ladspa-source: %v\n", err)
} }
virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
inpLoaded = ladspasource inpLoaded = ladspasource
inputInc = false inputInc = false
} else { } else {
@ -59,11 +60,13 @@ func supressorState(ctx *ntcontext) int {
if err != nil { if err != nil {
log.Printf("Couldn't fetch module list to check for module-loopback: %v\n", err) 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 { if err != nil {
log.Printf("Couldn't fetch module list to check for module-remap-source: %v\n", err) 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 { if nullsink && ladspasink && loopback && remap {
inpLoaded = true inpLoaded = true
} else if nullsink || ladspasink || loopback || remap { } else if nullsink || ladspasink || loopback || remap {
@ -76,10 +79,11 @@ func supressorState(ctx *ntcontext) int {
if ctx.config.FilterOutput { if ctx.config.FilterOutput {
if ctx.serverInfo.servertype == servertype_pipewire { 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 { if err != nil {
log.Printf("Couldn't fetch module list to check for module-ladspa-sink: %v\n", err) log.Printf("Couldn't fetch module list to check for module-ladspa-sink: %v\n", err)
} }
virtualDeviceInUse = virtualDeviceInUse || (module.NUsed != 0)
outLoaded = ladspasink outLoaded = ladspasink
outputInc = false outputInc = false
} else { } else {
@ -95,10 +99,11 @@ func supressorState(ctx *ntcontext) int {
if err != nil { if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err) 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 { if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err) 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") _, loop2, err := findModule(c, "module-loopback", "source=nui_out_in_sink.monitor")
if err != nil { if err != nil {
log.Printf("Couldn't fetch module list to check for output module-ladspa-sink: %v\n", err) 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 { 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 { 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 { func loadSupressor(ctx *ntcontext, inp *device, out *device) error {

125
ui.go

@ -34,6 +34,7 @@ type ntcontext struct {
capsMismatch bool capsMismatch bool
views *ViewStack views *ViewStack
serverInfo audioserverinfo serverInfo audioserverinfo
virtualDeviceInUse bool
} }
//TODO pull some of these strucs out of UI, they don't belong here //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) { if w.CheckboxText("Filter Microphone", &ctx.config.FilterInput) {
ctx.sourceListColdWidthIndex++ //recompute the with because of new elements ctx.sourceListColdWidthIndex++ //recompute the with because of new elements
go writeConfig(ctx.config) go writeConfig(ctx.config)
go (func() { ctx.noiseSupressorState = supressorState(ctx) })() go (func() { ctx.noiseSupressorState, _ = supressorState(ctx) })()
} }
if w.CheckboxText("Filter Headphones", &ctx.config.FilterOutput) { if w.CheckboxText("Filter Headphones", &ctx.config.FilterOutput) {
ctx.sourceListColdWidthIndex++ //recompute the with because of new elements ctx.sourceListColdWidthIndex++ //recompute the with because of new elements
go writeConfig(ctx.config) go writeConfig(ctx.config)
go (func() { ctx.noiseSupressorState = supressorState(ctx) })() go (func() { ctx.noiseSupressorState, _ = supressorState(ctx) })()
} }
w.TreePop() w.TreePop()
@ -239,21 +240,19 @@ func mainView(ctx *ntcontext, w *nucular.Window) {
w.Row(25).Dynamic(2) w.Row(25).Dynamic(2)
if ctx.noiseSupressorState != unloaded { if ctx.noiseSupressorState != unloaded {
if w.ButtonText("Unload NoiseTorch") { if w.ButtonText("Unload NoiseTorch") {
ctx.views.Push(loadingView)
ctx.reloadRequired = false 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.virtualDeviceInUse {
if err := unloadSupressor(ctx); err != nil { confirm := makeConfirmView(ctx,
log.Println(err) "Virtual Device in Use",
} "Some applications may behave weirdly when you remove a device they're currently using",
//wait until PA reports it has actually loaded it, timeout at 10s "Unload",
for i := 0; i < 20; i++ { "Go back",
if supressorState(ctx) != unloaded { func() { uiUnloadNoisetorch(ctx) },
time.Sleep(time.Millisecond * 500) func() {})
} ctx.views.Push(confirm)
} } else {
ctx.views.Pop() go uiUnloadNoisetorch(ctx)
(*ctx.masterWindow).Changed() }
}()
} }
} else { } else {
w.Spacing(1) w.Spacing(1)
@ -271,30 +270,20 @@ func mainView(ctx *ntcontext, w *nucular.Window) {
((ctx.config.FilterOutput && ctx.config.GuiltTripped) || !ctx.config.FilterOutput) && ((ctx.config.FilterOutput && ctx.config.GuiltTripped) || !ctx.config.FilterOutput) &&
ctx.noiseSupressorState != inconsistent { ctx.noiseSupressorState != inconsistent {
if w.ButtonText(txt) { if w.ButtonText(txt) {
ctx.views.Push(loadingView)
ctx.reloadRequired = false 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 if ctx.virtualDeviceInUse {
for i := 0; i < 20; i++ { confirm := makeConfirmView(ctx,
if supressorState(ctx) != loaded { "Virtual Device in Use",
time.Sleep(time.Millisecond * 500) "Some applications may behave weirdly when you reload a device they're currently using",
} "Reload",
} "Go back",
ctx.config.LastUsedInput = inp.ID func() { uiReloadNoisetorch(ctx, inp, out) },
ctx.config.LastUsedOutput = out.ID func() {})
go writeConfig(ctx.config) ctx.views.Push(confirm)
ctx.views.Pop() } else {
(*ctx.masterWindow).Changed() go uiReloadNoisetorch(ctx, inp, out)
}() }
} }
} else { } else {
w.Spacing(1) 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) { func ensureOnlyOneInputSelected(inps *[]device, current *device) {
if current.checked != true { if current.checked != true {
return 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) { func resetUI(ctx *ntcontext) {
ctx.views = NewViewStack() ctx.views = NewViewStack()
ctx.views.Push(mainView) ctx.views.Push(mainView)

@ -383,7 +383,7 @@ func cookiePath() (string, error) {
return p, nil return p, nil
} }
p = filepath.Join(os.Getenv("HOME"), "/.pulse_cookie") p = filepath.Join(os.Getenv("HOME"), "/.pulse-cookie")
if exists(p) { if exists(p) {
return p, nil return p, nil
} }

@ -71,7 +71,7 @@ github.com/golang/freetype/truetype
# github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad # github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad
github.com/hashicorp/golang-lru github.com/hashicorp/golang-lru
github.com/hashicorp/golang-lru/simplelru github.com/hashicorp/golang-lru/simplelru
# github.com/lawl/pulseaudio v0.0.0-20200802093727-ab0735955fd0 # github.com/lawl/pulseaudio v0.0.0-20210604102109-cb2596d6a8ef
## explicit ## explicit
github.com/lawl/pulseaudio github.com/lawl/pulseaudio
# github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 # github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635

Loading…
Cancel
Save