Created
October 5, 2018 17:41
-
-
Save nagayev/c98f3e40bf34e7be1acb7f039b4e8a35 to your computer and use it in GitHub Desktop.
wave.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var http = require('http'); | |
var fs = require('fs'); | |
var sys= require('sys') | |
var Canvas = require('canvas'); | |
var path = '/my/files/TH.wav'; // Путь к файлу. | |
var wave = {}; // создаём объект в который будем помещать все полученные данные | |
function addByte(byt) { | |
while (8 != byt.length) { | |
byt = '0' + byt; | |
} | |
return byt; | |
} | |
fs.readFile(path, function (err, data) { | |
if (err) throw err; // Считываем файл и помещаем его содержимое в переменную data | |
var text = ''; | |
var j = 0 | |
for (var i = j; i < j + 4; i++) { | |
text += String.fromCharCode(data[i]); | |
} | |
}) | |
j = i; | |
wave.type = text; | |
// получили тип - «RIFF» | |
var text = ''; | |
for (var i = j; i < j + 4; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
wave.size = parseInt(text, 2); | |
//Полученный размер файла всегда на 8 байт меньше чем тот, что говорит нам ОС. | |
var text = ''; | |
for (var i = j; i < j + 4; i++) { | |
text += String.fromCharCode(data[i]); | |
} | |
j = i; | |
console.log(j + ' Label -' + text); | |
wave.label = text; | |
//Метка «wave» | |
var text = ''; | |
for (var i = j; i < j + 4; i++) { | |
text += String.fromCharCode(data[i]); | |
//text += data[i].toString(16); | |
} | |
j = i; | |
// extra bytes fmt | |
var text = ''; | |
for (var i = j; i < j + 4; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
wave.extra_bytes_fmt = parseInt(text, 2); | |
//Compression code | |
var text = ''; | |
for (var i = j; i < j + 2; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
var compression = ''; | |
switch (parseInt(text, 2)) { | |
case 0: | |
compression = 'Unknown'; | |
break; | |
case 1: | |
compression = 'PCM/uncompressed'; | |
break; | |
case 2: | |
compression = 'Microsoft ADPCM'; | |
break; | |
case 6: | |
compression = 'ITU G.711 a-law'; | |
break; | |
case 7: | |
compression = 'ITU G.711 µ-law'; | |
break; | |
case 17: | |
compression = 'IMA ADPCM'; | |
break; | |
case 20: | |
compression = 'ITU G.723 ADPCM (Yamaha)'; | |
break; | |
case 49: | |
compression = 'GSM 6.10'; | |
break; | |
case 64: | |
compression = 'ITU G.721 ADPCM'; | |
break; | |
case 80: | |
compression = 'MPEG'; | |
break; | |
case 65536: | |
compression = 'Experimental'; | |
break; | |
default: | |
compression = 'Other'; | |
break; | |
} | |
wave.compression = compression; | |
//Number of channels | |
var text = ''; | |
for (var i = j; i < j + 2; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
console.log(j + ' Number of channels - ' + parseInt(text, 2)); | |
wave.number_of_channels = parseInt(text, 2); | |
//Sample rate | |
var text = ''; | |
for (var i = j; i < j + 4; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
console.log(j + ' Sample rate - ' + parseInt(text, 2) + ' hz '); | |
wave.sample_rate = parseInt(text, 2); | |
//Average bytes per second | |
var text = ''; | |
for (var i = j; i < j + 4; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
wave.average_bytes_per_second = parseInt(text, 2) * 8 / 1000; | |
// переводим в гораздо более родные и понятные кбит/с | |
//Block align | |
var text = ''; | |
for (var i = j; i < j + 2; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
wave.block_align = parseInt(text, 2); | |
//Significant bits per sample | |
var text = ''; | |
for (var i = j; i < j + 2; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
wave.significant_bits_per_sample = parseInt(text, 2); | |
//Extra format bytes | |
var text = ''; | |
for (var i = j; i < j + 2; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
wave.extra_format_bytes = parseInt(text, 2); | |
//end fmt | |
while (!(text == 'data' || j == wave.size)) { | |
text = String.fromCharCode(data[j]) + String.fromCharCode(data[j + 1]) + String.fromCharCode(data[j + 2]) + String.fromCharCode(data[j + 3]); | |
j++; | |
} | |
wave.data_position = j; | |
var text = ''; | |
for (var i = j; i < j + 4; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = byt + text; | |
} | |
j = i; | |
wave.chunk_size = parseInt(text, 2); | |
//sound | |
wave.lc = []; | |
wave.rc = []; | |
var k = 16; /* поскольку в несжатом очень много данных - мы будем брать не все данные, а через каждые k байтов*/ | |
wave.n = wave.block_align * k; | |
while (j < wave.size) { | |
var text = ''; | |
for (var i = j; i < j + wave.block_align; i++) { | |
var byt = data[i].toString(2); | |
if (byt.length != 8) { | |
byt = addByte(byt) | |
} | |
text = text + byt; | |
} | |
var s1 = text.slice(0, text.length / 2); | |
if (s1[0] == 1) { | |
s1 = -(parseInt(text.slice(1, text.length / 2), 2)) | |
} else { | |
s1 = parseInt(text.slice(0, text.length / 2), 2) | |
} | |
var s2 = text.slice(text.length / 2, text.length); | |
if (s2[0] == 1) { | |
s2 = -(parseInt(text.slice(text.length / 2 + 1, text.length), 2)) | |
} else { | |
s2 = parseInt(text.slice(text.length / 2, text.length), 2) | |
} /*если на 1 фрейм приходится 8 бит, то байт беззнаковый, если больше (16,24, 32… ), первый бит байта будет знаком */ | |
wave.lc.push(s1); | |
wave.rc.push(s2); | |
j = i; | |
j += wave.n; | |
} | |
var canvas = new Canvas(900, 300); | |
var ctx = canvas.getContext('2d'); | |
var canvas2 = new Canvas(900, 300); | |
var ctx2 = canvas2.getContext('2d'); | |
ctx.strokeStyle = 'rgba(0,187,255,1)'; | |
ctx.beginPath(); | |
ctx.moveTo(0, 150); | |
ctx2.strokeStyle = 'rgba(0,187,255,1)'; | |
ctx2.beginPath(); | |
ctx2.moveTo(0, 150); | |
wave.k = 900 / wave.lc.length; | |
wave.l = 300 / Math.pow(2, wave.significant_bits_per_sample); | |
// эти параметры необходимы для того чтобы полученная волна корректно умещалась на нашем холсте размером 900 на 300 | |
var q = Math.pow(2, wave.significant_bits_per_sample) / 2; | |
/* Поскольку node.js у меня крутится на виртуалке с FreeBSD, то чтобы посмотреть результат поднимем маленький сервер*/ | |
var web = http.createServer(function (req, res) { | |
res.writeHead(200, { | |
'Content-Type': 'text/html' | |
}); | |
for (var i = 1; i < wave.lc.length; i++) { | |
if (wave.lc[i] > 0) { | |
var y = 150 + Math.floor(wave.lc[i] * wave.l) | |
} else { | |
var y = 150 + Math.floor((-q - wave.lc[i]) * wave.l) | |
} | |
if (wave.lc[i] == 0) y = 150 | |
ctx.lineTo(Math.floor(i * wave.k), y); | |
} | |
ctx.stroke(); | |
res.write('<img src="' + canvas.toDataURL() + '" /><br/>'); | |
//левый канал готов | |
for (var i = 1; i < wave.rc.length; i++) { | |
if (wave.rc[i] > 0) { | |
var y = 150 + Math.floor(wave.rc[i] * wave.l) | |
} else { | |
var y = 150 + Math.floor((-q - wave.rc[i]) * wave.l) | |
} | |
if (wave.rc[i] == 0) y = 150 | |
ctx2.lineTo(Math.floor(i * wave.k), y); | |
} | |
ctx2.stroke(); | |
res.write('<img src="' + canvas2.toDataURL() + '" /><br/>'); | |
// правый канал готов | |
res.end(); | |
}).listen(8000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment