diff --git a/stream/flac/decode.go b/stream/flac/decode.go index e69de29b..2ecfb737 100644 --- a/stream/flac/decode.go +++ b/stream/flac/decode.go @@ -0,0 +1,108 @@ +package flac + +import ( + "bytes" + "errors" + "io" + + "github.com/go-audio/audio" + "github.com/go-audio/wav" + "github.com/mewkiz/flac" +) + +const wavAudioFormat = 1 + +type buffer struct { + Buffer bytes.Buffer + Index int64 +} + +func (b *buffer) Bytes() []byte { + return b.Buffer.Bytes() +} + +func (b *buffer) Read(p []byte) (int, error) { + n, err := bytes.NewBuffer(b.Buffer.Bytes()[b.Index:]).Read(p) + + if err == nil { + if b.Index+int64(len(p)) < int64(b.Buffer.Len()) { + b.Index += int64(len(p)) + } else { + b.Index = int64(b.Buffer.Len()) + } + } + + return n, err +} + +func (b *buffer) Write(p []byte) (int, error) { + n, err := b.Buffer.Write(p) + + if err == nil { + b.Index = int64(b.Buffer.Len()) + } + + return n, err +} + +func (b *buffer) Seek(offset int64, whence int) (int64, error) { + var err error + var Index int64 = 0 + + switch whence { + case 0: + if offset >= int64(b.Buffer.Len()) || offset < 0 { + err = errors.New("Invalid Offset.") + } else { + b.Index = offset + Index = offset + } + default: + err = errors.New("Unsupported Seek Method.") + } + + return Index, err +} + +// Decode takes a slice of flac and decodes to wav +func Decode(buf []byte) ([]byte, error) { + r := bytes.NewReader(buf) + stream, err := flac.Parse(r) + if err != nil { + return nil, errors.New("Could not parse FLAC") + } + fb := &buffer{} + enc := wav.NewEncoder(fb, int(stream.Info.SampleRate), int(stream.Info.BitsPerSample), int(stream.Info.NChannels), wavAudioFormat) + defer enc.Close() + var data []int + for { + // Decode FLAC audio samples. + frame, err := stream.ParseNext() + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + + // Encode WAV audio samples. + data = data[:0] + for i := 0; i < frame.Subframes[0].NSamples; i++ { + for _, subframe := range frame.Subframes { + data = append(data, int(subframe.Samples[i])) + } + } + buf := &audio.IntBuffer{ + Format: &audio.Format{ + NumChannels: int(stream.Info.NChannels), + SampleRate: int(stream.Info.SampleRate), + }, + Data: data, + SourceBitDepth: int(stream.Info.BitsPerSample), + } + if err := enc.Write(buf); err != nil { + return nil, err + } + } + return fb.Bytes(), nil +} diff --git a/stream/flac/flac_test.go b/stream/flac/flac_test.go index e69de29b..763731d4 100644 --- a/stream/flac/flac_test.go +++ b/stream/flac/flac_test.go @@ -0,0 +1,31 @@ +package flac + +import ( + "io/ioutil" + "os" + "testing" +) + +const ( + testFile = "/home/saxon/Desktop/robot.flac" + outFile = "out.wav" +) + +func TestDecodeFlac(t *testing.T) { + b, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatalf("Could not read test file, failed with err: %v", err.Error()) + } + out, err := Decode(b) + if err != nil { + t.Errorf("Could not decode, failed with err: %v", err.Error()) + } + f, err := os.Create(outFile) + if err != nil { + t.Fatalf("Could not create output file, failed with err: %v", err.Error()) + } + _, err = f.Write(out) + if err != nil { + t.Fatalf("Could not write to output file, failed with err: %v", err.Error()) + } +}