| @@ -14,12 +14,16 @@ func main() { | |||||
| configPath := flag.String("config", "", "path to JSON config") | configPath := flag.String("config", "", "path to JSON config") | ||||
| out := flag.String("output", "", "output IQ file path") | out := flag.String("output", "", "output IQ file path") | ||||
| duration := flag.Duration("duration", 2*time.Second, "generation duration") | duration := flag.Duration("duration", 2*time.Second, "generation duration") | ||||
| outputRate := flag.Int("output-rate", 0, "override composite output sample rate") | |||||
| flag.Parse() | flag.Parse() | ||||
| cfg, err := cfgpkg.Load(*configPath) | cfg, err := cfgpkg.Load(*configPath) | ||||
| if err != nil { | if err != nil { | ||||
| log.Fatalf("load config: %v", err) | log.Fatalf("load config: %v", err) | ||||
| } | } | ||||
| if *outputRate > 0 { | |||||
| cfg.FM.CompositeRateHz = *outputRate | |||||
| } | |||||
| gen := offpkg.NewGenerator(cfg) | gen := offpkg.NewGenerator(cfg) | ||||
| if err := gen.WriteFile(*out, *duration); err != nil { | if err := gen.WriteFile(*out, *duration); err != nil { | ||||
| @@ -10,6 +10,9 @@ func TestPCM16ToSample(t *testing.T) { | |||||
| if pcm16ToSample(32767) <= 0 { | if pcm16ToSample(32767) <= 0 { | ||||
| t.Fatal("expected positive sample") | t.Fatal("expected positive sample") | ||||
| } | } | ||||
| if pcm16ToSample(-32768) < -1.0 { | |||||
| t.Fatal("expected clamped lower bound") | |||||
| } | |||||
| } | } | ||||
| func TestLoadWAVSource(t *testing.T) { | func TestLoadWAVSource(t *testing.T) { | ||||
| @@ -36,3 +39,14 @@ func TestLoadWAVSource(t *testing.T) { | |||||
| } | } | ||||
| _ = src.NextFrame() | _ = src.NextFrame() | ||||
| } | } | ||||
| func TestRejectInvalidWAV(t *testing.T) { | |||||
| dir := t.TempDir() | |||||
| path := filepath.Join(dir, "bad.wav") | |||||
| if err := os.WriteFile(path, []byte("nope"), 0o644); err != nil { | |||||
| t.Fatalf("write wav: %v", err) | |||||
| } | |||||
| if _, err := LoadWAVSource(path); err == nil { | |||||
| t.Fatal("expected wav load error") | |||||
| } | |||||
| } | |||||
| @@ -3,6 +3,7 @@ package offline | |||||
| import ( | import ( | ||||
| "os" | "os" | ||||
| "path/filepath" | "path/filepath" | ||||
| "strings" | |||||
| "testing" | "testing" | ||||
| "time" | "time" | ||||
| @@ -36,3 +37,13 @@ func TestWriteFile(t *testing.T) { | |||||
| t.Fatal("expected non-empty file") | t.Fatal("expected non-empty file") | ||||
| } | } | ||||
| } | } | ||||
| func TestSummaryUsesToneFallback(t *testing.T) { | |||||
| cfg := cfgpkg.Default() | |||||
| cfg.Audio.InputPath = "" | |||||
| g := NewGenerator(cfg) | |||||
| summary := g.Summary(10 * time.Millisecond) | |||||
| if !strings.Contains(summary, "source=tones") { | |||||
| t.Fatalf("unexpected summary: %s", summary) | |||||
| } | |||||
| } | |||||