Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

77 line
2.3KB

  1. package dryrun
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. cfgpkg "github.com/jan/fm-rds-tx/internal/config"
  8. )
  9. type FrameSummary struct {
  10. Mode string `json:"mode"`
  11. FrequencyMHz float64 `json:"frequencyMHz"`
  12. StereoEnabled bool `json:"stereoEnabled"`
  13. RDSEnabled bool `json:"rdsEnabled"`
  14. CompositeRate int `json:"compositeRateHz"`
  15. DeviceRate float64 `json:"deviceSampleRateHz"`
  16. PilotLevel float64 `json:"pilotLevel"`
  17. RDSInjection float64 `json:"rdsInjection"`
  18. OutputDrive float64 `json:"outputDrive"`
  19. PreEmphasisTauUS float64 `json:"preEmphasisTauUS"`
  20. MaxDeviationHz float64 `json:"maxDeviationHz"`
  21. LimiterEnabled bool `json:"limiterEnabled"`
  22. FMModulation bool `json:"fmModulationEnabled"`
  23. Source string `json:"source"`
  24. PreviewSamples []float64 `json:"previewSamples"`
  25. }
  26. func Generate(cfg cfgpkg.Config) FrameSummary {
  27. preview := make([]float64, 16)
  28. base := cfg.Audio.Gain * cfg.FM.OutputDrive
  29. for i := range preview {
  30. sign := 1.0
  31. if i%2 == 1 {
  32. sign = -1.0
  33. }
  34. preview[i] = sign * base * float64(i+1) / 16.0
  35. }
  36. source := "tones"
  37. if cfg.Audio.InputPath != "" {
  38. source = cfg.Audio.InputPath
  39. }
  40. return FrameSummary{
  41. Mode: "dry-run",
  42. FrequencyMHz: cfg.FM.FrequencyMHz,
  43. StereoEnabled: cfg.FM.StereoEnabled,
  44. RDSEnabled: cfg.RDS.Enabled,
  45. CompositeRate: cfg.FM.CompositeRateHz,
  46. DeviceRate: cfg.EffectiveDeviceRate(),
  47. PilotLevel: cfg.FM.PilotLevel,
  48. RDSInjection: cfg.FM.RDSInjection,
  49. OutputDrive: cfg.FM.OutputDrive,
  50. PreEmphasisTauUS: cfg.FM.PreEmphasisTauUS,
  51. MaxDeviationHz: cfg.FM.MaxDeviationHz,
  52. LimiterEnabled: cfg.FM.LimiterEnabled,
  53. FMModulation: cfg.FM.FMModulationEnabled,
  54. Source: source,
  55. PreviewSamples: preview,
  56. }
  57. }
  58. func WriteJSON(path string, frame FrameSummary) error {
  59. data, err := json.MarshalIndent(frame, "", " ")
  60. if err != nil {
  61. return fmt.Errorf("marshal dry-run frame: %w", err)
  62. }
  63. if path == "" || path == "-" {
  64. _, err = os.Stdout.Write(append(data, '\n'))
  65. return err
  66. }
  67. if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
  68. return fmt.Errorf("create output dir: %w", err)
  69. }
  70. return os.WriteFile(path, append(data, '\n'), 0o644)
  71. }