Skip to content

Commit

Permalink
First, hacky commit
Browse files Browse the repository at this point in the history
  • Loading branch information
latentflip committed Jun 29, 2013
0 parents commit 68278f7
Show file tree
Hide file tree
Showing 9 changed files with 633 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Hark

### Warning: This is a work in progress

Hark is a tiny browser/commonJS module that listens to an audio stream, and emits events indicating whether the user is speaking or not.

## Usage:

`npm install hark`

If you aren't using browserify, you'll want hark.bundle.js.

```javascript
var hark = require('../hark.js')

var getUserMedia = require('getusermedia')

getUserMedia(function(err, stream) {
if (err) throw err

var speechEvents = hark(stream);

speechEvents.on('speaking', function() {
console.log('speaking');
});

speechEvents.on('stopped_speaking', function() {
console.log('stopped_speaking');
});
});
```

## Demo:

Clone and open example/index.html

## Requirements:

Chrome with webrtc audio input flag enabled

## License

MIT

12 changes: 12 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var browserify = require('browserify'),
fs = require('fs');

var bundle = browserify();
bundle.add('./hark');
bundle.bundle({standalone: 'hark'})
.pipe(fs.createWriteStream('hark.bundle.js'));


var demo = browserify(['./example/demo.js'])
.bundle()
.pipe(fs.createWriteStream('./example/demo.bundle.js'));
303 changes: 303 additions & 0 deletions example/demo.bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
;(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
var hark = require('../hark.js')
var log = require('bows')('Demo');

var getUserMedia = require('getusermedia')

getUserMedia(function(err, stream) {
if (err) throw err

var speechEvents = hark(stream);

speechEvents.on('speaking', function() {
document.write('Speaking<br>');
log('speaking');
});

speechEvents.on('stopped_speaking', function() {
document.write('Not Speaking<br>');
log('stopped_speaking');
});
});

},{"../hark.js":2,"bows":3,"getusermedia":4}],4:[function(require,module,exports){
// getUserMedia helper by @HenrikJoreteg
var func = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);


module.exports = function (contstraints, cb) {
var options;
var haveOpts = arguments.length === 2;
var defaultOpts = {video: true, audio: true};

// make contstraints optional
if (!haveOpts) {
cb = contstraints;
contstraints = defaultOpts;
}

// treat lack of browser support like an error
if (!func) return cb(new Error('notSupported'));

func.call(navigator, contstraints, function (stream) {
cb(null, stream);
}, function (err) {
cb(err);
});
};

},{}],2:[function(require,module,exports){
var WildEmitter = require('wildemitter');

module.exports = function(stream) {
var speakingThreshold = -45;
var smoothing = 0.5;
var pollPeriod = 100;
var audioContext = new webkitAudioContext();
var sourceNode = audioContext.createMediaStreamSource(stream);
var analyser = audioContext.createAnalyser();
var fftBins = new Float32Array(analyser.fftSize);

analyser.fftSize = 512;
analyser.smoothingTimeConstant = smoothing;
sourceNode.connect(analyser);

var emitter = new WildEmitter();
var speaking = false;

// Poll the analyser node to determine if speaking
// and emit events if changed
setInterval(function() {
var currentVolume = -Infinity;
analyser.getFloatFrequencyData(fftBins)

for(var i=0, ii=fftBins.length; i < ii; i++) {
if (fftBins[i] > currentVolume && fftBins[i] < 0) {
currentVolume = fftBins[i];
}
};

if (currentVolume > speakingThreshold) {
if (!speaking) {
speaking = true;
emitter.emit('speaking');
}
} else {
if (speaking) {
speaking = false;
emitter.emit('stopped_speaking');
}
}
}, pollPeriod);

return emitter;
}

},{"wildemitter":5}],5:[function(require,module,exports){
/*
WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based
on @visionmedia's Emitter from UI Kit.
Why? I wanted it standalone.
I also wanted support for wildcard emitters like this:
emitter.on('*', function (eventName, other, event, payloads) {
});
emitter.on('somenamespace*', function (eventName, payloads) {
});
Please note that callbacks triggered by wildcard registered events also get
the event name as the first argument.
*/
module.exports = WildEmitter;

function WildEmitter() {
this.callbacks = {};
}

// Listen on the given `event` with `fn`. Store a group name if present.
WildEmitter.prototype.on = function (event, groupName, fn) {
var hasGroup = (arguments.length === 3),
group = hasGroup ? arguments[1] : undefined,
func = hasGroup ? arguments[2] : arguments[1];
func._groupName = group;
(this.callbacks[event] = this.callbacks[event] || []).push(func);
return this;
};

// Adds an `event` listener that will be invoked a single
// time then automatically removed.
WildEmitter.prototype.once = function (event, groupName, fn) {
var self = this,
hasGroup = (arguments.length === 3),
group = hasGroup ? arguments[1] : undefined,
func = hasGroup ? arguments[2] : arguments[1];
function on() {
self.off(event, on);
func.apply(this, arguments);
}
this.on(event, group, on);
return this;
};

// Unbinds an entire group
WildEmitter.prototype.releaseGroup = function (groupName) {
var item, i, len, handlers;
for (item in this.callbacks) {
handlers = this.callbacks[item];
for (i = 0, len = handlers.length; i < len; i++) {
if (handlers[i]._groupName === groupName) {
//console.log('removing');
// remove it and shorten the array we're looping through
handlers.splice(i, 1);
i--;
len--;
}
}
}
return this;
};

// Remove the given callback for `event` or all
// registered callbacks.
WildEmitter.prototype.off = function (event, fn) {
var callbacks = this.callbacks[event],
i;

if (!callbacks) return this;

// remove all handlers
if (arguments.length === 1) {
delete this.callbacks[event];
return this;
}

// remove specific handler
i = callbacks.indexOf(fn);
callbacks.splice(i, 1);
return this;
};

// Emit `event` with the given args.
// also calls any `*` handlers
WildEmitter.prototype.emit = function (event) {
var args = [].slice.call(arguments, 1),
callbacks = this.callbacks[event],
specialCallbacks = this.getWildcardCallbacks(event),
i,
len,
item;

if (callbacks) {
for (i = 0, len = callbacks.length; i < len; ++i) {
if (callbacks[i]) {
callbacks[i].apply(this, args);
} else {
break;
}
}
}

if (specialCallbacks) {
for (i = 0, len = specialCallbacks.length; i < len; ++i) {
if (specialCallbacks[i]) {
specialCallbacks[i].apply(this, [event].concat(args));
} else {
break;
}
}
}

return this;
};

// Helper for for finding special wildcard event handlers that match the event
WildEmitter.prototype.getWildcardCallbacks = function (eventName) {
var item,
split,
result = [];

for (item in this.callbacks) {
split = item.split('*');
if (item === '*' || (split.length === 2 && eventName.slice(0, split[1].length) === split[1])) {
result = result.concat(this.callbacks[item]);
}
}
return result;
};

},{}],3:[function(require,module,exports){
(function(window) {
var logger = require('andlog'),
goldenRatio = 0.618033988749895,
hue = 0,
padLength = 15,
yieldColor,
bows;

yieldColor = function() {
hue += goldenRatio;
hue = hue % 1;
return hue * 360;
};

bows = function(str) {
var msg;
msg = "%c" + (str.slice(0, padLength));
msg += Array(padLength + 3 - msg.length).join(' ') + '|';

return logger.log.bind(logger, msg, "color: hsl(" + (yieldColor()) + ",99%,40%); font-weight: bold");
};

bows.config = function(config) {
if (config.padLength) {
return padLength = config.padLength;
}
};

if (typeof module !== 'undefined') {
module.exports = bows;
} else {
window.bows = bows;
}
}).call(this);

},{"andlog":6}],6:[function(require,module,exports){
// follow @HenrikJoreteg and @andyet if you like this ;)
(function () {
var inNode = typeof window === 'undefined',
ls = !inNode && window.localStorage,
out = {};

if (inNode) {
module.exports = console;
return;
}

if (ls && ls.debug && window.console) {
out = window.console;
} else {
var methods = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),
l = methods.length,
fn = function () {};

while (l--) {
out[methods[l]] = fn;
}
}
if (typeof exports !== 'undefined') {
module.exports = out;
} else {
window.console = out;
}
})();

},{}]},{},[1])
;
20 changes: 20 additions & 0 deletions example/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var hark = require('../hark.js')
var log = require('bows')('Demo');

var getUserMedia = require('getusermedia')

getUserMedia(function(err, stream) {
if (err) throw err

var speechEvents = hark(stream);

speechEvents.on('speaking', function() {
document.write('Speaking<br>');
log('speaking');
});

speechEvents.on('stopped_speaking', function() {
document.write('Not Speaking<br>');
log('stopped_speaking');
});
});
1 change: 1 addition & 0 deletions example/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script src='demo.bundle.js'></script>
Loading

0 comments on commit 68278f7

Please sign in to comment.