go-vorbis/decoder.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
}