|
- package stereo
-
- import (
- "github.com/jan/fm-rds-tx/internal/audio"
- "github.com/jan/fm-rds-tx/internal/dsp"
- )
-
- // Components holds the individual MPX components produced by the stereo encoder.
- type Components struct {
- Mono float64 // L+R baseband
- Stereo float64 // L-R modulated onto 38 kHz DSB-SC
- Pilot float64 // 19 kHz pilot tone
- }
-
- // StereoEncoder generates stereo MPX primitives from stereo audio frames.
- // It internally maintains phase-coherent 19 kHz pilot and 38 kHz subcarrier
- // oscillators so that block-boundary phase continuity is guaranteed.
- type StereoEncoder struct {
- pilot dsp.PilotGenerator
- subcarrier dsp.Oscillator // 38 kHz, phase-locked to pilot (2× pilot)
- LevelStereo float64
- }
-
- // NewStereoEncoder creates a StereoEncoder configured for the provided sample rate.
- func NewStereoEncoder(sampleRate float64) StereoEncoder {
- return StereoEncoder{
- pilot: dsp.NewPilotGenerator(sampleRate, 0.1),
- subcarrier: dsp.Oscillator{Frequency: 38000, SampleRate: sampleRate},
- LevelStereo: 1.0,
- }
- }
-
- // Encode converts a stereo frame into MPX components.
- // The 38 kHz subcarrier is generated from the internal oscillator,
- // maintaining continuous phase across calls.
- func (s *StereoEncoder) Encode(frame audio.Frame) Components {
- pilot := s.pilot.Sample()
- sub38 := s.subcarrier.Tick()
-
- return Components{
- Mono: float64(frame.Mono()),
- Stereo: float64(frame.Difference()) * s.LevelStereo * sub38,
- Pilot: pilot,
- }
- }
-
- // Reset restarts the pilot and subcarrier generators.
- func (s *StereoEncoder) Reset() {
- s.pilot.Reset()
- s.subcarrier.Reset()
- }
|