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.

54 line
1.3KB

  1. package dsp
  2. import "math"
  3. // Oscillator produces a sine wave at a configured frequency and sample rate.
  4. type Oscillator struct {
  5. Frequency float64
  6. SampleRate float64
  7. phase float64
  8. }
  9. // Tick advances the oscillator by one sample and returns the current sine value.
  10. func (o *Oscillator) Tick() float64 {
  11. if o.SampleRate <= 0 || o.Frequency == 0 {
  12. return 0
  13. }
  14. value := math.Sin(2 * math.Pi * o.phase)
  15. step := o.Frequency / o.SampleRate
  16. o.phase += step
  17. if o.phase >= 1 || o.phase < 0 {
  18. o.phase -= math.Floor(o.phase)
  19. }
  20. return value
  21. }
  22. // Reset brings the phase back to zero.
  23. func (o *Oscillator) Reset() {
  24. o.phase = 0
  25. }
  26. // Phase returns the current phase of the oscillator in [0, 1).
  27. func (o *Oscillator) Phase() float64 {
  28. return o.phase
  29. }
  30. // PilotGenerator emits the 19 kHz pilot tone required by FM stereo.
  31. // Output is unity-normalized (peak ±1.0). The caller controls the
  32. // actual injection level via the combiner gain.
  33. type PilotGenerator struct {
  34. Oscillator
  35. }
  36. // NewPilotGenerator constructs a pilot tone generator for the given sample rate.
  37. func NewPilotGenerator(sampleRate float64) PilotGenerator {
  38. return PilotGenerator{
  39. Oscillator: Oscillator{Frequency: 19000, SampleRate: sampleRate},
  40. }
  41. }
  42. // Sample returns the next pilot sample (unity amplitude).
  43. func (p *PilotGenerator) Sample() float64 {
  44. return p.Oscillator.Tick()
  45. }