Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

104 líneas
2.1KB

  1. package oggvorbis
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "math"
  7. "time"
  8. "github.com/jan/fm-rds-tx/internal/ingest"
  9. "github.com/jan/fm-rds-tx/internal/ingest/decoder"
  10. libvorbis "github.com/jfreymuth/oggvorbis"
  11. )
  12. type Decoder struct{}
  13. func New() *Decoder { return &Decoder{} }
  14. func (d *Decoder) Name() string { return "oggvorbis-native" }
  15. func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.StreamMeta, emit func(ingest.PCMChunk) error) error {
  16. if r == nil {
  17. return fmt.Errorf("%w: ogg/vorbis decoder stream reader is nil", decoder.ErrUnsupported)
  18. }
  19. if emit == nil {
  20. return fmt.Errorf("%w: ogg/vorbis decoder emit callback is nil", decoder.ErrUnsupported)
  21. }
  22. dec, err := libvorbis.NewReader(r)
  23. if err != nil {
  24. return fmt.Errorf("%w: ogg/vorbis decoder init: %v", decoder.ErrUnsupported, err)
  25. }
  26. channels := dec.Channels()
  27. if channels <= 0 {
  28. if meta.Channels > 0 {
  29. channels = meta.Channels
  30. } else {
  31. return fmt.Errorf("%w: ogg/vorbis decoder invalid channel count", decoder.ErrUnsupported)
  32. }
  33. }
  34. sampleRate := dec.SampleRate()
  35. if sampleRate <= 0 {
  36. if meta.SampleRateHz > 0 {
  37. sampleRate = meta.SampleRateHz
  38. } else {
  39. sampleRate = 44100
  40. }
  41. }
  42. const chunkFrames = 1024
  43. buf := make([]float32, chunkFrames*channels)
  44. seq := uint64(0)
  45. for {
  46. select {
  47. case <-ctx.Done():
  48. return nil
  49. default:
  50. }
  51. n, readErr := dec.Read(buf)
  52. if n > 0 {
  53. chunk := ingest.PCMChunk{
  54. Samples: float32ToPCM32(buf[:n]),
  55. Channels: channels,
  56. SampleRateHz: sampleRate,
  57. Sequence: seq,
  58. Timestamp: time.Now(),
  59. SourceID: meta.SourceID,
  60. }
  61. if err := emit(chunk); err != nil {
  62. return err
  63. }
  64. seq++
  65. }
  66. if readErr != nil {
  67. if readErr == io.EOF {
  68. return nil
  69. }
  70. return fmt.Errorf("ogg/vorbis decoder read pcm: %w", readErr)
  71. }
  72. }
  73. }
  74. func float32ToPCM32(in []float32) []int32 {
  75. out := make([]int32, len(in))
  76. for i, sample := range in {
  77. if sample > 1 {
  78. sample = 1
  79. } else if sample < -1 {
  80. sample = -1
  81. }
  82. if sample == -1 {
  83. out[i] = math.MinInt32
  84. continue
  85. }
  86. out[i] = int32(sample * math.MaxInt32)
  87. }
  88. return out
  89. }