289 lines
7.4 KiB
Go
289 lines
7.4 KiB
Go
package go_vorbis
|
|
|
|
/*
|
|
#cgo pkg-config: vorbisfile
|
|
#include <vorbis/vorbisfile.h>
|
|
|
|
typedef size_t (*vorbisfile_read_func)(void *ptr, size_t size, size_t nmemb, void *datasource);
|
|
typedef int (*vorbisfile_seek_func)(void *datasource, ogg_int64_t offset, int whence);
|
|
typedef long (*vorbisfile_tell_func)(void *datasource);
|
|
typedef int (*vorbisfile_close_func)(void *datasource);
|
|
|
|
size_t cgoReadCallback(void *ptr, size_t size, size_t nmemb, void *datasource);
|
|
int cgoSeekCallback(void *datasource, ogg_int64_t offset, int whence);
|
|
long cgoTellCallback(void *datasource);
|
|
int cgoCloseCallback(void *datasource);
|
|
|
|
int handle_ov_open_callbacks(uintptr_t datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);
|
|
*/
|
|
import "C"
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"runtime/cgo"
|
|
"unsafe"
|
|
)
|
|
|
|
type Decoder struct {
|
|
file *C.OggVorbis_File
|
|
//allocated file memory
|
|
mem []byte
|
|
handle cgo.Handle
|
|
reader io.Reader
|
|
}
|
|
|
|
func NewDecoder(reader io.Reader) (*Decoder, error) {
|
|
d := &Decoder{
|
|
reader: reader,
|
|
mem: make([]byte, unsafe.Sizeof(C.OggVorbis_File{})),
|
|
}
|
|
|
|
d.file = (*C.OggVorbis_File)(unsafe.Pointer(&d.mem[0]))
|
|
d.handle = cgo.NewHandle(d)
|
|
if ret := C.handle_ov_open_callbacks(C.uintptr_t(d.handle), d.file, nil, 0, C.ov_callbacks{
|
|
read_func: C.vorbisfile_read_func(C.cgoReadCallback),
|
|
seek_func: nil,
|
|
close_func: nil,
|
|
tell_func: nil,
|
|
}); ret != 0 {
|
|
d.handle.Delete()
|
|
switch ret {
|
|
case C.OV_EREAD:
|
|
return nil, errors.New("a read from media returned an error")
|
|
case C.OV_ENOTVORBIS:
|
|
return nil, errors.New("bitstream does not contain any Vorbis data")
|
|
case C.OV_EVERSION:
|
|
return nil, errors.New("vorbis version mismatch")
|
|
case C.OV_EBADHEADER:
|
|
return nil, errors.New("invalid Vorbis bitstream header")
|
|
case C.OV_EFAULT:
|
|
return nil, errors.New("internal logic fault")
|
|
default:
|
|
return nil, errors.New("unknown error")
|
|
}
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
func NewSeekerDecoder(reader io.ReadSeeker) (*Decoder, error) {
|
|
d := &Decoder{
|
|
reader: reader,
|
|
mem: make([]byte, unsafe.Sizeof(C.OggVorbis_File{})),
|
|
}
|
|
|
|
d.file = (*C.OggVorbis_File)(unsafe.Pointer(&d.mem[0]))
|
|
d.handle = cgo.NewHandle(d)
|
|
if ret := C.handle_ov_open_callbacks(C.uintptr_t(d.handle), d.file, nil, 0, C.ov_callbacks{
|
|
read_func: C.vorbisfile_read_func(C.cgoReadCallback),
|
|
seek_func: C.vorbisfile_seek_func(C.cgoSeekCallback),
|
|
close_func: nil,
|
|
tell_func: C.vorbisfile_tell_func(C.cgoTellCallback),
|
|
}); ret != 0 {
|
|
d.handle.Delete()
|
|
switch ret {
|
|
case C.OV_EREAD:
|
|
return nil, errors.New("a read from media returned an error")
|
|
case C.OV_ENOTVORBIS:
|
|
return nil, errors.New("bitstream does not contain any Vorbis data")
|
|
case C.OV_EVERSION:
|
|
return nil, errors.New("vorbis version mismatch")
|
|
case C.OV_EBADHEADER:
|
|
return nil, errors.New("invalid Vorbis bitstream header")
|
|
case C.OV_EFAULT:
|
|
return nil, errors.New("internal logic fault")
|
|
default:
|
|
return nil, errors.New("unknown error")
|
|
}
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
func NewSeekCloserDecoder(reader io.ReadSeekCloser) (*Decoder, error) {
|
|
d := &Decoder{
|
|
reader: reader,
|
|
mem: make([]byte, unsafe.Sizeof(C.OggVorbis_File{})),
|
|
}
|
|
|
|
d.file = (*C.OggVorbis_File)(unsafe.Pointer(&d.mem[0]))
|
|
d.handle = cgo.NewHandle(d)
|
|
if ret := C.handle_ov_open_callbacks(C.uintptr_t(d.handle), d.file, nil, 0, C.ov_callbacks{
|
|
read_func: C.vorbisfile_read_func(C.cgoReadCallback),
|
|
seek_func: C.vorbisfile_seek_func(C.cgoSeekCallback),
|
|
close_func: C.vorbisfile_close_func(C.cgoCloseCallback),
|
|
tell_func: C.vorbisfile_tell_func(C.cgoTellCallback),
|
|
}); ret != 0 {
|
|
d.handle.Delete()
|
|
switch ret {
|
|
case C.OV_EREAD:
|
|
return nil, errors.New("a read from media returned an error")
|
|
case C.OV_ENOTVORBIS:
|
|
return nil, errors.New("bitstream does not contain any Vorbis data")
|
|
case C.OV_EVERSION:
|
|
return nil, errors.New("vorbis version mismatch")
|
|
case C.OV_EBADHEADER:
|
|
return nil, errors.New("invalid Vorbis bitstream header")
|
|
case C.OV_EFAULT:
|
|
return nil, errors.New("internal logic fault")
|
|
default:
|
|
return nil, errors.New("unknown error")
|
|
}
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
func (d *Decoder) ReadShort() ([]int16, int, error) {
|
|
info := C.ov_info(d.file, -1)
|
|
if info == nil {
|
|
return nil, 0, errors.New("unknown error")
|
|
}
|
|
|
|
channelCount := int(info.channels)
|
|
|
|
buf := make([]int16, 2048)
|
|
var bitstream C.int
|
|
ret := C.ov_read(d.file, (*C.char)(unsafe.Pointer(&buf[0])), C.int(len(buf)*2), C.int(0), C.int(2), C.int(1), &bitstream)
|
|
switch ret {
|
|
case C.OV_HOLE:
|
|
return nil, channelCount, errors.New("OV_HOLE")
|
|
case C.OV_EBADLINK:
|
|
return nil, channelCount, errors.New("OV_EBADLINK")
|
|
case C.OV_EINVAL:
|
|
return nil, channelCount, errors.New("OV_EINVAL")
|
|
case 0:
|
|
return nil, channelCount, io.EOF
|
|
default:
|
|
return buf[:ret/2], channelCount, nil
|
|
}
|
|
}
|
|
|
|
func (d *Decoder) ReadFloat() ([]float32, int, error) {
|
|
info := C.ov_info(d.file, -1)
|
|
if info == nil {
|
|
return nil, 0, errors.New("unknown error")
|
|
}
|
|
|
|
channelCount := int(info.channels)
|
|
|
|
var bitstream C.int
|
|
var pcmChannels **C.float
|
|
ret := C.ov_read_float(d.file, &pcmChannels, 1024, &bitstream)
|
|
switch ret {
|
|
case C.OV_HOLE:
|
|
return nil, channelCount, errors.New("OV_HOLE")
|
|
case C.OV_EBADLINK:
|
|
return nil, channelCount, errors.New("OV_EBADLINK")
|
|
case C.OV_EINVAL:
|
|
return nil, channelCount, errors.New("OV_EINVAL")
|
|
case 0:
|
|
return nil, channelCount, io.EOF
|
|
default:
|
|
|
|
buf := make([]float32, int(ret)*channelCount)
|
|
|
|
ptrSlice := unsafe.Slice(pcmChannels, channelCount)
|
|
for ch, ptr := range ptrSlice {
|
|
floatSlice := unsafe.Slice((*float32)(ptr), int(ret))
|
|
for i, sample := range floatSlice {
|
|
buf[i*channelCount+ch] = sample
|
|
}
|
|
}
|
|
|
|
return buf, channelCount, nil
|
|
}
|
|
}
|
|
|
|
type VorbisInformation struct {
|
|
Version int
|
|
Channels int
|
|
Rate int64
|
|
|
|
Bitrate struct {
|
|
Upper int64
|
|
Nominal int64
|
|
Lower int64
|
|
Window int64
|
|
}
|
|
}
|
|
|
|
func (d *Decoder) Information() *VorbisInformation {
|
|
info := C.ov_info(d.file, -1)
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
|
|
return &VorbisInformation{
|
|
Version: int(info.version),
|
|
Channels: int(info.channels),
|
|
Rate: int64(info.rate),
|
|
Bitrate: struct {
|
|
Upper int64
|
|
Nominal int64
|
|
Lower int64
|
|
Window int64
|
|
}{
|
|
Upper: int64(info.bitrate_upper),
|
|
Nominal: int64(info.bitrate_nominal),
|
|
Lower: int64(info.bitrate_lower),
|
|
Window: int64(info.bitrate_window),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (d *Decoder) Close() {
|
|
if d.file != nil {
|
|
defer d.handle.Delete()
|
|
C.ov_clear(d.file)
|
|
d.file = nil
|
|
return
|
|
}
|
|
}
|
|
|
|
//export cgoReadCallback
|
|
func cgoReadCallback(ptr unsafe.Pointer, size C.size_t, nmemb C.size_t, datasource unsafe.Pointer) C.size_t {
|
|
decoder := (cgo.Handle(datasource)).Value().(*Decoder)
|
|
|
|
slice := unsafe.Slice((*byte)(ptr), size*nmemb)
|
|
n, _ := io.ReadFull(decoder.reader, slice)
|
|
|
|
return C.size_t(n)
|
|
}
|
|
|
|
//export cgoSeekCallback
|
|
func cgoSeekCallback(datasource unsafe.Pointer, offset C.ogg_int64_t, whence C.int) C.int {
|
|
decoder := (cgo.Handle(datasource)).Value().(*Decoder)
|
|
|
|
_, err := decoder.reader.(io.Seeker).Seek(int64(offset), int(whence))
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
//export cgoTellCallback
|
|
func cgoTellCallback(datasource unsafe.Pointer) C.long {
|
|
decoder := (cgo.Handle(datasource)).Value().(*Decoder)
|
|
|
|
n, err := decoder.reader.(io.Seeker).Seek(0, io.SeekCurrent)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
|
|
return C.long(n)
|
|
}
|
|
|
|
//export cgoCloseCallback
|
|
func cgoCloseCallback(datasource unsafe.Pointer) C.int {
|
|
decoder := (cgo.Handle(datasource)).Value().(*Decoder)
|
|
|
|
err := decoder.reader.(io.Closer).Close()
|
|
if err != nil {
|
|
return C.EOF
|
|
}
|
|
|
|
return 0
|
|
}
|