| @@ -8,8 +8,12 @@ import ( | |||
| "path/filepath" | |||
| "time" | |||
| "github.com/jan/fm-rds-tx/internal/audio" | |||
| cfgpkg "github.com/jan/fm-rds-tx/internal/config" | |||
| "github.com/jan/fm-rds-tx/internal/mpx" | |||
| "github.com/jan/fm-rds-tx/internal/output" | |||
| "github.com/jan/fm-rds-tx/internal/rds" | |||
| "github.com/jan/fm-rds-tx/internal/stereo" | |||
| ) | |||
| type Generator struct { | |||
| @@ -37,20 +41,35 @@ func (g *Generator) GenerateFrame(duration time.Duration) *output.CompositeFrame | |||
| Sequence: 1, | |||
| } | |||
| stereoEncoder := stereo.NewStereoEncoder(sampleRate) | |||
| combiner := mpx.NewDefaultCombiner() | |||
| combiner.PilotGain = g.cfg.FM.PilotLevel | |||
| combiner.RDSGain = g.cfg.FM.RDSInjection | |||
| rdsEnc, _ := rds.NewEncoder(rds.RDSConfig{ | |||
| PI: 0x1234, | |||
| PS: g.cfg.RDS.PS, | |||
| RT: g.cfg.RDS.RadioText, | |||
| PTY: uint8(g.cfg.RDS.PTY), | |||
| SampleRate: sampleRate, | |||
| }) | |||
| rdsSamples := rdsEnc.Generate(samples) | |||
| leftFreq := 1000.0 | |||
| rightFreq := 1600.0 | |||
| pilotFreq := 19000.0 | |||
| rdsFreq := 57000.0 | |||
| stereoCarrierFreq := 38000.0 | |||
| for i := 0; i < samples; i++ { | |||
| t := float64(i) / sampleRate | |||
| left := 0.4 * math.Sin(2*math.Pi*leftFreq*t) | |||
| right := 0.4 * math.Sin(2*math.Pi*rightFreq*t+math.Pi/3) | |||
| mono := (left + right) / 2 | |||
| stereo := (left - right) / 2 * 0.8 * math.Sin(2*math.Pi*38000*t) | |||
| pilot := g.cfg.FM.PilotLevel * math.Sin(2*math.Pi*pilotFreq*t) | |||
| rds := g.cfg.FM.RDSInjection * math.Sin(2*math.Pi*rdsFreq*t) | |||
| composite := (mono + stereo + pilot + rds) * g.cfg.FM.OutputDrive | |||
| left := audio.Sample(0.4 * math.Sin(2*math.Pi*leftFreq*t)) | |||
| right := audio.Sample(0.4 * math.Sin(2*math.Pi*rightFreq*t+math.Pi/3)) | |||
| comps := stereoEncoder.Encode(audio.NewFrame(left, right)) | |||
| stereoDSB := comps.Stereo * math.Sin(2*math.Pi*stereoCarrierFreq*t) | |||
| rdsValue := 0.0 | |||
| if g.cfg.RDS.Enabled && i < len(rdsSamples) { | |||
| rdsValue = rdsSamples[i] | |||
| } | |||
| composite := combiner.Combine(comps.Mono, stereoDSB, comps.Pilot, rdsValue) * g.cfg.FM.OutputDrive | |||
| frame.Samples[i] = output.IQSample{I: float32(composite), Q: 0} | |||
| } | |||
| @@ -92,5 +111,5 @@ func (g *Generator) WriteFile(path string, duration time.Duration) error { | |||
| } | |||
| func (g *Generator) Summary(duration time.Duration) string { | |||
| return fmt.Sprintf("offline frame: freq=%.1fMHz sampleRate=%d duration=%s outputDrive=%.2f", g.cfg.FM.FrequencyMHz, g.cfg.FM.CompositeRateHz, duration.String(), g.cfg.FM.OutputDrive) | |||
| return fmt.Sprintf("offline frame: freq=%.1fMHz sampleRate=%d duration=%s outputDrive=%.2f stereo=%t rds=%t", g.cfg.FM.FrequencyMHz, g.cfg.FM.CompositeRateHz, duration.String(), g.cfg.FM.OutputDrive, g.cfg.FM.StereoEnabled, g.cfg.RDS.Enabled) | |||
| } | |||