2016-08-12 08:20:15 +00:00
|
|
|
// Copyright 2015-2016 Cocoon Labs Ltd.
|
2015-03-05 11:09:17 +00:00
|
|
|
//
|
|
|
|
// See LICENSE file for terms and conditions.
|
|
|
|
|
|
|
|
// Package libflac provides Go bindings to the libFLAC codec library.
|
|
|
|
package libflac
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2015-05-12 15:47:09 +00:00
|
|
|
"reflect"
|
2015-03-05 11:09:17 +00:00
|
|
|
"runtime"
|
2016-02-09 17:23:55 +00:00
|
|
|
"sync"
|
2015-03-05 11:09:17 +00:00
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
2015-03-13 12:54:07 +00:00
|
|
|
#cgo pkg-config: flac
|
2015-03-05 11:09:17 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#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 *);
|
2015-05-12 15:51:39 +00:00
|
|
|
|
2015-05-12 15:47:09 +00:00
|
|
|
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 *);
|
2015-03-05 11:09:17 +00:00
|
|
|
|
2015-05-13 13:35:41 +00:00
|
|
|
FLAC__StreamEncoderSeekStatus
|
|
|
|
encoderSeekCallback_cgo(const FLAC__StreamEncoder *,
|
|
|
|
FLAC__uint64,
|
|
|
|
void *);
|
|
|
|
|
|
|
|
FLAC__StreamEncoderTellStatus
|
|
|
|
encoderTellCallback_cgo(const FLAC__StreamEncoder *,
|
|
|
|
FLAC__uint64 *,
|
|
|
|
void *);
|
2015-03-05 11:09:17 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2015-03-09 10:52:19 +00:00
|
|
|
extern void
|
|
|
|
get_audio_samples(int32_t *output, const FLAC__int32 **input,
|
|
|
|
unsigned int blocksize, unsigned int channels);
|
2015-03-05 11:09:17 +00:00
|
|
|
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
2016-02-09 17:23:55 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-05-20 08:19:59 +00:00
|
|
|
func (m *decoderPtrMap) get(d *C.FLAC__StreamDecoder) *Decoder {
|
2016-02-09 17:23:55 +00:00
|
|
|
ptr := uintptr(unsafe.Pointer(d))
|
|
|
|
m.RLock()
|
|
|
|
defer m.RUnlock()
|
|
|
|
return m.ptrs[ptr]
|
|
|
|
}
|
|
|
|
|
2016-05-20 08:19:59 +00:00
|
|
|
func (m *decoderPtrMap) add(d *Decoder) {
|
2016-02-09 17:23:55 +00:00
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
m.ptrs[uintptr(unsafe.Pointer(d.d))] = d
|
|
|
|
}
|
|
|
|
|
2016-05-20 08:19:59 +00:00
|
|
|
func (m *decoderPtrMap) del(d *Decoder) {
|
2016-02-09 17:23:55 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-06-23 11:30:36 +00:00
|
|
|
func (m *encoderPtrMap) get(e *C.FLAC__StreamEncoder) *Encoder {
|
2016-02-09 17:23:55 +00:00
|
|
|
ptr := uintptr(unsafe.Pointer(e))
|
|
|
|
m.RLock()
|
|
|
|
defer m.RUnlock()
|
|
|
|
return m.ptrs[ptr]
|
|
|
|
}
|
|
|
|
|
2016-06-23 11:30:36 +00:00
|
|
|
func (m *encoderPtrMap) add(e *Encoder) {
|
2016-02-09 17:23:55 +00:00
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
m.ptrs[uintptr(unsafe.Pointer(e.e))] = e
|
|
|
|
}
|
|
|
|
|
2016-06-23 11:30:36 +00:00
|
|
|
func (m *encoderPtrMap) del(e *Encoder) {
|
2016-02-09 17:23:55 +00:00
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
delete(m.ptrs, uintptr(unsafe.Pointer(e.e)))
|
|
|
|
}
|
|
|
|
|
|
|
|
var encoderPtrs = encoderPtrMap{ptrs: make(map[uintptr]*Encoder)}
|
|
|
|
|
2015-05-13 13:35:41 +00:00
|
|
|
type FlacWriter interface {
|
|
|
|
io.Writer
|
|
|
|
io.Closer
|
|
|
|
io.Seeker
|
|
|
|
}
|
|
|
|
|
2015-03-05 11:09:17 +00:00
|
|
|
// Frame is an interleaved buffer of audio data with the specified parameters.
|
|
|
|
type Frame struct {
|
|
|
|
Channels int
|
2015-03-12 14:17:52 +00:00
|
|
|
Depth int
|
|
|
|
Rate int
|
|
|
|
Buffer []int32
|
2015-03-05 11:09:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Decoder is a FLAC decoder.
|
|
|
|
type Decoder struct {
|
2015-03-12 14:17:52 +00:00
|
|
|
d *C.FLAC__StreamDecoder
|
2015-05-12 15:47:09 +00:00
|
|
|
reader io.ReadCloser
|
2015-03-05 11:09:17 +00:00
|
|
|
Channels int
|
2015-03-12 14:17:52 +00:00
|
|
|
Depth int
|
|
|
|
Rate int
|
|
|
|
error bool
|
2015-03-05 11:09:17 +00:00
|
|
|
errorStr string
|
2015-03-12 14:17:52 +00:00
|
|
|
frame *Frame
|
2015-03-05 11:09:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Encoder is a FLAC encoder.
|
|
|
|
type Encoder struct {
|
2015-03-12 14:17:52 +00:00
|
|
|
e *C.FLAC__StreamEncoder
|
2015-05-13 13:35:41 +00:00
|
|
|
writer FlacWriter
|
2015-03-05 11:09:17 +00:00
|
|
|
Channels int
|
2015-03-12 14:17:52 +00:00
|
|
|
Depth int
|
|
|
|
Rate int
|
2015-03-05 11:09:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//export decoderErrorCallback
|
|
|
|
func decoderErrorCallback(d *C.FLAC__StreamDecoder, status C.FLAC__StreamDecoderErrorStatus, data unsafe.Pointer) {
|
2016-02-09 17:23:55 +00:00
|
|
|
decoder := decoderPtrs.get(d)
|
2015-03-05 11:09:17 +00:00
|
|
|
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) {
|
2016-02-09 17:23:55 +00:00
|
|
|
decoder := decoderPtrs.get(d)
|
2015-03-05 11:09:17 +00:00
|
|
|
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
|
2015-03-12 14:17:52 +00:00
|
|
|
func decoderWriteCallback(d *C.FLAC__StreamDecoder, frame *C.FLAC__Frame, buffer **C.FLAC__int32, data unsafe.Pointer) C.FLAC__StreamDecoderWriteStatus {
|
2016-02-09 17:23:55 +00:00
|
|
|
decoder := decoderPtrs.get(d)
|
2015-03-05 11:09:17 +00:00
|
|
|
blocksize := int(frame.header.blocksize)
|
|
|
|
decoder.frame = new(Frame)
|
|
|
|
f := decoder.frame
|
|
|
|
f.Channels = decoder.Channels
|
|
|
|
f.Depth = decoder.Depth
|
|
|
|
f.Rate = decoder.Rate
|
2015-03-12 14:17:52 +00:00
|
|
|
f.Buffer = make([]int32, blocksize*decoder.Channels)
|
2015-03-09 10:52:19 +00:00
|
|
|
C.get_audio_samples((*C.int32_t)(&f.Buffer[0]), buffer, C.uint(blocksize), C.uint(decoder.Channels))
|
2015-03-05 11:09:17 +00:00
|
|
|
return C.FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:47:09 +00:00
|
|
|
//export decoderReadCallback
|
|
|
|
func decoderReadCallback(d *C.FLAC__StreamDecoder, buffer *C.FLAC__byte, bytes *C.size_t, data unsafe.Pointer) C.FLAC__StreamDecoderReadStatus {
|
2016-02-09 17:23:55 +00:00
|
|
|
decoder := decoderPtrs.get(d)
|
2015-05-12 15:47:09 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-03-05 11:09:17 +00:00
|
|
|
// 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,
|
2015-05-12 15:51:39 +00:00
|
|
|
(C.FLAC__StreamDecoderWriteCallback)(unsafe.Pointer(C.decoderWriteCallback_cgo)),
|
|
|
|
(C.FLAC__StreamDecoderMetadataCallback)(unsafe.Pointer(C.decoderMetadataCallback_cgo)),
|
|
|
|
(C.FLAC__StreamDecoderErrorCallback)(unsafe.Pointer(C.decoderErrorCallback_cgo)),
|
2016-02-09 17:23:55 +00:00
|
|
|
nil)
|
2015-03-05 11:09:17 +00:00
|
|
|
if status != C.FLAC__STREAM_DECODER_INIT_STATUS_OK {
|
|
|
|
return nil, errors.New("failed to open file")
|
|
|
|
}
|
2016-02-09 17:23:55 +00:00
|
|
|
decoderPtrs.add(d)
|
2015-03-05 11:09:17 +00:00
|
|
|
ret := C.FLAC__stream_decoder_process_until_end_of_metadata(d.d)
|
2016-05-19 16:48:09 +00:00
|
|
|
if ret == 0 || d.error || d.Channels == 0 {
|
2015-03-05 11:09:17 +00:00
|
|
|
return nil, fmt.Errorf("failed to process metadata %s", d.errorStr)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:47:09 +00:00
|
|
|
// 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,
|
2015-05-12 15:51:39 +00:00
|
|
|
(C.FLAC__StreamDecoderWriteCallback)(unsafe.Pointer(C.decoderWriteCallback_cgo)),
|
|
|
|
(C.FLAC__StreamDecoderMetadataCallback)(unsafe.Pointer(C.decoderMetadataCallback_cgo)),
|
|
|
|
(C.FLAC__StreamDecoderErrorCallback)(unsafe.Pointer(C.decoderErrorCallback_cgo)),
|
2016-02-09 17:23:55 +00:00
|
|
|
nil)
|
2015-05-12 15:47:09 +00:00
|
|
|
if status != C.FLAC__STREAM_DECODER_INIT_STATUS_OK {
|
|
|
|
return nil, errors.New("failed to open stream")
|
|
|
|
}
|
2016-02-09 17:23:55 +00:00
|
|
|
decoderPtrs.add(d)
|
2015-05-12 15:47:09 +00:00
|
|
|
ret := C.FLAC__stream_decoder_process_until_end_of_metadata(d.d)
|
2016-05-19 16:48:09 +00:00
|
|
|
if ret == 0 || d.error || d.Channels == 0 {
|
2015-05-12 15:47:09 +00:00
|
|
|
return nil, fmt.Errorf("failed to process metadata %s", d.errorStr)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-03-05 11:09:17 +00:00
|
|
|
// Close closes a decoder and frees the resources.
|
|
|
|
func (d *Decoder) Close() {
|
|
|
|
if d.d != nil {
|
|
|
|
C.FLAC__stream_decoder_delete(d.d)
|
2016-02-09 17:23:55 +00:00
|
|
|
decoderPtrs.del(d)
|
2016-02-22 16:57:36 +00:00
|
|
|
d.d = nil
|
2015-03-05 11:09:17 +00:00
|
|
|
}
|
2015-05-12 15:47:09 +00:00
|
|
|
if d.reader != nil {
|
|
|
|
d.reader.Close()
|
|
|
|
}
|
2015-03-05 11:09:17 +00:00
|
|
|
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)
|
2016-05-19 16:48:09 +00:00
|
|
|
if ret == 0 || d.error {
|
2015-03-05 11:09:17 +00:00
|
|
|
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) {
|
2015-03-27 13:10:14 +00:00
|
|
|
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")
|
|
|
|
}
|
2015-03-05 11:09:17 +00:00
|
|
|
e = new(Encoder)
|
|
|
|
e.e = C.FLAC__stream_encoder_new()
|
|
|
|
if e.e == nil {
|
|
|
|
return nil, errors.New("failed to create decoder")
|
|
|
|
}
|
2016-02-09 17:23:55 +00:00
|
|
|
encoderPtrs.add(e)
|
2015-03-05 11:09:17 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:47:09 +00:00
|
|
|
//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 {
|
2016-02-09 17:23:55 +00:00
|
|
|
encoder := encoderPtrs.get(e)
|
2015-05-12 15:47:09 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-05-13 13:35:41 +00:00
|
|
|
//export encoderSeekCallback
|
|
|
|
func encoderSeekCallback(e *C.FLAC__StreamEncoder, absPos C.FLAC__uint64, data unsafe.Pointer) C.FLAC__StreamEncoderWriteStatus {
|
2016-02-09 17:23:55 +00:00
|
|
|
encoder := encoderPtrs.get(e)
|
2015-05-13 13:35:41 +00:00
|
|
|
_, err := encoder.writer.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__StreamEncoderWriteStatus {
|
2016-02-09 17:23:55 +00:00
|
|
|
encoder := encoderPtrs.get(e)
|
2015-05-13 13:35:41 +00:00
|
|
|
newPos, err := encoder.writer.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
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewEncoderWriter creates a new Encoder object from a FlacWriter.
|
|
|
|
func NewEncoderWriter(writer FlacWriter, channels int, depth int, rate int) (e *Encoder, err error) {
|
2015-05-12 15:47:09 +00:00
|
|
|
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")
|
|
|
|
}
|
2016-02-09 17:23:55 +00:00
|
|
|
encoderPtrs.add(e)
|
2015-05-12 15:47:09 +00:00
|
|
|
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,
|
2015-05-13 13:35:41 +00:00
|
|
|
(C.FLAC__StreamEncoderWriteCallback)(unsafe.Pointer(C.encoderWriteCallback_cgo)),
|
|
|
|
(C.FLAC__StreamEncoderSeekCallback)(unsafe.Pointer(C.encoderSeekCallback_cgo)),
|
|
|
|
(C.FLAC__StreamEncoderTellCallback)(unsafe.Pointer(C.encoderTellCallback_cgo)),
|
2016-02-09 17:23:55 +00:00
|
|
|
nil, nil)
|
2015-05-12 15:47:09 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-03-05 11:09:17 +00:00
|
|
|
// 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")
|
|
|
|
}
|
2015-03-27 13:10:14 +00:00
|
|
|
if len(f.Buffer) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2015-03-12 14:17:52 +00:00
|
|
|
ret := C.FLAC__stream_encoder_process_interleaved(e.e, (*C.FLAC__int32)(&f.Buffer[0]), C.uint(len(f.Buffer)/e.Channels))
|
2015-03-05 11:09:17 +00:00
|
|
|
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)
|
2017-02-09 11:13:11 +00:00
|
|
|
C.FLAC__stream_encoder_delete(e.e)
|
2016-02-09 17:23:55 +00:00
|
|
|
encoderPtrs.del(e)
|
2016-02-22 16:57:36 +00:00
|
|
|
e.e = nil
|
2015-03-05 11:09:17 +00:00
|
|
|
}
|
|
|
|
runtime.SetFinalizer(e, nil)
|
|
|
|
}
|