1- package catnip
1+ package main
22
33import (
44 "context"
55 "fmt"
6- "time"
6+ "log"
7+ "sync"
78
8- "github.com/noriah/catnip/config"
99 "github.com/noriah/catnip/dsp"
1010 "github.com/noriah/catnip/graphic"
1111 "github.com/noriah/catnip/input"
1212 "github.com/noriah/catnip/visualizer"
1313
14+ _ "github.com/noriah/catnip/input/ffmpeg"
15+ _ "github.com/noriah/catnip/input/parec"
16+
17+ "github.com/integrii/flaggy"
1418 "github.com/pkg/errors"
1519)
1620
17- // Catnip starts to draw the visualizer on the termbox screen.
18- func Catnip (cfg * config.Config ) error {
19- // allocate as much as possible as soon as possible
21+ // AppName is the app name
22+ const AppName = "catnip"
23+
24+ // AppDesc is the app description
25+ const AppDesc = "Continuous Automatic Terminal Number Interpretation Printer"
26+
27+ // AppSite is the app website
28+ const AppSite = "https://github.com/noriah/catnip"
29+
30+ var version = "unknown"
31+
32+ func main () {
33+ log .SetFlags (0 )
34+
35+ var cfg = newZeroConfig ()
2036
21- var sessConfig = input.SessionConfig {
22- FrameSize : cfg .ChannelCount ,
23- SampleSize : cfg .SampleSize ,
24- SampleRate : cfg .SampleRate ,
37+ if doFlags (& cfg ) {
38+ return
2539 }
2640
41+ chk (cfg .Sanitize (), "invalid config" )
42+
43+ chk (catnip (& cfg ), "failed to run catnip" )
44+ }
45+
46+ // Catnip starts to draw the visualizer on the termbox screen.
47+ func catnip (cfg * config ) error {
48+
2749 display := & graphic.Display {}
2850
29- inputBuffers := input .MakeBuffers (cfg .ChannelCount , cfg .SampleSize )
30- vis := visualizer .New (cfg , inputBuffers )
51+ anlzConfig := dsp.Config {
52+ SampleRate : cfg .sampleRate ,
53+ SampleSize : cfg .sampleSize ,
54+ ChannelCount : cfg .channelCount ,
55+ SmoothingFactor : cfg .smoothFactor ,
56+ }
57+
58+ inputBuffers := input .MakeBuffers (cfg .channelCount , cfg .sampleSize )
59+ // visBuffers := input.MakeBuffers(cfg.channelCount, cfg.sampleSize)
3160
32- vis .Spectrum = dsp .NewSpectrum (cfg )
33- vis .Display = display
61+ procConfig := visualizer.Config {
62+ SampleRate : cfg .sampleRate ,
63+ SampleSize : cfg .sampleSize ,
64+ ChannelCount : cfg .channelCount ,
65+ FrameRate : cfg .frameRate ,
66+ Buffers : inputBuffers ,
67+ Analyzer : dsp .NewAnalyzer (anlzConfig ),
68+ Display : display ,
69+ }
70+
71+ var vis visualizer.Visualizer
72+
73+ if cfg .useThreaded {
74+ vis = visualizer .NewThreaded (procConfig )
75+ } else {
76+ vis = visualizer .New (procConfig )
77+ }
3478
3579 // INPUT SETUP
3680
37- var backend , err = InitBackend (cfg )
81+ var backend , err = input . InitBackend (cfg . backend )
3882 if err != nil {
3983 return err
4084 }
4185
42- if sessConfig .Device , err = getDevice (backend , cfg ); err != nil {
86+ sessConfig := input.SessionConfig {
87+ FrameSize : cfg .channelCount ,
88+ SampleSize : cfg .sampleSize ,
89+ SampleRate : cfg .sampleRate ,
90+ }
91+
92+ if sessConfig .Device , err = input .GetDevice (backend , cfg .device ); err != nil {
4393 return err
4494 }
4595
@@ -55,10 +105,10 @@ func Catnip(cfg *config.Config) error {
55105 }
56106 defer display .Close ()
57107
58- display .SetSizes (cfg .BarSize , cfg .SpaceSize )
59- display .SetBase (cfg .BaseSize )
60- display .SetDrawType (graphic .DrawType (cfg .DrawType ))
61- display .SetStyles (cfg .Styles )
108+ display .SetSizes (cfg .barSize , cfg .spaceSize )
109+ display .SetBase (cfg .baseSize )
110+ display .SetDrawType (graphic .DrawType (cfg .drawType ))
111+ display .SetStyles (cfg .styles )
62112
63113 // Root Context
64114 ctx , cancel := context .WithCancel (context .Background ())
@@ -69,23 +119,50 @@ func Catnip(cfg *config.Config) error {
69119 ctx = display .Start (ctx )
70120 defer display .Stop ()
71121
72- if cfg .FrameRate > 0 {
73- go func () {
74- ticker := time .NewTicker (time .Second / time .Duration (cfg .FrameRate ))
75- defer ticker .Stop ()
76-
77- for {
78- select {
79- case <- ctx .Done ():
80- return
81- case <- ticker .C :
82- vis .Draw (true )
83- }
84- }
85- }()
86- }
122+ ctx = vis .Start (ctx )
123+ defer vis .Stop ()
124+
125+ kickChan := make (chan bool , 1 )
126+ // visChan := make(chan bool, 1)
127+
128+ mu := & sync.Mutex {}
129+
130+ // if cfg.frameRate <= 0 {
131+ // // if we do not have a framerate set, allow at most 1 second per sampling
132+ // cfg.frameRate = 1
133+ // }
134+
135+ // go func() {
136+ // dur := time.Second / time.Duration(cfg.frameRate)
137+ // ticker := time.NewTicker(dur)
138+ // defer ticker.Stop()
139+
140+ // for {
141+
142+ // // select {
143+ // // case <-ctx.Done():
144+ // // return
145+ // // case <-kickChan:
146+ // // // default:
147+ // // }
148+ // // // input.CopyBuffers(visBuffers, inputBuffers)
87149
88- if err := audio .Start (ctx , inputBuffers , vis ); err != nil {
150+ // select {
151+ // case <-ctx.Done():
152+ // return
153+ // case <-kickChan:
154+ // // case <-ticker.C:
155+ // // default:
156+ // }
157+ // vis.Process(visChan, mu)
158+
159+ // ticker.Reset(dur)
160+ // }
161+ // }()
162+
163+ go vis .Process (ctx , kickChan , mu )
164+
165+ if err := audio .Start (ctx , inputBuffers , kickChan , mu ); err != nil {
89166 if ! errors .Is (ctx .Err (), context .Canceled ) {
90167 return errors .Wrap (err , "failed to start input session" )
91168 }
@@ -94,38 +171,119 @@ func Catnip(cfg *config.Config) error {
94171 return nil
95172}
96173
97- func InitBackend (cfg * config.Config ) (input.Backend , error ) {
98- var backend = input .FindBackend (cfg .Backend )
99- if backend == nil {
100- return nil , fmt .Errorf ("backend not found: %q; check list-backends" , cfg .Backend )
174+ // NewZeroConfig returns a zero config
175+ // it is the "default"
176+ //
177+ // nora's defaults:
178+ // - sampleRate: 122880
179+ // - sampleSize: 2048
180+ // - smoothFactor: 80.15
181+ // - super smooth detail view
182+ func newZeroConfig () config {
183+ return config {
184+ backend : "portaudio" ,
185+ sampleRate : 44100 ,
186+ sampleSize : 1024 ,
187+ smoothFactor : 80.15 ,
188+ frameRate : 0 ,
189+ baseSize : 1 ,
190+ barSize : 2 ,
191+ spaceSize : 1 ,
192+ channelCount : 2 ,
193+ drawType : int (graphic .DrawDefault ),
194+ combine : false ,
195+ useThreaded : false ,
101196 }
197+ }
102198
103- if err := backend .Init (); err != nil {
104- return nil , errors .Wrap (err , "failed to initialize input backend" )
105- }
199+ func doFlags (cfg * config ) bool {
106200
107- return backend , nil
108- }
201+ var parser = flaggy .NewParser (AppName )
202+ parser .Description = AppDesc
203+ parser .AdditionalHelpPrepend = AppSite
204+ parser .Version = version
109205
110- func getDevice (backend input.Backend , cfg * config.Config ) (input.Device , error ) {
111- if cfg .Device == "" {
112- var def , err = backend .DefaultDevice ()
113- if err != nil {
114- return nil , errors .Wrap (err , "failed to get default device" )
115- }
116- return def , nil
206+ var listBackendsCmd = flaggy.Subcommand {
207+ Name : "list-backends" ,
208+ ShortName : "lb" ,
209+ Description : "list all supported backends" ,
210+ AdditionalHelpAppend : "\n use the full name after the '-'" ,
117211 }
118212
119- var devices , err = backend .Devices ()
120- if err != nil {
121- return nil , errors .Wrap (err , "failed to get devices" )
213+ parser .AttachSubcommand (& listBackendsCmd , 1 )
214+
215+ var listDevicesCmd = flaggy.Subcommand {
216+ Name : "list-devices" ,
217+ ShortName : "ld" ,
218+ Description : "list all devices for a backend" ,
219+ AdditionalHelpAppend : "\n use the full name after the '-'" ,
122220 }
123221
124- for idx := range devices {
125- if devices [idx ].String () == cfg .Device {
126- return devices [idx ], nil
222+ parser .AttachSubcommand (& listDevicesCmd , 1 )
223+
224+ parser .String (& cfg .backend , "b" , "backend" , "backend name" )
225+ parser .String (& cfg .device , "d" , "device" , "device name" )
226+ parser .Float64 (& cfg .sampleRate , "r" , "rate" , "sample rate" )
227+ parser .Int (& cfg .sampleSize , "n" , "samples" , "sample size" )
228+ parser .Int (& cfg .frameRate , "f" , "fps" , "frame rate (0 to draw on every sample)" )
229+ parser .Int (& cfg .channelCount , "ch" , "channels" , "channel count (1 or 2)" )
230+ parser .Float64 (& cfg .smoothFactor , "sf" , "smoothing" , "smooth factor (0-100)" )
231+ parser .Int (& cfg .baseSize , "bt" , "base" , "base thickness [0, +Inf)" )
232+ parser .Int (& cfg .barSize , "bw" , "bar" , "bar width [1, +Inf)" )
233+ parser .Int (& cfg .spaceSize , "sw" , "space" , "space width [0, +Inf)" )
234+ parser .Int (& cfg .drawType , "dt" , "draw" , "draw type (1, 2, 3)" )
235+ parser .Bool (& cfg .useThreaded , "t" , "threaded" , "use the threaded visualizer" )
236+
237+ fg , bg , center := graphic .DefaultStyles ().AsUInt16s ()
238+ parser .UInt16 (& fg , "fg" , "foreground" ,
239+ "foreground color within the 256-color range [0, 255] with attributes" )
240+ parser .UInt16 (& bg , "bg" , "background" ,
241+ "background color within the 256-color range [0, 255] with attributes" )
242+ parser .UInt16 (& center , "ct" , "center" ,
243+ "center line color within the 256-color range [0, 255] with attributes" )
244+
245+ chk (parser .Parse (), "failed to parse arguments" )
246+
247+ // Manually set the styles.
248+ cfg .styles = graphic .StylesFromUInt16 (fg , bg , center )
249+
250+ switch {
251+ case listBackendsCmd .Used :
252+ for _ , backend := range input .Backends {
253+ fmt .Printf ("- %s\n " , backend .Name )
127254 }
255+
256+ return true
257+
258+ case listDevicesCmd .Used :
259+ backend , err := input .InitBackend (cfg .backend )
260+ chk (err , "failed to init backend" )
261+
262+ devices , err := backend .Devices ()
263+ chk (err , "failed to get devices" )
264+
265+ // We don't really need the default device to be indicated.
266+ defaultDevice , _ := backend .DefaultDevice ()
267+
268+ fmt .Printf ("all devices for %q backend. '*' marks default\n " , cfg .backend )
269+
270+ for idx := range devices {
271+ var star = ' '
272+ if defaultDevice != nil && devices [idx ].String () == defaultDevice .String () {
273+ star = '*'
274+ }
275+
276+ fmt .Printf ("- %v %c\n " , devices [idx ], star )
277+ }
278+
279+ return true
128280 }
129281
130- return nil , errors .Errorf ("device %q not found; check list-devices" , cfg .Device )
282+ return false
283+ }
284+
285+ func chk (err error , wrap string ) {
286+ if err != nil {
287+ log .Fatalln (wrap + ": " , err )
288+ }
131289}
0 commit comments