2015-07-04 17:48:44 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
|
|
<title>VISUALIZING ENVELOPES</title>
|
|
|
|
|
2019-01-09 01:21:29 +00:00
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
|
|
|
<link rel="icon" type="image/png" sizes="174x174" href="./favicon.png">
|
2015-07-04 17:48:44 +00:00
|
|
|
|
2019-01-09 01:21:29 +00:00
|
|
|
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@^2/webcomponents-bundle.js"></script>
|
|
|
|
<script src="../build/Tone.js"></script>
|
|
|
|
<script src="./js/tonejs-ui.js"></script>
|
|
|
|
<script src="./js/p5.js"></script>
|
2015-07-04 17:48:44 +00:00
|
|
|
</head>
|
|
|
|
<body>
|
2015-12-05 18:20:53 +00:00
|
|
|
<style type="text/css">
|
|
|
|
canvas {
|
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
z-index: -1;
|
2019-01-09 01:21:29 +00:00
|
|
|
left: 0px;
|
2015-12-05 18:20:53 +00:00
|
|
|
}
|
|
|
|
</style>
|
2019-01-09 01:21:29 +00:00
|
|
|
<tone-example>
|
|
|
|
<tone-explanation label="Tone with p5.js">
|
|
|
|
Access the envelopes current value to synchronize visuals. This sketch uses <a href="https://p5js.org" target="_blank">p5.js</a> for canvas rendering.
|
|
|
|
<br><br>
|
|
|
|
Example by <a href="https://github.com/polyrhythmatic">polyrhythmatic</a>
|
|
|
|
</tone-explanation>
|
|
|
|
|
|
|
|
<tone-content>
|
|
|
|
<tone-play-toggle></tone-play-toggle>
|
|
|
|
</tone-content>
|
|
|
|
|
|
|
|
<tone-drawer collapsed>
|
|
|
|
<tone-filter frequency id="hihatFilter" collapsed label="Hihat Filter"></tone-filter>
|
|
|
|
<tone-noise-synth id="openHiHat" collapsed label="Open Hihat"></tone-noise-synth>
|
|
|
|
<tone-noise-synth id="closedHiHat" collapsed label="Closed Hihat"></tone-noise-synth>
|
|
|
|
<tone-oscillator id="bassOsc" collapsed label="Bass Osc" sourcetype="pulse"></tone-oscillator>
|
|
|
|
<tone-filter frequency id="bassFilter" collapsed label="Bass Filter"></tone-filter>
|
|
|
|
<tone-envelope id="bassEnvelope" collapsed label="Bass Envelope"></tone-envelope>
|
|
|
|
<tone-oscillator frequency id="bleep" collapsed label="Bleep"></tone-oscillator>
|
|
|
|
<tone-envelope id="bleepEnvelope" collapsed label="Bleep Envelope"></tone-envelope>
|
|
|
|
<tone-oscillator frequency id="kickOsc" collapsed label="Kick Oscillator"></tone-oscillator>
|
|
|
|
<tone-envelope id="kickEnvelope" collapsed label="Kick Envelope"></tone-envelope>
|
|
|
|
<tone-frequency-envelope id="kickFreqEnvelope" collapsed label="Kick Frequency Envelope"></tone-frequency-envelope>
|
|
|
|
</tone-drawer>
|
|
|
|
</tone-example>
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
|
function FunkyShape(){}
|
2015-07-04 17:48:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
FunkyShape init gives initial and offset values for
|
|
|
|
the perlin noise functions in update.
|
|
|
|
Giving different initial values ensures that
|
|
|
|
each funky shape follows its own funky path
|
|
|
|
*/
|
2019-01-09 01:21:29 +00:00
|
|
|
FunkyShape.prototype.init = function(xInc, yInc, xOff, yOff, radius){
|
|
|
|
this.xInc = xInc;
|
|
|
|
this.yInc = yInc;
|
|
|
|
this.xOff = xOff;
|
|
|
|
this.yOff = yOff;
|
|
|
|
this.radius = radius;
|
|
|
|
this.xPos = 0;
|
|
|
|
this.yPos = 0;
|
|
|
|
};
|
2015-07-04 17:48:44 +00:00
|
|
|
|
|
|
|
//updates the x, y, and radius values of the shape
|
2019-01-09 01:21:29 +00:00
|
|
|
FunkyShape.prototype.update = function(envelope){
|
|
|
|
this.xPos = noise(this.xOff) * width;
|
|
|
|
this.yPos = noise(this.yOff) * height;
|
|
|
|
this.xOff += this.xInc;
|
|
|
|
this.yOff += this.yInc;
|
|
|
|
this.sRadius = this.radius * envelope;
|
|
|
|
return {
|
|
|
|
"xPos" : this.xPos,
|
|
|
|
"yPos" : this.yPos,
|
|
|
|
"radius" : this.sRadius
|
|
|
|
};
|
|
|
|
};
|
2015-07-04 17:48:44 +00:00
|
|
|
|
|
|
|
//using our FunkyShape class
|
|
|
|
//to create a funkyCircle class
|
|
|
|
var funkyCircle = new FunkyShape();
|
|
|
|
|
|
|
|
//creating an empty array
|
|
|
|
var funkySquare = [];
|
|
|
|
//and populating it with 3 FunkyShapes
|
2019-01-09 01:21:29 +00:00
|
|
|
for (var i = 0; i < 3; i++){
|
|
|
|
funkySquare[i] = new FunkyShape();
|
2015-07-04 17:48:44 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 01:21:29 +00:00
|
|
|
function setup(){
|
|
|
|
//create a canvas width and height of the screen
|
|
|
|
createCanvas(windowWidth, windowHeight);
|
|
|
|
//no fill
|
|
|
|
fill(255);
|
|
|
|
strokeWeight(1);
|
|
|
|
rectMode(CENTER);
|
|
|
|
//initializing our funky circle
|
|
|
|
funkyCircle.init(0.01, 0.02, 0.0, 0.0, 400);
|
|
|
|
//initializing our squares with random values
|
|
|
|
//to ensure they don't follow the same path
|
|
|
|
for (var i = 0; i < 3; i++){
|
|
|
|
var xInc = Math.random() / 10;
|
|
|
|
var yInc = Math.random() / 10;
|
|
|
|
funkySquare[i].init(xInc, yInc, 0, 0, 800);
|
|
|
|
}
|
2015-07-04 17:48:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var phase = 0;
|
|
|
|
|
2019-01-09 01:21:29 +00:00
|
|
|
function draw(){
|
|
|
|
background(255);
|
|
|
|
stroke(0);
|
|
|
|
//drawing the kick wave at the bottom
|
|
|
|
//it is composed of a simple sine wave that
|
|
|
|
//changes in height with the kick envelope
|
|
|
|
for (var i = 0; i < width; i++){
|
|
|
|
//scaling kickEnvelope value by 200
|
|
|
|
//since default is 0-1
|
|
|
|
var kickValue = kickEnvelope.value * 200;
|
|
|
|
//multiplying this value to scale the sine wave
|
|
|
|
//depending on x position
|
|
|
|
var yDot = Math.sin((i / 60) + phase) * kickValue;
|
|
|
|
point(i, height -150 + yDot);
|
|
|
|
}
|
|
|
|
//increasing phase means that the kick wave will
|
|
|
|
//not be standing and looks more dynamic
|
|
|
|
phase += 1;
|
|
|
|
//updating circle and square positions with
|
|
|
|
//bass and bleep envelope values
|
|
|
|
var circlePos = funkyCircle.update(bassEnvelope.value);
|
|
|
|
//circlePos returns x and y positions as an object
|
|
|
|
ellipse(circlePos.xPos, circlePos.yPos, circlePos.radius, circlePos.radius);
|
|
|
|
stroke("red");
|
|
|
|
for (var i = 0; i < 3; i++){
|
|
|
|
var squarePos = funkySquare[i].update(bleepEnvelope.value);
|
|
|
|
rect(squarePos.xPos, squarePos.yPos, squarePos.radius, squarePos.radius);
|
|
|
|
}
|
2015-07-04 17:48:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//filtering the hi-hats a bit
|
|
|
|
//to make them sound nicer
|
|
|
|
var lowPass = new Tone.Filter({
|
2019-01-09 01:21:29 +00:00
|
|
|
"frequency" : 14000,
|
2015-07-04 17:48:44 +00:00
|
|
|
}).toMaster();
|
|
|
|
|
|
|
|
//we can make our own hi hats with
|
|
|
|
//the noise synth and a sharp filter envelope
|
|
|
|
var openHiHat = new Tone.NoiseSynth({
|
|
|
|
"volume" : -10,
|
2019-01-09 01:21:29 +00:00
|
|
|
"filter" : {
|
|
|
|
"Q" : 1
|
|
|
|
},
|
|
|
|
"envelope" : {
|
|
|
|
"attack" : 0.01,
|
|
|
|
"decay" : 0.3
|
|
|
|
},
|
|
|
|
"filterEnvelope" : {
|
|
|
|
"attack" : 0.01,
|
|
|
|
"decay" : 0.03,
|
|
|
|
"baseFrequency" : 4000,
|
|
|
|
"octaves" : -2.5,
|
|
|
|
"exponent" : 4,
|
|
|
|
}
|
2015-07-04 17:48:44 +00:00
|
|
|
}).connect(lowPass);
|
|
|
|
|
2015-12-05 18:20:53 +00:00
|
|
|
var openHiHatPart = new Tone.Part(function(time){
|
|
|
|
openHiHat.triggerAttack(time);
|
2019-01-09 01:21:29 +00:00
|
|
|
}, [{ "8n" : 2 }, { "8n" : 6 }]).start(0);
|
2015-12-05 18:20:53 +00:00
|
|
|
|
2015-07-04 17:48:44 +00:00
|
|
|
var closedHiHat = new Tone.NoiseSynth({
|
|
|
|
"volume" : -10,
|
2019-01-09 01:21:29 +00:00
|
|
|
"filter" : {
|
|
|
|
"Q" : 1
|
|
|
|
},
|
|
|
|
"envelope" : {
|
|
|
|
"attack" : 0.01,
|
|
|
|
"decay" : 0.15
|
|
|
|
},
|
|
|
|
"filterEnvelope" : {
|
|
|
|
"attack" : 0.01,
|
|
|
|
"decay" : 0.03,
|
|
|
|
"baseFrequency" : 4000,
|
|
|
|
"octaves" : -2.5,
|
|
|
|
"exponent" : 4,
|
2015-07-04 17:48:44 +00:00
|
|
|
|
2019-01-09 01:21:29 +00:00
|
|
|
}
|
2015-07-04 17:48:44 +00:00
|
|
|
}).connect(lowPass);
|
|
|
|
|
2015-12-05 18:20:53 +00:00
|
|
|
var closedHatPart = new Tone.Part(function(time){
|
|
|
|
closedHiHat.triggerAttack(time);
|
2019-01-09 01:21:29 +00:00
|
|
|
}, [0, { "16n" : 1 }, { "8n" : 1 }, { "8n" : 3 }, { "8n" : 4 }, { "8n" : 5 }, { "8n" : 7 }, { "8n" : 8 }]).start(0);
|
2015-12-05 18:20:53 +00:00
|
|
|
|
2015-07-04 17:48:44 +00:00
|
|
|
//BASS
|
|
|
|
var bassEnvelope = new Tone.AmplitudeEnvelope({
|
2019-01-09 01:21:29 +00:00
|
|
|
"attack" : 0.01,
|
|
|
|
"decay" : 0.2,
|
|
|
|
"sustain" : 0,
|
2015-07-04 17:48:44 +00:00
|
|
|
}).toMaster();
|
|
|
|
|
|
|
|
var bassFilter = new Tone.Filter({
|
2019-01-09 01:21:29 +00:00
|
|
|
"frequency" : 600,
|
|
|
|
"Q" : 8
|
2015-07-04 17:48:44 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
var bass = new Tone.PulseOscillator("A2", 0.4).chain(bassFilter, bassEnvelope);
|
|
|
|
bass.start();
|
|
|
|
|
2015-12-05 18:20:53 +00:00
|
|
|
var bassPart = new Tone.Part(function(time, note){
|
|
|
|
bass.frequency.setValueAtTime(note, time);
|
2019-01-09 01:21:29 +00:00
|
|
|
bassEnvelope.triggerAttack(time);
|
2015-12-05 18:20:53 +00:00
|
|
|
}, [["0:0", "A1"],
|
|
|
|
["0:2", "G1"],
|
|
|
|
["0:2:2", "C2"],
|
|
|
|
["0:3:2", "A1"]]).start(0);
|
|
|
|
|
2015-07-04 17:48:44 +00:00
|
|
|
//BLEEP
|
|
|
|
var bleepEnvelope = new Tone.AmplitudeEnvelope({
|
2019-01-09 01:21:29 +00:00
|
|
|
"attack" : 0.01,
|
|
|
|
"decay" : 0.4,
|
|
|
|
"sustain" : 0,
|
2015-07-04 17:48:44 +00:00
|
|
|
}).toMaster();
|
|
|
|
|
|
|
|
var bleep = new Tone.Oscillator("A4").connect(bleepEnvelope);
|
|
|
|
bleep.start();
|
|
|
|
|
2015-12-05 18:20:53 +00:00
|
|
|
var bleepLoop = new Tone.Loop(function(time){
|
|
|
|
bleepEnvelope.triggerAttack(time);
|
|
|
|
}, "2n").start(0);
|
|
|
|
|
2015-07-04 17:48:44 +00:00
|
|
|
//KICK
|
|
|
|
var kickEnvelope = new Tone.AmplitudeEnvelope({
|
2019-01-09 01:21:29 +00:00
|
|
|
"attack" : 0.01,
|
|
|
|
"decay" : 0.2,
|
|
|
|
"sustain" : 0,
|
2015-07-04 17:48:44 +00:00
|
|
|
}).toMaster();
|
|
|
|
|
2015-12-05 18:20:53 +00:00
|
|
|
var kick = new Tone.Oscillator("A2").connect(kickEnvelope).start();
|
2015-07-04 17:48:44 +00:00
|
|
|
|
2019-01-09 01:21:29 +00:00
|
|
|
var kickSnapEnv = new Tone.FrequencyEnvelope({
|
|
|
|
"attack" : 0.005,
|
|
|
|
"decay" : 0.01,
|
|
|
|
"sustain" : 0,
|
|
|
|
"baseFrequency" : "A2",
|
|
|
|
"octaves" : 2.7
|
2015-07-04 17:48:44 +00:00
|
|
|
}).connect(kick.frequency);
|
|
|
|
|
2015-12-05 18:20:53 +00:00
|
|
|
var kickPart = new Tone.Part(function(time){
|
|
|
|
kickEnvelope.triggerAttack(time);
|
|
|
|
kickSnapEnv.triggerAttack(time);
|
|
|
|
}, ["0", "0:0:3", "0:2:0", "0:3:1"]).start(0);
|
2015-07-04 17:48:44 +00:00
|
|
|
|
2015-12-05 18:20:53 +00:00
|
|
|
//TRANSPORT
|
2015-07-04 17:48:44 +00:00
|
|
|
Tone.Transport.loopStart = 0;
|
|
|
|
Tone.Transport.loopEnd = "1:0";
|
|
|
|
Tone.Transport.loop = true;
|
|
|
|
|
2019-01-09 01:21:29 +00:00
|
|
|
//bind the interface
|
|
|
|
document.querySelector("tone-play-toggle").bind(Tone.Transport);
|
|
|
|
document.querySelector("#hihatFilter").bind(lowPass);
|
|
|
|
document.querySelector("#openHiHat").bind(openHiHat);
|
|
|
|
document.querySelector("#closedHiHat").bind(closedHiHat);
|
|
|
|
document.querySelector("#bassOsc").bind(bass);
|
|
|
|
document.querySelector("#bassFilter").bind(bassFilter);
|
|
|
|
document.querySelector("#bassEnvelope").bind(bassEnvelope);
|
|
|
|
document.querySelector("#bleep").bind(bleep);
|
|
|
|
document.querySelector("#bleepEnvelope").bind(bleepEnvelope);
|
|
|
|
document.querySelector("#kickOsc").bind(kick);
|
|
|
|
document.querySelector("#kickEnvelope").bind(kickEnvelope);
|
|
|
|
document.querySelector("#kickFreqEnvelope").bind(kickSnapEnv);
|
2015-07-04 17:48:44 +00:00
|
|
|
</script>
|
2017-03-26 20:39:19 +00:00
|
|
|
|
2015-07-04 17:48:44 +00:00
|
|
|
</body>
|
2017-03-18 16:35:37 +00:00
|
|
|
</html>
|