diff --git a/cmd/audio-player/main.go b/cmd/audio-player/main.go index 92d02c9a..9f613320 100644 --- a/cmd/audio-player/main.go +++ b/cmd/audio-player/main.go @@ -1,27 +1,73 @@ package main import ( - "fmt" + "bytes" + "reflect" "syscall/js" + "unsafe" "bitbucket.org/ausocean/av/codec/adpcm" ) func main() { - c := make(chan struct{}, 0) - - println("WASM Go Initialized") - // register functions - registerCallbacks() - - <-c + dec := NewDecoder() + dec.Start() } -func registerCallbacks() { - js.Global().Set("decode", js.FuncOf(decode)) +// Decoder is a client side adpcm decoder +type Decoder struct { + inBuf []uint8 + onImgLoadCb, initMemCb js.Func + console js.Value + done chan struct{} } -func decode(this js.Value, args []js.Value) interface{} { - fmt.Println("calculating") - return adpcm.EncBytes(args[0].Int()) +// NewDecoder returns a new instance of decoder +func NewDecoder() *Decoder { + return &Decoder{ + console: js.Global().Get("console"), + done: make(chan struct{}), + } +} + +func (s *Decoder) setupOnImgLoadCb() { + s.onImgLoadCb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + // reader := bytes.NewReader(s.inBuf) + + //DECODING HAPPENS HERE + + decoded := bytes.NewBuffer(make([]byte, 0, len(s.inBuf)*4)) + dec := adpcm.NewDecoder(decoded) + dec.Write(s.inBuf) + + s.inBuf = decoded.Bytes() + + return nil + }) +} + +func (s *Decoder) setupInitMemCb() { + // The length of the data array buffer is passed. + // Then the buf slice is initialized to that length. + // A pointer to that slice is passed back to the browser. + s.initMemCb = js.FuncOf(func(this js.Value, i []js.Value) interface{} { + length := i[0].Int() + s.console.Call("log", "length:", length) + s.inBuf = make([]uint8, length) + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s.inBuf)) + ptr := uintptr(unsafe.Pointer(hdr.Data)) + s.console.Call("log", "ptr:", ptr) + js.Global().Call("gotMem", ptr) + return nil + }) +} + +// Start sets up all the callbacks and waits for the close signal +// to be sent from the browser. +func (s *Decoder) Start() { + s.setupInitMemCb() + js.Global().Set("initMem", s.initMemCb) + s.setupOnImgLoadCb() + js.Global().Set("loadData", s.onImgLoadCb) + <-s.done } diff --git a/cmd/audio-player/main.js b/cmd/audio-player/main.js index c18173fd..fef42805 100644 --- a/cmd/audio-player/main.js +++ b/cmd/audio-player/main.js @@ -1,59 +1,81 @@ +const go = new Go() +// memoryBytes is an Uint8Array pointing to the webassembly linear memory. +let memoryBytes; +let bytes; +let mod, inst +console.log("Initializing wasm...") +WebAssembly.instantiateStreaming( + fetch('lib.wasm'), go.importObject).then( + result => { + mod = result.module + inst = result.instance + memoryBytes = new Uint8Array(inst.exports.mem.buffer) + console.log("Initialization complete.") + run() + } +) + +async function run() { + await go.run(inst) + inst = await WebAssembly.instantiate(mod, go.importObject) // reset instance +} + +// gotMem sets the webassembly linear memory with the data buffer result +// at the slice header pointer passed from Go. +function gotMem(pointer) { + memoryBytes.set(bytes, pointer); + // Now the data can be loaded from the slice. + loadData(); +} + +// // displayImage takes the pointer to the target image in the wasm linear memory +// // and its length. Gets the resulting byte slice and creates an image blob. +// function displayImage(pointer, length) { +// let resultBytes = memoryBytes.slice(pointer, pointer + length); +// let blob = new Blob([resultBytes], { +// 'type': imageType +// }); +// document.getElementById('targetImg').src = URL.createObjectURL(blob); +// } + +// document.getElementById('input').addEventListener('change', function () { +// let reader = new FileReader(); + +// reader.onload = (ev) => { +// bytes = new Uint8Array(ev.target.result); +// initMem(bytes.length); +// let blob = new Blob([bytes], { +// 'type': imageType +// }); +// document.getElementById("sourceImg").src = URL.createObjectURL(blob); +// }; +// imageType = this.files[0].type; +// reader.readAsArrayBuffer(this.files[0]); +// }); window.onload = function () { - initWasm() - document.getElementById('input').addEventListener('change', playFile) -} -function playFile() { - console.log(decode(48000)) - const input = event.target.files[0] + document.getElementById('input').addEventListener('change', function () { + const input = event.target.files[0] + const reader = new FileReader() - const reader = new FileReader() - reader.onload = event => playData(event.target.result) - reader.onerror = error => reject(error) - reader.readAsArrayBuffer(input) -} + reader.onload = event => { + bytes = new Uint8Array(event.target.result) -function playData(array) { - var data = new Uint8Array(array) - console.log("playing file") - var player = new PCMPlayer({ - encoding: '16bitInt', - channels: 1, - sampleRate: 48000, - flushingTime: 2000 - }); - player.feed(data) -} + initMem(bytes.length) -function initWasm() { - if (!WebAssembly.instantiateStreaming) { - // polyfill - WebAssembly.instantiateStreaming = async (resp, importObject) => { - const source = await (await resp).arrayBuffer() - return await WebAssembly.instantiate(source, importObject) + // bytes should be decoded at this point, if not try memoryBytes.set(bytes, pointer) + + console.log("playing file") + var player = new PCMPlayer({ + encoding: '16bitInt', + channels: 1, + sampleRate: 48000, + flushingTime: 2000 + }); + player.feed(bytes) } - } - - const go = new Go() - let mod, inst - // memoryBytes is an Uint8Array pointing to the webassembly linear memory. - let memoryBytes; - console.log("Initializing wasm...") - WebAssembly.instantiateStreaming( - fetch('lib.wasm'), go.importObject).then( - result => { - mod = result.module - inst = result.instance - memoryBytes = new Uint8Array(inst.exports.mem.buffer) - console.log("Initialization complete.") - run() - } - ) - - async function run() { - await go.run(inst) - inst = await WebAssembly.instantiate(mod, go.importObject) // reset instance - } - + reader.onerror = error => reject(error) + reader.readAsArrayBuffer(input) + }) } \ No newline at end of file