Mercurial > hg > piper-vamp-js
changeset 175:a91b4defa581
WebAssembly-related fixes and updates
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Wed, 13 Jun 2018 15:55:39 +0100 |
parents | a007360392e0 |
children | eaf46e7647a0 |
files | Makefile Makefile.inc test/node-load-test.js test/perf-test-node.js test/perf-test-wasm.html test/perf-test.html test/perf-test.js test/quick-test.js |
diffstat | 8 files changed, 735 insertions(+), 689 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Tue Jun 12 14:58:05 2018 +0100 +++ b/Makefile Wed Jun 13 15:55:39 2018 +0100 @@ -1,5 +1,5 @@ -all: prerequisites generator examples +all: prerequisites generator examples-wasm prerequisites: ./bin/check-prerequisites.sh @@ -13,9 +13,16 @@ generator: $(MAKE) -C generator -.PHONY: examples -examples: +.PHONY: examples-em +examples-em: $(MAKE) -C examples/vamp-example-plugins em $(MAKE) -C examples/vamp-test-plugin em - $(MAKE) -C examples/vamp-example-plugins test - $(MAKE) -C examples/vamp-test-plugin test + $(MAKE) -C examples/vamp-example-plugins test-em + $(MAKE) -C examples/vamp-test-plugin test-em + +.PHONY: examples-wasm +examples-wasm: + $(MAKE) -C examples/vamp-example-plugins wasm + $(MAKE) -C examples/vamp-test-plugin wasm + $(MAKE) -C examples/vamp-example-plugins test-wasm + $(MAKE) -C examples/vamp-test-plugin test-wasm
--- a/Makefile.inc Tue Jun 12 14:58:05 2018 +0100 +++ b/Makefile.inc Wed Jun 13 15:55:39 2018 +0100 @@ -36,10 +36,14 @@ @echo @echo "Supported make targets:" @echo + @echo "$$ make wasm" + @echo " - build Javascript/WebAssembly module using Emscripten" + @echo "$$ make test-wasm" + @echo " - build and run simple test of Javascript/WebAssembly module using node.js" @echo "$$ make em" @echo " - build Javascript module using Emscripten" - @echo "$$ make test" - @echo " - build and run simple load test of Javascript module using node.js" + @echo "$$ make test-em" + @echo " - build and run simple test of Javascript module using node.js" @echo "$$ make linux" @echo " - build native-code module on Linux (currently this is mostly for testing)" @echo "$$ make clean" @@ -95,21 +99,25 @@ SO_MODULE_EXT := .so SO_MODULE := $(MODULE_NAME)$(SO_MODULE_EXT) -EMFLAGS := \ - --memory-init-file 0 \ +EMFLAGS_ANY := \ + --memory-init-file 0 \ -s MODULARIZE=1 \ -s NO_FILESYSTEM=1 \ + -s MEM_INIT_METHOD=0 \ -s ERROR_ON_UNDEFINED_SYMBOLS=1 \ -s "EXPORT_NAME='$(EM_MODULE_SYMBOL)'" \ - -s "EXPORTED_FUNCTIONS=['_piperRequestJson','_piperProcessRaw','_piperFreeJson']" \ + -s "EXPORTED_FUNCTIONS=['_piperRequestJson', '_piperProcessRaw', '_piperFreeJson', '_malloc', '_free']" \ -s "EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap', 'allocate', 'ALLOC_NORMAL', 'Pointer_stringify', 'intArrayFromString']" \ $(EMFLAGS) -WASMFLAGS := \ - $(EMFLAGS) \ - -s WASM=1 +EMFLAGS_ASMJS := \ + $(EMFLAGS_ANY) \ + -s WASM=0 -# -s "BINARYEN_METHOD='native-wasm'" +EMFLAGS_WASM := \ + $(EMFLAGS_ANY) \ + -s WASM=1 \ + -s SINGLE_FILE=1 CXX_SOURCES := $(MODULE_SOURCE) $(ADAPTER_SOURCES) $(PLUGIN_SOURCES) $(SDK_SOURCES) $(OTHER_SOURCES) C_SOURCES := $(PLUGIN_C_SOURCES) $(C_SOURCES) @@ -123,16 +131,16 @@ em: $(EM_MODULE_U) em: CXX := em++ em: CC := emcc -em: CXXFLAGS := -std=c++11 -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(EMFLAGS) $(INCLUDES) -em: CFLAGS := -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(EMFLAGS) $(INCLUDES) -em: LDFLAGS := $(EMFLAGS) +em: CXXFLAGS := -std=c++11 -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(EMFLAGS_ASMJS) $(INCLUDES) +em: CFLAGS := -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(EMFLAGS_ASMJS) $(INCLUDES) +em: LDFLAGS := $(EMFLAGS_ASMJS) wasm: $(WASM_MODULE_U) wasm: CXX := em++ wasm: CC := emcc -wasm: CXXFLAGS := -std=c++11 -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(WASMFLAGS) $(INCLUDES) -wasm: CFLAGS := -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(WASMFLAGS) $(INCLUDES) -wasm: LDFLAGS := $(WASMFLAGS) +wasm: CXXFLAGS := -std=c++11 -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(EMFLAGS_WASM) $(INCLUDES) +wasm: CFLAGS := -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(EMFLAGS_WASM) $(INCLUDES) +wasm: LDFLAGS := $(EMFLAGS_WASM) linux: $(SO_MODULE) linux: CXXFLAGS := -std=c++11 -fPIC -Wall -Wextra $(DEFINES) $(OPTFLAGS) $(INCLUDES) @@ -171,10 +179,10 @@ $(SO_MODULE): $(OBJECTS) $(CXX) -o $@ $^ $(LDFLAGS) -test: em +test-em: em $(NODE) $(MY_DIR)/test/node-load-test.js $(shell pwd)/$(EM_MODULE_U) -test-wasm: wasm +test-wasm: wasm $(NODE) $(MY_DIR)/test/node-load-test.js $(shell pwd)/$(WASM_MODULE_U) clean:
--- a/test/node-load-test.js Tue Jun 12 14:58:05 2018 +0100 +++ b/test/node-load-test.js Wed Jun 13 15:55:39 2018 +0100 @@ -22,239 +22,242 @@ note("Loading library \"" + libraryPath + "\"..."); var extractor = require(libraryPath); -var extractorModule = extractor(); -var piperRequestJson = extractorModule.cwrap( - 'piperRequestJson', 'number', ['number'] -); +extractor().then(function(extractorModule) { -var piperProcessRaw = extractorModule.cwrap( - "piperProcessRaw", "number", ["number", "number", "number", "number"] -); + var piperRequestJson = extractorModule.cwrap( + 'piperRequestJson', 'number', ['number'] + ); -var piperFreeJson = extractorModule.cwrap( - 'piperFreeJson', 'void', ['number'] -); + var piperProcessRaw = extractorModule.cwrap( + "piperProcessRaw", "number", ["number", "number", "number", "number"] + ); -function processRaw(request) { - - const nChannels = request.processInput.inputBuffers.length; - const nFrames = request.processInput.inputBuffers[0].length; + var piperFreeJson = extractorModule.cwrap( + 'piperFreeJson', 'void', ['number'] + ); - const buffersPtr = extractorModule._malloc(nChannels * 4); - const buffers = new Uint32Array( - extractorModule.HEAPU8.buffer, buffersPtr, nChannels); + function processRaw(request) { + + const nChannels = request.processInput.inputBuffers.length; + const nFrames = request.processInput.inputBuffers[0].length; - for (let i = 0; i < nChannels; ++i) { - const framesPtr = extractorModule._malloc(nFrames * 4); - const frames = new Float32Array( - extractorModule.HEAPU8.buffer, framesPtr, nFrames); - frames.set(request.processInput.inputBuffers[i]); - buffers[i] = framesPtr; + const buffersPtr = extractorModule._malloc(nChannels * 4); + const buffers = new Uint32Array( + extractorModule.HEAPU8.buffer, buffersPtr, nChannels); + + for (let i = 0; i < nChannels; ++i) { + const framesPtr = extractorModule._malloc(nFrames * 4); + const frames = new Float32Array( + extractorModule.HEAPU8.buffer, framesPtr, nFrames); + frames.set(request.processInput.inputBuffers[i]); + buffers[i] = framesPtr; + } + + const responseJson = piperProcessRaw( + request.handle, + buffersPtr, + request.processInput.timestamp.s, + request.processInput.timestamp.n); + + for (let i = 0; i < nChannels; ++i) { + extractorModule._free(buffers[i]); + } + extractorModule._free(buffersPtr); + + const responseJstr = extractorModule.Pointer_stringify(responseJson); + const response = JSON.parse(responseJstr); + + piperFreeJson(responseJson); + + return response; } - - const responseJson = piperProcessRaw( - request.handle, - buffersPtr, - request.processInput.timestamp.s, - request.processInput.timestamp.n); - - for (let i = 0; i < nChannels; ++i) { - extractorModule._free(buffers[i]); - } - extractorModule._free(buffersPtr); - const responseJstr = extractorModule.Pointer_stringify(responseJson); - const response = JSON.parse(responseJstr); - - piperFreeJson(responseJson); - - return response; -} - -function makeTimestamp(seconds) { - if (seconds >= 0.0) { - return { - s: Math.floor(seconds), - n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5) - }; - } else { - const { s, n } = makeTimestamp(-seconds); - return { s: -s, n: -n }; - } -} - -function frame2timestamp(frame, rate) { - return makeTimestamp(frame / rate); -} - -function request(req) { - var jsonStr = JSON.stringify(req); - var m = extractorModule; - // Inspection reveals that intArrayFromString converts the string - // from utf16 to utf8, which is what we want (though the docs - // don't mention this). Note the *Cstr values are Emscripten heap - // pointers - let inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); - let outCstr = piperRequestJson(inCstr); - m._free(inCstr); - const responseJstr = m.Pointer_stringify(outCstr); - const response = JSON.parse(responseJstr); - piperFreeJson(outCstr); - return response; -} - -function myFromBase64(b64) { - while (b64.length % 4 > 0) { b64 += "="; } - let conv = new Float32Array(base64.toByteArray(b64).buffer); - return conv; -} - -function convertWireFeature(wfeature) { - let out = {}; - if (wfeature.timestamp != null) { - out.timestamp = wfeature.timestamp; - } - if (wfeature.duration != null) { - out.duration = wfeature.duration; - } - if (wfeature.label != null) { - out.label = wfeature.label; - } - const vv = wfeature.featureValues; - if (vv != null) { - if (typeof vv === "string") { - out.featureValues = myFromBase64(vv); + function makeTimestamp(seconds) { + if (seconds >= 0.0) { + return { + s: Math.floor(seconds), + n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5) + }; } else { - out.featureValues = new Float32Array(vv); + const { s, n } = makeTimestamp(-seconds); + return { s: -s, n: -n }; } } - return out; -} -function convertWireFeatureList(wfeatures) { - return wfeatures.map(convertWireFeature); -} + function frame2timestamp(frame, rate) { + return makeTimestamp(frame / rate); + } -function responseToFeatureSet(response) { - const features = new Map(); - const processResponse = response.result; - const wireFeatures = processResponse.features; - Object.keys(wireFeatures).forEach(key => { - return features.set(key, convertWireFeatureList(wireFeatures[key])); - }); - return features; -} + function request(req) { + var jsonStr = JSON.stringify(req); + var m = extractorModule; + // Inspection reveals that intArrayFromString converts the string + // from utf16 to utf8, which is what we want (though the docs + // don't mention this). Note the *Cstr values are Emscripten heap + // pointers + let inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); + let outCstr = piperRequestJson(inCstr); + m._free(inCstr); + const responseJstr = m.Pointer_stringify(outCstr); + const response = JSON.parse(responseJstr); + piperFreeJson(outCstr); + return response; + } -function checkSuccess(response) { - if (response.error) { - console.log("Request type " + response.method + " failed: " + - response.error.message); - throw response.error.message; + function myFromBase64(b64) { + while (b64.length % 4 > 0) { b64 += "="; } + let conv = new Float32Array(base64.toByteArray(b64).buffer); + return conv; } -} -function test() { - - let start = (new Date()).getTime(); - - const rate = 44100; - - note("Listing plugins..."); - let response = request({ - method: "list" - }); - checkSuccess(response); - - if (pluginKey === "") { - pluginKey = response.result.available[0].key; - note("Loading first plugin \"" + pluginKey + "\"..."); - } else { - note("Loading requested plugin \"" + pluginKey + "\"..."); - } - - response = request({ - method: "load", - params: { - key: pluginKey, - inputSampleRate: rate, - adapterFlags: ["AdaptAllSafe"] + function convertWireFeature(wfeature) { + let out = {}; + if (wfeature.timestamp != null) { + out.timestamp = wfeature.timestamp; } - }); - -// note("Load request returned: " + JSON.stringify(response) + "\n"); - - checkSuccess(response); - - var blockSize = response.result.defaultConfiguration.framing.blockSize; - var stepSize = response.result.defaultConfiguration.framing.stepSize; - - response = request({ - method: "configure", - params: { - handle: 1, - configuration: { - framing: { - blockSize: blockSize, - stepSize: stepSize, - }, - channelCount: 1 + if (wfeature.duration != null) { + out.duration = wfeature.duration; + } + if (wfeature.label != null) { + out.label = wfeature.label; + } + const vv = wfeature.featureValues; + if (vv != null) { + if (typeof vv === "string") { + out.featureValues = myFromBase64(vv); + } else { + out.featureValues = new Float32Array(vv); } } - }); - checkSuccess(response); + return out; + } - blockSize = response.result.framing.blockSize; - stepSize = response.result.framing.stepSize; - - const nblocks = 1000; + function convertWireFeatureList(wfeatures) { + return wfeatures.map(convertWireFeature); + } - const makeBlock = (n => { - return { - timestamp : frame2timestamp(n * blockSize, rate), - inputBuffers : [ - new Float32Array(Array.from(Array(blockSize).keys(), - n => n / blockSize)) - ], + function responseToFeatureSet(response) { + const features = new Map(); + const processResponse = response.result; + const wireFeatures = processResponse.features; + Object.keys(wireFeatures).forEach(key => { + return features.set(key, convertWireFeatureList(wireFeatures[key])); + }); + return features; + } + + function checkSuccess(response) { + if (response.error) { + console.log("Request type " + response.method + " failed: " + + response.error.message); + throw response.error.message; } - }); - - const blocks = Array.from(Array(nblocks).keys(), makeBlock); - - note("Now processing " + nblocks + " blocks of 1024 samples each..."); + } - let featureCount = 0; - - for (let i = 0; i < nblocks; ++i) { - response = processRaw({ - "handle": 1, - "processInput": blocks[i] - }); + function test() { + + let start = (new Date()).getTime(); + + const rate = 44100; + + note("Listing plugins..."); + let response = request({ + method: "list" + }); + checkSuccess(response); + + if (pluginKey === "") { + pluginKey = response.result.available[0].key; + note("Loading first plugin \"" + pluginKey + "\"..."); + } else { + note("Loading requested plugin \"" + pluginKey + "\"..."); + } + + response = request({ + method: "load", + params: { + key: pluginKey, + inputSampleRate: rate, + adapterFlags: ["AdaptAllSafe"] + } + }); + + // note("Load request returned: " + JSON.stringify(response) + "\n"); + + checkSuccess(response); + + var blockSize = response.result.defaultConfiguration.framing.blockSize; + var stepSize = response.result.defaultConfiguration.framing.stepSize; + + response = request({ + method: "configure", + params: { + handle: 1, + configuration: { + framing: { + blockSize: blockSize, + stepSize: stepSize, + }, + channelCount: 1 + } + } + }); + checkSuccess(response); + + blockSize = response.result.framing.blockSize; + stepSize = response.result.framing.stepSize; + + const nblocks = 1000; + + const makeBlock = (n => { + return { + timestamp : frame2timestamp(n * blockSize, rate), + inputBuffers : [ + new Float32Array(Array.from(Array(blockSize).keys(), + n => n / blockSize)) + ], + } + }); + + const blocks = Array.from(Array(nblocks).keys(), makeBlock); + + note("Now processing " + nblocks + " blocks of 1024 samples each..."); + + let featureCount = 0; + + for (let i = 0; i < nblocks; ++i) { + response = processRaw({ + "handle": 1, + "processInput": blocks[i] + }); + let features = responseToFeatureSet(response); + for (let featureList of features.values()) { + featureCount += featureList.length; + } + } + + note("Cleaning up the plugin and getting any remaining features..."); + response = request({ + method: "finish", + params: { + handle: 1 + } + }); + checkSuccess(response); + let features = responseToFeatureSet(response); for (let featureList of features.values()) { featureCount += featureList.length; } + + note("Done, total number of features across all outputs = " + featureCount); + + let finish = (new Date()).getTime(); + note("Total time taken " + (finish - start) + " ms"); } - note("Cleaning up the plugin and getting any remaining features..."); - response = request({ - method: "finish", - params: { - handle: 1 - } - }); - checkSuccess(response); + test(); +}); - let features = responseToFeatureSet(response); - for (let featureList of features.values()) { - featureCount += featureList.length; - } - - note("Done, total number of features across all outputs = " + featureCount); - let finish = (new Date()).getTime(); - note("Total time taken " + (finish - start) + " ms"); -} - -test(); -
--- a/test/perf-test-node.js Tue Jun 12 14:58:05 2018 +0100 +++ b/test/perf-test-node.js Wed Jun 13 15:55:39 2018 +0100 @@ -2,224 +2,225 @@ var extractorName = "VampExamplePlugins"; var pluginKey = "vamp-example-plugins:zerocrossing"; -var extractor = require("../examples/" + extractorName); +var extractor = require("../examples/vamp-example-plugins/" + extractorName + "_w.umd"); var base64 = require("./base64"); -var extractorModule = extractor(); -// It is possible to declare both parameters and return values as -// "string", in which case Emscripten will take care of -// conversions. But it's not clear how one would manage memory for -// newly-constructed returned C strings -- the returned pointer from -// piperRequestJson would appear (?) to be thrown away by the -// Emscripten string converter if we declare it as returning a string, -// so we have no opportunity to pass it to piperFreeJson, which -// suggests this would leak memory if the string isn't static. Not -// wholly sure though. Anyway, passing and returning pointers (as -// numbers) means we can manage the Emscripten heap memory however we -// want in our request wrapper function below. +extractor().then(function (extractorModule) { -var piperRequestJson = extractorModule.cwrap( - 'piperRequestJson', 'number', ['number'] -); + // It is possible to declare both parameters and return values as + // "string", in which case Emscripten will take care of + // conversions. But it's not clear how one would manage memory for + // newly-constructed returned C strings -- the returned pointer from + // piperRequestJson would appear (?) to be thrown away by the + // Emscripten string converter if we declare it as returning a string, + // so we have no opportunity to pass it to piperFreeJson, which + // suggests this would leak memory if the string isn't static. Not + // wholly sure though. Anyway, passing and returning pointers (as + // numbers) means we can manage the Emscripten heap memory however we + // want in our request wrapper function below. -var piperProcessRaw = extractorModule.cwrap( - "piperProcessRaw", "number", ["number", "number", "number", "number"] -); + var piperRequestJson = extractorModule.cwrap( + 'piperRequestJson', 'number', ['number'] + ); -var piperFreeJson = extractorModule.cwrap( - 'piperFreeJson', 'void', ['number'] -); + var piperProcessRaw = extractorModule.cwrap( + "piperProcessRaw", "number", ["number", "number", "number", "number"] + ); -function note(blah) { - console.log(blah); -} + var piperFreeJson = extractorModule.cwrap( + 'piperFreeJson', 'void', ['number'] + ); -function comment(blah) { - console.log(blah); -} + function note(blah) { + console.log(blah); + } -function processRaw(request) { - - const nChannels = request.processInput.inputBuffers.length; - const nFrames = request.processInput.inputBuffers[0].length; + function comment(blah) { + console.log(blah); + } - const buffersPtr = extractorModule._malloc(nChannels * 4); - const buffers = new Uint32Array( - extractorModule.HEAPU8.buffer, buffersPtr, nChannels); + function processRaw(request) { + + const nChannels = request.processInput.inputBuffers.length; + const nFrames = request.processInput.inputBuffers[0].length; - for (let i = 0; i < nChannels; ++i) { - const framesPtr = extractorModule._malloc(nFrames * 4); - const frames = new Float32Array( - extractorModule.HEAPU8.buffer, framesPtr, nFrames); - frames.set(request.processInput.inputBuffers[i]); - buffers[i] = framesPtr; + const buffersPtr = extractorModule._malloc(nChannels * 4); + const buffers = new Uint32Array( + extractorModule.HEAPU8.buffer, buffersPtr, nChannels); + + for (let i = 0; i < nChannels; ++i) { + const framesPtr = extractorModule._malloc(nFrames * 4); + const frames = new Float32Array( + extractorModule.HEAPU8.buffer, framesPtr, nFrames); + frames.set(request.processInput.inputBuffers[i]); + buffers[i] = framesPtr; + } + + const responseJson = piperProcessRaw( + request.handle, + buffersPtr, + request.processInput.timestamp.s, + request.processInput.timestamp.n); + + for (let i = 0; i < nChannels; ++i) { + extractorModule._free(buffers[i]); + } + extractorModule._free(buffersPtr); + + const responseJstr = extractorModule.Pointer_stringify(responseJson); + const response = JSON.parse(responseJstr); + + piperFreeJson(responseJson); + + return response; } - - const responseJson = piperProcessRaw( - request.handle, - buffersPtr, - request.processInput.timestamp.s, - request.processInput.timestamp.n); - - for (let i = 0; i < nChannels; ++i) { - extractorModule._free(buffers[i]); - } - extractorModule._free(buffersPtr); - const responseJstr = extractorModule.Pointer_stringify(responseJson); - const response = JSON.parse(responseJstr); - - piperFreeJson(responseJson); - - return response; -} - -function makeTimestamp(seconds) { - if (seconds >= 0.0) { - return { - s: Math.floor(seconds), - n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5) - }; - } else { - const { s, n } = makeTimestamp(-seconds); - return { s: -s, n: -n }; - } -} - -function frame2timestamp(frame, rate) { - return makeTimestamp(frame / rate); -} - -function request(jsonStr) { - note("Request JSON = " + jsonStr); - var m = extractorModule; - // Inspection reveals that intArrayFromString converts the string - // from utf16 to utf8, which is what we want (though the docs - // don't mention this). Note the *Cstr values are Emscripten heap - // pointers - var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); - var outCstr = piperRequestJson(inCstr); - m._free(inCstr); - var result = m.Pointer_stringify(outCstr); - piperFreeJson(outCstr); - note("Returned JSON = " + result); - return result; -} - -function myFromBase64(b64) { - while (b64.length % 4 > 0) { b64 += "="; } - let conv = new Float32Array(base64.toByteArray(b64).buffer); - return conv; -} - -function convertWireFeature(wfeature) { - let out = {}; - if (wfeature.timestamp != null) { - out.timestamp = wfeature.timestamp; - } - if (wfeature.duration != null) { - out.duration = wfeature.duration; - } - if (wfeature.label != null) { - out.label = wfeature.label; - } - const vv = wfeature.featureValues; - if (vv != null) { - if (typeof vv === "string") { - out.featureValues = myFromBase64(vv); + function makeTimestamp(seconds) { + if (seconds >= 0.0) { + return { + s: Math.floor(seconds), + n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5) + }; } else { - out.featureValues = new Float32Array(vv); + const { s, n } = makeTimestamp(-seconds); + return { s: -s, n: -n }; } } - return out; -} -function convertWireFeatureList(wfeatures) { - return wfeatures.map(convertWireFeature); -} - -function responseToFeatureSet(response) { - const features = new Map(); - const processResponse = response.result; - const wireFeatures = processResponse.features; - Object.keys(wireFeatures).forEach(key => { - return features.set(key, convertWireFeatureList(wireFeatures[key])); - }); - return features; -} - -function test() { - - const rate = 44100; - - comment("Loading zero crossings plugin..."); - let result = request('{"method":"load","params": {"key":"' + pluginKey + '","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}'); - - const blockSize = 1024; - - result = request('{"method":"configure","params":{"handle":1,"configuration":{"blockSize": ' + blockSize + ', "channelCount": 1, "stepSize": ' + blockSize + '}}}'); - - const nblocks = 1000; - - const makeBlock = (n => { - return { - timestamp : frame2timestamp(n * blockSize, rate), - inputBuffers : [ - new Float32Array(Array.from(Array(blockSize).keys(), - n => n / blockSize)) - ], - } - }); - - const blocks = Array.from(Array(nblocks).keys(), makeBlock); - - comment("Now processing " + nblocks + " blocks of 1024 samples each..."); - - let total = 0; - - let start = (new Date()).getTime(); - comment("Start at " + start); - - for (let i = 0; i < nblocks; ++i) { - result = processRaw({ - "handle": 1, - "processInput": blocks[i] - }); - let features = responseToFeatureSet(result); - let count = features.get("counts")[0].featureValues[0]; - total += count; + function frame2timestamp(frame, rate) { + return makeTimestamp(frame / rate); } - let finish = (new Date()).getTime(); - comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); - - comment("Total = " + total); - - comment("Again..."); - - total = 0; - - start = (new Date()).getTime(); - comment("Start at " + start); - - for (let i = 0; i < nblocks; ++i) { - result = processRaw({ - "handle": 1, - "processInput": blocks[i] - }); - let features = responseToFeatureSet(result); - let count = features.get("counts")[0].featureValues[0]; - total += count; + function request(jsonStr) { + note("Request JSON = " + jsonStr); + var m = extractorModule; + // Inspection reveals that intArrayFromString converts the string + // from utf16 to utf8, which is what we want (though the docs + // don't mention this). Note the *Cstr values are Emscripten heap + // pointers + var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); + var outCstr = piperRequestJson(inCstr); + m._free(inCstr); + var result = m.Pointer_stringify(outCstr); + piperFreeJson(outCstr); + note("Returned JSON = " + result); + return result; } - finish = (new Date()).getTime(); - comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); - - comment("Total = " + total); - - comment("Cleaning up the plugin and getting any remaining features..."); - result = request('{"method":"finish","params":{"handle":1}}'); -} + function myFromBase64(b64) { + while (b64.length % 4 > 0) { b64 += "="; } + let conv = new Float32Array(base64.toByteArray(b64).buffer); + return conv; + } -test(); + function convertWireFeature(wfeature) { + let out = {}; + if (wfeature.timestamp != null) { + out.timestamp = wfeature.timestamp; + } + if (wfeature.duration != null) { + out.duration = wfeature.duration; + } + if (wfeature.label != null) { + out.label = wfeature.label; + } + const vv = wfeature.featureValues; + if (vv != null) { + if (typeof vv === "string") { + out.featureValues = myFromBase64(vv); + } else { + out.featureValues = new Float32Array(vv); + } + } + return out; + } + function convertWireFeatureList(wfeatures) { + return wfeatures.map(convertWireFeature); + } + + function responseToFeatureSet(response) { + const features = new Map(); + const processResponse = response.result; + const wireFeatures = processResponse.features; + Object.keys(wireFeatures).forEach(key => { + return features.set(key, convertWireFeatureList(wireFeatures[key])); + }); + return features; + } + + function test(extractorModule) { + + const rate = 44100; + + comment("Loading zero crossings plugin..."); + let result = request('{"method":"load","params": {"key":"' + pluginKey + '","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}'); + + const blockSize = 1024; + + result = request('{"method":"configure","params":{"handle":1,"configuration":{"framing": { "blockSize": ' + blockSize + ', "stepSize": ' + blockSize + '}, "channelCount": 1 }}}'); + + const nblocks = 1000; + + const makeBlock = (n => { + return { + timestamp : frame2timestamp(n * blockSize, rate), + inputBuffers : [ + new Float32Array(Array.from(Array(blockSize).keys(), + n => n / blockSize)) + ], + } + }); + + const blocks = Array.from(Array(nblocks).keys(), makeBlock); + + comment("Now processing " + nblocks + " blocks of 1024 samples each..."); + + let total = 0; + + let start = (new Date()).getTime(); + comment("Start at " + start); + + for (let i = 0; i < nblocks; ++i) { + result = processRaw({ + "handle": 1, + "processInput": blocks[i] + }); + let features = responseToFeatureSet(result); + let count = features.get("counts")[0].featureValues[0]; + total += count; + } + + let finish = (new Date()).getTime(); + comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); + + comment("Total = " + total); + + comment("Again..."); + + total = 0; + + start = (new Date()).getTime(); + comment("Start at " + start); + + for (let i = 0; i < nblocks; ++i) { + result = processRaw({ + "handle": 1, + "processInput": blocks[i] + }); + let features = responseToFeatureSet(result); + let count = features.get("counts")[0].featureValues[0]; + total += count; + } + + finish = (new Date()).getTime(); + comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); + + comment("Total = " + total); + + comment("Cleaning up the plugin and getting any remaining features..."); + result = request('{"method":"finish","params":{"handle":1}}'); + } + + test(); +});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/perf-test-wasm.html Wed Jun 13 15:55:39 2018 +0100 @@ -0,0 +1,22 @@ +<html> + <head> + <meta charset="UTF-8"> + <title>Piper Adapter Performance Test</title> + + <style type="text/css"> + body { margin: 5%; } + table, td, th { border: 0.1em solid #e0e0e0; border-collapse: collapse } + td, th { padding: 0.5em } + </style> + + <script src="../examples/vamp-example-plugins/VampExamplePlugins_w.umd.js"></script> + <script src="base64.js"></script> + <script src="perf-test.js"></script> + </head> + <body> + <h3>Piper Adapter Performance Test</h3> + + <p id="test-result"></p> + + </body> +</html>
--- a/test/perf-test.html Tue Jun 12 14:58:05 2018 +0100 +++ b/test/perf-test.html Wed Jun 13 15:55:39 2018 +0100 @@ -1,7 +1,7 @@ <html> <head> <meta charset="UTF-8"> - <title>VamPipe Adapter Test</title> + <title>Piper Adapter Performance Test</title> <style type="text/css"> body { margin: 5%; } @@ -9,12 +9,12 @@ td, th { padding: 0.5em } </style> - <script src="VampExamplePlugins.js"></script> + <script src="../examples/vamp-example-plugins/VampExamplePlugins.umd.js"></script> <script src="base64.js"></script> <script src="perf-test.js"></script> </head> <body> - <h3>VamPipe Adapter Test</h3> + <h3>Piper Adapter Performance Test</h3> <p id="test-result"></p>
--- a/test/perf-test.js Tue Jun 12 14:58:05 2018 +0100 +++ b/test/perf-test.js Wed Jun 13 15:55:39 2018 +0100 @@ -1,220 +1,223 @@ "use strict"; -var exampleModule = VampExamplePluginsModule(); - -// It is possible to declare both parameters and return values as -// "string", in which case Emscripten will take care of -// conversions. But it's not clear how one would manage memory for -// newly-constructed returned C strings -- the returned pointer from -// piperRequestJson would appear (?) to be thrown away by the -// Emscripten string converter if we declare it as returning a string, -// so we have no opportunity to pass it to piperFreeJson, which -// suggests this would leak memory if the string isn't static. Not -// wholly sure though. Anyway, passing and returning pointers (as -// numbers) means we can manage the Emscripten heap memory however we -// want in our request wrapper function below. - -var piperRequestJson = exampleModule.cwrap( - 'piperRequestJson', 'number', ['number'] -); - -var piperProcessRaw = exampleModule.cwrap( - "piperProcessRaw", "number", ["number", "number", "number", "number"] -); - -var piperFreeJson = exampleModule.cwrap( - 'piperFreeJson', 'void', ['number'] -); - -function note(blah) { - document.getElementById("test-result").innerHTML += blah + "<br>"; -} - -function comment(blah) { - note("<br><i>" + blah + "</i>"); -} - -function processRaw(request) { - - const nChannels = request.processInput.inputBuffers.length; - const nFrames = request.processInput.inputBuffers[0].length; - - const buffersPtr = exampleModule._malloc(nChannels * 4); - const buffers = new Uint32Array( - exampleModule.HEAPU8.buffer, buffersPtr, nChannels); - - for (let i = 0; i < nChannels; ++i) { - const framesPtr = exampleModule._malloc(nFrames * 4); - const frames = new Float32Array( - exampleModule.HEAPU8.buffer, framesPtr, nFrames); - frames.set(request.processInput.inputBuffers[i]); - buffers[i] = framesPtr; - } - - const responseJson = piperProcessRaw( - request.handle, - buffersPtr, - request.processInput.timestamp.s, - request.processInput.timestamp.n); - - for (let i = 0; i < nChannels; ++i) { - exampleModule._free(buffers[i]); - } - exampleModule._free(buffersPtr); - - const responseJstr = exampleModule.Pointer_stringify(responseJson); - const response = JSON.parse(responseJstr); - - piperFreeJson(responseJson); - - return response; -} - -function makeTimestamp(seconds) { - if (seconds >= 0.0) { - return { - s: Math.floor(seconds), - n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5) - }; - } else { - const { s, n } = makeTimestamp(-seconds); - return { s: -s, n: -n }; - } -} - -function frame2timestamp(frame, rate) { - return makeTimestamp(frame / rate); -} - -function request(jsonStr) { - note("Request JSON = " + jsonStr); - var m = exampleModule; - // Inspection reveals that intArrayFromString converts the string - // from utf16 to utf8, which is what we want (though the docs - // don't mention this). Note the *Cstr values are Emscripten heap - // pointers - var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); - var outCstr = piperRequestJson(inCstr); - m._free(inCstr); - var result = m.Pointer_stringify(outCstr); - piperFreeJson(outCstr); - note("Returned JSON = " + result); - return result; -} - -function myFromBase64(b64) { - while (b64.length % 4 > 0) { b64 += "="; } - let conv = new Float32Array(toByteArray(b64).buffer); - return conv; -} - -function convertWireFeature(wfeature) { - let out = {}; - if (wfeature.timestamp != null) { - out.timestamp = wfeature.timestamp; - } - if (wfeature.duration != null) { - out.duration = wfeature.duration; - } - if (wfeature.label != null) { - out.label = wfeature.label; - } - const vv = wfeature.featureValues; - if (vv != null) { - if (typeof vv === "string") { - out.featureValues = myFromBase64(vv); - } else { - out.featureValues = new Float32Array(vv); - } - } - return out; -} - -function convertWireFeatureList(wfeatures) { - return wfeatures.map(convertWireFeature); -} - -function responseToFeatureSet(response) { - const features = new Map(); - const processResponse = response.result; - const wireFeatures = processResponse.features; - Object.keys(wireFeatures).forEach(key => { - return features.set(key, convertWireFeatureList(wireFeatures[key])); - }); - return features; -} - function test() { - const rate = 44100; - - comment("Loading zero crossings plugin..."); - let result = request('{"method":"load","params": {"key":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}'); + VampExamplePluginsModule().then(function(exampleModule) { + + // It is possible to declare both parameters and return values + // as "string", in which case Emscripten will take care of + // conversions. But it's not clear how one would manage memory + // for newly-constructed returned C strings -- the returned + // pointer from piperRequestJson would appear (?) to be thrown + // away by the Emscripten string converter if we declare it as + // returning a string, so we have no opportunity to pass it to + // piperFreeJson, which suggests this would leak memory if the + // string isn't static. Not wholly sure though. Anyway, + // passing and returning pointers (as numbers) means we can + // manage the Emscripten heap memory however we want in our + // request wrapper function below. - const blockSize = 1024; + var piperRequestJson = exampleModule.cwrap( + 'piperRequestJson', 'number', ['number'] + ); - result = request('{"method":"configure","params":{"handle":1,"configuration":{"blockSize": ' + blockSize + ', "channelCount": 1, "stepSize": ' + blockSize + '}}}'); + var piperProcessRaw = exampleModule.cwrap( + "piperProcessRaw", "number", ["number", "number", "number", "number"] + ); - const nblocks = 1000; + var piperFreeJson = exampleModule.cwrap( + 'piperFreeJson', 'void', ['number'] + ); - const makeBlock = (n => { - return { - timestamp : frame2timestamp(n * blockSize, rate), - inputBuffers : [ - new Float32Array(Array.from(Array(blockSize).keys(), - n => n / blockSize)) - ], + function note(blah) { + document.getElementById("test-result").innerHTML += blah + "<br>"; } + + function comment(blah) { + note("<br><i>" + blah + "</i>"); + } + + function processRaw(request) { + + const nChannels = request.processInput.inputBuffers.length; + const nFrames = request.processInput.inputBuffers[0].length; + + const buffersPtr = exampleModule._malloc(nChannels * 4); + const buffers = new Uint32Array( + exampleModule.HEAPU8.buffer, buffersPtr, nChannels); + + for (let i = 0; i < nChannels; ++i) { + const framesPtr = exampleModule._malloc(nFrames * 4); + const frames = new Float32Array( + exampleModule.HEAPU8.buffer, framesPtr, nFrames); + frames.set(request.processInput.inputBuffers[i]); + buffers[i] = framesPtr; + } + + const responseJson = piperProcessRaw( + request.handle, + buffersPtr, + request.processInput.timestamp.s, + request.processInput.timestamp.n); + + for (let i = 0; i < nChannels; ++i) { + exampleModule._free(buffers[i]); + } + exampleModule._free(buffersPtr); + + const responseJstr = exampleModule.Pointer_stringify(responseJson); + const response = JSON.parse(responseJstr); + + piperFreeJson(responseJson); + + return response; + } + + function makeTimestamp(seconds) { + if (seconds >= 0.0) { + return { + s: Math.floor(seconds), + n: Math.floor((seconds - Math.floor(seconds)) * 1e9 + 0.5) + }; + } else { + const { s, n } = makeTimestamp(-seconds); + return { s: -s, n: -n }; + } + } + + function frame2timestamp(frame, rate) { + return makeTimestamp(frame / rate); + } + + function request(jsonStr) { + note("Request JSON = " + jsonStr); + var m = exampleModule; + // Inspection reveals that intArrayFromString converts the string + // from utf16 to utf8, which is what we want (though the docs + // don't mention this). Note the *Cstr values are Emscripten heap + // pointers + var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); + var outCstr = piperRequestJson(inCstr); + m._free(inCstr); + var result = m.Pointer_stringify(outCstr); + piperFreeJson(outCstr); + note("Returned JSON = " + result); + return result; + } + + function myFromBase64(b64) { + while (b64.length % 4 > 0) { b64 += "="; } + let conv = new Float32Array(toByteArray(b64).buffer); + return conv; + } + + function convertWireFeature(wfeature) { + let out = {}; + if (wfeature.timestamp != null) { + out.timestamp = wfeature.timestamp; + } + if (wfeature.duration != null) { + out.duration = wfeature.duration; + } + if (wfeature.label != null) { + out.label = wfeature.label; + } + const vv = wfeature.featureValues; + if (vv != null) { + if (typeof vv === "string") { + out.featureValues = myFromBase64(vv); + } else { + out.featureValues = new Float32Array(vv); + } + } + return out; + } + + function convertWireFeatureList(wfeatures) { + return wfeatures.map(convertWireFeature); + } + + function responseToFeatureSet(response) { + const features = new Map(); + const processResponse = response.result; + const wireFeatures = processResponse.features; + Object.keys(wireFeatures).forEach(key => { + return features.set(key, convertWireFeatureList(wireFeatures[key])); + }); + return features; + } + + const rate = 44100; + + comment("Loading zero crossings plugin..."); + let result = request('{"method":"load","params": {"key":"vamp-example-plugins:zerocrossing","inputSampleRate":' + rate + ',"adapterFlags":["AdaptAllSafe"]}}'); + + const blockSize = 1024; + + comment("Configuring plugin..."); + result = request('{"method":"configure","params":{"handle":1,"configuration":{"framing": { "blockSize": ' + blockSize + ', "stepSize": ' + blockSize + '}, "channelCount": 1}}}'); + + const nblocks = 1000; + + const makeBlock = (n => { + return { + timestamp : frame2timestamp(n * blockSize, rate), + inputBuffers : [ + new Float32Array(Array.from(Array(blockSize).keys(), + n => n / blockSize)) + ], + } + }); + + const blocks = Array.from(Array(nblocks).keys(), makeBlock); + + comment("Now processing " + nblocks + " blocks of 1024 samples each..."); + + let total = 0; + + let start = (new Date()).getTime(); + comment("Start at " + start); + + for (let i = 0; i < nblocks; ++i) { + result = processRaw({ + "handle": 1, + "processInput": blocks[i] + }); + let features = responseToFeatureSet(result); + let count = features.get("counts")[0].featureValues[0]; + total += count; + } + + let finish = (new Date()).getTime(); + comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); + + comment("Total = " + total); + + comment("Again..."); + + total = 0; + + start = (new Date()).getTime(); + comment("Start at " + start); + + for (let i = 0; i < nblocks; ++i) { + result = processRaw({ + "handle": 1, + "processInput": blocks[i] + }); + let features = responseToFeatureSet(result); + let count = features.get("counts")[0].featureValues[0]; + total += count; + } + + finish = (new Date()).getTime(); + comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); + + comment("Total = " + total); + + comment("Cleaning up the plugin and getting any remaining features..."); + result = request('{"method":"finish","params":{"handle":1}}'); }); - - const blocks = Array.from(Array(nblocks).keys(), makeBlock); - - comment("Now processing " + nblocks + " blocks of 1024 samples each..."); - - let total = 0; - - let start = (new Date()).getTime(); - comment("Start at " + start); - - for (let i = 0; i < nblocks; ++i) { - result = processRaw({ - "handle": 1, - "processInput": blocks[i] - }); - let features = responseToFeatureSet(result); - let count = features.get("counts")[0].featureValues[0]; - total += count; - } - - let finish = (new Date()).getTime(); - comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); - - comment("Total = " + total); - - comment("Again..."); - - total = 0; - - start = (new Date()).getTime(); - comment("Start at " + start); - - for (let i = 0; i < nblocks; ++i) { - result = processRaw({ - "handle": 1, - "processInput": blocks[i] - }); - let features = responseToFeatureSet(result); - let count = features.get("counts")[0].featureValues[0]; - total += count; - } - - finish = (new Date()).getTime(); - comment("Finish at " + finish + " for a time of " + (finish - start) + " ms"); - - comment("Total = " + total); - - comment("Cleaning up the plugin and getting any remaining features..."); - result = request('{"method":"finish","params":{"handle":1}}'); } window.onload = function() {
--- a/test/quick-test.js Tue Jun 12 14:58:05 2018 +0100 +++ b/test/quick-test.js Wed Jun 13 15:55:39 2018 +0100 @@ -1,27 +1,5 @@ "use strict"; -var exampleModule = VampExamplePluginsModule(); - -// It is possible to declare both parameters and return values as -// "string", in which case Emscripten will take care of -// conversions. But it's not clear how one would manage memory for -// newly-constructed returned C strings -- the returned pointer from -// piperRequestJson would appear (?) to be thrown away by the -// Emscripten string converter if we declare it as returning a string, -// so we have no opportunity to pass it to piperFreeJson, which -// suggests this would leak memory if the string isn't static. Not -// wholly sure though. Anyway, passing and returning pointers (as -// numbers) means we can manage the Emscripten heap memory however we -// want in our request wrapper function below. - -var piperRequestJson = exampleModule.cwrap( - 'piperRequestJson', 'number', ['number'] -); - -var piperFreeJson = exampleModule.cwrap( - 'piperFreeJson', 'void', ['number'] -); - function note(blah) { document.getElementById("test-result").innerHTML += blah + "<br>"; } @@ -30,45 +8,69 @@ note("<br><i>" + blah + "</i>"); } -function request(jsonStr) { - note("Request JSON = " + jsonStr); - var m = exampleModule; - // Inspection reveals that intArrayFromString converts the string - // from utf16 to utf8, which is what we want (though the docs - // don't mention this). Note the *Cstr values are Emscripten heap - // pointers - var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); - var outCstr = piperRequestJson(inCstr); - m._free(inCstr); - var result = m.Pointer_stringify(outCstr); - piperFreeJson(outCstr); - note("Returned JSON = " + result); - return result; -} - function test() { - comment("Querying plugin list..."); - var result = request('{"method": "list"}'); + VampExamplePluginsModule().then(function(exampleModule) { + + // It is possible to declare both parameters and return values + // as "string", in which case Emscripten will take care of + // conversions. But it's not clear how one would manage memory + // for newly-constructed returned C strings -- the returned + // pointer from piperRequestJson would appear (?) to be thrown + // away by the Emscripten string converter if we declare it as + // returning a string, so we have no opportunity to pass it to + // piperFreeJson, which suggests this would leak memory if the + // string isn't static. Not wholly sure though. Anyway, + // passing and returning pointers (as numbers) means we can + // manage the Emscripten heap memory however we want in our + // request wrapper function below. - comment("Loading zero crossings plugin..."); - result = request('{"method":"load","params": {"key":"vamp-example-plugins:powerspectrum","inputSampleRate":16,"adapterFlags":["AdaptAllSafe"]}}'); + var piperRequestJson = exampleModule.cwrap( + 'piperRequestJson', 'number', ['number'] + ); + + var piperFreeJson = exampleModule.cwrap( + 'piperFreeJson', 'void', ['number'] + ); - comment("I'm now assuming that the load succeeded and the returned handle was 1. I haven't bothered to parse the JSON. If those assumptions are wrong, this obviously isn't going to work. Configuring the plugin..."); - result = request('{"method":"configure","params":{"handle":1,"configuration":{"blockSize": 8, "channelCount": 1, "stepSize": 8}}}'); + function request(jsonStr) { + note("Request JSON = " + jsonStr); + var m = exampleModule; + // Inspection reveals that intArrayFromString converts the + // string from utf16 to utf8, which is what we want + // (though the docs don't mention this). Note the *Cstr + // values are Emscripten heap pointers + var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL); + var outCstr = piperRequestJson(inCstr); + m._free(inCstr); + var result = m.Pointer_stringify(outCstr); + piperFreeJson(outCstr); + note("Returned JSON = " + result); + return result; + } - comment("If I try to configure it again, it should fail because it's already configured... but this doesn't change anything, and subsequent processing should work fine. Just an example of a failure call. NB this only works if Emscripten has exception catching enabled -- it's off by default in opt builds, which would just end the script here. Wonder what the performance penalty is like."); - result = request('{"method":"configure","params":{"handle":1,"configuration":{"blockSize": 8, "channelCount": 1, "stepSize": 8}}}'); + comment("Querying plugin list..."); + var result = request('{"method": "list"}'); - comment("Now processing a couple of blocks of data, on the same assumptions..."); - result = request('{"method":"process","params":{"handle":1,"processInput":{"timestamp":{"s":0,"n":0},"inputBuffers":[[0,1,-1,0,1,-1,0,1]]}}}'); - result = request('{"method":"process","params":{"handle":1,"processInput":{"timestamp":{"s":0,"n":500000000},"inputBuffers":[[0,1,-1,0,1,-1,0,1]]}}}'); + comment("Loading zero crossings plugin..."); + result = request('{"method":"load","params": {"key":"vamp-example-plugins:powerspectrum","inputSampleRate":16,"adapterFlags":["AdaptAllSafe"]}}'); - comment("Cleaning up the plugin and getting any remaining features..."); - result = request('{"method":"finish","params":{"handle":1}}'); + comment("I'm now assuming that the load succeeded and the returned handle was 1. I haven't bothered to parse the JSON. If those assumptions are wrong, this obviously isn't going to work. Configuring the plugin..."); + result = request('{"method":"configure","params":{"handle":1,"configuration":{"framing": { "blockSize": 8, "stepSize": 8 }, "channelCount": 1 }}}'); - comment("A process call should now fail, as the plugin has been cleaned up."); - result = request('{"method":"process","params":{"handle":1,"processInput":{"timestamp":{"s":0,"n":1000000000},"inputBuffers":[{"values":[0,1,-1,0,1,-1,0,1]}]}}}'); + comment("If I try to configure it again, it should fail because it's already configured... but this doesn't change anything, and subsequent processing should work fine. Just an example of a failure call. NB this only works if Emscripten has exception catching enabled -- it's off by default in opt builds, which would just end the script here. Wonder what the performance penalty is like."); + result = request('{"method":"configure","params":{"handle":1,"configuration":{"framing": { "blockSize": 8, "stepSize": 8 }, "channelCount": 1 }}}'); + + comment("Now processing a couple of blocks of data, on the same assumptions..."); + result = request('{"method":"process","params":{"handle":1,"processInput":{"timestamp":{"s":0,"n":0},"inputBuffers":[[0,1,-1,0,1,-1,0,1]]}}}'); + result = request('{"method":"process","params":{"handle":1,"processInput":{"timestamp":{"s":0,"n":500000000},"inputBuffers":[[0,1,-1,0,1,-1,0,1]]}}}'); + + comment("Cleaning up the plugin and getting any remaining features..."); + result = request('{"method":"finish","params":{"handle":1}}'); + + comment("A process call should now fail, as the plugin has been cleaned up."); + result = request('{"method":"process","params":{"handle":1,"processInput":{"timestamp":{"s":0,"n":1000000000},"inputBuffers":[{"values":[0,1,-1,0,1,-1,0,1]}]}}}'); + }); } window.onload = function() {