From 8a3eeec59d54019d053f9524cfa1fc514ca500b2 Mon Sep 17 00:00:00 2001 From: Trek H Date: Wed, 24 Jul 2019 17:12:24 +0930 Subject: [PATCH] audio-player: added js adpcm decode func --- cmd/audio-player/index.html | 1 - cmd/audio-player/main.js | 65 +-------------------------------- cmd/audio-player/server.go | 32 +++++------------ codec/adpcm/adpcm.go | 2 +- codec/adpcm/adpcm.js | 72 +++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 89 deletions(-) create mode 100644 codec/adpcm/adpcm.js diff --git a/cmd/audio-player/index.html b/cmd/audio-player/index.html index 12487c53..d8e7aa23 100644 --- a/cmd/audio-player/index.html +++ b/cmd/audio-player/index.html @@ -5,7 +5,6 @@ Audio Player - diff --git a/cmd/audio-player/main.js b/cmd/audio-player/main.js index 962ef19a..c81deb05 100644 --- a/cmd/audio-player/main.js +++ b/cmd/audio-player/main.js @@ -1,60 +1,3 @@ -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 -} - -let memPointer - -// gotMem sets the webassembly linear memory with the data buffer result -// at the slice header pointer passed from Go. -function gotMem(pointer) { - memPointer = 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 () { document.getElementById('input').addEventListener('change', function () { @@ -64,12 +7,6 @@ window.onload = function () { reader.onload = event => { bytes = new Uint8Array(event.target.result) - initMem(bytes.length) - - let resultBuffer = new ArrayBuffer(getDecLen()); - let resultBytes = new Uint8Array(resultBuffer) - resultBytes.set(memoryBytes.slice(memPointer, memPointer + getDecLen())) - console.log("playing file") var player = new PCMPlayer({ encoding: '16bitInt', @@ -77,7 +14,7 @@ window.onload = function () { sampleRate: 48000, flushingTime: 2000 }); - player.feed(resultBytes) + player.feed(bytes) } reader.onerror = error => reject(error) reader.readAsArrayBuffer(input) diff --git a/cmd/audio-player/server.go b/cmd/audio-player/server.go index f8100992..407be14b 100644 --- a/cmd/audio-player/server.go +++ b/cmd/audio-player/server.go @@ -1,32 +1,18 @@ package main import ( - "flag" - "log" - "net/http" - "strings" + "flag" + "log" + "net/http" ) var ( - listen = flag.String("listen", ":8080", "listen address") - dir = flag.String("dir", ".", "directory to serve") + listen = flag.String("listen", ":8080", "listen address") + dir = flag.String("dir", ".", "directory to serve") ) func main() { - flag.Parse() - log.Printf("listening on %q...", *listen) - h := wasmContentTypeSetter(http.FileServer(http.Dir(*dir))) - err := http.ListenAndServe(*listen, h) - if err != http.ErrServerClosed { - log.Fatal(err) - } -} - -func wasmContentTypeSetter(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if strings.HasSuffix(r.URL.Path, ".wasm") { - w.Header().Set("content-type", "application/wasm") - } - h.ServeHTTP(w, r) - }) -} + flag.Parse() + log.Printf("listening on %q...", *listen) + log.Fatal(http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir)))) +} \ No newline at end of file diff --git a/codec/adpcm/adpcm.go b/codec/adpcm/adpcm.go index ce8ae9f7..c59bf873 100644 --- a/codec/adpcm/adpcm.go +++ b/codec/adpcm/adpcm.go @@ -295,7 +295,7 @@ func (d *Decoder) Write(b []byte) (int, error) { // For each byte, seperate it into two nibbles (each nibble is a compressed sample), // then decode each nibble and output the resulting 16-bit samples. - // If padding flag is true (Adpcm[3]), only decode up until the last byte, then decode that separately. + // If padding flag is true (b[3]), only decode up until the last byte, then decode that separately. for i := headBytes; i < len(b)-int(b[3]); i++ { twoNibs := b[i] nib2 := byte(twoNibs >> 4) diff --git a/codec/adpcm/adpcm.js b/codec/adpcm/adpcm.js new file mode 100644 index 00000000..2637fff5 --- /dev/null +++ b/codec/adpcm/adpcm.js @@ -0,0 +1,72 @@ +const indexTable = [ + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +] + +const stepTable = [ + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 +] + +const byteDepth = 2, // We are working with 16-bit samples. TODO(Trek): make configurable. + initSamps = 2, // Number of samples used to initialise the encoder. + initBytes = initSamps * byteDepth, + headBytes = 4, // Number of bytes in the header of ADPCM. + samplesPerEnc = 2, // Number of sample encoded at a time eg. 2 16-bit samples get encoded into 1 byte. + bytesPerEnc = samplesPerEnc * byteDepth, + compFact = 4; // In general ADPCM compresses by a factor of 4. + +let est, // Estimation of sample based on quantised ADPCM nibble. + idx, // Index to step used for estimation. + step; + +function decode(b) { + // b should be a Uint8Array + if (!(b instanceof Uint8Array)) { + console.log("Error: data is not a Uint8Array"); + return; + } + + // Initialize Decoder with first 4 bytes of b. + est = (new Uint16Array(b.slice(0, 2).buffer))[1]; + idx = b[byteDepth]; + step = stepTable[idx]; + + var result = new Uint8Array(b.slice(0, 2)); + + for (var i = headBytes; i < b.length - b[3]; i++) { + var twoNibs = b[i]; + var nib2 = twoNibs >> 4; + var nib1 = (nib2 << 4) ^ twoNibs + var firstBytes = decodeSample(nib1) + concat(result, firstBytes) + + var secondBytes = decodeSample(nib2) + concat(result, secondBytes) + } + if (b[3] == 1) { + var padNib = b[b.length - 1] + var samp = decodeSample(padNib) + concat(result, samp) + } + + return result; +} + +// concat concatenates TypedArrays a and b of same type. +function concat(a, b) { + var c = new(a.constructor)(a.length + b.length); + c.set(a, 0); + c.set(b, a.length); + return c; +} \ No newline at end of file