audio/vorbis/internal/stb: Use float32 version of decoder for efficiency

This commit is contained in:
Hajime Hoshi 2018-07-05 01:37:51 +09:00
parent 9bf2eaff58
commit de7065bbf7
10 changed files with 63 additions and 24 deletions

View File

@ -1,2 +1,2 @@
emcc -Os -o stbvorbis.js -g -DSTB_VORBIS_NO_STDIO -s WASM=1 -s EXPORTED_FUNCTIONS='["_stb_vorbis_decode_memory"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' -s ALLOW_MEMORY_GROWTH=1 stb_vorbis.c emcc -Os -o stbvorbis.js -g -DSTB_VORBIS_NO_INTEGER_CONVERSION -DSTB_VORBIS_NO_STDIO -s WASM=1 -s EXPORTED_FUNCTIONS='["_stb_vorbis_decode_memory_float"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' -s ALLOW_MEMORY_GROWTH=1 stb_vorbis.c
go run genwasmjs.go < stbvorbis.wasm > wasm.js go run genwasmjs.go < stbvorbis.wasm > wasm.js

View File

@ -34,12 +34,12 @@ func init() {
<-ch <-ch
} }
func DecodeVorbis(buf []byte) ([]int16, int, int, error) { func DecodeVorbis(buf []byte) ([]float32, int, int, error) {
r := js.Global().Get("_ebiten").Call("decodeVorbis", buf) r := js.Global().Get("_ebiten").Call("decodeVorbis", buf)
if r == js.Null() { if r == js.Null() {
return nil, 0, 0, fmt.Errorf("audio/vorbis/internal/stb: decode failed") return nil, 0, 0, fmt.Errorf("audio/vorbis/internal/stb: decode failed")
} }
data := make([]int16, r.Get("data").Get("length").Int()) data := make([]float32, r.Get("data").Get("length").Int())
// TODO: Use js.TypeArrayOf // TODO: Use js.TypeArrayOf
arr := js.ValueOf(data) arr := js.ValueOf(data)
arr.Call("set", r.Get("data")) arr.Call("set", r.Get("data"))

View File

@ -24,7 +24,7 @@ var _ebiten = {};
}; };
Module.onRuntimeInitialized = () => { Module.onRuntimeInitialized = () => {
decodeMemory = Module.cwrap('stb_vorbis_decode_memory', 'number', ['number', 'number', 'number', 'number', 'number']); decodeMemory = Module.cwrap('stb_vorbis_decode_memory_float', 'number', ['number', 'number', 'number', 'number', 'number']);
if (vorbisDecoderInitialized) { if (vorbisDecoderInitialized) {
vorbisDecoderInitialized(); vorbisDecoderInitialized();
} }
@ -47,10 +47,10 @@ var _ebiten = {};
return a[0]; return a[0];
} }
function ptrToInt16s(ptr, length) { function ptrToFloat32s(ptr, length) {
const buf = new ArrayBuffer(length * Int16Array.BYTES_PER_ELEMENT); const buf = new ArrayBuffer(length * Float32Array.BYTES_PER_ELEMENT);
const copied = new Int16Array(buf); const copied = new Float32Array(buf);
copied.set(new Int16Array(Module.HEAPU8.buffer, ptr, length)); copied.set(new Float32Array(Module.HEAPU8.buffer, ptr, length));
return copied; return copied;
} }
@ -65,7 +65,7 @@ var _ebiten = {};
} }
const channels = ptrToInt32(channelsPtr); const channels = ptrToInt32(channelsPtr);
const result = { const result = {
data: ptrToInt16s(ptrToInt32(outputPtr), length * channels), data: ptrToFloat32s(ptrToInt32(outputPtr), length * channels),
channels: channels, channels: channels,
sampleRate: ptrToInt32(sampleRatePtr), sampleRate: ptrToInt32(sampleRatePtr),
}; };

View File

@ -3,4 +3,4 @@
package stb package stb
var decode_js = []byte("// Copyright 2018 The Ebiten Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nvar _ebiten = {};\n\n(() => {\n var decodeMemory = null;\n var vorbisDecoderInitialized = null;\n\n _ebiten.initializeVorbisDecoder = (callback) => {\n Module.run();\n vorbisDecoderInitialized = callback;\n };\n\n Module.onRuntimeInitialized = () => {\n decodeMemory = Module.cwrap('stb_vorbis_decode_memory', 'number', ['number', 'number', 'number', 'number', 'number']);\n if (vorbisDecoderInitialized) {\n vorbisDecoderInitialized();\n }\n }\n\n function arrayToHeap(typedArray){\n const ptr = Module._malloc(typedArray.byteLength);\n const heapBytes = new Uint8Array(Module.HEAPU8.buffer, ptr, typedArray.byteLength);\n heapBytes.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength));\n return heapBytes;\n }\n\n function ptrToInt32(ptr) {\n const a = new Int32Array(Module.HEAPU8.buffer, ptr, 1);\n return a[0];\n }\n\n function ptrToFloat32(ptr) {\n const a = new Float32Array(Module.HEAPU8.buffer, ptr, 1);\n return a[0];\n }\n\n function ptrToInt16s(ptr, length) {\n const buf = new ArrayBuffer(length * Int16Array.BYTES_PER_ELEMENT);\n const copied = new Int16Array(buf);\n copied.set(new Int16Array(Module.HEAPU8.buffer, ptr, length));\n return copied;\n }\n\n _ebiten.decodeVorbis = (buf) => {\n const copiedBuf = arrayToHeap(buf);\n const channelsPtr = Module._malloc(4);\n const sampleRatePtr = Module._malloc(4);\n const outputPtr = Module._malloc(4);\n const length = decodeMemory(copiedBuf.byteOffset, copiedBuf.length, channelsPtr, sampleRatePtr, outputPtr);\n if (length < 0) {\n return null;\n }\n const channels = ptrToInt32(channelsPtr);\n const result = {\n data: ptrToInt16s(ptrToInt32(outputPtr), length * channels),\n channels: channels,\n sampleRate: ptrToInt32(sampleRatePtr),\n };\n\n Module._free(copiedBuf.byteOffset);\n Module._free(channelsPtr);\n Module._free(sampleRatePtr);\n Module._free(ptrToInt32(outputPtr));\n Module._free(outputPtr);\n return result;\n };\n})();\n") var decode_js = []byte("// Copyright 2018 The Ebiten Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nvar _ebiten = {};\n\n(() => {\n var decodeMemory = null;\n var vorbisDecoderInitialized = null;\n\n _ebiten.initializeVorbisDecoder = (callback) => {\n Module.run();\n vorbisDecoderInitialized = callback;\n };\n\n Module.onRuntimeInitialized = () => {\n decodeMemory = Module.cwrap('stb_vorbis_decode_memory_float', 'number', ['number', 'number', 'number', 'number', 'number']);\n if (vorbisDecoderInitialized) {\n vorbisDecoderInitialized();\n }\n }\n\n function arrayToHeap(typedArray){\n const ptr = Module._malloc(typedArray.byteLength);\n const heapBytes = new Uint8Array(Module.HEAPU8.buffer, ptr, typedArray.byteLength);\n heapBytes.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength));\n return heapBytes;\n }\n\n function ptrToInt32(ptr) {\n const a = new Int32Array(Module.HEAPU8.buffer, ptr, 1);\n return a[0];\n }\n\n function ptrToFloat32(ptr) {\n const a = new Float32Array(Module.HEAPU8.buffer, ptr, 1);\n return a[0];\n }\n\n function ptrToFloat32s(ptr, length) {\n const buf = new ArrayBuffer(length * Float32Array.BYTES_PER_ELEMENT);\n const copied = new Float32Array(buf);\n copied.set(new Float32Array(Module.HEAPU8.buffer, ptr, length));\n return copied;\n }\n\n _ebiten.decodeVorbis = (buf) => {\n const copiedBuf = arrayToHeap(buf);\n const channelsPtr = Module._malloc(4);\n const sampleRatePtr = Module._malloc(4);\n const outputPtr = Module._malloc(4);\n const length = decodeMemory(copiedBuf.byteOffset, copiedBuf.length, channelsPtr, sampleRatePtr, outputPtr);\n if (length < 0) {\n return null;\n }\n const channels = ptrToInt32(channelsPtr);\n const result = {\n data: ptrToFloat32s(ptrToInt32(outputPtr), length * channels),\n channels: channels,\n sampleRate: ptrToInt32(sampleRatePtr),\n };\n\n Module._free(copiedBuf.byteOffset);\n Module._free(channelsPtr);\n Module._free(sampleRatePtr);\n Module._free(ptrToInt32(outputPtr));\n Module._free(outputPtr);\n return result;\n };\n})();\n")

View File

@ -5462,3 +5462,48 @@ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
*/ */
// Added by Hajime Hoshi
int stb_vorbis_decode_memory_float(const uint8 *mem, int len, int *channels, int *sample_rate, float **output) {
int data_len, offset, total, limit, error;
float* data;
stb_vorbis* v = stb_vorbis_open_memory(mem, len, &error, NULL);
if (v == NULL) {
return -1;
}
limit = v->channels * 4096;
*channels = v->channels;
if (sample_rate) {
*sample_rate = v->sample_rate;
}
offset = data_len = 0;
total = limit;
data = (float*)malloc(total * sizeof(*data));
if (data == NULL) {
stb_vorbis_close(v);
return -2;
}
for (;;) {
int n = stb_vorbis_get_samples_float_interleaved(v, v->channels, data+offset, total-offset);
if (n == 0) {
break;
}
data_len += n;
offset += n * v->channels;
if (offset + limit > total) {
float* data2;
total *= 2;
data2 = (float*)realloc(data, total * sizeof(*data));
if (data2 == NULL) {
free(data);
stb_vorbis_close(v);
return -2;
}
data = data2;
}
}
*output = data;
stb_vorbis_close(v);
return data_len;
}

View File

@ -1663,7 +1663,7 @@ var ASM_CONSTS = [];
STATIC_BASE = GLOBAL_BASE; STATIC_BASE = GLOBAL_BASE;
STATICTOP = STATIC_BASE + 3888; STATICTOP = STATIC_BASE + 3776;
/* global initializers */ __ATINIT__.push(); /* global initializers */ __ATINIT__.push();
@ -1672,7 +1672,7 @@ STATICTOP = STATIC_BASE + 3888;
var STATIC_BUMP = 3888; var STATIC_BUMP = 3776;
Module["STATIC_BASE"] = STATIC_BASE; Module["STATIC_BASE"] = STATIC_BASE;
Module["STATIC_BUMP"] = STATIC_BUMP; Module["STATIC_BUMP"] = STATIC_BUMP;
@ -1805,7 +1805,7 @@ var _malloc = Module["_malloc"] = function() { return Module["asm"]["_malloc"].
var _memcpy = Module["_memcpy"] = function() { return Module["asm"]["_memcpy"].apply(null, arguments) }; var _memcpy = Module["_memcpy"] = function() { return Module["asm"]["_memcpy"].apply(null, arguments) };
var _memset = Module["_memset"] = function() { return Module["asm"]["_memset"].apply(null, arguments) }; var _memset = Module["_memset"] = function() { return Module["asm"]["_memset"].apply(null, arguments) };
var _sbrk = Module["_sbrk"] = function() { return Module["asm"]["_sbrk"].apply(null, arguments) }; var _sbrk = Module["_sbrk"] = function() { return Module["asm"]["_sbrk"].apply(null, arguments) };
var _stb_vorbis_decode_memory = Module["_stb_vorbis_decode_memory"] = function() { return Module["asm"]["_stb_vorbis_decode_memory"].apply(null, arguments) }; var _stb_vorbis_decode_memory_float = Module["_stb_vorbis_decode_memory_float"] = function() { return Module["asm"]["_stb_vorbis_decode_memory_float"].apply(null, arguments) };
var establishStackSpace = Module["establishStackSpace"] = function() { return Module["asm"]["establishStackSpace"].apply(null, arguments) }; var establishStackSpace = Module["establishStackSpace"] = function() { return Module["asm"]["establishStackSpace"].apply(null, arguments) };
var getTempRet0 = Module["getTempRet0"] = function() { return Module["asm"]["getTempRet0"].apply(null, arguments) }; var getTempRet0 = Module["getTempRet0"] = function() { return Module["asm"]["getTempRet0"].apply(null, arguments) };
var runPostSets = Module["runPostSets"] = function() { return Module["asm"]["runPostSets"].apply(null, arguments) }; var runPostSets = Module["runPostSets"] = function() { return Module["asm"]["runPostSets"].apply(null, arguments) };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -25,7 +25,7 @@ import (
) )
type decoderImpl struct { type decoderImpl struct {
data []int16 data []float32
channels int channels int
sampleRate int sampleRate int
} }
@ -35,13 +35,7 @@ func (d *decoderImpl) Read(buf []float32) (int, error) {
return 0, io.EOF return 0, io.EOF
} }
n := len(buf) n := copy(buf, d.data)
if n > len(d.data) {
n = len(d.data)
}
for i := 0; i < n; i++ {
buf[i] = float32(d.data[i]) / 32768
}
d.data = d.data[n:] d.data = d.data[n:]
return n, nil return n, nil
} }