feat: Implement TeamSpeak 3 client with connection management, event handling, and audio support.
This commit is contained in:
100
pkg/audio/level.go
Normal file
100
pkg/audio/level.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package audio
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// CalculateRMSLevel calculates the RMS level of PCM samples and returns 0-100
|
||||
func CalculateRMSLevel(samples []int16) int {
|
||||
if len(samples) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var sum float64
|
||||
for _, s := range samples {
|
||||
sum += float64(s) * float64(s)
|
||||
}
|
||||
|
||||
rms := math.Sqrt(sum / float64(len(samples)))
|
||||
// Normalize to 0-100 (max int16 is 32767)
|
||||
level := int(rms / 32767.0 * 100.0)
|
||||
if level > 100 {
|
||||
level = 100
|
||||
}
|
||||
return level
|
||||
}
|
||||
|
||||
// CalculatePeakLevel returns the peak level of PCM samples as 0-100
|
||||
func CalculatePeakLevel(samples []int16) int {
|
||||
if len(samples) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var peak int16
|
||||
for _, s := range samples {
|
||||
if s < 0 {
|
||||
s = -s
|
||||
}
|
||||
if s > peak {
|
||||
peak = s
|
||||
}
|
||||
}
|
||||
|
||||
return int(float64(peak) / 32767.0 * 100.0)
|
||||
}
|
||||
|
||||
// LevelToBar converts a 0-100 level to a visual bar string
|
||||
func LevelToBar(level, width int) string {
|
||||
if level < 0 {
|
||||
level = 0
|
||||
}
|
||||
if level > 100 {
|
||||
level = 100
|
||||
}
|
||||
|
||||
filled := level * width / 100
|
||||
empty := width - filled
|
||||
|
||||
bar := ""
|
||||
for i := 0; i < filled; i++ {
|
||||
bar += "█"
|
||||
}
|
||||
for i := 0; i < empty; i++ {
|
||||
bar += "░"
|
||||
}
|
||||
|
||||
return bar
|
||||
}
|
||||
|
||||
// LevelToMeter converts a 0-100 level to a visual VU meter with varying heights
|
||||
func LevelToMeter(level, width int) string {
|
||||
if level < 0 {
|
||||
level = 0
|
||||
}
|
||||
if level > 100 {
|
||||
level = 100
|
||||
}
|
||||
|
||||
// Use block characters of varying heights
|
||||
blocks := []rune{'░', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'}
|
||||
|
||||
meter := ""
|
||||
for i := 0; i < width; i++ {
|
||||
// Each position represents a portion of the level
|
||||
threshold := (i + 1) * 100 / width
|
||||
if level >= threshold {
|
||||
meter += string(blocks[8]) // Full
|
||||
} else if level >= threshold-10 {
|
||||
// Partial - calculate which block to use
|
||||
partial := (level - (threshold - 10)) * 8 / 10
|
||||
if partial < 0 {
|
||||
partial = 0
|
||||
}
|
||||
meter += string(blocks[partial])
|
||||
} else {
|
||||
meter += string(blocks[0]) // Empty
|
||||
}
|
||||
}
|
||||
|
||||
return meter
|
||||
}
|
||||
Reference in New Issue
Block a user