audio/vorbis: Avoid copying by using C.short values directly

This commit is contained in:
Hajime Hoshi 2016-04-02 21:56:09 +09:00
parent d5ac3f831d
commit 764c755dd0
3 changed files with 88 additions and 24 deletions

View File

@ -17,15 +17,34 @@
package vorbis package vorbis
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/hajimehoshi/ebiten/exp/audio" "github.com/hajimehoshi/ebiten/exp/audio"
) )
// TODO: src should be ReadCloser? type Stream struct {
decoded *decoded
}
func (s *Stream) Read(p []byte) (int, error) {
return s.decoded.Read(p)
}
func (s *Stream) Seek(offset int64, whence int) (int64, error) {
return s.decoded.Seek(offset, whence)
}
func (s *Stream) Close() error {
return s.decoded.Close()
}
// TODO: Should be int?
func (s *Stream) Size() int64 {
return s.decoded.Size()
}
// TODO: src should be ReadCloser?
func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) { func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) {
decoded, channelNum, sampleRate, err := decode(src) decoded, channelNum, sampleRate, err := decode(src)
if err != nil { if err != nil {
@ -39,7 +58,7 @@ func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) {
return nil, fmt.Errorf("vorbis: sample rate must be %d but %d", context.SampleRate(), sampleRate) return nil, fmt.Errorf("vorbis: sample rate must be %d but %d", context.SampleRate(), sampleRate)
} }
s := &Stream{ s := &Stream{
buf: bytes.NewReader(decoded), decoded: decoded,
} }
return s, nil return s, nil
} }

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// +build js
package vorbis package vorbis
import ( import (

View File

@ -21,7 +21,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"math" "math"
"reflect" "runtime"
"unsafe" "unsafe"
) )
@ -5488,7 +5488,9 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in
#endif // STB_VORBIS_HEADER_ONLY #endif // STB_VORBIS_HEADER_ONLY
static inline short shortPIndex(short* s, int i) { return s[i]; } static inline short shortValue(short* data, int i) {
return data[i];
}
*/ */
import "C" import "C"
@ -5537,18 +5539,63 @@ func go_assert(x C.int, sentence *C.char) {
panic(fmt.Sprintf("go-vorbis: assertion error: %s", str)) panic(fmt.Sprintf("go-vorbis: assertion error: %s", str))
} }
func cFloatsToSlice(p *C.float, n int) []float32 { type decoded struct {
s := reflect.SliceHeader{ data *C.short
Data: uintptr(unsafe.Pointer(p)), sampleNum int
Len: n, posInBytes int
Cap: n, bytesPerSample int
} }
return *(*[]float32)(unsafe.Pointer(&s))
func (d *decoded) at(offset int) C.short {
// return *(*C.short)(unsafe.Pointer(uintptr(unsafe.Pointer(d.data)) + uintptr(offset)))
return C.shortValue(d.data, C.int(offset))
}
func (d *decoded) Read(b []byte) (int, error) {
l := d.sampleNum*d.bytesPerSample - d.posInBytes
if l > len(b) {
l = len(b)
}
// l must be even so that d.posInBytes is always even.
l = l / 2 * 2
for i := 0; i < l/2; i++ {
s := d.at(d.posInBytes/2 + i)
b[2*i] = byte(s)
b[2*i+1] = byte(s >> 8)
}
d.posInBytes += l
if d.posInBytes == d.sampleNum*d.bytesPerSample {
return l, io.EOF
}
return l, nil
}
func (d *decoded) Seek(offset int64, whence int) (int64, error) {
next := int64(0)
switch whence {
case 0:
next = offset
case 1:
next = int64(d.posInBytes) + offset
case 2:
next = int64(d.sampleNum*d.bytesPerSample) + offset
}
d.posInBytes = int(next)
return next, nil
}
func (d *decoded) Close() error {
runtime.SetFinalizer(d, nil)
C.free(unsafe.Pointer(d.data))
return nil
}
func (d *decoded) Size() int64 {
return int64(d.sampleNum * d.bytesPerSample)
} }
// decode accepts an ogg stream and returns a decorded stream. // 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.ReadCloser) (*decoded, int, int, error) {
func decode(in io.ReadCloser) ([]byte, int, int, error) {
mem, err := ioutil.ReadAll(in) mem, err := ioutil.ReadAll(in)
if err != nil { if err != nil {
return nil, 0, 0, err return nil, 0, 0, err
@ -5560,16 +5607,12 @@ func decode(in io.ReadCloser) ([]byte, int, int, error) {
sampleRate := C.int(0) sampleRate := C.int(0)
output := (*C.short)(nil) output := (*C.short)(nil)
sampleNum := C.stb_vorbis_decode_memory((*C.uchar)(&mem[0]), C.int(len(mem)), &channelNum, &sampleRate, &output) sampleNum := C.stb_vorbis_decode_memory((*C.uchar)(&mem[0]), C.int(len(mem)), &channelNum, &sampleRate, &output)
defer C.free(unsafe.Pointer(output)) d := &decoded{
b := make([]byte, sampleNum*4) data: output,
// What if we don't have to copy to []byte and use C.short directly? sampleNum: int(sampleNum),
for i := 0; i < int(sampleNum); i++ { posInBytes: 0,
l := C.shortPIndex(output, C.int(2*i)) bytesPerSample: 4,
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)
} }
return b, int(channelNum), int(sampleRate), nil runtime.SetFinalizer(d, (*decoded).Close)
return d, int(channelNum), int(sampleRate), nil
} }