libflac: Add support for Reader and Writer I/O

Add support for encoding to a Writer and decoding from a Reader object.
This commit is contained in:
Will Newton 2015-05-12 16:47:09 +01:00
parent 65ef819a46
commit b583070538
3 changed files with 232 additions and 0 deletions

View file

@ -34,6 +34,26 @@ decoderWriteCallback_cgo(const FLAC__StreamDecoder *decoder,
return decoderWriteCallback(decoder, frame, buffer, data);
}
FLAC__StreamDecoderReadStatus
decoderReadCallback_cgo(const FLAC__StreamDecoder *decoder,
const FLAC__byte buffer[],
size_t *bytes,
void *data)
{
return decoderReadCallback(decoder, buffer, bytes, data);
}
FLAC__StreamEncoderWriteStatus
encoderWriteCallback_cgo(const FLAC__StreamEncoder *encoder,
const FLAC__byte buffer[],
size_t bytes, unsigned samples,
unsigned current_frame,
void *data)
{
return encoderWriteCallback(encoder, buffer, bytes, samples, current_frame,
data);
}
extern const char *
get_decoder_error_str(FLAC__StreamDecoderErrorStatus status)
{

View file

@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"io"
"reflect"
"runtime"
"unsafe"
)
@ -46,6 +47,18 @@ typedef FLAC__StreamDecoderWriteStatus (*decoderWriteCallbackFn)(const FLAC__Str
const FLAC__Frame *,
const FLAC__int32 **,
void *);
FLAC__StreamDecoderReadStatus
decoderReadCallback_cgo(const FLAC__StreamDecoder *,
const FLAC__byte *,
size_t *,
void *);
FLAC__StreamEncoderWriteStatus
encoderWriteCallback_cgo(const FLAC__StreamEncoder *,
const FLAC__byte *,
size_t, unsigned,
unsigned,
void *);
extern const char *
@ -78,6 +91,7 @@ type Frame struct {
// Decoder is a FLAC decoder.
type Decoder struct {
d *C.FLAC__StreamDecoder
reader io.ReadCloser
Channels int
Depth int
Rate int
@ -89,6 +103,7 @@ type Decoder struct {
// Encoder is a FLAC encoder.
type Encoder struct {
e *C.FLAC__StreamEncoder
writer io.WriteCloser
Channels int
Depth int
Rate int
@ -125,6 +140,29 @@ func decoderWriteCallback(d *C.FLAC__StreamDecoder, frame *C.FLAC__Frame, buffer
return C.FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE
}
//export decoderReadCallback
func decoderReadCallback(d *C.FLAC__StreamDecoder, buffer *C.FLAC__byte, bytes *C.size_t, data unsafe.Pointer) C.FLAC__StreamDecoderReadStatus {
decoder := (*Decoder)(data)
numBytes := int(*bytes)
if numBytes <= 0 {
return C.FLAC__STREAM_DECODER_READ_STATUS_ABORT
}
hdr := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(buffer)),
Len: numBytes,
Cap: numBytes,
}
buf := *(*[]byte)(unsafe.Pointer(&hdr))
n, err := decoder.reader.Read(buf)
*bytes = C.size_t(n)
if err == io.EOF && n == 0 {
return C.FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM
} else if err != nil && err != io.EOF {
return C.FLAC__STREAM_DECODER_READ_STATUS_ABORT
}
return C.FLAC__STREAM_DECODER_READ_STATUS_CONTINUE
}
// NewDecoder creates a new Decoder object.
func NewDecoder(name string) (d *Decoder, err error) {
d = new(Decoder)
@ -150,12 +188,41 @@ func NewDecoder(name string) (d *Decoder, err error) {
return
}
// NewDecoderReader creates a new Decoder object from a Reader.
func NewDecoderReader(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_stream(d.d,
(C.FLAC__StreamDecoderReadCallback)(unsafe.Pointer(C.decoderReadCallback_cgo)),
nil, nil, nil, nil,
(C.decoderWriteCallbackFn)(unsafe.Pointer(C.decoderWriteCallback_cgo)),
(C.decoderMetadataCallbackFn)(unsafe.Pointer(C.decoderMetadataCallback_cgo)),
(C.decoderErrorCallbackFn)(unsafe.Pointer(C.decoderErrorCallback_cgo)),
unsafe.Pointer(d))
if status != C.FLAC__STREAM_DECODER_INIT_STATUS_OK {
return nil, errors.New("failed to open stream")
}
ret := C.FLAC__stream_decoder_process_until_end_of_metadata(d.d)
if ret == 0 || d.error == true || 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 {
C.FLAC__stream_decoder_delete(d.d)
d.d = nil
}
if d.reader != nil {
d.reader.Close()
}
runtime.SetFinalizer(d, nil)
}
@ -203,6 +270,55 @@ func NewEncoder(name string, channels int, depth int, rate int) (e *Encoder, err
return
}
//export encoderWriteCallback
func encoderWriteCallback(e *C.FLAC__StreamEncoder, buffer *C.FLAC__byte, bytes C.size_t, samples, current_frame C.unsigned, data unsafe.Pointer) C.FLAC__StreamEncoderWriteStatus {
encoder := (*Encoder)(data)
numBytes := int(bytes)
if numBytes <= 0 {
return C.FLAC__STREAM_DECODER_READ_STATUS_ABORT
}
hdr := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(buffer)),
Len: numBytes,
Cap: numBytes,
}
buf := *(*[]byte)(unsafe.Pointer(&hdr))
_, err := encoder.writer.Write(buf)
if err != nil {
return C.FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR
}
return C.FLAC__STREAM_ENCODER_WRITE_STATUS_OK
}
// NewEncoderWriter creates a new Encoder object from a Writer.
func NewEncoderWriter(writer io.WriteCloser, channels int, depth int, rate int) (e *Encoder, err error) {
if channels == 0 {
return nil, errors.New("channels must be greater than 0")
}
if !(depth == 16 || depth == 24) {
return nil, errors.New("depth must be 16 or 24")
}
e = new(Encoder)
e.e = C.FLAC__stream_encoder_new()
if e.e == nil {
return nil, errors.New("failed to create decoder")
}
e.writer = writer
runtime.SetFinalizer(e, (*Encoder).Close)
C.FLAC__stream_encoder_set_channels(e.e, C.uint(channels))
C.FLAC__stream_encoder_set_bits_per_sample(e.e, C.uint(depth))
C.FLAC__stream_encoder_set_sample_rate(e.e, C.uint(rate))
status := C.FLAC__stream_encoder_init_stream(e.e,
(C.FLAC__StreamEncoderWriteCallback)(unsafe.Pointer(C.encoderWriteCallback_cgo)), nil, nil, nil, unsafe.Pointer(e))
if status != C.FLAC__STREAM_ENCODER_INIT_STATUS_OK {
return nil, errors.New("failed to open file")
}
e.Channels = channels
e.Depth = depth
e.Rate = rate
return
}
// WriteFrame writes a frame of audio data to the encoder.
func (e *Encoder) WriteFrame(f Frame) (err error) {
if f.Channels != e.Channels || f.Depth != e.Depth || f.Rate != e.Rate {
@ -224,5 +340,8 @@ func (e *Encoder) Close() {
C.FLAC__stream_encoder_finish(e.e)
e.e = nil
}
if e.writer != nil {
e.writer.Close()
}
runtime.SetFinalizer(e, nil)
}

View file

@ -58,6 +58,57 @@ func TestDecode(t *testing.T) {
d.Close()
}
func TestDecodeReader(t *testing.T) {
a := assert.New(t)
reader, _ := os.Open("data/nonexistent.flac")
d, err := NewDecoderReader(reader)
a.Equal(d, (*Decoder)(nil), "decoder is nil")
a.Error(err)
reader, _ = os.Open("data/sine24-00.flac")
d, err = NewDecoderReader(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)
@ -164,3 +215,45 @@ func TestRoundTripStereo(t *testing.T) {
os.Remove(outputFile)
}
func TestRoundTripReaderWriter(t *testing.T) {
a := assert.New(t)
inputFile := "data/sine24-00.flac"
outputFile := "data/test.flac"
reader, _ := os.Open(inputFile)
d, err := NewDecoderReader(reader)
a.Equal(err, nil, "err is nil")
writer, _ := os.Create(outputFile)
e, err := NewEncoderWriter(writer, d.Channels, d.Depth, d.Rate)
samples := 0
for {
f, err := d.ReadFrame()
if err == nil || err == io.EOF {
if f != nil {
_ = e.WriteFrame(*f)
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()
e.Close()
os.Remove(outputFile)
}