// Copyright 2015-2016 Cocoon Labs Ltd. // // See LICENSE file for terms and conditions. // Package libflac provides Go bindings to the libFLAC codec library. package libflac import ( "errors" "fmt" "io" "reflect" "runtime" "sync" "unsafe" ) /* #cgo pkg-config: flac #include #include "FLAC/stream_decoder.h" #include "FLAC/stream_encoder.h" extern void decoderErrorCallback_cgo(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus, void *); extern void decoderMetadataCallback_cgo(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *, void *); extern FLAC__StreamDecoderWriteStatus decoderWriteCallback_cgo(const FLAC__StreamDecoder *, const FLAC__Frame *, const FLAC__int32 **, void *); FLAC__StreamDecoderReadStatus decoderReadCallback_cgo(const FLAC__StreamDecoder *, const FLAC__byte *, size_t *, void *); FLAC__StreamEncoderReadStatus encoderReadCallback_cgo(const FLAC__StreamEncoder *encoder, const FLAC__byte *, size_t *, void *); FLAC__StreamEncoderWriteStatus encoderWriteCallback_cgo(const FLAC__StreamEncoder *, const FLAC__byte *, size_t, unsigned, unsigned, void *); FLAC__StreamEncoderSeekStatus encoderSeekCallback_cgo(const FLAC__StreamEncoder *, FLAC__uint64, void *); FLAC__StreamEncoderTellStatus encoderTellCallback_cgo(const FLAC__StreamEncoder *, FLAC__uint64 *, void *); extern const char * get_decoder_error_str(FLAC__StreamDecoderErrorStatus status); extern int get_decoder_channels(FLAC__StreamMetadata *metadata); extern int get_decoder_depth(FLAC__StreamMetadata *metadata); extern int get_decoder_rate(FLAC__StreamMetadata *metadata); extern void get_audio_samples(int32_t *output, const FLAC__int32 **input, unsigned int blocksize, unsigned int channels); */ import "C" // Go 1.6 does not allow us to pass go pointers into C that will be stored and // used in callbacks and suggests we use a value lookup for pointer callbacks. // https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md // Concurrent safe map for mapping pointers between C and Go. type decoderPtrMap struct { sync.RWMutex ptrs map[uintptr]*Decoder } func (m *decoderPtrMap) get(d *C.FLAC__StreamDecoder) *Decoder { ptr := uintptr(unsafe.Pointer(d)) m.RLock() defer m.RUnlock() return m.ptrs[ptr] } func (m *decoderPtrMap) add(d *Decoder) { m.Lock() defer m.Unlock() m.ptrs[uintptr(unsafe.Pointer(d.d))] = d } func (m *decoderPtrMap) del(d *Decoder) { m.Lock() defer m.Unlock() delete(m.ptrs, uintptr(unsafe.Pointer(d.d))) } var decoderPtrs = decoderPtrMap{ptrs: make(map[uintptr]*Decoder)} // Concurrent safe map for mapping pointers between C and Go. type encoderPtrMap struct { sync.RWMutex ptrs map[uintptr]*Encoder } func (m *encoderPtrMap) get(e *C.FLAC__StreamEncoder) *Encoder { ptr := uintptr(unsafe.Pointer(e)) m.RLock() defer m.RUnlock() return m.ptrs[ptr] } func (m *encoderPtrMap) add(e *Encoder) { m.Lock() defer m.Unlock() m.ptrs[uintptr(unsafe.Pointer(e.e))] = e } func (m *encoderPtrMap) del(e *Encoder) { m.Lock() defer m.Unlock() delete(m.ptrs, uintptr(unsafe.Pointer(e.e))) } var encoderPtrs = encoderPtrMap{ptrs: make(map[uintptr]*Encoder)} type FlacWriter interface { io.Writer io.Closer io.Seeker } // Frame is an interleaved buffer of audio data with the specified parameters. type Frame struct { Channels int Depth int Rate int Buffer []int32 } // Decoder is a FLAC decoder. type Decoder struct { d *C.FLAC__StreamDecoder reader io.ReadCloser Channels int Depth int Rate int error bool errorStr string frame *Frame } // Encoder is a FLAC encoder. type Encoder struct { e *C.FLAC__StreamEncoder writer io.WriteCloser writeSeeker FlacWriter Channels int Depth int Rate int } //export decoderErrorCallback func decoderErrorCallback(d *C.FLAC__StreamDecoder, status C.FLAC__StreamDecoderErrorStatus, data unsafe.Pointer) { decoder := decoderPtrs.get(d) decoder.error = true decoder.errorStr = C.GoString(C.get_decoder_error_str(status)) } //export decoderMetadataCallback func decoderMetadataCallback(d *C.FLAC__StreamDecoder, metadata *C.FLAC__StreamMetadata, data unsafe.Pointer) { decoder := decoderPtrs.get(d) if metadata._type == C.FLAC__METADATA_TYPE_STREAMINFO { decoder.Channels = int(C.get_decoder_channels(metadata)) decoder.Depth = int(C.get_decoder_depth(metadata)) decoder.Rate = int(C.get_decoder_rate(metadata)) } } //export decoderWriteCallback func decoderWriteCallback(d *C.FLAC__StreamDecoder, frame *C.FLAC__Frame, buffer **C.FLAC__int32, data unsafe.Pointer) C.FLAC__StreamDecoderWriteStatus { decoder := decoderPtrs.get(d) blocksize := int(frame.header.blocksize) decoder.frame = new(Frame) f := decoder.frame f.Channels = decoder.Channels f.Depth = decoder.Depth f.Rate = decoder.Rate f.Buffer = make([]int32, blocksize*decoder.Channels) C.get_audio_samples((*C.int32_t)(&f.Buffer[0]), buffer, C.uint(blocksize), C.uint(decoder.Channels)) 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 := decoderPtrs.get(d) 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) 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_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 } // 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) 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.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 } // 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 { C.FLAC__stream_decoder_delete(d.d) decoderPtrs.del(d) d.d = nil } if d.reader != nil { d.reader.Close() } runtime.SetFinalizer(d, nil) } // ReadFrame reads a frame of audio data from the decoder. func (d *Decoder) ReadFrame() (f *Frame, err error) { ret := C.FLAC__stream_decoder_process_single(d.d) if ret == 0 || d.error { return nil, errors.New("error reading frame") } state := C.FLAC__stream_decoder_get_state(d.d) if state == C.FLAC__STREAM_DECODER_END_OF_STREAM { err = io.EOF } f = d.frame d.frame = nil return } // NewEncoder creates a new Encoder object. func NewEncoder(name string, 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") } encoderPtrs.add(e) c := C.CString(name) defer C.free(unsafe.Pointer(c)) 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_file(e.e, c, nil, nil) 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 } // NewEncoderOgg creates a new Encoder object. func NewEncoderOgg(name string, 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") } encoderPtrs.add(e) c := C.CString(name) defer C.free(unsafe.Pointer(c)) 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_ogg_file(e.e, c, nil, nil) 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 } //export encoderReadCallback func encoderReadCallback(e *C.FLAC__StreamEncoder, buffer *C.FLAC__byte, bytes *C.size_t, data unsafe.Pointer) C.FLAC__StreamEncoderReadStatus { encoder := encoderPtrs.get(e) numBytes := int(*bytes) if numBytes <= 0 { return C.FLAC__STREAM_ENCODER_READ_STATUS_ABORT } hdr := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(buffer)), Len: numBytes, Cap: numBytes, } buf := *(*[]byte)(unsafe.Pointer(&hdr)) n, err := encoder.writeSeeker.(io.Reader).Read(buf) *bytes = C.size_t(n) if err == io.EOF && n == 0 { return C.FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM } else if err != nil && err != io.EOF { return C.FLAC__STREAM_ENCODER_READ_STATUS_ABORT } return C.FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE } //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 := encoderPtrs.get(e) 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 } //export encoderSeekCallback func encoderSeekCallback(e *C.FLAC__StreamEncoder, absPos C.FLAC__uint64, data unsafe.Pointer) C.FLAC__StreamEncoderSeekStatus { encoder := encoderPtrs.get(e) if encoder.writeSeeker == nil { return C.FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR } _, err := encoder.writeSeeker.Seek(int64(absPos), 0) if err != nil { return C.FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR } return C.FLAC__STREAM_ENCODER_SEEK_STATUS_OK } //export encoderTellCallback func encoderTellCallback(e *C.FLAC__StreamEncoder, absPos *C.FLAC__uint64, data unsafe.Pointer) C.FLAC__StreamEncoderTellStatus { encoder := encoderPtrs.get(e) if encoder.writeSeeker == nil { return C.FLAC__STREAM_ENCODER_TELL_STATUS_ERROR } newPos, err := encoder.writeSeeker.Seek(0, 1) if err != nil { return C.FLAC__STREAM_ENCODER_TELL_STATUS_ERROR } *absPos = C.FLAC__uint64(newPos) return C.FLAC__STREAM_ENCODER_TELL_STATUS_OK } // NewEncoderWriteSeeker creates a new Encoder object from a FlacWriter. func NewEncoderWriteSeeker(writer FlacWriter, channels int, depth int, rate int, compressionLevel int, streamable bool, blockSize 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") } encoderPtrs.add(e) e.writer = writer e.writeSeeker = 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)) C.FLAC__stream_encoder_set_compression_level(e.e, C.uint(compressionLevel)) C.FLAC__stream_encoder_set_blocksize(e.e, C.uint(blockSize)) if streamable { C.FLAC__stream_encoder_set_streamable_subset(e.e, C.FLAC__bool(1)) } else { C.FLAC__stream_encoder_set_streamable_subset(e.e, C.FLAC__bool(0)) } status := C.FLAC__stream_encoder_init_stream(e.e, (C.FLAC__StreamEncoderWriteCallback)(unsafe.Pointer(C.encoderWriteCallback_cgo)), (C.FLAC__StreamEncoderSeekCallback)(unsafe.Pointer(C.encoderSeekCallback_cgo)), (C.FLAC__StreamEncoderTellCallback)(unsafe.Pointer(C.encoderTellCallback_cgo)), nil, nil) if status != C.FLAC__STREAM_ENCODER_INIT_STATUS_OK { return nil, fmt.Errorf("failed to open file, status = %d, state = %d", int(status), int(C.FLAC__stream_encoder_get_state(e.e))) } e.Channels = channels e.Depth = depth e.Rate = rate return } // NewEncoderWriteSeekerOgg creates a new Encoder object from a FlacWriter. func NewEncoderWriteSeekerOgg(writer FlacWriter, channels int, depth int, rate int, compressionLevel int, streamable bool, blockSize int) (e *Encoder, err error) { if _, ok := writer.(io.Reader); !ok { return nil, errors.New("writer must also implement io.Reader") } 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") } encoderPtrs.add(e) e.writer = writer e.writeSeeker = 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)) C.FLAC__stream_encoder_set_compression_level(e.e, C.uint(compressionLevel)) C.FLAC__stream_encoder_set_blocksize(e.e, C.uint(blockSize)) if streamable { C.FLAC__stream_encoder_set_streamable_subset(e.e, C.FLAC__bool(1)) } else { C.FLAC__stream_encoder_set_streamable_subset(e.e, C.FLAC__bool(0)) } status := C.FLAC__stream_encoder_init_ogg_stream(e.e, (C.FLAC__StreamEncoderReadCallback)(unsafe.Pointer(C.encoderReadCallback_cgo)), (C.FLAC__StreamEncoderWriteCallback)(unsafe.Pointer(C.encoderWriteCallback_cgo)), (C.FLAC__StreamEncoderSeekCallback)(unsafe.Pointer(C.encoderSeekCallback_cgo)), (C.FLAC__StreamEncoderTellCallback)(unsafe.Pointer(C.encoderTellCallback_cgo)), nil, nil) if status != C.FLAC__STREAM_ENCODER_INIT_STATUS_OK { return nil, fmt.Errorf("failed to open file, status = %d, state = %d", int(status), int(C.FLAC__stream_encoder_get_state(e.e))) } e.Channels = channels e.Depth = depth e.Rate = rate return } // NewEncoderWriter creates a new Encoder object from an io.WriteCloser. func NewEncoderWriter(writer io.WriteCloser, channels int, depth int, rate int, compressionLevel int, streamable bool, blockSize 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") } encoderPtrs.add(e) 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)) C.FLAC__stream_encoder_set_compression_level(e.e, C.uint(compressionLevel)) C.FLAC__stream_encoder_set_blocksize(e.e, C.uint(blockSize)) if streamable { C.FLAC__stream_encoder_set_streamable_subset(e.e, C.FLAC__bool(1)) } else { C.FLAC__stream_encoder_set_streamable_subset(e.e, C.FLAC__bool(0)) } status := C.FLAC__stream_encoder_init_stream(e.e, (C.FLAC__StreamEncoderWriteCallback)(unsafe.Pointer(C.encoderWriteCallback_cgo)), nil, nil, nil, nil) if status != C.FLAC__STREAM_ENCODER_INIT_STATUS_OK { return nil, fmt.Errorf("failed to open file, status = %d, state = %d", int(status), int(C.FLAC__stream_encoder_get_state(e.e))) } e.Channels = channels e.Depth = depth e.Rate = rate return } // NewEncoderWriterOgg creates a new Encoder object from an io.WriteCloser. func NewEncoderWriterOgg(writer io.WriteCloser, channels int, depth int, rate int, compressionLevel int, streamable bool, blockSize 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") } encoderPtrs.add(e) 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)) C.FLAC__stream_encoder_set_compression_level(e.e, C.uint(compressionLevel)) C.FLAC__stream_encoder_set_blocksize(e.e, C.uint(blockSize)) if streamable { C.FLAC__stream_encoder_set_streamable_subset(e.e, C.FLAC__bool(1)) } else { C.FLAC__stream_encoder_set_streamable_subset(e.e, C.FLAC__bool(0)) } status := C.FLAC__stream_encoder_init_ogg_stream(e.e, nil, (C.FLAC__StreamEncoderWriteCallback)(unsafe.Pointer(C.encoderWriteCallback_cgo)), nil, nil, nil, nil) if status != C.FLAC__STREAM_ENCODER_INIT_STATUS_OK { return nil, fmt.Errorf("failed to open file, status = %d, state = %d", int(status), int(C.FLAC__stream_encoder_get_state(e.e))) } 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 { return errors.New("frame type does not match encoder") } if len(f.Buffer) == 0 { return } ret := C.FLAC__stream_encoder_process_interleaved(e.e, (*C.FLAC__int32)(&f.Buffer[0]), C.uint(len(f.Buffer)/e.Channels)) if ret == 0 { return errors.New("error encoding frame") } return } // Close closes an encoder and frees the resources. func (e *Encoder) Close() { if e.e != nil { C.FLAC__stream_encoder_finish(e.e) C.FLAC__stream_encoder_delete(e.e) encoderPtrs.del(e) e.e = nil } runtime.SetFinalizer(e, nil) } func Version() string { return C.GoString(C.FLAC__VERSION_STRING) } func Vendor() string { return C.GoString(C.FLAC__VENDOR_STRING) }