From 274fe279d8c321feacdd2a3e5fd759e68d2da757 Mon Sep 17 00:00:00 2001 From: legop3 Date: Fri, 26 Jun 2026 13:47:23 -0400 Subject: [PATCH] trying stereo audio! --- pi/bin/audio-only-publisher.sh | 67 ++++++++++++++++++++++------------ pi/install_roverd.sh | 2 - pi/roverd/config.go | 15 -------- pi/roverd/media_env.go | 8 ---- pi/roverd/roverd.sample.yaml | 3 -- 5 files changed, 44 insertions(+), 51 deletions(-) diff --git a/pi/bin/audio-only-publisher.sh b/pi/bin/audio-only-publisher.sh index 0cf7f73f..a5a9cbe4 100644 --- a/pi/bin/audio-only-publisher.sh +++ b/pi/bin/audio-only-publisher.sh @@ -19,8 +19,6 @@ fi : "${AUDIO_PUBLISH_URL:?AUDIO_PUBLISH_URL not set in ${ENV_FILE}}" AUDIO_DEVICE="${AUDIO_DEVICE:-hw:0,0}" -AUDIO_RATE="${AUDIO_RATE:-48000}" -AUDIO_CHANNELS="${AUDIO_CHANNELS:-2}" if [[ -n "${FFMPEG_BIN:-}" ]]; then FFMPEG_BIN_PATH="$FFMPEG_BIN" @@ -32,27 +30,50 @@ else fi run_pipeline() { - arecord -D "${AUDIO_DEVICE}" -f S32_LE -c "${AUDIO_CHANNELS}" -r "${AUDIO_RATE}" -B 65536 -F 2048 -q -t raw \ - | "${FFMPEG_BIN_PATH}" \ - -hide_banner \ - -loglevel warning \ - -fflags nobuffer \ - -rtbufsize 0 \ - -thread_queue_size 4096 \ - -f s32le \ - -ar "${AUDIO_RATE}" \ - -ac "${AUDIO_CHANNELS}" \ - -i pipe:0 \ - -af "aresample=16000,pan=mono|c0=0.5*FL+0.5*FR,volume=25dB" \ - -c:a libopus \ - -b:a 24000 \ - -ar:a 16000 \ - -ac:a 1 \ - -application lowdelay \ - -frame_duration 20 \ - -compression_level 0 \ - -f mpegts \ - "${AUDIO_PUBLISH_URL}" + local ffmpeg_args=( + -hide_banner + -loglevel warning + -fflags nobuffer + -rtbufsize 0 + -thread_queue_size 4096 + + # Match the raw ALSA stream exactly so ffmpeg does not guess the pipe + # format and so the published audio stays full-band stereo before the + # Opus encoder sees it. + -f s32le + -ar 48000 + -ac 2 + -i pipe:0 + + # Keep the known-required microphone boost, but remove the old + # resample/downmix filter. That old filter threw away stereo and + # limited the stream to narrow 16 kHz mono before encoding. + -af "volume=25dB" + + # Opus supports up to 510 kbps. Using that ceiling keeps the stream at + # the highest practical quality browsers and MediaMTX can carry without + # trying to push raw PCM through the live path. + -c:a libopus + -b:a 510000 + -ar:a 48000 + -ac:a 2 + + # Use the audio profile for quality. Latency is still controlled by the + # 20 ms Opus frame size and the low-buffering capture/publish options + # around the encoder. + -application audio + -frame_duration 20 + -compression_level 0 + -f mpegts + "${AUDIO_PUBLISH_URL}" + ) + + # The Google Voice HAT microphone path is intentionally fixed instead of + # configurable. The previous env-driven sample rate/channel knobs made it + # easy for the rover config and the actual ffmpeg pipeline to drift apart, + # while the hardware path we install is always 48 kHz stereo capture. + arecord -D "${AUDIO_DEVICE}" -f S32_LE -c 2 -r 48000 -B 65536 -F 2048 -q -t raw \ + | "${FFMPEG_BIN_PATH}" "${ffmpeg_args[@]}" } trap 'kill 0 2>/dev/null' EXIT INT TERM diff --git a/pi/install_roverd.sh b/pi/install_roverd.sh index 1ab262f3..dd6b0db9 100755 --- a/pi/install_roverd.sh +++ b/pi/install_roverd.sh @@ -321,8 +321,6 @@ VIDEO_BITRATE=2000000 AUDIO_ENABLE=0 AUDIO_DEVICE=hw:0,0 AUDIO_PLAYBACK_DEVICE=forward -AUDIO_RATE=48000 -AUDIO_CHANNELS=2 ENV chown roverd:roverd /var/lib/roverd/video.env chmod 0640 /var/lib/roverd/video.env diff --git a/pi/roverd/config.go b/pi/roverd/config.go index 7bf40e14..77860b9b 100644 --- a/pi/roverd/config.go +++ b/pi/roverd/config.go @@ -59,9 +59,6 @@ type AudioConfig struct { CaptureEnabled bool `yaml:"captureEnabled" json:"captureEnabled"` CaptureDevice string `yaml:"captureDevice" json:"captureDevice,omitempty"` PlaybackDevice string `yaml:"playbackDevice" json:"playbackDevice,omitempty"` - SampleRate int `yaml:"sampleRate" json:"sampleRate,omitempty"` - Channels int `yaml:"channels" json:"channels,omitempty"` - Bitrate int `yaml:"bitrate" json:"bitrate,omitempty"` TTSEnabled bool `yaml:"ttsEnabled" json:"ttsEnabled"` DefaultEngine string `yaml:"defaultEngine" json:"defaultEngine,omitempty"` DefaultVoice string `yaml:"defaultVoice" json:"defaultVoice,omitempty"` @@ -199,9 +196,6 @@ func LoadConfig(path string) (*Config, error) { CaptureEnabled: false, CaptureDevice: "rovermic", PlaybackDevice: "forward", - SampleRate: 48000, - Channels: 2, - Bitrate: 24000, TTSEnabled: false, DefaultEngine: "flite", DefaultVoice: "rms", @@ -364,15 +358,6 @@ func validateAudioConfig(cfg *AudioConfig) { if cfg.PlaybackDevice == "" || cfg.PlaybackDevice == "default" { cfg.PlaybackDevice = "forward" } - if cfg.SampleRate <= 0 { - cfg.SampleRate = 48000 - } - if cfg.Channels <= 0 { - cfg.Channels = 2 - } - if cfg.Bitrate <= 0 { - cfg.Bitrate = 64000 - } if cfg.DefaultEngine == "" { cfg.DefaultEngine = "flite" } diff --git a/pi/roverd/media_env.go b/pi/roverd/media_env.go index 66fa636b..4e3a2abb 100644 --- a/pi/roverd/media_env.go +++ b/pi/roverd/media_env.go @@ -45,12 +45,6 @@ func UpdatePublisherEnv(media MediaConfig, audio AudioConfig) error { if audioDevice == "" || audioDevice == "rovermic" { audioDevice = "hw:0,0" } - if audio.SampleRate <= 0 { - audio.SampleRate = 48000 - } - if audio.Channels <= 0 { - audio.Channels = 2 - } fmt.Fprintf(&buf, "AUDIO_ENABLE=%d\n", boolToInt(audio.CaptureEnabled)) fmt.Fprintf(&buf, "AUDIO_DEVICE=%s\n", audioDevice) playbackDevice := audio.PlaybackDevice @@ -58,8 +52,6 @@ func UpdatePublisherEnv(media MediaConfig, audio AudioConfig) error { playbackDevice = "forward" } fmt.Fprintf(&buf, "AUDIO_PLAYBACK_DEVICE=%s\n", playbackDevice) - fmt.Fprintf(&buf, "AUDIO_RATE=%d\n", audio.SampleRate) - fmt.Fprintf(&buf, "AUDIO_CHANNELS=%d\n", audio.Channels) if err := os.WriteFile(publisherEnvPath, buf.Bytes(), 0o640); err != nil { return err } diff --git a/pi/roverd/roverd.sample.yaml b/pi/roverd/roverd.sample.yaml index 15c5b236..cd2b8d66 100644 --- a/pi/roverd/roverd.sample.yaml +++ b/pi/roverd/roverd.sample.yaml @@ -44,9 +44,6 @@ audio: captureEnabled: false captureDevice: hw:0,0 playbackDevice: forward - sampleRate: 48000 - channels: 2 - bitrate: 24000 ttsEnabled: false defaultEngine: flite defaultVoice: rms