mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-12 07:44:03 +09:00
mp3 wip
This commit is contained in:
@@ -2,22 +2,90 @@ const Mp3 = require('mp3dec')
|
|||||||
const pcm = require("pcm")
|
const pcm = require("pcm")
|
||||||
|
|
||||||
|
|
||||||
|
class SequentialFileBuffer {
|
||||||
|
|
||||||
|
constructor(path, offset, length) {
|
||||||
|
if (Array.isArray(path)) throw Error("arg #1 is path(string), not array")
|
||||||
|
|
||||||
|
this.path = path
|
||||||
|
this.file = files.open(path)
|
||||||
|
|
||||||
|
this.offset = offset || 0
|
||||||
|
this.originalOffset = offset
|
||||||
|
this.length = length || this.file.size
|
||||||
|
|
||||||
|
this.seq = require("seqread")
|
||||||
|
this.seq.prepare(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*readFull(n) {
|
||||||
|
throw Error()
|
||||||
|
let ptr = this.seq.readBytes(n)
|
||||||
|
return ptr
|
||||||
|
}*/
|
||||||
|
|
||||||
|
readStr(n) {
|
||||||
|
let ptr = this.seq.readBytes(n)
|
||||||
|
let s = ''
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
if (i >= this.length) break
|
||||||
|
s += String.fromCharCode(sys.peek(ptr + i))
|
||||||
|
}
|
||||||
|
sys.free(ptr)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
readByteNumbers(n) {
|
||||||
|
let ptr = this.seq.readBytes(n)
|
||||||
|
let s = []
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
if (i >= this.length) break
|
||||||
|
s.push(sys.peek(ptr + i))
|
||||||
|
}
|
||||||
|
sys.free(ptr)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
unread(diff) {
|
||||||
|
let newSkipLen = this.seq.getReadCount() - diff
|
||||||
|
this.seq.prepare(this.path)
|
||||||
|
this.seq.skip(newSkipLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
rewind() {
|
||||||
|
this.seq.prepare(this.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
seek(p) {
|
||||||
|
this.seq.prepare(this.path)
|
||||||
|
this.seq.skip(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
get byteLength() {
|
||||||
|
return this.length
|
||||||
|
}
|
||||||
|
|
||||||
|
/*get remaining() {
|
||||||
|
return this.length - this.getReadCount()
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Object.keys(Mp3).forEach(e=>{
|
Object.keys(Mp3).forEach(e=>{
|
||||||
print(`${e}\t`)
|
print(`${e}\t`)
|
||||||
})
|
})
|
||||||
println()
|
println()
|
||||||
|
|
||||||
println("reading...")
|
println("reading...")
|
||||||
let arr = files.open("A:/gateless.mp3").bread()
|
//let arr = files.open("A:/gateless.mp3").bread()
|
||||||
let ab = new ArrayBuffer(arr.length)
|
//let ab = new ArrayBuffer(arr.length)
|
||||||
let abba = new Uint8Array(ab)
|
//let abba = new Uint8Array(ab)
|
||||||
arr.forEach((v,i)=>{ abba[i] = v })
|
//arr.forEach((v,i)=>{ abba[i] = v })
|
||||||
|
//let mp3ArrayBuffer = new Uint8Array(ab, 0, arr.length)*
|
||||||
|
|
||||||
let mp3ArrayBuffer = new Uint8Array(ab, 0, arr.length)
|
|
||||||
|
|
||||||
println("decoding...")
|
println("decoding...")
|
||||||
let decoder = Mp3.newDecoder(ab)
|
let decoder = Mp3.newDecoder(new SequentialFileBuffer("A:/gateless0.mp3"))
|
||||||
if (decoder === null) throw Error("decoder is null")
|
if (decoder === null) throw Error("decoder is null")
|
||||||
|
|
||||||
audio.resetParams(0)
|
audio.resetParams(0)
|
||||||
@@ -47,7 +115,11 @@ function printPlayBar() {
|
|||||||
|
|
||||||
|
|
||||||
const QUEUE_MAX = 4
|
const QUEUE_MAX = 4
|
||||||
|
let t1 = sys.nanoTime()
|
||||||
decoder.decode(obj=>{
|
decoder.decode(obj=>{
|
||||||
|
let t2 = sys.nanoTime()
|
||||||
|
|
||||||
|
|
||||||
let buf = obj.buf
|
let buf = obj.buf
|
||||||
let err = obj.err
|
let err = obj.err
|
||||||
|
|
||||||
@@ -60,8 +132,16 @@ decoder.decode(obj=>{
|
|||||||
audio.startSampleUpload(0)
|
audio.startSampleUpload(0)
|
||||||
audio.play(0)
|
audio.play(0)
|
||||||
|
|
||||||
serial.println(`Send sample (${audio.getPosition(0)})`)
|
|
||||||
// sys.sleep(0) // decoding time is slower than realtime :(
|
// sys.sleep(10) // decoding time is slower than realtime :(
|
||||||
|
|
||||||
|
|
||||||
|
let decodingTime = t2 - t1
|
||||||
|
let bufRealTimeLen = (declen) / 64000.0 * 1000000000
|
||||||
|
t1 = t2
|
||||||
|
println(`Decoded ${decodedLength} bytes; lag: ${(decodingTime - bufRealTimeLen) / 1000000} ms`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}) // now you got decoded PCM data
|
}) // now you got decoded PCM data
|
||||||
|
|||||||
@@ -643,14 +643,8 @@ var Frame = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
readCRC: function (source) {
|
readCRC: function (source) {
|
||||||
var result = source.readFull(2);
|
var buf = source.readFull(2)
|
||||||
if (result.err) {
|
if (buf.length < 2) {
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var buf = result.buf;
|
|
||||||
if (buf.byteLength < 2) {
|
|
||||||
return "mp3: error at readCRC";
|
return "mp3: error at readCRC";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ var consts = require('A:/tvdos/include/js-mp3/consts.js');
|
|||||||
|
|
||||||
var Frameheader = {
|
var Frameheader = {
|
||||||
createNew: function (value) {
|
createNew: function (value) {
|
||||||
|
|
||||||
// A mepg1FrameHeader is MPEG1 Layer 1-3 frame header
|
// A mepg1FrameHeader is MPEG1 Layer 1-3 frame header
|
||||||
var fh = {
|
var fh = {
|
||||||
value: value
|
value: value
|
||||||
@@ -214,14 +215,8 @@ var Frameheader = {
|
|||||||
read: function (source, position) {
|
read: function (source, position) {
|
||||||
var pos = position;
|
var pos = position;
|
||||||
|
|
||||||
var result = source.readFull(4);
|
var buf = source.readFull(4)
|
||||||
if (result.err) {
|
if (buf.length < 4) {
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var buf = result.buf;
|
|
||||||
if (buf.byteLength < 4) {
|
|
||||||
return {
|
return {
|
||||||
h: 0,
|
h: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
@@ -238,15 +233,9 @@ var Frameheader = {
|
|||||||
while (!fh.isValid()) {
|
while (!fh.isValid()) {
|
||||||
// stopPosition++;
|
// stopPosition++;
|
||||||
|
|
||||||
result = source.readFull(1);
|
buf = source.readFull(1);
|
||||||
if (result.err) {
|
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf = result.buf;
|
|
||||||
|
|
||||||
if (buf.byteLength < 1) {
|
if (buf.length < 1) {
|
||||||
return {
|
return {
|
||||||
h: 0,
|
h: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
|
|||||||
@@ -186,23 +186,24 @@ var read = function (source, prev, size, offset) {
|
|||||||
if (prev !== null) {
|
if (prev !== null) {
|
||||||
vec = prev.Tail(offset);
|
vec = prev.Tail(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// serial.println(`maindata readFull(${size})`)
|
||||||
|
|
||||||
// Read the main_data from file
|
// Read the main_data from file
|
||||||
var result = source.readFull(size);
|
var buf = source.readFull(size);
|
||||||
if (result.err) {
|
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var buf = result.buf;
|
|
||||||
// var buf = new Uint8Array(source, 0, size);
|
// var buf = new Uint8Array(source, 0, size);
|
||||||
if (buf.byteLength < size) {
|
if (buf.length < size) {
|
||||||
return {
|
return {
|
||||||
b: null,
|
b: null,
|
||||||
err: "maindata.Read (2)"
|
err: "maindata.Read (2)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
b: bits.createNew(util.concatBuffers(vec, new Uint8Array(buf.slice()).buffer)),
|
// b: bits.createNew(util.concatBuffers(vec, new Uint8Array(buf.slice()).buffer)),
|
||||||
|
b: bits.createNew(util.concatBuffers(vec, new Uint8Array(buf).buffer)),
|
||||||
err: null
|
err: null
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,23 +73,21 @@ var Sideinfo = {
|
|||||||
if (fheader.protectionBit() === 0) {
|
if (fheader.protectionBit() === 0) {
|
||||||
main_data_size -= 2;
|
main_data_size -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// serial.println(`sideinfo readFull(${sideinfo_size})`)
|
||||||
|
|
||||||
// Read sideinfo from bitstream into buffer used by Bits()
|
// Read sideinfo from bitstream into buffer used by Bits()
|
||||||
var result = source.readFull(sideinfo_size);
|
var buf = source.readFull(sideinfo_size);
|
||||||
if (result.err) {
|
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var buf = result.buf;
|
|
||||||
// var buf = new Uint8Array(source.buf, pos, sideinfo_size);
|
// var buf = new Uint8Array(source.buf, pos, sideinfo_size);
|
||||||
if (buf.byteLength < sideinfo_size) {
|
if (buf.length < sideinfo_size) {
|
||||||
return {
|
return {
|
||||||
v: null,
|
v: null,
|
||||||
pos: pos,
|
pos: pos,
|
||||||
err: "mp3: couldn't read sideinfo " + sideinfo_size + " bytes"
|
err: "mp3: couldn't read sideinfo " + sideinfo_size + " bytes"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var s = Bits.createNew(new Uint8Array(buf.slice()).buffer);
|
var s = Bits.createNew(new Uint8Array(buf).buffer);
|
||||||
|
|
||||||
// Parse audio data
|
// Parse audio data
|
||||||
// Pointer to where we should start reading main data
|
// Pointer to where we should start reading main data
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ var Mp3 = {
|
|||||||
err: "position not correct"
|
err: "position not correct"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
source.buf.seek(position)
|
||||||
source.pos = position;
|
source.pos = position;
|
||||||
return {
|
return {
|
||||||
pos: source.pos
|
pos: source.pos
|
||||||
@@ -60,35 +61,23 @@ var Mp3 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
source.readFull = function (length) {
|
source.readFull = function (length) {
|
||||||
|
if (length < 0) throw Error("Source.pos less than 0: "+source.pos)
|
||||||
|
|
||||||
try {
|
var l = Math.min(source.buf.byteLength - source.pos, length);
|
||||||
if (length < 0) throw Error("Source.pos less than 0: "+source.pos)
|
|
||||||
|
|
||||||
var l = Math.min(source.buf.byteLength - source.pos, length);
|
if (l < 0) {
|
||||||
|
serial.println("l < 0: "+l)
|
||||||
if (l < 0) {
|
throw Error("l < 0: "+l)
|
||||||
serial.println("l < 0: "+l)
|
|
||||||
throw Error("l < 0: "+l)
|
|
||||||
}
|
|
||||||
|
|
||||||
var bbuf = new Uint8Array(source.buf, source.pos, l);
|
|
||||||
|
|
||||||
source.pos += bbuf.byteLength;
|
|
||||||
|
|
||||||
if (source.pos < 0) {
|
|
||||||
throw Error("pos < 0: "+source.pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
buf: bbuf,
|
|
||||||
err: null
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
buf: null,
|
|
||||||
err: e.toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serial.println(`readFull(${length} -> ${l}); pos: ${source.pos}`)
|
||||||
|
|
||||||
|
if (source.pos + l < 0) {
|
||||||
|
throw Error("pos < 0: "+source.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
source.pos += l;
|
||||||
|
return source.buf.readByteNumbers(l)
|
||||||
};
|
};
|
||||||
|
|
||||||
source.getPos = function () {
|
source.getPos = function () {
|
||||||
@@ -99,69 +88,39 @@ var Mp3 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
source.skipTags = function () {
|
source.skipTags = function () {
|
||||||
var result = source.readFull(3);
|
var t = source.buf.readStr(3);
|
||||||
if (result.err) {
|
|
||||||
return {
|
// var buf = result.buf;
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var buf = result.buf;
|
|
||||||
|
|
||||||
// decode UTF-8
|
// decode UTF-8
|
||||||
var t = String.fromCharCode.apply(null, buf);
|
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case "TAG":
|
case "TAG":
|
||||||
result = source.readFull(125);
|
source.readFull(125);
|
||||||
if (result.err) {
|
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf = result.buf;
|
|
||||||
break;
|
break;
|
||||||
case 'ID3':
|
case 'ID3':
|
||||||
// Skip version (2 bytes) and flag (1 byte)
|
// Skip version (2 bytes) and flag (1 byte)
|
||||||
result = source.readFull(3);
|
source.readFull(3);
|
||||||
if (result.err) {
|
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = source.readFull(4);
|
let buf = source.readFull(4)
|
||||||
if (result.err) {
|
if (buf.length !== 4) {
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf = result.buf;
|
|
||||||
if (buf.byteLength !== 4) {
|
|
||||||
return {
|
return {
|
||||||
err: "data not enough."
|
err: "data not enough."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var size = (((buf[0] >>> 0) << 21) >>> 0) | (((buf[1] >>> 0) << 14) >>> 0) | (((buf[2] >>> 0) << 7) >>> 0) | (buf[3] >>> 0);
|
var size = (((buf[0] >>> 0) << 21) >>> 0) | (((buf[1] >>> 0) << 14) >>> 0) | (((buf[2] >>> 0) << 7) >>> 0) | (buf[3] >>> 0);
|
||||||
result = source.readFull(size);
|
|
||||||
if (result.err) {
|
source.readFull(size)
|
||||||
return {
|
|
||||||
err: result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf = result.buf;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
source.unread(buf);
|
source.buf.unread(3);
|
||||||
// source.pos -= 3;
|
// source.pos -= 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
source.unread = function (buf) {
|
|
||||||
source.pos -= buf.byteLength
|
|
||||||
};
|
|
||||||
|
|
||||||
source.rewind = function() {
|
source.rewind = function() {
|
||||||
|
source.buf.rewind()
|
||||||
source.pos = 0;
|
source.pos = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -197,6 +156,9 @@ var Mp3 = {
|
|||||||
|
|
||||||
decoder.decode = function (callback) {
|
decoder.decode = function (callback) {
|
||||||
var result;
|
var result;
|
||||||
|
|
||||||
|
serial.println("Start decoding")
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
result = decoder.readFrame();
|
result = decoder.readFrame();
|
||||||
|
|
||||||
@@ -227,6 +189,9 @@ var Mp3 = {
|
|||||||
|
|
||||||
var l = 0;
|
var l = 0;
|
||||||
while(true) {
|
while(true) {
|
||||||
|
|
||||||
|
// serial.println(`Reading Frames; l = ${l}`)
|
||||||
|
|
||||||
var result = Frameheader.read(decoder.source, decoder.source.pos);
|
var result = Frameheader.read(decoder.source, decoder.source.pos);
|
||||||
if (result.err) {
|
if (result.err) {
|
||||||
if (result.err.toString().indexOf("UnexpectedEOF") > -1) {
|
if (result.err.toString().indexOf("UnexpectedEOF") > -1) {
|
||||||
@@ -236,10 +201,12 @@ var Mp3 = {
|
|||||||
err: result.err
|
err: result.err
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
decoder.frameStarts.push(result.position);
|
decoder.frameStarts.push(result.position);
|
||||||
l += consts.BytesPerFrame;
|
l += consts.BytesPerFrame;
|
||||||
|
|
||||||
result = decoder.source.readFull(result.h.frameSize() - 4); // move to next frame position
|
result = decoder.source.readFull(result.h.frameSize() - 4)[0]; // move to next frame position
|
||||||
if (result.err) {
|
if (result.err) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -255,21 +222,31 @@ var Mp3 = {
|
|||||||
};
|
};
|
||||||
// ======= Methods of decoder :: end =========
|
// ======= Methods of decoder :: end =========
|
||||||
|
|
||||||
|
// serial.println("Reading tags")
|
||||||
|
|
||||||
var r = s.skipTags();
|
var r = s.skipTags();
|
||||||
if (r && r.err) {
|
if (r && r.err) {
|
||||||
throw Error(`Error creating new MP3 source: ${r.err}`)
|
throw Error(`Error creating new MP3 source: ${r.err}`)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serial.println("Reading first frame")
|
||||||
|
|
||||||
var result = decoder.readFrame();
|
var result = decoder.readFrame();
|
||||||
if (result.err) {
|
if (result.err) {
|
||||||
throw Error(`Error reading frame: ${result.err}`)
|
throw Error(`Error reading frame: ${result.err}`)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serial.println("First frame finished reading")
|
||||||
|
|
||||||
decoder.sampleRate = decoder.frame.samplingFrequency();
|
decoder.sampleRate = decoder.frame.samplingFrequency();
|
||||||
|
|
||||||
|
serial.println("Sampling rate: "+decoder.sampleRate + " Hz")
|
||||||
|
|
||||||
result = decoder.ensureFrameStartsAndLength();
|
result = decoder.ensureFrameStartsAndLength();
|
||||||
|
|
||||||
|
serial.println("Decode end")
|
||||||
if (result.err) {
|
if (result.err) {
|
||||||
throw Error(`Error ensuring Frame starts and length: ${result.err}`)
|
throw Error(`Error ensuring Frame starts and length: ${result.err}`)
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user