|
- package oggvorbis
-
- import (
- "context"
- "fmt"
- "io"
-
- "github.com/jan/fm-rds-tx/internal/ingest"
- "github.com/jan/fm-rds-tx/internal/ingest/decoder"
- libvorbis "github.com/jfreymuth/oggvorbis"
- )
-
- type Decoder struct{}
-
- func New() *Decoder { return &Decoder{} }
-
- func (d *Decoder) Name() string { return "oggvorbis-native" }
-
- func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.StreamMeta, emit func(ingest.PCMChunk) error) error {
- if r == nil {
- return fmt.Errorf("%w: ogg/vorbis decoder stream reader is nil", decoder.ErrUnsupported)
- }
- if emit == nil {
- return fmt.Errorf("%w: ogg/vorbis decoder emit callback is nil", decoder.ErrUnsupported)
- }
-
- dec, err := libvorbis.NewReader(r)
- if err != nil {
- return fmt.Errorf("%w: ogg/vorbis decoder init: %v", decoder.ErrUnsupported, err)
- }
-
- channels := dec.Channels()
- if channels <= 0 {
- if meta.Channels > 0 {
- channels = meta.Channels
- } else {
- return fmt.Errorf("%w: ogg/vorbis decoder invalid channel count", decoder.ErrUnsupported)
- }
- }
-
- sampleRate := decoder.ResolveSampleRate(dec.SampleRate(), meta)
-
- const chunkFrames = 1024
- buf := make([]float32, chunkFrames*channels)
- seq := uint64(0)
-
- for {
- select {
- case <-ctx.Done():
- return nil
- default:
- }
-
- n, readErr := dec.Read(buf)
- if n > 0 {
- chunk := decoder.BuildChunk(
- decoder.Float32ToPCM32(buf[:n]),
- channels,
- sampleRate,
- seq,
- meta.SourceID,
- )
- if err := emit(chunk); err != nil {
- return err
- }
- seq++
- }
-
- if readErr != nil {
- if readErr == io.EOF {
- return nil
- }
- return fmt.Errorf("ogg/vorbis decoder read pcm: %w", readErr)
- }
- }
- }
|