aboutsummaryrefslogtreecommitdiff
path: root/cli.go
blob: 7e026ff21f1ad9e1f4c275add51bc18458277a2f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/lawl/pulseaudio"
)

type CLIOpts struct {
	doLog      bool
	setcap     bool
	sinkName   string
	unload     bool
	loadInput  bool
	loadOutput bool
	threshold  int
	list       bool
}

func parseCLIOpts() CLIOpts {
	var opt CLIOpts
	flag.BoolVar(&opt.doLog, "log", false, "Print debugging output to stdout")
	flag.BoolVar(&opt.setcap, "setcap", false, "for internal use only")
	flag.StringVar(&opt.sinkName, "s", "", "Use the specified source/sink device ID")
	flag.BoolVar(&opt.loadInput, "i", false, "Load supressor for input. If no source device ID is specified the default pulse audio source is used.")
	flag.BoolVar(&opt.loadOutput, "o", false, "Load supressor for output. If no source device ID is specified the default pulse audio source is used.")
	flag.BoolVar(&opt.unload, "u", false, "Unload supressor")
	flag.IntVar(&opt.threshold, "t", -1, "Voice activation threshold")
	flag.BoolVar(&opt.list, "l", false, "List available PulseAudio devices")
	flag.Parse()

	return opt
}

func doCLI(opt CLIOpts, config *config, librnnoise string) {

	if opt.setcap {
		err := makeBinarySetcapped()
		if err != nil {
			cleanupExit(librnnoise, 1)
		}
		cleanupExit(librnnoise, 0)
	}

	paClient, err := pulseaudio.NewClient()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Couldn't create pulseaudio client: %v\n", err)
		cleanupExit(librnnoise, 1)
	}
	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

	ctx.paClient = paClient

	if opt.list {
		fmt.Println("Sources:")
		sources := getSources(paClient)
		for i := range sources {
			fmt.Printf("\tDevice Name: %s\n\tDevice ID: %s\n\n", sources[i].Name, sources[i].ID)
		}

		fmt.Println("Sinks:")
		sinks := getSinks(paClient)
		for i := range sinks {
			fmt.Printf("\tDevice Name: %s\n\tDevice ID: %s\n\n", sinks[i].Name, sinks[i].ID)
		}

		cleanupExit(librnnoise, 0)
	}

	if opt.threshold > 0 {
		if opt.threshold > 95 {
			fmt.Fprintf(os.Stderr, "Threshold of '%d' too high, setting to maximum of 95.\n", opt.threshold)
			ctx.config.Threshold = 95
		} else {
			ctx.config.Threshold = opt.threshold
		}
	}

	if opt.unload {
		err := unloadSupressor(&ctx)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error unloading PulseAudio Module: %+v\n", err)
			cleanupExit(librnnoise, 1)
		}
		cleanupExit(librnnoise, 0)
	}

	if opt.loadInput {
		sources := getSources(paClient)

		if opt.sinkName == "" {
			defaultSource, err := getDefaultSourceID(paClient)
			if err != nil {
				fmt.Fprintf(os.Stderr, "No source specified to load and failed to load default source: %+v\n", err)
				cleanupExit(librnnoise, 1)
			}
			opt.sinkName = defaultSource
		}
		for i := range sources {
			if sources[i].ID == opt.sinkName {
				sources[i].checked = true
				err := loadSupressor(&ctx, &sources[i], &device{})
				if err != nil {
					fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err)
					cleanupExit(librnnoise, 1)
				}
				cleanupExit(librnnoise, 0)
			}
		}
		fmt.Fprintf(os.Stderr, "PulseAudio source not found: %s\n", opt.sinkName)
		cleanupExit(librnnoise, 1)

	}
	if opt.loadOutput {
		sinks := getSinks(paClient)

		if opt.sinkName == "" {
			defaultSink, err := getDefaultSinkID(paClient)
			if err != nil {
				fmt.Fprintf(os.Stderr, "No sink specified to load and failed to load default sink: %+v\n", err)
				cleanupExit(librnnoise, 1)
			}
			opt.sinkName = defaultSink
		}
		for i := range sinks {
			if sinks[i].ID == opt.sinkName {
				sinks[i].checked = true
				err := loadSupressor(&ctx, &device{}, &sinks[i])
				if err != nil {
					fmt.Fprintf(os.Stderr, "Error loading PulseAudio Module: %+v\n", err)
					cleanupExit(librnnoise, 1)
				}
				cleanupExit(librnnoise, 0)
			}
		}
		fmt.Fprintf(os.Stderr, "PulseAudio sink not found: %s\n", opt.sinkName)
		cleanupExit(librnnoise, 1)

	}
}

func cleanupExit(librnnoise string, exitCode int) {
	removeLib(librnnoise)
	os.Exit(exitCode)
}