mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
audio
This commit is contained in:
+52
-5
@@ -3,6 +3,7 @@ package roverd
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -47,29 +48,75 @@ func (c *WSClient) handleTTSPayload(ctx context.Context, payload *ttsPayload) er
|
||||
runCtx, cancel := context.WithTimeout(ctx, 12*time.Second)
|
||||
defer cancel()
|
||||
|
||||
tmp, err := os.CreateTemp("", "roverd-tts-*.wav")
|
||||
if err != nil {
|
||||
return fmt.Errorf("tts temp file: %w", err)
|
||||
}
|
||||
tmpPath := tmp.Name()
|
||||
_ = tmp.Close()
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
if err := synthTTS(runCtx, engine, voice, pitch, text, tmpPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := playTTSFile(runCtx, tmpPath, c.cfg.Audio.PlaybackDevice, c.getAudioLevels().TTSGain); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func synthTTS(ctx context.Context, engine, voice string, pitch int, text, outputWavPath string) error {
|
||||
var cmd *exec.Cmd
|
||||
switch engine {
|
||||
case "espeak", "e":
|
||||
args := []string{}
|
||||
args := []string{"-w", outputWavPath}
|
||||
if pitch > 0 {
|
||||
args = append(args, "-p", fmt.Sprintf("%d", pitch))
|
||||
}
|
||||
args = append(args, text)
|
||||
cmd = exec.CommandContext(runCtx, "espeak", args...)
|
||||
cmd = exec.CommandContext(ctx, "espeak", args...)
|
||||
case "flite", "f":
|
||||
args := []string{}
|
||||
if voice != "" {
|
||||
args = append(args, "-voice", voice)
|
||||
}
|
||||
args = append(args, "-t", text)
|
||||
cmd = exec.CommandContext(runCtx, "flite", args...)
|
||||
args = append(args, "-t", text, "-o", outputWavPath)
|
||||
cmd = exec.CommandContext(ctx, "flite", args...)
|
||||
default:
|
||||
return fmt.Errorf("unsupported tts engine: %s", engine)
|
||||
}
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("tts exec failed: %w (%s)", err, string(out))
|
||||
return fmt.Errorf("tts synth failed: %w (%s)", err, string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func playTTSFile(ctx context.Context, wavPath, playbackDevice string, gain float64) error {
|
||||
if playbackDevice == "" {
|
||||
playbackDevice = "default"
|
||||
}
|
||||
gain = clampAudioGain(gain)
|
||||
if gain == 0 {
|
||||
// Mute is an explicit value users may choose.
|
||||
return nil
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-hide_banner",
|
||||
"-loglevel", "warning",
|
||||
"-i", wavPath,
|
||||
"-af", fmt.Sprintf("aresample=16000,volume=%g", gain),
|
||||
"-ac", "1",
|
||||
"-ar", "16000",
|
||||
"-f", "alsa",
|
||||
playbackDevice,
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, "ffmpeg", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("tts playback failed: %w (%s)", err, string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user