Przeglądaj źródła

Add buffered duration metric for audio stream stats

tags/v0.9.0
Jan Svabenik 1 miesiąc temu
rodzic
commit
1d20e798d1
3 zmienionych plików z 45 dodań i 12 usunięć
  1. +3
    -0
      docs/API.md
  2. +26
    -12
      internal/audio/stream.go
  3. +16
    -0
      internal/audio/stream_test.go

+ 3
- 0
docs/API.md Wyświetl plik

@@ -298,6 +298,7 @@ Requires `--audio-stdin`, `--audio-http`, or another configured stream source to
"available": 12000, "available": 12000,
"capacity": 131072, "capacity": 131072,
"buffered": 0.09, "buffered": 0.09,
"bufferedDurationSeconds": 0.27,
"written": 890000, "written": 890000,
"underruns": 0, "underruns": 0,
"overflows": 0 "overflows": 0
@@ -366,6 +367,7 @@ The stream uses a lock-free ring buffer (default: 2 seconds at input rate). Buff
"available": 12000, "available": 12000,
"capacity": 131072, "capacity": 131072,
"buffered": 0.09, "buffered": 0.09,
"bufferedDurationSeconds": 0.27,
"written": 890000, "written": 890000,
"underruns": 0, "underruns": 0,
"overflows": 0 "overflows": 0
@@ -376,5 +378,6 @@ The stream uses a lock-free ring buffer (default: 2 seconds at input rate). Buff
- **underruns**: DSP consumed faster than audio arrived (silence inserted) - **underruns**: DSP consumed faster than audio arrived (silence inserted)
- **overflows**: Audio arrived faster than DSP consumed (data dropped) - **overflows**: Audio arrived faster than DSP consumed (data dropped)
- **buffered**: Fill ratio (0.0 = empty, 1.0 = full) - **buffered**: Fill ratio (0.0 = empty, 1.0 = full)
- **bufferedDurationSeconds**: Approximate seconds of audio queued in the buffer (`available` frames divided by the sample rate)


When no audio is streaming, the transmitter falls back to the configured tone generator or silence. When no audio is streaming, the transmitter falls back to the configured tone generator or silence.

+ 26
- 12
internal/audio/stream.go Wyświetl plik

@@ -109,24 +109,38 @@ func (s *StreamSource) Buffered() float64 {


// Stats returns diagnostic counters. // Stats returns diagnostic counters.
func (s *StreamSource) Stats() StreamStats { func (s *StreamSource) Stats() StreamStats {
available := s.Available()
buffered := 0.0
if s.size > 0 {
buffered = float64(available) / float64(s.size)
}
return StreamStats{ return StreamStats{
Available: s.Available(),
Capacity: s.size,
Buffered: s.Buffered(),
Written: s.Written.Load(),
Underruns: s.Underruns.Load(),
Overflows: s.Overflows.Load(),
Available: available,
Capacity: s.size,
Buffered: buffered,
BufferedDurationSeconds: s.bufferedDurationSeconds(available),
Written: s.Written.Load(),
Underruns: s.Underruns.Load(),
Overflows: s.Overflows.Load(),
} }
} }


// StreamStats exposes runtime telemetry for the stream buffer. // StreamStats exposes runtime telemetry for the stream buffer.
type StreamStats struct { type StreamStats struct {
Available int `json:"available"`
Capacity int `json:"capacity"`
Buffered float64 `json:"buffered"`
Written uint64 `json:"written"`
Underruns uint64 `json:"underruns"`
Overflows uint64 `json:"overflows"`
Available int `json:"available"`
Capacity int `json:"capacity"`
Buffered float64 `json:"buffered"`
BufferedDurationSeconds float64 `json:"bufferedDurationSeconds"`
Written uint64 `json:"written"`
Underruns uint64 `json:"underruns"`
Overflows uint64 `json:"overflows"`
}

func (s *StreamSource) bufferedDurationSeconds(available int) float64 {
if s.SampleRate <= 0 {
return 0
}
return float64(available) / float64(s.SampleRate)
} }


// --- StreamResampler --- // --- StreamResampler ---


+ 16
- 0
internal/audio/stream_test.go Wyświetl plik

@@ -205,6 +205,22 @@ func TestStreamSource_ConcurrentSPSC(t *testing.T) {
} }
} }


func TestStreamSource_StatsBufferedDuration(t *testing.T) {
rate := 48000
s := NewStreamSource(128, rate)
for i := 0; i < 24; i++ {
s.WriteFrame(NewFrame(0, 0))
}
stats := s.Stats()
if stats.BufferedDurationSeconds <= 0 {
t.Fatalf("expected buffered duration > 0, got %.6f", stats.BufferedDurationSeconds)
}
expected := float64(stats.Available) / float64(rate)
if math.Abs(stats.BufferedDurationSeconds-expected) > 1e-9 {
t.Fatalf("buffered duration %.9f != expected %.9f", stats.BufferedDurationSeconds, expected)
}
}

// --- StreamResampler tests --- // --- StreamResampler tests ---


func TestStreamResampler_1to1(t *testing.T) { func TestStreamResampler_1to1(t *testing.T) {


Ładowanie…
Anuluj
Zapisz