mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
audio/vorbis: Simplify to use stb_vorbis_decode_memory
This commit is contained in:
parent
5c7d36e62e
commit
f73b679fe6
@ -20,7 +20,6 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/exp/audio"
|
||||
)
|
||||
@ -28,25 +27,19 @@ import (
|
||||
// TODO: src should be ReadCloser?
|
||||
|
||||
func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) {
|
||||
decoded, err := decode(src)
|
||||
decoded, channelNum, sampleRate, err := decode(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: Remove this magic number
|
||||
if decoded.Channels() != 2 {
|
||||
if channelNum != 2 {
|
||||
return nil, errors.New("vorbis: number of channels must be 2")
|
||||
}
|
||||
if decoded.SampleRate() != context.SampleRate() {
|
||||
return nil, fmt.Errorf("vorbis: sample rate must be %d but %d", context.SampleRate(), decoded.SampleRate())
|
||||
}
|
||||
// TODO: Read all data once so that Seek can be implemented easily.
|
||||
// We should look for a wiser way.
|
||||
b, err := ioutil.ReadAll(decoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if sampleRate != context.SampleRate() {
|
||||
return nil, fmt.Errorf("vorbis: sample rate must be %d but %d", context.SampleRate(), sampleRate)
|
||||
}
|
||||
s := &Stream{
|
||||
buf: bytes.NewReader(b),
|
||||
buf: bytes.NewReader(decoded),
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
@ -17,12 +17,11 @@
|
||||
package vorbis
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@ -5489,7 +5488,7 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in
|
||||
|
||||
#endif // STB_VORBIS_HEADER_ONLY
|
||||
|
||||
static inline float* floatPPIndex(float** p, int i) { return p[i]; }
|
||||
static inline short shortPIndex(short* s, int i) { return s[i]; }
|
||||
|
||||
*/
|
||||
import "C"
|
||||
@ -5547,123 +5546,29 @@ func cFloatsToSlice(p *C.float, n int) []float32 {
|
||||
return *(*[]float32)(unsafe.Pointer(&s))
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
v *C.stb_vorbis
|
||||
in io.Reader
|
||||
inbuf []byte
|
||||
outbuf []byte
|
||||
ineof bool
|
||||
}
|
||||
|
||||
const bufferSize = 4096
|
||||
|
||||
func (d *decoder) Read(out []byte) (int, error) {
|
||||
if !d.ineof {
|
||||
b := make([]byte, bufferSize)
|
||||
n, err := d.in.Read(b)
|
||||
d.inbuf = append(d.inbuf, b[:n]...)
|
||||
if err == io.EOF {
|
||||
d.ineof = true
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
ns := C.int(0)
|
||||
outputs := (**C.float)(nil)
|
||||
numCh := C.int(0)
|
||||
used := C.int(0)
|
||||
if 0 < len(d.inbuf) {
|
||||
used = C.stb_vorbis_decode_frame_pushdata(d.v, (*C.uchar)(&d.inbuf[0]), C.int(len(d.inbuf)), &numCh, &outputs, &ns)
|
||||
d.inbuf = d.inbuf[used:]
|
||||
}
|
||||
if 0 < used {
|
||||
if ns == 0 {
|
||||
// seek/error
|
||||
return 0, nil
|
||||
}
|
||||
left := C.floatPPIndex(outputs, 0)
|
||||
right := C.floatPPIndex(outputs, 0)
|
||||
if numCh > 1 {
|
||||
right = C.floatPPIndex(outputs, 1)
|
||||
}
|
||||
l := cFloatsToSlice(left, int(ns))
|
||||
r := cFloatsToSlice(right, int(ns))
|
||||
out := make([]byte, int(ns)*4)
|
||||
for i := 0; i < int(ns); i++ {
|
||||
l := int16(l[i] * math.MaxInt16)
|
||||
r := int16(r[i] * math.MaxInt16)
|
||||
out[4*i] = uint8(l)
|
||||
out[4*i+1] = uint8(l >> 8)
|
||||
out[4*i+2] = uint8(r)
|
||||
out[4*i+3] = uint8(r >> 8)
|
||||
}
|
||||
d.outbuf = append(d.outbuf, out...)
|
||||
}
|
||||
|
||||
ncopied := copy(out, d.outbuf)
|
||||
d.outbuf = d.outbuf[ncopied:]
|
||||
if !d.ineof {
|
||||
return ncopied, nil
|
||||
}
|
||||
if 0 < len(d.inbuf) || 0 < len(d.outbuf) {
|
||||
return ncopied, nil
|
||||
}
|
||||
return ncopied, io.EOF
|
||||
}
|
||||
|
||||
func (d *decoder) Close() error {
|
||||
runtime.SetFinalizer(d, nil)
|
||||
C.stb_vorbis_close(d.v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decoder) Channels() int {
|
||||
return int(d.v.channels)
|
||||
}
|
||||
|
||||
func (d *decoder) SampleRate() int {
|
||||
return int(d.v.sample_rate)
|
||||
}
|
||||
|
||||
// decode accepts an ogg stream and returns a decorded stream.
|
||||
// The decorded format is 1 or 2-channel interleaved littleendian int16 values.
|
||||
func decode(in io.Reader) (*decoder, error) {
|
||||
d := &decoder{
|
||||
in: in,
|
||||
inbuf: []byte{},
|
||||
outbuf: []byte{},
|
||||
}
|
||||
runtime.SetFinalizer(d, (*decoder).Close)
|
||||
for {
|
||||
b := make([]byte, bufferSize)
|
||||
n, err := in.Read(b)
|
||||
if 0 < n {
|
||||
d.inbuf = append(d.inbuf, b[:n]...)
|
||||
}
|
||||
if 0 < len(d.inbuf) {
|
||||
used := C.int(0)
|
||||
error := C.int(0)
|
||||
d.v = C.stb_vorbis_open_pushdata((*C.uchar)(&d.inbuf[0]), C.int(len(d.inbuf)), &used, &error, nil)
|
||||
d.inbuf = d.inbuf[used:]
|
||||
if d.v != nil {
|
||||
break
|
||||
}
|
||||
if error == C.VORBIS_need_more_data {
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("vorbis: decoding error %d", error)
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
func decode(in io.Reader) ([]byte, int, int, error) {
|
||||
// TODO: in should be io.ReadCloser
|
||||
|
||||
mem, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
channelNum := C.int(0)
|
||||
sampleRate := C.int(0)
|
||||
output := (*C.short)(nil)
|
||||
sampleNum := C.stb_vorbis_decode_memory((*C.uchar)(&mem[0]), C.int(len(mem)), &channelNum, &sampleRate, &output)
|
||||
defer C.free(unsafe.Pointer(output))
|
||||
b := make([]byte, sampleNum*4)
|
||||
// What if we don't have to copy to []byte and use C.short directly?
|
||||
for i := 0; i < int(sampleNum); i++ {
|
||||
l := C.shortPIndex(output, C.int(2*i))
|
||||
r := C.shortPIndex(output, C.int(2*i+1))
|
||||
b[4*i] = byte(l)
|
||||
b[4*i+1] = byte(l >> 8)
|
||||
b[4*i+2] = byte(r)
|
||||
b[4*i+3] = byte(r >> 8)
|
||||
}
|
||||
if d.v == nil {
|
||||
return nil, errors.New("vorbis: initializing failed")
|
||||
}
|
||||
return d, nil
|
||||
return b, int(channelNum), int(sampleRate), nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user