101 lines
1.9 KiB
Go
101 lines
1.9 KiB
Go
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
|
|
}
|