Skip to content

Commit 1ee01f5

Browse files
committed
smoother drawing without fps flag
1 parent ac43b45 commit 1ee01f5

File tree

15 files changed

+987
-697
lines changed

15 files changed

+987
-697
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
/catnip
44
.vscode/
55
/profile.go
6+
/prof.go

catnip.go

Lines changed: 217 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,95 @@
1-
package catnip
1+
package main
22

33
import (
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: "\nuse 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: "\nuse 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

Comments
 (0)