mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
audio/vorbis: Avoid copying by using C.short values directly
This commit is contained in:
parent
d5ac3f831d
commit
764c755dd0
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 (
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
return *(*[]float32)(unsafe.Pointer(&s))
|
// 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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user