diff --git a/libflac.go b/libflac.go index 3a283ec..42d123f 100644 --- a/libflac.go +++ b/libflac.go @@ -255,6 +255,32 @@ func NewDecoder(name string) (d *Decoder, err error) { return } +// NewDecoderOgg creates a new Decoder object. +func NewDecoderOgg(name string) (d *Decoder, err error) { + d = new(Decoder) + d.d = C.FLAC__stream_decoder_new() + if d.d == nil { + return nil, errors.New("failed to create decoder") + } + c := C.CString(name) + defer C.free(unsafe.Pointer(c)) + runtime.SetFinalizer(d, (*Decoder).Close) + status := C.FLAC__stream_decoder_init_ogg_file(d.d, c, + (C.FLAC__StreamDecoderWriteCallback)(unsafe.Pointer(C.decoderWriteCallback_cgo)), + (C.FLAC__StreamDecoderMetadataCallback)(unsafe.Pointer(C.decoderMetadataCallback_cgo)), + (C.FLAC__StreamDecoderErrorCallback)(unsafe.Pointer(C.decoderErrorCallback_cgo)), + nil) + if status != C.FLAC__STREAM_DECODER_INIT_STATUS_OK { + return nil, errors.New("failed to open file") + } + decoderPtrs.add(d) + ret := C.FLAC__stream_decoder_process_until_end_of_metadata(d.d) + if ret == 0 || d.error || d.Channels == 0 { + return nil, fmt.Errorf("failed to process metadata %s", d.errorStr) + } + return +} + // NewDecoderReader creates a new Decoder object from a Reader. func NewDecoderReader(reader io.ReadCloser) (d *Decoder, err error) { d = new(Decoder) @@ -282,6 +308,33 @@ func NewDecoderReader(reader io.ReadCloser) (d *Decoder, err error) { return } +// NewDecoderReaderOgg creates a new Decoder object from a Reader of Ogg. +func NewDecoderReaderOgg(reader io.ReadCloser) (d *Decoder, err error) { + d = new(Decoder) + d.d = C.FLAC__stream_decoder_new() + if d.d == nil { + return nil, errors.New("failed to create decoder") + } + d.reader = reader + runtime.SetFinalizer(d, (*Decoder).Close) + status := C.FLAC__stream_decoder_init_ogg_stream(d.d, + (C.FLAC__StreamDecoderReadCallback)(unsafe.Pointer(C.decoderReadCallback_cgo)), + nil, nil, nil, nil, + (C.FLAC__StreamDecoderWriteCallback)(unsafe.Pointer(C.decoderWriteCallback_cgo)), + (C.FLAC__StreamDecoderMetadataCallback)(unsafe.Pointer(C.decoderMetadataCallback_cgo)), + (C.FLAC__StreamDecoderErrorCallback)(unsafe.Pointer(C.decoderErrorCallback_cgo)), + nil) + if status != C.FLAC__STREAM_DECODER_INIT_STATUS_OK { + return nil, errors.New("failed to open stream") + } + decoderPtrs.add(d) + ret := C.FLAC__stream_decoder_process_until_end_of_metadata(d.d) + if ret == 0 || d.error || d.Channels == 0 { + return nil, fmt.Errorf("failed to process metadata %s", d.errorStr) + } + return +} + // Close closes a decoder and frees the resources. func (d *Decoder) Close() { if d.d != nil { diff --git a/libflac_test.go b/libflac_test.go index 0166316..74473fb 100644 --- a/libflac_test.go +++ b/libflac_test.go @@ -58,6 +58,52 @@ func TestDecode(t *testing.T) { d.Close() } +func TestDecodeOgg(t *testing.T) { + a := assert.New(t) + + d, err := NewDecoderOgg("testdata/nonexistent.ogg") + a.Equal(d, (*Decoder)(nil), "decoder is nil") + a.NotNil(err, "err is not nil") + + d, err = NewDecoderOgg("testdata/sine24-00.ogg") + + a.Equal(err, nil, "err is nil") + a.Equal(d.Channels, 1, "channels is 1") + a.Equal(d.Depth, 24, "depth is 24") + a.Equal(d.Rate, 48000, "depth is 48000") + + samples := 0 + + f, err := d.ReadFrame() + + a.Equal(err, nil, "err is nil") + a.Equal(f.Channels, 1, "channels is 1") + a.Equal(f.Depth, 24, "depth is 24") + a.Equal(f.Rate, 48000, "depth is 48000") + + samples = samples + len(f.Buffer) + + for { + f, err := d.ReadFrame() + + if err == nil || err == io.EOF { + if f != nil { + samples = samples + len(f.Buffer) + } + } else { + a.Equal(err, nil, "error reported") + break + } + + if err == io.EOF { + break + } + } + + a.Equal(samples, 200000, "all samples read") + d.Close() +} + func TestDecodeReader(t *testing.T) { a := assert.New(t) @@ -109,6 +155,57 @@ func TestDecodeReader(t *testing.T) { d.Close() } +func TestDecodeReaderOgg(t *testing.T) { + a := assert.New(t) + + reader, _ := os.Open("testdata/nonexistent.ogg") + + d, err := NewDecoderReaderOgg(reader) + + a.Equal(d, (*Decoder)(nil), "decoder is nil") + a.Error(err) + + reader, _ = os.Open("testdata/sine24-00.ogg") + + d, err = NewDecoderReaderOgg(reader) + + a.Equal(err, nil, "err is nil") + a.Equal(d.Channels, 1, "channels is 1") + a.Equal(d.Depth, 24, "depth is 24") + a.Equal(d.Rate, 48000, "depth is 48000") + + samples := 0 + + f, err := d.ReadFrame() + + a.Equal(err, nil, "err is nil") + a.Equal(f.Channels, 1, "channels is 1") + a.Equal(f.Depth, 24, "depth is 24") + a.Equal(f.Rate, 48000, "depth is 48000") + + samples = samples + len(f.Buffer) + + for { + f, err := d.ReadFrame() + + if err == nil || err == io.EOF { + if f != nil { + samples = samples + len(f.Buffer) + } + } else { + a.Equal(err, nil, "error reported") + break + } + + if err == io.EOF { + break + } + } + + a.Equal(samples, 200000, "all samples read") + d.Close() +} + func TestEncode(t *testing.T) { a := assert.New(t) diff --git a/testdata/sine24-00.ogg b/testdata/sine24-00.ogg new file mode 100644 index 0000000..c7c54cf Binary files /dev/null and b/testdata/sine24-00.ogg differ