Tone.js/examples/pianoPhase.html
2020-07-17 17:58:09 -07:00

207 lines
5.6 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Phasing</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="icon" type="image/png" sizes="174x174" href="./favicon.png">
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.4.3/webcomponents-bundle.js"></script>
<link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet"/>
<script src="../build/Tone.js"></script>
<script src="./js/tone-ui.js"></script>
<script src="./js/components.js"></script>
</head>
<body>
<style type="text/css">
#loop {
margin-bottom: 10px;
display: flex;
}
#loop .bar {
flex-grow: 1;
width: 40%;
height: 20px;
margin: 10px;
background-color: #ececec;
border-radius: 4px;
overflow: hidden;
position: relative;
}
#loop .bar .fill {
height: 100%;
width: 50%;
background-color:#7F33ED;
position: absolute;
left: 50%;
transform: translate(-50%, 0);
}
</style>
<tone-example label="Piano Phase">
<div slot="explanation">
By slightly slowing down the playbackRate of the Tone.Sequence in the right channel,
the two identical melodies phase against each other in interesting ways.
<br><br>
Composition by Steve Reich. Inspiration from Alexander Chen.
</div>
<div id="content">
<div id="loop">
<div class="bar">
<div class="fill" id="left"></div>
</div>
<div class="bar">
<div class="fill" id="right"></div>
</div>
</div>
<tone-play-toggle></tone-play-toggle>
</div>
</tone-example>
<script type="text/javascript">
// set the bpm and time signature first
Tone.Transport.timeSignature = [6, 4];
Tone.Transport.bpm.value = 180;
// L/R channel merging
const merge = new Tone.Merge();
// a little reverb
const reverb = new Tone.Reverb({
wet: 0.3
});
merge.chain(reverb, Tone.Destination);
// the synth settings
const synthSettings = {
oscillator: {
detune: 0,
type: "custom",
partials: [2, 1, 2, 2],
phase: 0,
volume: 0
},
envelope: {
attack: 0.005,
decay: 0.3,
sustain: 0.2,
release: 1,
},
portamento: 0.01,
volume: -20
};
// left and right synthesizers
const synthL = new Tone.Synth(synthSettings).connect(merge, 0, 0);
const synthR = new Tone.Synth(synthSettings).connect(merge, 0, 1);
// the two Tone.Sequences
const partL = new Tone.Sequence(((time, note) => {
synthL.triggerAttackRelease(note, "8n", time);
}), ["E4", "F#4", "B4", "C#5", "D5", "F#4", "E4", "C#5", "B4", "F#4", "D5", "C#5"], "8n").start();
const partR = new Tone.Sequence(((time, note) => {
synthR.triggerAttackRelease(note, "8n", time);
}), ["E4", "F#4", "B4", "C#5", "D5", "F#4", "E4", "C#5", "B4", "F#4", "D5", "C#5"], "8n").start("2m");
// set the playback rate of the right part to be slightly slower
partR.playbackRate = 0.985;
//bind the interface
document.querySelector('tone-play-toggle').addEventListener('start', () => Tone.Transport.start())
document.querySelector('tone-play-toggle').addEventListener('stop', () => Tone.Transport.stop())
// update the width of the fill bars to reflect the left and right progress
function progressToWidth(progress){
progress = Math.cos(progress * Math.PI * 2 + Math.PI)
progress = (progress + 1) / 2
return `${progress * 100}%`
}
function loop(){
requestAnimationFrame(loop)
// make it go out and back for each loop
document.querySelector("#left").style.width = progressToWidth(partL.progress)
document.querySelector("#right").style.width = progressToWidth(partR.progress)
}
loop()
// document.querySelector("#left").bind(partL);
// document.querySelector("#right").bind(partR);
/*// GUI //
Interface.Button({
key : 32,
type : "toggle",
text : "Start",
activeText : "Stop",
start : function(){
Tone.Transport.start("+0.1");
},
end : function(){
Tone.Transport.stop();
}
});
//draw two circles
var leftCanvas = $("#Left");
var rightCanvas = $("#Right");
var leftContext = leftCanvas.get(0).getContext("2d");
var rightContext = rightCanvas.get(0).getContext("2d");
var canvasWidth = leftCanvas.width() * 2;
var canvasHeight = leftCanvas.height() * 2;
var radius = Math.min(canvasWidth, canvasHeight);
function sizeCanvas(){
canvasWidth = leftCanvas.width() * 2;
canvasHeight = leftCanvas.height() * 2;
radius = Math.min(canvasWidth, canvasHeight) * 0.8;
leftContext.canvas.width = canvasWidth;
leftContext.canvas.height = canvasHeight;
rightContext.canvas.width = canvasWidth;
rightContext.canvas.height = canvasHeight;
}
$(window).on("resize", sizeCanvas);
sizeCanvas();
var twoPi = Math.PI * 2;
function loop(){
requestAnimationFrame(loop);
leftContext.lineWidth = radius * 0.1;
rightContext.lineWidth = radius * 0.1;
//draw the left progress
leftContext.clearRect(0, 0, canvasWidth, canvasHeight);
leftContext.strokeStyle = "#7F33ED";
leftContext.save();
leftContext.translate(canvasWidth / 2, canvasHeight / 2);
leftContext.rotate(-Math.PI / 2);
leftContext.beginPath();
leftContext.arc(0, 0, radius/2, 0, twoPi * partL.progress, false);
leftContext.stroke();
leftContext.restore();
//draw the left progress
rightContext.clearRect(0, 0, canvasWidth, canvasHeight);
rightContext.strokeStyle = "#1EDF3E";
rightContext.save();
rightContext.translate(canvasWidth / 2, canvasHeight / 2);
rightContext.rotate(-Math.PI / 2);
rightContext.beginPath();
rightContext.arc(0, 0, radius/2, 0, twoPi * partR.progress, false);
rightContext.stroke();
rightContext.restore();
}
loop();*/
</script>
</body>
</html>