Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Re-implemented looping FLAC with a different lib #4

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 58 additions & 44 deletions flac/decode.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import (
"fmt"
"io"

meta "github.com/go-flac/go-flac"
flac "github.com/go-musicfox/goflac"
"github.com/ikemen-engine/beep"
"github.com/mewkiz/flac"
"github.com/pkg/errors"
)

Expand All @@ -14,42 +15,50 @@ import (
//
// Do not close the supplied Reader, instead, use the Close method of the returned
// StreamSeekCloser when you want to release the resources.
func Decode(r io.Reader) (s beep.StreamSeekCloser, format beep.Format, err error) {
d := decoder{r: r}
func Decode(rc io.ReadSeekCloser) (s beep.StreamSeekCloser, format beep.Format, err error) {
d := decoder{r: rc}
defer func() { // hacky way to always close r if an error occurred
if closer, ok := d.r.(io.Closer); ok {
if err != nil {
closer.Close()
err = errors.Wrap(err, "flac")
}
}
}()

rs, seeker := r.(io.ReadSeeker)
if seeker {
d.stream, err = flac.NewSeek(rs)
d.seekEnabled = true
} else {
d.stream, err = flac.New(r)
// First we need the metadata (just to get the # of samples)
var f *meta.File
if f, err = meta.ParseMetadata(rc); err != nil {
return nil, beep.Format{}, errors.Wrap(err, "flac-meta")
}
var streaminfo *meta.StreamInfoBlock
if streaminfo, err = f.GetStreamInfo(); err != nil {
return nil, beep.Format{}, errors.Wrap(err, "flac-meta")
}
d.len = streaminfo.SampleCount

if err != nil {
// Seek to 0 so the decoder doesn't freak out.
rc.Seek(0, io.SeekStart)
if d.stream, err = flac.NewDecoderReader(rc); err != nil {
return nil, beep.Format{}, errors.Wrap(err, "flac")
}

format = beep.Format{
SampleRate: beep.SampleRate(d.stream.Info.SampleRate),
NumChannels: int(d.stream.Info.NChannels),
Precision: int(d.stream.Info.BitsPerSample / 8),
SampleRate: beep.SampleRate(d.stream.Rate),
NumChannels: d.stream.Channels,
Precision: d.stream.Depth / 8,
}

return &d, format, nil
}

type decoder struct {
r io.Reader
stream *flac.Stream
buf [][2]float64
pos int
err error
seekEnabled bool
r io.Reader
stream *flac.Decoder
buf [][2]float64
pos int
err error
len int64
}

func (d *decoder) Stream(samples [][2]float64) (n int, ok bool) {
Expand All @@ -62,8 +71,16 @@ func (d *decoder) Stream(samples [][2]float64) (n int, ok bool) {
if j >= len(d.buf) {
// refill buffer.
if err := d.refill(); err != nil {
d.err = err
d.pos += n
// Only set the error if it's not EOF
if err != io.EOF {
d.err = err
d.pos += n
} else {
// Set the pos to the end if less than length because we've reached EOF (hack to work with loop)
if int64(d.pos) < d.len {
d.pos = int(d.len)
}
}
return n, n > 0
}
j = 0
Expand All @@ -82,52 +99,53 @@ func (d *decoder) refill() error {
// Empty buffer.
d.buf = d.buf[:0]
// Parse audio frame.
frame, err := d.stream.ParseNext()
frame, err := d.stream.ReadFrame()
if err != nil {
return err
}

// Decode audio samples.
bps := d.stream.Depth
nchannels := d.stream.Channels
// Expand buffer size if needed.
n := len(frame.Subframes[0].Samples)
n := len(frame.Buffer) / nchannels
if cap(d.buf) < n {
d.buf = make([][2]float64, n)
} else {
d.buf = d.buf[:n]
}
// Decode audio samples.
bps := d.stream.Info.BitsPerSample
nchannels := d.stream.Info.NChannels
s := 1 << (bps - 1)
q := 1 / float64(s)
switch {
case bps == 8 && nchannels == 1:
for i := 0; i < n; i++ {
d.buf[i][0] = float64(int8(frame.Subframes[0].Samples[i])) * q
d.buf[i][1] = float64(int8(frame.Subframes[0].Samples[i])) * q
d.buf[i][0] = float64(int8(frame.Buffer[i])) * q
d.buf[i][1] = float64(int8(frame.Buffer[i])) * q
}
case bps == 16 && nchannels == 1:
for i := 0; i < n; i++ {
d.buf[i][0] = float64(int16(frame.Subframes[0].Samples[i])) * q
d.buf[i][1] = float64(int16(frame.Subframes[0].Samples[i])) * q
d.buf[i][0] = float64(int16(frame.Buffer[i])) * q
d.buf[i][1] = float64(int16(frame.Buffer[i])) * q
}
case bps == 24 && nchannels == 1:
for i := 0; i < n; i++ {
d.buf[i][0] = float64(int32(frame.Subframes[0].Samples[i])) * q
d.buf[i][1] = float64(int32(frame.Subframes[0].Samples[i])) * q
d.buf[i][0] = float64(frame.Buffer[i]) * q
d.buf[i][1] = float64(frame.Buffer[i]) * q
}
case bps == 8 && nchannels >= 2:
for i := 0; i < n; i++ {
d.buf[i][0] = float64(int8(frame.Subframes[0].Samples[i])) * q
d.buf[i][1] = float64(int8(frame.Subframes[1].Samples[i])) * q
d.buf[i][0] = float64(int8(frame.Buffer[i*nchannels])) * q
d.buf[i][1] = float64(int8(frame.Buffer[i*nchannels+1])) * q
}
case bps == 16 && nchannels >= 2:
for i := 0; i < n; i++ {
d.buf[i][0] = float64(int16(frame.Subframes[0].Samples[i])) * q
d.buf[i][1] = float64(int16(frame.Subframes[1].Samples[i])) * q
d.buf[i][0] = float64(int16(frame.Buffer[i*nchannels])) * q
d.buf[i][1] = float64(int16(frame.Buffer[i*nchannels+1])) * q
}
case bps == 24 && nchannels >= 2:
for i := 0; i < n; i++ {
d.buf[i][0] = float64(frame.Subframes[0].Samples[i]) * q
d.buf[i][1] = float64(frame.Subframes[1].Samples[i]) * q
d.buf[i][0] = float64(frame.Buffer[i*nchannels]) * q
d.buf[i][1] = float64(frame.Buffer[i*nchannels+1]) * q
}
default:
panic(fmt.Errorf("support for %d bits-per-sample and %d channels combination not yet implemented", bps, nchannels))
Expand All @@ -140,19 +158,15 @@ func (d *decoder) Err() error {
}

func (d *decoder) Len() int {
return int(d.stream.Info.NSamples)
return int(d.len)
}

func (d *decoder) Position() int {
return d.pos
return int(d.pos)
}

// p represents flac sample num perhaps?
func (d *decoder) Seek(p int) error {
if !d.seekEnabled {
return errors.New("flac.decoder.Seek: not enabled")
}

pos, err := d.stream.Seek(uint64(p))
d.pos = int(pos)
return err
Expand Down
4 changes: 3 additions & 1 deletion go.mod
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ module github.com/ikemen-engine/beep
go 1.14

require (
github.com/cocoonlife/testify v0.0.0-20160218172820-792cc1faeb64 // indirect
github.com/gdamore/tcell v1.3.0
github.com/go-flac/go-flac v1.0.0
github.com/go-musicfox/goflac v0.1.5
github.com/hajimehoshi/go-mp3 v0.3.0
github.com/hajimehoshi/oto v0.7.1
github.com/jfreymuth/oggvorbis v1.0.2
github.com/mewkiz/flac v1.0.7
github.com/pkg/errors v0.9.1
github.com/samhocevar/go-meltysynth v0.0.0-20230403180939-aca4a036cb16
)
23 changes: 7 additions & 16 deletions go.sum
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
github.com/cocoonlife/testify v0.0.0-20160218172820-792cc1faeb64 h1:LjPYdzoFSAJ5Tr/ElL8kzTJghXgpnOjJVbgd1UvZB1o=
github.com/cocoonlife/testify v0.0.0-20160218172820-792cc1faeb64/go.mod h1:LoCAz53rbPcqs8Da2BjB/yDy4gxMtiSQmqnYI/DGH+U=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
github.com/go-flac/go-flac v1.0.0 h1:6qI9XOVLcO50xpzm3nXvO31BgDgHhnr/p/rER/K/doY=
github.com/go-flac/go-flac v1.0.0/go.mod h1:WnZhcpmq4u1UdZMNn9LYSoASpWOCMOoxXxcWEHSzkW8=
github.com/go-musicfox/goflac v0.1.5 h1:1fSNZphzXaTq++fwSiPdGt/nDw7IUsPwcSy6C9vUuh8=
github.com/go-musicfox/goflac v0.1.5/go.mod h1:vSxTubJfc8EfFFMmJqxdSp4/iwBVf9TPcapx8I/+W5g=
github.com/hajimehoshi/go-mp3 v0.3.0 h1:fTM5DXjp/DL2G74HHAs/aBGiS9Tg7wnp+jkU38bHy4g=
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/hajimehoshi/oto v0.7.1 h1:I7maFPz5MBCwiutOrz++DLdbr4rTzBsbBuV2VpgU9kk=
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
github.com/icza/bitio v1.0.0 h1:squ/m1SHyFeCA6+6Gyol1AxV9nmPPlJFT8c2vKdj3U8=
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k=
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
github.com/jfreymuth/oggvorbis v1.0.2 h1:MjAb64MjsqlUBBOlyqyZvB7H7om58F6I9iP4skamaII=
github.com/jfreymuth/oggvorbis v1.0.2/go.mod h1:hTCEBUJIOM8voBYPIa/TZima67oFAM1eB2Lzjq/nVK0=
github.com/jfreymuth/vorbis v1.0.1 h1:iA3VC/w+H0rHzPVrv1dkwxMMjk3LJTlSXlpYw6800u0=
Expand All @@ -25,23 +23,16 @@ github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mewkiz/flac v1.0.7 h1:uIXEjnuXqdRaZttmSFM5v5Ukp4U6orrZsnYGGR3yow8=
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2 h1:EyTNMdePWaoWsRSGQnXiSoQu0r6RS1eA557AwJhlzHU=
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/samhocevar/go-meltysynth v0.0.0-20230403173226-b7428b5feb9e/go.mod h1:J+GU4sgu3oAPHCceoTIXNKzFHSybNhF/LyFkWZlqhvE=
github.com/samhocevar/go-meltysynth v0.0.0-20230403180939-aca4a036cb16 h1:slzh3BWJ6FyMM8gkDzDwHz+gjU4+82ldB6oPyYi82Ho=
github.com/samhocevar/go-meltysynth v0.0.0-20230403180939-aca4a036cb16/go.mod h1:J+GU4sgu3oAPHCceoTIXNKzFHSybNhF/LyFkWZlqhvE=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 h1:vyLBGJPIl9ZYbcQFM2USFmJBK6KI+t+z6jL0lbwjrnc=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
Expand Down