/*
* /rutes/index.js
*/
var express = require('express');
var router = express.Router();
var path = require('path');
var media = path.join(__dirname, "../public/media/");
/* GET home page. */
router.get('/', function(req, res, next) {
var fs = require('fs');
fs.readdir(media, function(err, names){
if (err) {
console.log(err);
} else{
res.render('index', { title: 'Passionate Music', music: names });
};
})
});
module.exports = router;
<!--
- /views/index.ejs
-->
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<header>
<h1><%= title %></h1>
<ul class="type" id="type">
<li data-type="dot">Dot</li>
<li data-type="column" class="selected">Column</li>
</ul>
<p>
<label>Volume <input id="volume" type="range" name="" value="60" min="0" max="100"></label>
</p>
</header>
<div class="left">
<ul id="list">
<% music.forEach(function (name) { %>
<li title="<%= name %>"><%= name %></li>
<% }) %>
</ul>
</div>
<div class="right" id="box"></div>
<script src="/javascripts/musicVisualizer.js"></script>
<script src="/javascripts/index.js"></script>
</body>
</html>
/*
* /public/javascript/index.js
*/
function $ (s) {
return document.querySelectorAll(s);
};
var lis = $('#list li');
var size = 32;
var box = $("#box")[0];
var width, height;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
box.appendChild(canvas);
var dots = [];
var linear;
var mv = new musicVisualizer({
size: size,
visualizer: draw
});
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function (argument) {
for (var j = 0; j < lis.length; j++) {
lis[j].className = "";
};
this.className = "selected";
mv.play("../media/" + this.title);
};
};
function random (m, n) {
return Math.round(Math.random() * (n - m) + m);
}
function getDots () {
dots = [];
for (var i = 0; i < size; i++) {
var x = random(0, width);
var y = random(0, height);
var color = "rgba(" + random(0, 255) + "," + random(0, 255) + "," + random(0, 255) + ", 0)";
dots.push({
x: x,
y: y,
dx: random(1, 2),
color: color,
cap: 0
});
};
};
function resize () {
height = box.clientHeight;
width = box.clientWidth;
canvas.height = height;
canvas.width = width;
linear = ctx.createLinearGradient(0, 0, 0, height);
linear.addColorStop(0, "red");
linear.addColorStop(0.5, "yellow");
linear.addColorStop(1, "green");
getDots();
};
resize();
function draw (arr) {
var w = width / size;
var cw = w * 0.6;
var capHeight = cw > 10 ? 10 : cw;
ctx.fillStyle = linear;
ctx.clearRect(0, 0, width, height)
for (var i = 0; i < size; i++) {
var o = dots[i];
if(draw.type == "column"){
var h = arr[i] / 256 * height;
ctx.fillRect(w * i, height - h, cw, h);
ctx.fillRect(w * i, height - (o.cap + capHeight), cw, capHeight);
o.cap --;
if(o.cap < 0){
o.cap = 0;
};
if(h > 0 && o.cap < h + 40){
o.cap = h + 40 > height - capHeight ? height - capHeight : h + 40;
};
}
else if(draw.type == "dot"){
ctx.beginPath();
var r = 10 + arr[i] / 256 * (height > width ? width : height) / 10;
ctx.arc(o.x, o.y, r, 0, Math.PI * 2, true);
var g = ctx.createRadialGradient(o.x, o.y, 0, o.x, o.y, r);
g.addColorStop(0, "#fff");
g.addColorStop(1, o.color);
ctx.fillStyle = g;
ctx.fill();
o.x += o.dx;
o.x = o.x > width ? 0 : o.x;
};
};
};
draw.type = "column";
var type = $("#type li");
for (var i = 0; i < type.length; i++) {
type[i].onclick = function () {
for (var j = 0; j < type.length; j++) {
type[j].className = "";
};
this.className = "selected";
draw.type = this.getAttribute("data-type");
};
};
window.onresize = resize;
$("#volume")[0].onchange = function (argument) {
mv.changeVolume(this.value / this.max);
};
$("#volume")[0].onchange();
/*
* /public/javascript/musicVisualizer.js
*/
function musicVisualizer (obj) {
this.source = null;
this.count = 0;
this.analyser = musicVisualizer.ac.createAnalyser();
this.size = obj.size;
this.analyser.fftSize = this.size * 2;
this.gainNode = musicVisualizer.ac[musicVisualizer.ac.createGain ? "createGain" : "createGainNode"]();
this.gainNode.connect(musicVisualizer.ac.destination);
this.analyser.connect(this.gainNode);
this.xhr = new XMLHttpRequest();
this.visualizer = obj.visualizer;
this.visualize();
};
musicVisualizer.ac = new ( window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext || window.AudioContext)();
musicVisualizer.prototype.load = function (url, fn) {
this.xhr.abort();
this.xhr.open("GET", url);
this.xhr.responseType = "arraybuffer";
var self = this;
this.xhr.onload = function () {
fn(self.xhr.response);
};
this.xhr.send();
};
musicVisualizer.prototype.decode = function (arraybuffer, fn) {
musicVisualizer.ac.decodeAudioData(arraybuffer, function (buffer) {
fn(buffer);
},
function (err) {
console.log(err);
});
};
musicVisualizer.prototype.play = function (url) {
var n = ++this.count;
var self = this;
this.source && this.stop();
this.load(url, function (arraybuffer) {
if(n != self.count) return;
self.decode(arraybuffer, function (buffer) {
if(n != self.count) return;
var bs = musicVisualizer.ac.createBufferSource();
bs.connect(self.analyser);
bs.buffer = buffer;
bs[bs.start ? "start" : "noteOn"](0);
self.source = bs;
});
});
};
musicVisualizer.prototype.stop = function (argument) {
this.source[this.source.stop ? "stop" : "noteOff"](0);
};
musicVisualizer.prototype.changeVolume = function (percent) {
this.gainNode.gain.value = percent * percent;
};
musicVisualizer.prototype.visualize = function (argument) {
var arr = new Uint8Array(this.analyser.frequencyBinCount);
requestAnimationFrame = window.webkkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.requestAnimationFrame;
var self = this;
function v () {
self.analyser.getByteFrequencyData(arr);
self.visualizer(arr);
requestAnimationFrame(v);
// console.log(arr);
};
requestAnimationFrame(v);
};
/*
* /public/stylesheets/sytle.css
*/
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html,
body {
height: 100%;
}
body {
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
background-color: #000;
text-align: center;
}
header,
.left,
.right {
position: absolute;
}
header {
color: white;
height: 150px;
top: 10px;
right: 10px;
left: 10px;
/*border: 1px #fff solid;*/
}
header h1 {
font-size: 40px;
height: 60px;
line-height: 60px;
}
header .type {
list-style: none;
display: inline-block;
border-radius: 4px;
border: 1px #ccc solid;
margin-bottom: 10px;
}
header .type li {
cursor: pointer;
float: left;
font-size: .75rem;
width: 80px;
height: 24px;
line-height: 24px;
text-align: center;
}
header .type .selected {
background-color: white;
color: black;
}
header .type:after {
content: "";
clear: both;
visibility: hidden;
}
.left {
left: 10px;
top: 170px;
bottom: 10px;
/*width: 200px;*/
width: 15%;
/*min-width: 140px;*/
border: 1px #fff solid;
}
.left ul {
overflow: auto;
}
.left ul li {
font-size: .75rem;
color: white;
height: 30px;
line-height: 30px;
text-align: left;
padding: 5px;
cursor: pointer;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.left ul .selected {
color: green;
}
.right {
top: 170px;
right: 10px;
bottom: 10px;
/*left: 220px;*/
left: calc(15% + 20px);
left: -webkit-calc(15% + 20px);
/*border: 1px #fff solid;*/
}
input[type="range"] { /*滑条*/
-webkit-appearance = none; /*取消默认样式*/
height: 8px;
background: #999;
border-radius: 10px;
outline: none;
}
input[type="range"]::-webkit-slider-thumb { /*滑块*/
-webkit-appearance = none;
height: 12px;
width: 12px;
background: #fff;
border-radius: 100%;
}
input[type="range"]::-moz-range-track { /*滑条*/
height: 8px;
background: #999;
border-radius: 10px;
border: none;
}
input[type="range"]::-moz-range-thumb { /*滑块伪类*/
-webkit-appearance = none;
height: 12px;
width: 12px;
background: #fff;
border-radius: 100%;
}
@media screen and (max-width: 800px), screen and (max-height: 500px) {
body {
font-size: 12px;
}
header {
height: 80px;
}
header h1 {
font-size: 24px;
height: 34px;
line-height: 34px;
}
header .type {
margin-bottom: 0;
}
header .type li {
width: 50px;
height: 16px;
line-height: 16px;
}
.left,
.right {
top: 100px;
}
}