mirror of https://bitbucket.org/ausocean/av.git
audio-player: added js adpcm decode func
This commit is contained in:
parent
908b8e6e56
commit
8a3eeec59d
|
@ -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">
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))))
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue