diag(audio): say why the Listening device is silent
"I turned the sound back on and nothing comes out of the speakers", with a log that reports success at every step: the network audio stream up, 664-byte packets arriving, the monitor started. The render goroutine's error was thrown away — a device unplugged, renamed by Windows or unable to open at 16 kHz fails exactly there, silently. It is logged now, with the device id. Two once-only lines either side of it say whether the decoded audio is reaching a running monitor or arriving with nobody listening, which separates a device fault from the speakers simply being switched off.
This commit is contained in:
@@ -15,6 +15,12 @@ type Manager struct {
|
||||
mu sync.Mutex
|
||||
recStop chan struct{}
|
||||
recDone chan recResult
|
||||
// Said once each: "the audio is reaching the speakers" and "it is arriving
|
||||
// with nobody listening". Both are answers to the same evening — sound
|
||||
// switched on, nothing out of the speakers — and neither is worth a line per
|
||||
// packet at fifty packets a second.
|
||||
gotAudioOnce sync.Once
|
||||
noSinkOnce sync.Once
|
||||
// monGainPct scales what the RX monitor plays, 100 = as captured.
|
||||
//
|
||||
// The "From radio" slider used to reach only the QSO recorder, so an
|
||||
@@ -224,6 +230,10 @@ func (m *Manager) StartMonitor(inputDev, outputDev string) error {
|
||||
return m.startMonitor(inputDev, outputDev, true)
|
||||
}
|
||||
|
||||
// Logf receives this package's diagnostic lines. Set to applog.Printf by the
|
||||
// app; a no-op in tests and for anything that vendors the package alone.
|
||||
var Logf = func(string, ...any) {}
|
||||
|
||||
// StartMonitorSink starts ONLY the render side (no USB capture) so an external
|
||||
// producer — the network 50003 stream — can feed decoded RX PCM via
|
||||
// PushMonitorAudio. Same output path as StartMonitor, minus the capture goroutine.
|
||||
@@ -255,8 +265,17 @@ func (m *Manager) startMonitor(inputDev, outputDev string, capture bool) error {
|
||||
}()
|
||||
}
|
||||
// Consumer: render the ring to the output device at the internal 16 kHz mono.
|
||||
//
|
||||
// The error was thrown away, and that is the whole of "I turned the sound on
|
||||
// and nothing comes out": a Listening device that has been unplugged, renamed
|
||||
// by Windows or cannot open at 16 kHz fails here, silently, while everything
|
||||
// upstream reports success — the stream is up, the packets arrive, the
|
||||
// monitor says it started. Said out loud, the operator knows to look at the
|
||||
// device rather than at the radio.
|
||||
go func() {
|
||||
_ = renderStream(outputDev, sampleRate, channels, bitsPerSample, stop, ring)
|
||||
if err := renderStream(outputDev, sampleRate, channels, bitsPerSample, stop, ring); err != nil {
|
||||
Logf("audio: the Listening device could not be opened (%q): %v", outputDev, err)
|
||||
}
|
||||
}()
|
||||
m.notify()
|
||||
return nil
|
||||
@@ -321,9 +340,19 @@ func (m *Manager) PushMonitorAudio(pcm []byte) {
|
||||
m.mu.Lock()
|
||||
ring := m.monRing
|
||||
m.mu.Unlock()
|
||||
if ring != nil {
|
||||
ring.Push(pcm)
|
||||
if ring == nil {
|
||||
// Nothing is listening: the stream is feeding the recorder and the voice
|
||||
// keyer only. Said ONCE, because the alternative — silence in the log for
|
||||
// silence in the speakers — is what makes this take an evening to find.
|
||||
m.noSinkOnce.Do(func() {
|
||||
Logf("audio: network RX audio is arriving but no monitor is running — the speakers are off (Listening)")
|
||||
})
|
||||
return
|
||||
}
|
||||
m.gotAudioOnce.Do(func() {
|
||||
Logf("audio: network RX audio reaching the Listening device (%d-byte chunks)", len(pcm))
|
||||
})
|
||||
ring.Push(pcm)
|
||||
}
|
||||
|
||||
// ---- TX audio passthrough (Phase 3: live mic → rig over USB) --------------
|
||||
|
||||
Reference in New Issue
Block a user