Skip to content

Instantly share code, notes, and snippets.

@nagayev
Created October 5, 2018 17:41
Show Gist options
  • Save nagayev/c98f3e40bf34e7be1acb7f039b4e8a35 to your computer and use it in GitHub Desktop.
Save nagayev/c98f3e40bf34e7be1acb7f039b4e8a35 to your computer and use it in GitHub Desktop.
wave.js
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