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">
|
<meta charset="utf-8">
|
||||||
<title>Audio Player</title>
|
<title>Audio Player</title>
|
||||||
<script type="text/javascript" src="pcm-player.min.js"></script>
|
<script type="text/javascript" src="pcm-player.min.js"></script>
|
||||||
<script src="wasm_exec.js"></script>
|
|
||||||
<script type="text/javascript" src="main.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"
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
|
||||||
integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
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 () {
|
window.onload = function () {
|
||||||
|
|
||||||
document.getElementById('input').addEventListener('change', function () {
|
document.getElementById('input').addEventListener('change', function () {
|
||||||
|
@ -64,12 +7,6 @@ window.onload = function () {
|
||||||
reader.onload = event => {
|
reader.onload = event => {
|
||||||
bytes = new Uint8Array(event.target.result)
|
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")
|
console.log("playing file")
|
||||||
var player = new PCMPlayer({
|
var player = new PCMPlayer({
|
||||||
encoding: '16bitInt',
|
encoding: '16bitInt',
|
||||||
|
@ -77,7 +14,7 @@ window.onload = function () {
|
||||||
sampleRate: 48000,
|
sampleRate: 48000,
|
||||||
flushingTime: 2000
|
flushingTime: 2000
|
||||||
});
|
});
|
||||||
player.feed(resultBytes)
|
player.feed(bytes)
|
||||||
}
|
}
|
||||||
reader.onerror = error => reject(error)
|
reader.onerror = error => reject(error)
|
||||||
reader.readAsArrayBuffer(input)
|
reader.readAsArrayBuffer(input)
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -15,18 +14,5 @@ var (
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
log.Printf("listening on %q...", *listen)
|
log.Printf("listening on %q...", *listen)
|
||||||
h := wasmContentTypeSetter(http.FileServer(http.Dir(*dir)))
|
log.Fatal(http.ListenAndServe(*listen, 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)
|
|
||||||
})
|
|
||||||
}
|
}
|
|
@ -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),
|
// 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.
|
// 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++ {
|
for i := headBytes; i < len(b)-int(b[3]); i++ {
|
||||||
twoNibs := b[i]
|
twoNibs := b[i]
|
||||||
nib2 := byte(twoNibs >> 4)
|
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