Web Audio API Test
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css">
<br>
<div class="options row">
<!-- <div class="col s12">
<span>
<input type="checkbox" id="sus">
<label for="sus">Sustain</label>
</span>
</div> -->
<span>
<label class="btn grey" for="record">
Record
</label>
<input type="checkbox" id="record">
</span>
<span>
<label class="btn grey" id="playback" for="">
Play
</label>
</span>
</div>
<div id="piano">
<div class="blacks">
<div class="key" data-octave="3" data-note="C#" data-freq="138.6"></div>
<div class="key" data-octave="3" data-note="D#" data-freq="155.6"></div>
<div class="key" data-octave="3" data-note="F#" data-freq="185"></div>
<div class="key" data-octave="3" data-note="G#" data-freq="207.7"></div>
<div class="key" data-octave="3" data-note="A#" data-freq="233.1"></div>
<div class="key" data-octave="4" data-note="C#" data-freq="277.2"></div>
<div class="key" data-octave="4" data-note="D#" data-freq="311.1"></div>
<div class="key" data-octave="4" data-note="F#" data-freq="370"></div>
<div class="key" data-octave="4" data-note="G#" data-freq="415.3"></div>
<div class="key" data-octave="4" data-note="A#" data-freq="466.2"></div>
</div>
<div class="whites">
<div class="key" data-octave="3" data-note="C" data-freq="130.8"></div>
<div class="key" data-octave="3" data-note="D" data-freq="146.8"></div>
<div class="key" data-octave="3" data-note="E" data-freq="164.8"></div>
<div class="key" data-octave="3" data-note="F" data-freq="174.6"></div>
<div class="key" data-octave="3" data-note="G" data-freq="196"></div>
<div class="key" data-octave="3" data-note="A" data-freq="220"></div>
<div class="key" data-octave="3" data-note="B"...
SCSS
$octaves: 2;
$white_keys: $octaves * 7;
$black_keys: $octaves * 5;
$white_keys_width: calc(100% / #{$white_keys});
$black_keys_width: calc(100% / #{$white_keys} * .75);
html,
body {
background-color: #fafafa;
}
#piano {
position: relative;
width: 800px;
height: 200px;
background-color: #fff;
border: 1px solid silver;
}
.whites {
position: relative;
width: 100%;
height: 100%;
}
.blacks {
width: 100%;
height: 75%;
position: absolute;
z-index: 10;
margin-left: calc(100% / 14 * .625);
.key {
width: calc(100% / 14 * .75); // 14 * 1.5 = 24.5
background-color: #333;
border: none;
margin-right: calc((100% / 14) - (100% / 14 * .75));
&:nth-child(5n+2),
&:nth-child(5n+5) {
margin-right: calc((100% / 14) + (100% / 14) - (100% / 14 * .75));
}
&[aria-pressed] {
background-color: #000;
}
}
}
.key {
height: 100%;
width: $white_keys_width;
float: left;
box-sizing: border-box;
border-left: 1px solid silver;
&:first-child {
border-left: none;
}
&[aria-pressed] {
background-color: #eee;
}
}
JavaScript
var keys = document.querySelectorAll('.key'),
sustain = document.querySelector('#sus'),
record = document.querySelector('#record'),
recordBtn = document.querySelector('[for="record"]'),
play = document.querySelector('#playback');
var context = new AudioContext(),
notes = {},
isRecording = false,
isPlayback = false,
_record = { start : null, sequence : [] };
record.addEventListener('change',function () {
if ( isPlayback ) {
this.checked = false;
return;
}
isRecording = this.checked;
if ( isRecording ) {
// new record evertime record is activated
_record.start = Date.now();
_record.sequence = [];
recordBtn.className = recordBtn.className.replace('grey','red');
} else {
recordBtn.className = recordBtn.className.replace('red','grey');
console.log('_record',_record);
}
});
play.addEventListener('click',function () {
if ( _record.sequence.length ) {
isPlayback = true;
console.log('playing');
_record.sequence.forEach(function (_note,_index) {
setTimeout(function () {
notes[_note.noteId].play();
if ( _index == _record.sequence.length - 1 ) {
isPlayback = false;
console.log('stopped');
}
},_note.delay);
});
}
});
[].forEach.call(keys,function(el){
var _note = {};
_note.el = el;
_note.f = el.getAttribute('data-freq');
_note.octave = el.getAttribute('data-octave');
_note.note = el.getAttribute('data-note');
_note.id = _note.octave+_note.note;
_note.uiTimeout = null,
_note.oscillator = null,
_note.gainNode = null;
_note.play = function () {
if ( _note.oscillator ) return;
_note.oscillator = context.createOscillator();
_note.gainNode = context.createGain();
// connect
_note.oscillator.connect(_note.gainNode);
_note.gainNode.connect(context.destination);
_note.oscillator.frequency.value = _note.f;
_note.gainNode.gain.value = 0.15;
_note.oscillator.start(context.currentTime);
// check use of sustain
// if ( !sustain.checked ) {
...