Доработка звука
This commit is contained in:
158
internal/audio/monitor.go
Normal file
158
internal/audio/monitor.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package audio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Monitor struct {
|
||||
mu sync.RWMutex
|
||||
currentLevel int
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewMonitor() *Monitor {
|
||||
return &Monitor{}
|
||||
}
|
||||
|
||||
func (m *Monitor) GetLevel() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.currentLevel
|
||||
}
|
||||
|
||||
func (m *Monitor) Start() error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
m.cancel = cancel
|
||||
|
||||
// Используем гибкий поиск USB устройств
|
||||
device := m.findUSBDevice()
|
||||
log.Printf("Используем аудиоустройство: %s", device)
|
||||
|
||||
cmd := exec.CommandContext(ctx, "arecord",
|
||||
"-D", device,
|
||||
"-t", "raw",
|
||||
"-c", "1",
|
||||
"-r", "16000",
|
||||
"-f", "S16_LE")
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Аудио-монитор запущен с устройством: %s", device)
|
||||
|
||||
go func() {
|
||||
buffer := make([]byte, 3200)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
_, err := io.ReadFull(stdout, buffer)
|
||||
if err != nil {
|
||||
log.Printf("Ошибка чтения аудио: %v", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
samplesCount := len(buffer) / 2
|
||||
samples := make([]int16, samplesCount)
|
||||
reader := bytes.NewReader(buffer)
|
||||
if err := binary.Read(reader, binary.LittleEndian, &samples); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var sum float64
|
||||
for _, sample := range samples {
|
||||
val := float64(sample)
|
||||
sum += val * val
|
||||
}
|
||||
|
||||
rms := math.Sqrt(sum / float64(samplesCount))
|
||||
level := int((rms / 32768.0) * 100 * 4)
|
||||
if level > 100 {
|
||||
level = 100
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
m.currentLevel = level
|
||||
m.mu.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_ = cmd.Wait()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Monitor) findUSBDevice() string {
|
||||
cmd := exec.Command("arecord", "-l")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Printf("Не удалось получить список устройств: %v", err)
|
||||
return "default"
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
// Ищем любое USB аудиоустройство
|
||||
lowerLine := strings.ToLower(line)
|
||||
if strings.Contains(lowerLine, "usb") &&
|
||||
(strings.Contains(lowerLine, "audio") || strings.Contains(lowerLine, "sound")) {
|
||||
|
||||
log.Printf("Найдено USB аудиоустройство: %s", line)
|
||||
|
||||
// Извлекаем номера карты и устройства
|
||||
re := regexp.MustCompile(`card (\d+):.*device (\d+):`)
|
||||
matches := re.FindStringSubmatch(line)
|
||||
if len(matches) == 3 {
|
||||
device := fmt.Sprintf("plughw:%s,%s", matches[1], matches[2])
|
||||
log.Printf("Используем устройство: %s", device)
|
||||
return device
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Если не нашли USB, пробуем найти любое устройство захвата
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "device") && strings.Contains(line, "Capture") {
|
||||
re := regexp.MustCompile(`card (\d+):.*device (\d+):`)
|
||||
matches := re.FindStringSubmatch(line)
|
||||
if len(matches) == 3 {
|
||||
device := fmt.Sprintf("plughw:%s,%s", matches[1], matches[2])
|
||||
log.Printf("Используем первое найденное устройство: %s", device)
|
||||
return device
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Аудиоустройства не найдены, используем default")
|
||||
return "default"
|
||||
}
|
||||
|
||||
func (m *Monitor) Stop() {
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user