From 9d7e49b76fdf0045d4ed50583ebddd98614ef04c Mon Sep 17 00:00:00 2001 From: Xavi Colomer <> Date: Sun, 19 Nov 2017 17:11:45 +0100 Subject: [PATCH 1/2] Added metadata information (In, Out, Volume, Timestamp, Hd, Clip) --- lib/metadata.js | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/mic.js | 57 ++++++++++++----------- 2 files changed, 149 insertions(+), 25 deletions(-) create mode 100644 lib/metadata.js diff --git a/lib/metadata.js b/lib/metadata.js new file mode 100644 index 0000000..8f99694 --- /dev/null +++ b/lib/metadata.js @@ -0,0 +1,117 @@ +'use strict'; + +const extractMetadata = (info) => { + if (info.indexOf('Hd:') === -1) { + info = info.replace(/]( )+Clip:/g, '] Hd:0.0 Clip:'); + } + + const regex = /In:([0-9.]+)% ([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2}) \[([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2})\] Out:([0-9.]+)k? +?\[[\! \-\=]+\|([\! \-\=]+)\] Hd:([0-9.]+)? Clip:([0-9]+)/g; + const metadata = { + in: 0.0, + timestamp: 0, + timestamp2: 0, + out: 0.0, + volume: 0, + hd: 0, + clip: 0 + }; + + let m; + + while ((m = regex.exec(info)) !== null) { + if (m.index === regex.lastIndex) { + regex.lastIndex++; + } + + if (m.length < 2) { + return 0; + } + + let timestampItems; + + for (let i = 1, len = m.length; i < len; ++i) { + switch (i) { + case 1: + metadata.in = parseFloat(m[i]); + break; + case 2: + timestampItems = m.slice(2, 6); + metadata.timestamp = extractTimestamp(timestampItems); + i = i + 3; + break; + case 6: + timestampItems = m.slice(6, 10); + metadata.timestamp2 = extractTimestamp(timestampItems); + i = i + 3; + break; + case 10: + metadata.out = parseFloat(m[i]); + break; + case 11: + metadata.volume = extractVolumeLevel(m[i]); + break; + case 12: + metadata.hd = parseFloat(m[i]); + break; + case 13: + metadata.clip = parseInt(m[i], 10); + break; + } + } + } + + return metadata; +} + +const extractTimestamp = (items) => { + let timestamp = 0; + let value = 0; + + for (let i = 0, len = items.length; i < len; ++i) { + value = parseInt(items[i], 10); + switch (i) { + case 0: + value *= 60; + case 1: + value *= 60; + case 2: + value *= 1000; + break; + case 3: + value *= 10; + break; + } + + timestamp = timestamp + value; + } + + return timestamp; +} + +const extractVolumeLevel = (ascii) => { + let volume = 0; + + for (let i = 0, len = ascii.length; i < len; ++i) { + switch (ascii[i]) { + case ' ': + break; + case '=': + volume = volume + 2; + break; + case '-': + volume = volume + 1; + break; + case '!': + volume = volume + 4; + break; + default: + break; + } + } + + return volume; +} + +module.exports = { + extractMetadata +} \ No newline at end of file diff --git a/lib/mic.js b/lib/mic.js index 2f76ae2..ec906ca 100644 --- a/lib/mic.js +++ b/lib/mic.js @@ -3,6 +3,7 @@ var isMac = require('os').type() == 'Darwin'; var isWindows = require('os').type().indexOf('Windows') > -1; var IsSilence = require('./silenceTransform.js'); var PassThrough = require('stream').PassThrough; +var extractMetadata = require('./metadata').extractMetadata; var mic = function mic(options) { options = options || {}; @@ -24,17 +25,15 @@ var mic = function mic(options) { stdio: ['ignore', 'pipe', 'ignore'] }; - if(debug) { - audioProcessOptions.stdio[2] = 'pipe'; - } + audioProcessOptions.stdio[2] = 'pipe'; // Setup format variable for arecord call - if(endian === 'big') { + if (endian === 'big') { formatEndian = 'BE'; } else { formatEndian = 'LE'; } - if(encoding === 'unsigned-integer') { + if (encoding === 'unsigned-integer') { formatEncoding = 'U'; } else { formatEncoding = 'S'; @@ -43,14 +42,14 @@ var mic = function mic(options) { audioStream.setNumSilenceFramesExitThresh(parseInt(exitOnSilence, 10)); that.start = function start() { - if(audioProcess === null) { - if(isWindows){ + if (audioProcess === null) { + if (isWindows){ audioProcess = spawn('sox', ['-b', bitwidth, '--endian', endian, '-c', channels, '-r', rate, '-e', encoding, '-t', 'waveaudio', 'default', '-p'], audioProcessOptions) } - else if(isMac){ + else if (isMac){ audioProcess = spawn('rec', ['-b', bitwidth, '--endian', endian, '-c', channels, '-r', rate, '-e', encoding, '-t', fileType, '-'], audioProcessOptions) @@ -61,47 +60,47 @@ var mic = function mic(options) { } audioProcess.on('exit', function(code, sig) { - if(code != null && sig === null) { + if (code != null && sig === null) { audioStream.emit('audioProcessExitComplete'); - if(debug) console.log("recording audioProcess has exited with code = %d", code); + if (debug) console.log("recording audioProcess has exited with code = %d", code); } }); + audioProcess.stdout.pipe(audioStream); - if(debug) { - audioProcess.stderr.pipe(infoStream); - } + audioProcess.stderr.pipe(infoStream); + audioStream.emit('startComplete'); } else { - if(debug) { + if (debug) { throw new Error("Duplicate calls to start(): Microphone already started!"); } } }; that.stop = function stop() { - if(audioProcess != null) { + if (audioProcess != null) { audioProcess.kill('SIGTERM'); audioProcess = null; audioStream.emit('stopComplete'); - if(debug) console.log("Microphone stopped"); + if (debug) console.log("Microphone stopped"); } }; that.pause = function pause() { - if(audioProcess != null) { + if (audioProcess != null) { audioProcess.kill('SIGSTOP'); audioStream.pause(); audioStream.emit('pauseComplete'); - if(debug) console.log("Microphone paused"); + if (debug) console.log("Microphone paused"); } }; that.resume = function resume() { - if(audioProcess != null) { + if (audioProcess != null) { audioProcess.kill('SIGCONT'); audioStream.resume(); audioStream.emit('resumeComplete'); - if(debug) console.log("Microphone resumed"); + if (debug) console.log("Microphone resumed"); } } @@ -109,13 +108,21 @@ var mic = function mic(options) { return audioStream; } - if(debug) { + infoStream.on('data', function(data) { + if (audioProcess != null) { + const metadata = extractMetadata(data.toString()); + audioStream.emit('metadata', metadata); + } + }); + + if (debug) { infoStream.on('data', function(data) { - console.log("Received Info: " + data); - }); + console.log("Received Info: " + data + "!!"); + }); + infoStream.on('error', function(error) { - console.log("Error in Info Stream: " + error); - }); + console.log("Error in Info Stream: " + error); + }); } return that; From 51a81245cecd511c781b9d940ea56f1cb1f90011 Mon Sep 17 00:00:00 2001 From: Xavi Colomer <> Date: Sun, 19 Nov 2017 17:12:55 +0100 Subject: [PATCH 2/2] Updated Documentation --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index e483afd..18232f4 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,11 @@ micInputStream.on('data', function(data) { console.log("Recieved Input Stream: " + data.length); }); +micInputStream.on('metadata', (metadata) => { + console.log('metadata', metadata); + //metadata { in: 0, timestamp: 22890, timestamp2: 0, out: 365, volume: 0, hd: 0, clip: 8 } +}); + micInputStream.on('error', function(err) { console.log("Error in Input Stream: " + err); });