Permalink
Browse files

first commit

  • Loading branch information...
Eiji Kitamura
Eiji Kitamura committed Feb 28, 2012
0 parents commit 7791fcd54127fb4ecc4befd5647192bada3b7fa1
Showing with 499 additions and 0 deletions.
  1. +10 −0 .gitignore
  2. +97 −0 app.js
  3. +10 −0 package.json
  4. +47 −0 public/css/style.css
  5. +199 −0 public/js/AudioStreamer.js
  6. +29 −0 public/js/SpectrumVisualizer.js
  7. +7 −0 routes/index.js
  8. +27 −0 views/index.ejs
  9. +73 −0 views/layout.ejs
@@ -0,0 +1,10 @@
+.DS_Store
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+node_modules
+*.sublime-*
97 app.js
@@ -0,0 +1,97 @@
+/**
+ * Module dependencies.
+ */
+
+var express = require('express')
+ , routes = require('./routes')
+ , WebSocketServer = require('ws').Server;
+
+var app = module.exports = express.createServer();
+
+// Configuration
+
+app.configure(function(){
+ app.set('views', __dirname + '/views');
+ app.set('view engine', 'ejs');
+ app.use(express.bodyParser());
+ app.use(express.methodOverride());
+ app.use(app.router);
+ app.use(express.static(__dirname + '/public'));
+});
+
+app.configure('development', function(){
+ app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
+});
+
+app.configure('production', function(){
+ app.use(express.errorHandler());
+});
+
+// Routes
+
+app.get('/', routes.index);
+// app.get('/play', routes.play);
+
+var listeners = [],
+ players = [];
+app.listen(3000, function() {
+ var wsp = new WebSocketServer({server:app, path:'/play'});
+ var wsl = new WebSocketServer({server:app, path:'/listen'});
+ wsp.on('connection', function(ws) {
+ players.push(ws);
+ console.log('player opened a connection.');
+ ws.on('message', function(buffer) {
+ console.log('player received a message:', buffer);
+ var length = buffer.length;
+ var binary = new Uint8Array(length);
+ for (var i = 0; i < length; i++) {
+ binary[i] = buffer.readUInt8(i);
+ };
+ for (var i = 0; i < listeners.length; i++) {
+ // if (listeners[i] == ws) continue;
+ if (i == 0) continue;
+ listeners[i].send(binary, {binary:true, mask:false});
+ };
+ });
+ ws.on('close', function() {
+ console.log('player closed connection.')
+ for (var i = 0; i < players.length; i ++) {
+ if (players[i] == ws) {
+ players.splice(i, 1);
+ break;
+ }
+ }
+ });
+ ws.on('error', function() {
+ });
+ });
+ wsl.on('connection', function(ws) {
+ var name = '';
+ listeners.push(ws);
+ console.log('listener opened a connection.');
+ ws.on('message', function(message) {
+ console.log('received a message: "'+message+'"');
+ for (var i = 0; i < listeners.length; i++) {
+ if (name == '') {
+ name = message;
+ listeners[i].send(message+' opened a connection.');
+ } else {
+ listeners[i].send(name+': '+message);
+ }
+ }
+ })
+ ws.on('close', function() {
+ console.log('listener closed the connection.');
+ for (var i = 0; i < listeners.length; i++) {
+ if (listeners[i] == ws) {
+ listeners.splice(i, 1);
+ break;
+ }
+ }
+ });
+ ws.on('error', function() {
+ });
+ })
+});
+
+console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
@@ -0,0 +1,10 @@
+{
+ "name": "AudioStreamer"
+ , "version": "0.0.1"
+ , "private": true
+ , "dependencies": {
+ "express": "2.5.2"
+ , "ejs": ">= 0.0.1"
+ , "ws": ">= 0.0.1"
+ }
+}
@@ -0,0 +1,47 @@
+body {
+ padding: 50px;
+ font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
+}
+
+a {
+ color: #00B7FF;
+}
+
+p {
+ margin:0;
+}
+
+input#text {
+ width:300px;
+}
+
+div#player {
+ margin: 10px 0;
+ padding: 5px 10px;
+ border: dotted 1px gray;
+ -webkit-box-orient: horizontal;
+ -webkit-box-pack: justify;
+}
+
+div#dropper {
+}
+
+div#visualizer {
+ border: 1px solid #eee;
+ width: 600px;
+ height:150px;
+}
+
+div#conversation {
+ -webkit-box-orient: horizontal;
+ -webkit-box-pack: justify;
+}
+
+ul#msg {
+ display:inline-block;
+}
+
+div#listener {
+ width:600px;
+ height:150px;
+}
@@ -0,0 +1,199 @@
+var AudioStreamer = (function() {
+ var listenerBuffer = [[], []],
+ BUFFER_LENGTH = 2048,
+ ws_host = 'ws://localhost:3000',
+ ac = new webkitAudioContext();
+
+ var AudioPlayer = function() {
+ var that = this;
+ this.type = 'Listener';
+ this.audioBuffer = [[], []];
+ this.isPlaying = false;
+ this.trailing = 0; // Stop playing a few audioprocess after decay
+
+ this.js = ac.createJavaScriptNode(BUFFER_LENGTH, 2, 2);
+ this.js.onaudioprocess = function(event) {
+ var l = that.audioBuffer[0].shift() || new Float32Array(BUFFER_LENGTH);
+ var r = that.audioBuffer[1].shift() || new Float32Array(BUFFER_LENGTH);
+ if (that.type == 'Player') {
+ if (that.audioBuffer[0].length == 0) {
+ if (that.trailing++ > 3) {
+ that.trailing = 0;
+ that.stop();
+ }
+ } else {
+ var buffer = new Float32Array(BUFFER_LENGTH * 2);
+ for (var i = 0; i < BUFFER_LENGTH; i++) {
+ buffer[i] = l[i];
+ }
+ for (var i = 0; i < BUFFER_LENGTH; i++) {
+ buffer[BUFFER_LENGTH+i] = r[i];
+ }
+ that.socket.send(buffer.buffer);
+ }
+ }
+ event.outputBuffer.getChannelData(0).set(l);
+ event.outputBuffer.getChannelData(1).set(r);
+ that.visualize();
+ };
+
+ this.analyser = ac.createAnalyser();
+ this.analyser.smoothingTimeConstant = 0.3;
+ };
+ AudioPlayer.prototype = {
+ load: function(source, visualizer, socket) {
+ // stock audio source to source as array of arraybuffers
+ this.visualizer = visualizer || null;
+ this.socket = socket || null;
+ if (source.getChannelData) {
+ this.source = [[], []];
+ for (var i = 0; i < source.getChannelData(0).length; i++) {
+ var index = ~~(i/BUFFER_LENGTH);
+ if (i%BUFFER_LENGTH == 0) {
+ this.source[0][index] = new Float32Array(BUFFER_LENGTH);
+ this.source[1][index] = new Float32Array(BUFFER_LENGTH);
+ }
+ this.source[0][index][i%BUFFER_LENGTH] = source.getChannelData(0)[i];
+ this.source[1][index][i%BUFFER_LENGTH] = source.getChannelData(1)[i];
+ }
+ this.type = 'Player';
+ } else {
+ this.audioBuffer = source;
+ this.type = 'Listener';
+ }
+ },
+ listen: function() {
+ this.js.connect(this.analyser);
+ this.analyser.connect(ac.destination);
+ },
+ play: function() {
+ this.js.connect(this.analyser);
+ this.analyser.connect(ac.destination);
+ for (var i = 0; i < this.source[0].length; i++) {
+ this.audioBuffer[0][i] = this.source[0][i];
+ this.audioBuffer[1][i] = this.source[1][i];
+ }
+ this.isPlaying = true;
+ },
+ stop: function() {
+ this.js.disconnect();
+ this.analyser.disconnect();
+ this.isPlaying = false;
+ },
+ visualize: function(that) {
+ if (typeof this.visualizer != null) {
+ var freqByteData = new Uint8Array(this.analyser.frequencyBinCount);
+ this.analyser.getByteFrequencyData(freqByteData);
+ this.visualizer.draw(freqByteData);
+ }
+ }
+ };
+
+ var AudioStreamer = function(host, visualizer, callback) {
+ var that = this;
+ ws_host = host;
+ listenerBuffer[0] = [];
+ listenerBuffer[1] = [];
+
+ this.listener = new WebSocket(ws_host+'/listen');
+ this.listener.onopen = function() {
+ that.listener.binaryType = 'arraybuffer';
+ console.debug('listner established.');
+ if (typeof callback == 'function') {
+ callback();
+ };
+ };
+ this.listener.onmessage = function(msg) {
+ if (typeof msg.data == 'string' && typeof that.onctrlmsg == 'function') {
+ // string
+ that.onctrlmsg(msg.data);
+ } else {
+ // binary
+ var binary = new Float32Array(msg.data);
+ var l = new Float32Array(BUFFER_LENGTH);
+ var r = new Float32Array(BUFFER_LENGTH);
+ for (var i = 0; i < BUFFER_LENGTH; i++) {
+ l[i] = binary[i];
+ r[i] = binary[BUFFER_LENGTH + i];
+ }
+ listenerBuffer[0].push(l);
+ listenerBuffer[1].push(r);
+ console.debug('listener received a message');
+ }
+ };
+ this.listener.onclose = function() {
+ console.debug('listner closed.');
+ };
+ this.listener.onerror = function() {
+ console.error('listner error.');
+ };
+ this.audioListener = new AudioPlayer();
+ this.audioListener.load(listenerBuffer, visualizer);
+ this.audioListener.listen();
+ this.player = null;
+ };
+ AudioStreamer.prototype = {
+ nameSelf: function(name) {
+ this.listener.send(name);
+ },
+ sendText: function(text) {
+ this.listener.send(text);
+ },
+ loadAudio: function(file, visualizer, callback) {
+ var that = this;
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ ac.decodeAudioData(e.target.result, function(buffer) {
+ that.audioReady = true;
+ that.player = new WebSocket(ws_host+'/play');
+ that.player.onopen = function() {
+ console.debug('player established.');
+ };
+ that.player.onmessage = function() {
+ console.debug('message');
+ };
+ that.player.onclose = function() {
+ console.debug('close');
+ };
+ that.player.onerror = function() {
+ console.debug('error');
+ };
+ that.audioPlayer = new AudioPlayer();
+ that.audioPlayer.load(buffer, visualizer, that.player);
+ callback();
+ }, function() {
+ errorCallback('failed to load audio.');
+ });
+ };
+ reader.readAsArrayBuffer(file);
+ },
+ play: function() {
+ var that = this;
+ this.audioPlayer.play();
+ },
+ stop: function() {
+ if (this.audioSource) {
+ this.audioSource.noteOff(0);
+ this.audioSource = null;
+ };
+ },
+ rewind: function() {
+ },
+ disconnect: function() {
+ if (this.listener.close) {
+ this.listener.close();
+ console.debug('listener disconnected.');
+ };
+ if (this.player && this.player.close) {
+ this.player.close();
+ console.debug('player disconnected.');
+ };
+ },
+ onctrlmsg: null,
+ audioReady: false
+ };
+
+ return function(host, visualizer, callback) {
+ return new AudioStreamer(host, visualizer, callback);
+ };
+})();
@@ -0,0 +1,29 @@
+var SpectrumVisualizer = function(elem, width, height) {
+ this.canvas = document.createElement('canvas');
+ this.canvas.setAttribute('width', width);
+ this.canvas.setAttribute('height', height);
+ this.ctx = this.canvas.getContext('2d');
+ elem.appendChild(this.canvas);
+
+ this.width = Number(this.canvas.width);
+ this.height = Number(this.canvas.height);
+};
+SpectrumVisualizer.prototype = {
+ draw: function(freq) {
+ var length = freq.length;
+ this.clear();
+
+ // Draw rectangle for each frequency bin.
+ this.ctx.beginPath();
+ for (var i = 0; i < this.width; i++) {
+ var index = ~~(length / this.width * i);
+ var value = ~~(this.height - ((freq[index] || 0) / 256 * this.height));
+ if (i == 0) this.ctx.moveTo(0, value);
+ this.ctx.lineTo(i + 1, value);
+ }
+ this.ctx.stroke();
+ },
+ clear: function() {
+ this.ctx.clearRect(0, 0, this.width, this.height);
+ }
+};
Oops, something went wrong.

0 comments on commit 7791fcd

Please sign in to comment.