]> Sergey Matveev's repositories - vors.git/blob - cmd/client/stats.go
Shorter flags
[vors.git] / cmd / client / stats.go
1 // VoRS -- Vo(IP) Really Simple
2 // Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU Affero General Public License for more details.
12 //
13 // You should have received a copy of the GNU Affero General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package main
17
18 import (
19         "fmt"
20         "math"
21         "strings"
22         "sync/atomic"
23         "time"
24
25         "github.com/dustin/go-humanize"
26         "github.com/jroimartin/gocui"
27         vors "go.stargrave.org/vors/v3/internal"
28 )
29
30 type Stats struct {
31         pkts    int64
32         bytes   uint64
33         bads    int64
34         lost    int64
35         reorder int64
36         last    time.Time
37         vol     uint64
38         volN    uint64
39         dead    chan struct{}
40 }
41
42 func (stats *Stats) AddRMS(pcm []int16) {
43         var vol uint64
44         for _, s := range pcm {
45                 vol += uint64(int64(s) * int64(s))
46         }
47         atomic.AddUint64(&stats.vol, vol)
48         atomic.AddUint64(&stats.volN, uint64(len(pcm)))
49 }
50
51 func statsDrawer(s *Stream) {
52         var err error
53         tick := time.Tick(vors.ScreenRefresh)
54         var now time.Time
55         var v *gocui.View
56         var vol, volN float64
57         var maxRMS float64
58         var rep int
59         for {
60                 select {
61                 case <-s.stats.dead:
62                         GUI.DeleteView(s.name)
63                         GUI.DeleteView(s.name + "-vol")
64                         return
65                 case now = <-tick:
66                         l := fmt.Sprintf(
67                                 " %s  |  %s  |  S/B/L/R: %s/%s/%s/%s",
68                                 humanize.Comma(s.stats.pkts),
69                                 humanize.IBytes(s.stats.bytes),
70                                 humanize.Comma(int64(s.actr-(s.ctr&0x00FFFF))/50),
71                                 humanize.Comma(s.stats.bads),
72                                 humanize.Comma(s.stats.lost),
73                                 humanize.Comma(s.stats.reorder),
74                         )
75                         peerFlags := []string{" ", " ", " "}
76                         if s.name == *Name && Muted {
77                                 peerFlags[0] = vors.CRed + "M" + vors.CReset
78                         } else {
79                                 if s.silenced {
80                                         peerFlags[2] = vors.CMagenta + "S" + vors.CReset
81                                 }
82                                 if s.stats.last.Add(vors.ScreenRefresh).After(now) {
83                                         peerFlags[1] = vors.CGreen + "T" + vors.CReset
84                                 }
85                                 if s.muted {
86                                         peerFlags[0] = vors.CRed + "M" + vors.CReset
87                                 }
88                         }
89                         l = strings.Join(peerFlags, "") + l
90                         v, err = GUI.View(s.name)
91                         if err == nil {
92                                 v.Clear()
93                                 v.Write([]byte(l))
94                         }
95                         vol = float64(atomic.SwapUint64(&s.stats.vol, 0))
96                         volN = float64(atomic.SwapUint64(&s.stats.volN, 0))
97                         v, err = GUI.View(s.name + "-vol")
98                         if err == nil {
99                                 v.Clear()
100                                 if volN == 0 {
101                                         continue
102                                 }
103                                 vol = math.Sqrt(vol / volN)
104                                 if vol/4 > maxRMS {
105                                         maxRMS = vol / 4
106                                 }
107                                 rep = int(float64(GUIMaxY) * vol / maxRMS)
108                                 if rep < 0 {
109                                         rep = 0
110                                 }
111                                 v.Write([]byte(strings.Repeat("▒", rep)))
112                         }
113                 }
114         }
115 }