Parcourir la source

ingest: share decoder pcm helpers

main
Jan il y a 1 mois
Parent
révision
a2e36cab15
3 fichiers modifiés avec 74 ajouts et 58 suppressions
  1. +58
    -0
      internal/ingest/decoder/helpers.go
  2. +8
    -23
      internal/ingest/decoder/mp3/decoder.go
  3. +8
    -35
      internal/ingest/decoder/oggvorbis/decoder.go

+ 58
- 0
internal/ingest/decoder/helpers.go Voir le fichier

@@ -0,0 +1,58 @@
package decoder

import (
"encoding/binary"
"math"
"time"

"github.com/jan/fm-rds-tx/internal/ingest"
)

const defaultSampleRateHz = 44100

func ResolveSampleRate(decodedSampleRateHz int, meta StreamMeta) int {
if decodedSampleRateHz > 0 {
return decodedSampleRateHz
}
if meta.SampleRateHz > 0 {
return meta.SampleRateHz
}
return defaultSampleRateHz
}

func BuildChunk(samples []int32, channels, sampleRateHz int, seq uint64, sourceID string) ingest.PCMChunk {
return ingest.PCMChunk{
Samples: samples,
Channels: channels,
SampleRateHz: sampleRateHz,
Sequence: seq,
Timestamp: time.Now(),
SourceID: sourceID,
}
}

func PCM16LEToPCM32(in []byte) []int32 {
out := make([]int32, 0, len(in)/2)
for i := 0; i+1 < len(in); i += 2 {
v := int16(binary.LittleEndian.Uint16(in[i : i+2]))
out = append(out, int32(v)<<16)
}
return out
}

func Float32ToPCM32(in []float32) []int32 {
out := make([]int32, len(in))
for i, sample := range in {
if sample > 1 {
sample = 1
} else if sample < -1 {
sample = -1
}
if sample == -1 {
out[i] = math.MinInt32
continue
}
out[i] = int32(sample * math.MaxInt32)
}
return out
}

+ 8
- 23
internal/ingest/decoder/mp3/decoder.go Voir le fichier

@@ -2,10 +2,8 @@ package mp3

import (
"context"
"encoding/binary"
"fmt"
"io"
"time"

gomp3 "github.com/hajimehoshi/go-mp3"
"github.com/jan/fm-rds-tx/internal/ingest"
@@ -32,14 +30,7 @@ func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.St
}

const channels = 2 // go-mp3 always decodes to stereo s16le
sampleRate := dec.SampleRate()
if sampleRate <= 0 {
if meta.SampleRateHz > 0 {
sampleRate = meta.SampleRateHz
} else {
sampleRate = 44100
}
}
sampleRate := decoder.ResolveSampleRate(dec.SampleRate(), meta)

const chunkFrames = 1024
const frameBytes = channels * 2
@@ -74,17 +65,11 @@ func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.St
}

func emitChunk(data []byte, seq uint64, sampleRate int, sourceID string, emit func(ingest.PCMChunk) error) error {
samples := make([]int32, 0, len(data)/2)
for i := 0; i+1 < len(data); i += 2 {
v := int16(binary.LittleEndian.Uint16(data[i : i+2]))
samples = append(samples, int32(v)<<16)
}
return emit(ingest.PCMChunk{
Samples: samples,
Channels: 2,
SampleRateHz: sampleRate,
Sequence: seq,
Timestamp: time.Now(),
SourceID: sourceID,
})
return emit(decoder.BuildChunk(
decoder.PCM16LEToPCM32(data),
2,
sampleRate,
seq,
sourceID,
))
}

+ 8
- 35
internal/ingest/decoder/oggvorbis/decoder.go Voir le fichier

@@ -4,8 +4,6 @@ import (
"context"
"fmt"
"io"
"math"
"time"

"github.com/jan/fm-rds-tx/internal/ingest"
"github.com/jan/fm-rds-tx/internal/ingest/decoder"
@@ -40,14 +38,7 @@ func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.St
}
}

sampleRate := dec.SampleRate()
if sampleRate <= 0 {
if meta.SampleRateHz > 0 {
sampleRate = meta.SampleRateHz
} else {
sampleRate = 44100
}
}
sampleRate := decoder.ResolveSampleRate(dec.SampleRate(), meta)

const chunkFrames = 1024
buf := make([]float32, chunkFrames*channels)
@@ -62,14 +53,13 @@ func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.St

n, readErr := dec.Read(buf)
if n > 0 {
chunk := ingest.PCMChunk{
Samples: float32ToPCM32(buf[:n]),
Channels: channels,
SampleRateHz: sampleRate,
Sequence: seq,
Timestamp: time.Now(),
SourceID: meta.SourceID,
}
chunk := decoder.BuildChunk(
decoder.Float32ToPCM32(buf[:n]),
channels,
sampleRate,
seq,
meta.SourceID,
)
if err := emit(chunk); err != nil {
return err
}
@@ -84,20 +74,3 @@ func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.St
}
}
}

func float32ToPCM32(in []float32) []int32 {
out := make([]int32, len(in))
for i, sample := range in {
if sample > 1 {
sample = 1
} else if sample < -1 {
sample = -1
}
if sample == -1 {
out[i] = math.MinInt32
continue
}
out[i] = int32(sample * math.MaxInt32)
}
return out
}

Chargement…
Annuler
Enregistrer