Kaynağa Gözat

feat: report active offline source and fallback state

tags/v0.4.0-pre
Jan Svabenik 1 ay önce
ebeveyn
işleme
47b46cb7cc
3 değiştirilmiş dosya ile 28 ekleme ve 8 silme
  1. +2
    -0
      docs/README.md
  2. +16
    -8
      internal/offline/generator.go
  3. +10
    -0
      internal/offline/generator_test.go

+ 2
- 0
docs/README.md Dosyayı Görüntüle

@@ -16,6 +16,7 @@ Current no-hardware sources:
- generated stereo tones via config
- 16-bit PCM WAV file input via `audio.inputPath`
- basic sample-rate adaptation for WAV sources into the composite generation path
- transparent tone fallback if the configured WAV source cannot be loaded

### Tone configuration

@@ -67,6 +68,7 @@ This is the current closest no-hardware stand-in for the future transmit pipelin

`cmd/offline` generates a deterministic no-hardware IQ/composite-style file using the repository's output backend path.
This is still an MVP path, but it is a more realistic offline artifact than the JSON-only dry-run.
The generator summary now reports whether the active source is tones, wav, or tone-fallback.

## Release posture



+ 16
- 8
internal/offline/generator.go Dosyayı Görüntüle

@@ -20,6 +20,12 @@ type frameSource interface {
NextFrame() audio.Frame
}

type SourceInfo struct {
Kind string
SampleRate float64
Detail string
}

type Generator struct {
cfg cfgpkg.Config
}
@@ -28,13 +34,14 @@ func NewGenerator(cfg cfgpkg.Config) *Generator {
return &Generator{cfg: cfg}
}

func (g *Generator) sourceFor(sampleRate float64) frameSource {
func (g *Generator) sourceFor(sampleRate float64) (frameSource, SourceInfo) {
if g.cfg.Audio.InputPath != "" {
if src, err := audio.LoadWAVSource(g.cfg.Audio.InputPath); err == nil {
return audio.NewResampledSource(src, sampleRate)
return audio.NewResampledSource(src, sampleRate), SourceInfo{Kind: "wav", SampleRate: float64(src.SampleRate), Detail: g.cfg.Audio.InputPath}
}
return audio.NewConfiguredToneSource(sampleRate, g.cfg.Audio.ToneLeftHz, g.cfg.Audio.ToneRightHz, g.cfg.Audio.ToneAmplitude), SourceInfo{Kind: "tone-fallback", SampleRate: sampleRate, Detail: g.cfg.Audio.InputPath}
}
return audio.NewConfiguredToneSource(sampleRate, g.cfg.Audio.ToneLeftHz, g.cfg.Audio.ToneRightHz, g.cfg.Audio.ToneAmplitude)
return audio.NewConfiguredToneSource(sampleRate, g.cfg.Audio.ToneLeftHz, g.cfg.Audio.ToneRightHz, g.cfg.Audio.ToneAmplitude), SourceInfo{Kind: "tones", SampleRate: sampleRate, Detail: "generated"}
}

func (g *Generator) GenerateFrame(duration time.Duration) *output.CompositeFrame {
@@ -68,7 +75,7 @@ func (g *Generator) GenerateFrame(duration time.Duration) *output.CompositeFrame
})
rdsSamples := rdsEnc.Generate(samples)

source := g.sourceFor(sampleRate)
source, _ := g.sourceFor(sampleRate)

for i := 0; i < samples; i++ {
t := float64(i) / sampleRate
@@ -121,9 +128,10 @@ func (g *Generator) WriteFile(path string, duration time.Duration) error {
}

func (g *Generator) Summary(duration time.Duration) string {
source := "tones"
if g.cfg.Audio.InputPath != "" {
source = g.cfg.Audio.InputPath
sampleRate := float64(g.cfg.FM.CompositeRateHz)
if sampleRate <= 0 {
sampleRate = 228000
}
return fmt.Sprintf("offline frame: freq=%.1fMHz sampleRate=%d duration=%s outputDrive=%.2f stereo=%t rds=%t source=%s", g.cfg.FM.FrequencyMHz, g.cfg.FM.CompositeRateHz, duration.String(), g.cfg.FM.OutputDrive, g.cfg.FM.StereoEnabled, g.cfg.RDS.Enabled, source)
_, info := g.sourceFor(sampleRate)
return fmt.Sprintf("offline frame: freq=%.1fMHz sampleRate=%d duration=%s outputDrive=%.2f stereo=%t rds=%t source=%s detail=%s", g.cfg.FM.FrequencyMHz, g.cfg.FM.CompositeRateHz, duration.String(), g.cfg.FM.OutputDrive, g.cfg.FM.StereoEnabled, g.cfg.RDS.Enabled, info.Kind, info.Detail)
}

+ 10
- 0
internal/offline/generator_test.go Dosyayı Görüntüle

@@ -47,3 +47,13 @@ func TestSummaryUsesToneFallback(t *testing.T) {
t.Fatalf("unexpected summary: %s", summary)
}
}

func TestSummaryUsesFallbackLabelOnBadWAV(t *testing.T) {
cfg := cfgpkg.Default()
cfg.Audio.InputPath = "missing.wav"
g := NewGenerator(cfg)
summary := g.Summary(10 * time.Millisecond)
if !strings.Contains(summary, "source=tone-fallback") {
t.Fatalf("unexpected summary: %s", summary)
}
}

Yükleniyor…
İptal
Kaydet