audio-player: added js adpcm decode func

This commit is contained in:
Trek H 2019-07-24 17:12:24 +09:30
parent 908b8e6e56
commit 8a3eeec59d
5 changed files with 83 additions and 89 deletions

View File

@ -5,7 +5,6 @@
<meta charset="utf-8">
<title>Audio Player</title>
<script type="text/javascript" src="pcm-player.min.js"></script>
<script src="wasm_exec.js"></script>
<script type="text/javascript" src="main.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

View File

@ -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)

View File

@ -4,7 +4,6 @@ import (
"flag"
"log"
"net/http"
"strings"
)
var (
@ -15,18 +14,5 @@ var (
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)
})
log.Fatal(http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir))))
}

View File

@ -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)

72
codec/adpcm/adpcm.js Normal file
View File

@ -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;
}