big bang
parent
d29abc7a0d
commit
e758ce0ac7
@ -0,0 +1,2 @@
|
||||
noiseui
|
||||
librnnoise.go
|
@ -0,0 +1,3 @@
|
||||
release:
|
||||
go generate
|
||||
CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w -extldflags "-static"' .
|
@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Threshold int
|
||||
DisplayMonitorSources bool
|
||||
}
|
||||
|
||||
const configDir = ".config/noiseui/"
|
||||
const configFile = "config.toml"
|
||||
|
||||
func initializeConfigIfNot() {
|
||||
log.Println("Checking if config needs to be initialized")
|
||||
conf := config{Threshold: 95, DisplayMonitorSources: false}
|
||||
configdir := filepath.Join(os.Getenv("HOME"), configDir)
|
||||
ok, err := exists(configdir)
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't check if config directory exists: %v\n", err)
|
||||
}
|
||||
if !ok {
|
||||
err = os.MkdirAll(configdir, 0700)
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't create config directory: %v\n", err)
|
||||
}
|
||||
}
|
||||
tomlfile := filepath.Join(configdir, configFile)
|
||||
ok, err = exists(tomlfile)
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't check if config file exists: %v\n", err)
|
||||
}
|
||||
if !ok {
|
||||
log.Println("Initializing config")
|
||||
writeConfig(&conf)
|
||||
}
|
||||
}
|
||||
|
||||
func readConfig() *config {
|
||||
f := filepath.Join(os.Getenv("HOME"), configDir, configFile)
|
||||
config := config{}
|
||||
if _, err := toml.DecodeFile(f, &config); err != nil {
|
||||
log.Fatalf("Couldn't read config file: %v\n", err)
|
||||
}
|
||||
|
||||
return &config
|
||||
}
|
||||
|
||||
func writeConfig(conf *config) {
|
||||
f := filepath.Join(os.Getenv("HOME"), configDir, configFile)
|
||||
var buffer bytes.Buffer
|
||||
if err := toml.NewEncoder(&buffer).Encode(&conf); err != nil {
|
||||
log.Fatalf("Couldn't write config file: %v\n", err)
|
||||
}
|
||||
ioutil.WriteFile(f, []byte(buffer.String()), 0644)
|
||||
}
|
||||
|
||||
func exists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
module noiseui
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
gioui.org v0.0.0-20200630184602-223f8fd40ae4 // indirect
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/aarzilli/nucular v0.0.0-20200615134801-81910c722bba
|
||||
github.com/lawl/pulseaudio v0.0.0-20200704145757-7d4b4b92e7b7
|
||||
)
|
@ -0,0 +1,48 @@
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
gioui.org v0.0.0-20200417085050-0cfc914d8b7d/go.mod h1:AHI9rFr6AEEHCb8EPVtb/p5M+NMJRKH58IOp8O3Je04=
|
||||
gioui.org v0.0.0-20200630184602-223f8fd40ae4 h1:Pgz3ROw4pcpdxs62BRaqt4PbBeRHsW+mf5ee92CKZ0M=
|
||||
gioui.org v0.0.0-20200630184602-223f8fd40ae4/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/aarzilli/nucular v0.0.0-20200615134801-81910c722bba h1:7OBB0+T/f0gGMdqTwoXF872nDKosq4dIK/H2cRlrnWI=
|
||||
github.com/aarzilli/nucular v0.0.0-20200615134801-81910c722bba/go.mod h1:TsFEH0qn2Uu3C3guJjfIaoCqgpoCvU+laq0SSK2TOyY=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72 h1:b+9H1GAsx5RsjvDFLoS5zkNBzIQMuVKUYQDmxU3N5XE=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/freetype v0.0.0-20161208064710-d9be45aaf745 h1:0d9whnMsm0iklqvoBXNEgHPt8pkXdfDplBAswA/F8YA=
|
||||
github.com/golang/freetype v0.0.0-20161208064710-d9be45aaf745/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad h1:eMxs9EL0PvIGS9TTtxg4R+JxuPGav82J8rA+GFnY7po=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/lawl/pulseaudio v0.0.0-20200704145757-7d4b4b92e7b7 h1:LjLOowMTfYESP3XW5/KV6TaQVuhdvkBHJoEaReGds6M=
|
||||
github.com/lawl/pulseaudio v0.0.0-20200704145757-7d4b4b92e7b7/go.mod h1:9h36x4KH7r2V8DOCKoPMt87IXZ++X90y8D5nnuwq290=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs=
|
||||
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
|
||||
golang.org/x/exp v0.0.0-20191224044220-1fea468a75e9 h1:HLuLY2KniBsHW28uXd1i2UZKjifeJUy//P/wTK6AJwI=
|
||||
golang.org/x/exp v0.0.0-20191224044220-1fea468a75e9/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
|
||||
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
@ -0,0 +1 @@
|
||||
*
|
@ -0,0 +1 @@
|
||||
Put a compiled librnnoise_ladspa.so in this folder, it will be required for compilation
|
@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/aarzilli/nucular/font"
|
||||
|
||||
"github.com/lawl/pulseaudio"
|
||||
|
||||
"github.com/aarzilli/nucular"
|
||||
"github.com/aarzilli/nucular/style"
|
||||
)
|
||||
|
||||
//go:generate go run scripts/embedlibrnnoise.go
|
||||
|
||||
type input struct {
|
||||
ID string
|
||||
Name string
|
||||
isMonitor bool
|
||||
checked bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
f, err := os.OpenFile("/tmp/noiseui.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("error opening file: %v\n", err)
|
||||
}
|
||||
defer f.Close()
|
||||
log.SetOutput(f)
|
||||
log.Println("Application starting.")
|
||||
|
||||
initializeConfigIfNot()
|
||||
rnnoisefile := dumpLib()
|
||||
defer removeLib(rnnoisefile)
|
||||
|
||||
ui := uistate{}
|
||||
ui.config = readConfig()
|
||||
ui.librnnoise = rnnoisefile
|
||||
|
||||
paClient, err := pulseaudio.NewClient()
|
||||
defer paClient.Close()
|
||||
|
||||
ui.paClient = paClient
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't create pulseaudio client\n")
|
||||
}
|
||||
|
||||
go updateNoiseSupressorLoaded(paClient, &ui.noiseSupressorState)
|
||||
|
||||
sources, err := paClient.Sources()
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't fetch sources from pulseaudio\n")
|
||||
}
|
||||
|
||||
inputs := make([]input, 0)
|
||||
for i := range sources {
|
||||
if sources[i].Name == "nui_mic_remap" {
|
||||
continue
|
||||
}
|
||||
|
||||
var inp input
|
||||
|
||||
inp.ID = sources[i].Name
|
||||
inp.Name = sources[i].PropList["device.description"]
|
||||
inp.isMonitor = (sources[i].MonitorSourceIndex != 0xffffffff)
|
||||
|
||||
inputs = append(inputs, inp)
|
||||
}
|
||||
|
||||
ui.inputList = inputs
|
||||
|
||||
wnd := nucular.NewMasterWindowSize(0, "NoiseUI", image.Point{550, 300}, func(w *nucular.Window) {
|
||||
updatefn(w, &ui)
|
||||
})
|
||||
style := style.FromTheme(style.DarkTheme, 2.0)
|
||||
style.Font = font.DefaultFont(16, 1)
|
||||
wnd.SetStyle(style)
|
||||
wnd.Main()
|
||||
|
||||
}
|
||||
|
||||
func dumpLib() string {
|
||||
f, err := ioutil.TempFile("", "librnnoise-*.so")
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't open temp file for librnnoise\n")
|
||||
}
|
||||
f.Write(libRNNoise)
|
||||
log.Printf("Wrote temp librnnoise to: %s\n", f.Name())
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
func removeLib(file string) {
|
||||
err := os.Remove(file)
|
||||
if err != nil {
|
||||
log.Printf("Couldn't delete temp librnnoise: %v\n", err)
|
||||
}
|
||||
log.Printf("Deleted temp librnnoise: %s\n", file)
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lawl/pulseaudio"
|
||||
)
|
||||
|
||||
const (
|
||||
loaded = iota
|
||||
unloaded
|
||||
inconsistent
|
||||
)
|
||||
|
||||
// the ugly and (partially) repeated strings are unforunately difficult to avoid, as it's what pulse audio expects
|
||||
|
||||
func updateNoiseSupressorLoaded(c *pulseaudio.Client, b *int) {
|
||||
|
||||
upd, err := c.Updates()
|
||||
if err != nil {
|
||||
fmt.Printf("Error listening for updates: %v\n", err)
|
||||
}
|
||||
|
||||
for {
|
||||
*b = supressorState(c)
|
||||
<-upd
|
||||
}
|
||||
}
|
||||
|
||||
func supressorState(c *pulseaudio.Client) int {
|
||||
//perform some checks to see if it looks like the noise supressor is loaded
|
||||
_, 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-nulll-sink: %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-nulll-sink: %v\n", err)
|
||||
}
|
||||
|
||||
if nullsink && ladspasink && loopback && remap {
|
||||
return loaded
|
||||
}
|
||||
if nullsink || ladspasink || loopback || remap {
|
||||
return inconsistent
|
||||
}
|
||||
return unloaded
|
||||
}
|
||||
|
||||
func loadSupressor(c *pulseaudio.Client, inp input, ui *uistate) error {
|
||||
log.Printf("Loading supressor\n")
|
||||
idx, err := c.LoadModule("module-null-sink", "sink_name=nui_mic_denoised_out")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Loaded null sink as idx: %d\n", idx)
|
||||
|
||||
time.Sleep(time.Millisecond * 500) // pulseaudio actually crashes if we send these too fast
|
||||
|
||||
idx, err = c.LoadModule("module-ladspa-sink",
|
||||
fmt.Sprintf("sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out "+
|
||||
"label=noise_suppressor_mono plugin=%s control=%d", ui.librnnoise, ui.config.Threshold))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Loaded ladspa sink as idx: %d\n", idx)
|
||||
|
||||
time.Sleep(time.Millisecond * 500) // pulseaudio actually crashes if we send these too fast
|
||||
|
||||
idx, err = c.LoadModule("module-loopback",
|
||||
fmt.Sprintf("source=%s sink=nui_mic_raw_in channels=1 latency_msec=1", inp.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Loaded loopback as idx: %d\n", idx)
|
||||
|
||||
time.Sleep(time.Millisecond * 500) // pulseaudio actually crashes if we send these too fast
|
||||
|
||||
idx, err = c.LoadModule("module-remap-source", `master=nui_mic_denoised_out.monitor `+
|
||||
`source_name=nui_mic_remap source_properties="device.description='Denoised Microphone'"`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Loaded ladspa sink as idx: %d\n", idx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unloadSupressor(c *pulseaudio.Client) error {
|
||||
log.Printf("Unloading pulseaudio modules\n")
|
||||
log.Printf("Searching for null-sink\n")
|
||||
m, found, err := findModule(c, "module-null-sink", "sink_name=nui_mic_denoised_out")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
log.Printf("Found null-sink at id [%d], sending unload command\n", m.Index)
|
||||
c.UnloadModule(m.Index)
|
||||
}
|
||||
log.Printf("Searching for ladspa-sink\n")
|
||||
m, found, err = findModule(c, "module-ladspa-sink", "sink_name=nui_mic_raw_in sink_master=nui_mic_denoised_out")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
log.Printf("Found ladspa-sink at id [%d], sending unload command\n", m.Index)
|
||||
c.UnloadModule(m.Index)
|
||||
}
|
||||
log.Printf("Searching for loopback\n")
|
||||
m, found, err = findModule(c, "module-loopback", "sink=nui_mic_raw_in")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
log.Printf("Found loopback at id [%d], sending unload command\n", m.Index)
|
||||
c.UnloadModule(m.Index)
|
||||
}
|
||||
|
||||
log.Printf("Searching for remap-source\n")
|
||||
m, found, err = findModule(c, "module-remap-source", "master=nui_mic_denoised_out.monitor source_name=nui_mic_remap")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
log.Printf("Found remap source at id [%d], sending unload command\n", m.Index)
|
||||
c.UnloadModule(m.Index)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
for _, m := range lst {
|
||||
if m.Name == name && strings.Contains(m.Argument, argMatch) {
|
||||
return m, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return pulseaudio.Module{}, false, nil
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
b, err := ioutil.ReadFile("librnnoise_ladspa/librnnoise_ladspa.so")
|
||||
if err != nil {
|
||||
fmt.Printf("Couldn't read librnnoise_ladspa.so: %v\n", err)
|
||||
fmt.Println("Drop a compiled librnnoise_ladspa.so in at librnnoise_ladspa/librnnoise_ladspa.so,\n" +
|
||||
"it will is required for compilation so we can embed it.")
|
||||
os.Exit(1)
|
||||
}
|
||||
out, _ := os.Create("librnnoise.go")
|
||||
defer out.Close()
|
||||
|
||||
out.Write([]byte("package main \n\nvar libRNNoise = []byte{\n"))
|
||||
for i, c := range b {
|
||||
out.Write([]byte(strconv.Itoa(int(c))))
|
||||
out.Write([]byte(","))
|
||||
if i%32 == 0 && i != 0 {
|
||||
out.Write([]byte("\n"))
|
||||
}
|
||||
}
|
||||
out.Write([]byte("}\n"))
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"log"
|
||||
|
||||
"github.com/aarzilli/nucular"
|
||||
"github.com/lawl/pulseaudio"
|
||||
)
|
||||
|
||||
type uistate struct {
|
||||
inputList []input
|
||||
noiseSupressorState int
|
||||
paClient *pulseaudio.Client
|
||||
librnnoise string
|
||||
sourceListColdWidthIndex int
|
||||
useBuiltinRNNoise bool
|
||||
config *config
|
||||
}
|
||||
|
||||
func updatefn(w *nucular.Window, ui *uistate) {
|
||||
w.Row(15).Dynamic(2)
|
||||
w.Label("NoiseUI", "LC")
|
||||
|
||||
if ui.noiseSupressorState == loaded {
|
||||
w.LabelColored("Denoised virtual microphone active", "RC", color.RGBA{0, 255, 0, 255} /*green*/)
|
||||
} else if ui.noiseSupressorState == unloaded {
|
||||
w.LabelColored("Denoised virtual microphone inactive", "RC", color.RGBA{255, 0, 0, 255} /*red*/)
|
||||
} else if ui.noiseSupressorState == inconsistent {
|
||||
w.LabelColored("Inconsistent state, please unload first.", "RC", color.RGBA{255, 140, 0, 255} /*orange*/)
|
||||
}
|
||||
|
||||
if w.TreePush(nucular.TreeTab, "Settings", true) {
|
||||
w.Row(15).Dynamic(2)
|
||||
if w.CheckboxText("Display Monitor Sources", &ui.config.DisplayMonitorSources) {
|
||||
ui.sourceListColdWidthIndex++ //recompute the with because of new elements
|
||||
go writeConfig(ui.config)
|
||||
}
|
||||
|
||||
w.Spacing(1)
|
||||
|
||||
w.Row(25).Ratio(0.5, 0.45, 0.05)
|
||||
w.Label("Filter strictness", "LC")
|
||||
if w.Input().Mouse.HoveringRect(w.LastWidgetBounds) {
|
||||
w.Tooltip("If you have a decent microphone, you can usually turn this all the way up.")
|
||||
}
|
||||
if w.SliderInt(0, &ui.config.Threshold, 95, 1) {
|
||||
go writeConfig(ui.config)
|
||||
}
|
||||
w.Label(fmt.Sprintf("%d%%", ui.config.Threshold), "RC")
|
||||
|
||||
w.TreePop()
|
||||
}
|
||||
if w.TreePush(nucular.TreeTab, "Select Device", true) {
|
||||
w.Row(15).Dynamic(1)
|
||||
w.Label("Select an input device below:", "LC")
|
||||
|
||||
for i := range ui.inputList {
|
||||
el := &ui.inputList[i]
|
||||
|
||||
if el.isMonitor && !ui.config.DisplayMonitorSources {
|
||||
continue
|
||||
}
|
||||
w.Row(15).Static()
|
||||
w.LayoutFitWidth(0, 0)
|
||||
if w.CheckboxText("", &el.checked) {
|
||||
ensureOnlyOneInputSelected(&ui.inputList, el)
|
||||
}
|
||||
|
||||
w.LayoutFitWidth(ui.sourceListColdWidthIndex, 0)
|
||||
w.Label(el.Name, "LC")
|
||||
}
|
||||
|
||||
w.Row(30).Dynamic(1)
|
||||
w.Spacing(1)
|
||||
|
||||
w.Row(25).Dynamic(2)
|
||||
if ui.noiseSupressorState != unloaded {
|
||||
if w.ButtonText("Unload Denoised Virtual Microphone") {
|
||||
if err := unloadSupressor(ui.paClient); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w.Spacing(1)
|
||||
}
|
||||
txt := "Load Denoised Virtual Microphone"
|
||||
if ui.noiseSupressorState == loaded {
|
||||
txt = "Reload Denoised Virtual Microphone"
|
||||
}
|
||||
if inp, ok := inputSelection(ui); ok && ui.noiseSupressorState != inconsistent {
|
||||
if w.ButtonText(txt) {
|
||||
if ui.noiseSupressorState == loaded {
|
||||
if err := unloadSupressor(ui.paClient); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
if err := loadSupressor(ui.paClient, inp, ui); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w.Spacing(1)
|
||||
}
|
||||
w.TreePop()
|
||||
}
|
||||
}
|
||||
|
||||
func ensureOnlyOneInputSelected(inps *[]input, current *input) {
|
||||
if current.checked != true {
|
||||
return
|
||||
}
|
||||
for i := range *inps {
|
||||
el := &(*inps)[i]
|
||||
el.checked = false
|
||||
}
|
||||
current.checked = true
|
||||
}
|
||||
|
||||
func inputSelection(ui *uistate) (input, bool) {
|
||||
for _, in := range ui.inputList {
|
||||
if in.checked {
|
||||
return in, true
|
||||
}
|
||||
}
|
||||
return input{}, false
|
||||
}
|
Loading…
Reference in New Issue