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 lines
1.6KB

  1. package oggvorbis
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "github.com/jan/fm-rds-tx/internal/ingest"
  7. "github.com/jan/fm-rds-tx/internal/ingest/decoder"
  8. libvorbis "github.com/jfreymuth/oggvorbis"
  9. )
  10. type Decoder struct{}
  11. func New() *Decoder { return &Decoder{} }
  12. func (d *Decoder) Name() string { return "oggvorbis-native" }
  13. func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.StreamMeta, emit func(ingest.PCMChunk) error) error {
  14. if r == nil {
  15. return fmt.Errorf("%w: ogg/vorbis decoder stream reader is nil", decoder.ErrUnsupported)
  16. }
  17. if emit == nil {
  18. return fmt.Errorf("%w: ogg/vorbis decoder emit callback is nil", decoder.ErrUnsupported)
  19. }
  20. dec, err := libvorbis.NewReader(r)
  21. if err != nil {
  22. return fmt.Errorf("%w: ogg/vorbis decoder init: %v", decoder.ErrUnsupported, err)
  23. }
  24. channels := dec.Channels()
  25. if channels <= 0 {
  26. if meta.Channels > 0 {
  27. channels = meta.Channels
  28. } else {
  29. return fmt.Errorf("%w: ogg/vorbis decoder invalid channel count", decoder.ErrUnsupported)
  30. }
  31. }
  32. sampleRate := decoder.ResolveSampleRate(dec.SampleRate(), meta)
  33. const chunkFrames = 1024
  34. buf := make([]float32, chunkFrames*channels)
  35. seq := uint64(0)
  36. for {
  37. select {
  38. case <-ctx.Done():
  39. return nil
  40. default:
  41. }
  42. n, readErr := dec.Read(buf)
  43. if n > 0 {
  44. chunk := decoder.BuildChunk(
  45. decoder.Float32ToPCM32(buf[:n]),
  46. channels,
  47. sampleRate,
  48. seq,
  49. meta.SourceID,
  50. )
  51. if err := emit(chunk); err != nil {
  52. return err
  53. }
  54. seq++
  55. }
  56. if readErr != nil {
  57. if readErr == io.EOF {
  58. return nil
  59. }
  60. return fmt.Errorf("ogg/vorbis decoder read pcm: %w", readErr)
  61. }
  62. }
  63. }