<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/blog_02.css">
<script src="./js/vue.min.js"></script>
<script src="./js/vue.piano.js"></script>
</head>
<body>
<h1>ピアノの鍵盤</h1>
<div class="piano" id="pf" v-on:keydown="keyborddown" v-on:keydown.up="keyborddown_oct8hi" v-on:keydown.down="keyborddown_oct8low" v-on:keyup="keybordup">
<div class="absoslm">
<div class="basetone"><input type="text" v-model="basehz" @change="bshzChange"/></div>
<div class="keyctl"><label><input type="checkbox" v-model="keyplay" />キーボードを使用する</label></div>
<div class="oct">オクターブ{{nowrange}}</div>
<div class="keyname">{{tonename}}</div>
</div>
<div v-for="range in ranges" v-bind:class="range.clsnm">
<button v-for="key in range.keys"v-on:mousedown="keydown" v-on:mouseup="keyup" v-bind:ref="range.range + '_' + key.tone" v-bind:hz="key.hz" v-bind:rangerate="range.rangerate" v-bind:range="range.range" v-bind:tone="key.tone" v-bind:class="key.clsnm"></button>
</div>
</div>
<div>
A:ラ
W:ラ#(シ♭)
S:シ
D:ド
R:ド#(レ♭)
F:レ
T:レ#(ミ♭)
G:ミ
H:ファ
U:ファ#(ソ♭)
J:ソ
I:ソ#(ラ♭)<br />
↑キー:1オクターブ上げる ↓キー:1オクターブ下げる
</div>
</body>
</html>
@charset "utf-8";
/*
各種色
黒鍵:#1a1c1b
黒鍵影:#121413
白鍵:#ffffff
白鍵影:#b2b2b2
背景黒:#060707
*/
:root{
--white_key_width:30px;
--black_key_width:20px;
--black_key_pad:5px;
}
.piano{
background:#060707;
height:250px;
display:flex;
padding:50px;
box-sizing:border-box;
}
.piano .scale{
display:flex;
height:100%;
position:relative;
}
#pf{
position:relative;
}
#pf .absoslm{
position:absolute;
left:10px; top:10px;
color:#fff;
display:flex;
}
#pf .absoslm > div{
margin:0 15px;
}
/* 重なりが順番になるように */
.scale.lowlow{ z-index:80; }
.scale.low{ z-index:70; }
.scale.midi1{ z-index:60; }
.scale.midi2{ z-index:50; }
.scale.hi{ z-index:40; }
.scale.hihi{ z-index:30; }
.scale.hihihi{ z-index:20; }
.scale.hihihihi{ z-index:10; }
.piano .key{
width:var(--white_key_width);
border:none;
border-radius:3px;
background:#b2b2b2;
height:100%;
border:#b2b2b2 solid 1px;
padding:0;
transition:all 0.2s ease-out;
}
.piano .key:before{
content:"";
height:calc(100% - 10px);
width:100%;
position:relative;
top:-5px;
display:block;
background:#fff;
}
.piano .key.bl:before{
background:#1a1c1b;
}
.piano .key.bl{
width:var(--black_key_width);
background:#121413;
height:50%;
position:absolute;
z-index:10
}
.piano .key.bl.aSh{ left:calc(var(--white_key_width) / 2 + var(--black_key_pad));}
.piano .key.bl.cSh{ left:calc(var(--white_key_width) *2 + var(--white_key_width) / 2 + var(--black_key_pad)); }
.piano .key.bl.dSh{ left:calc(var(--white_key_width) *3 + var(--white_key_width) / 2 + var(--black_key_pad)); }
.piano .key.bl.fSh{ left:calc(var(--white_key_width) *5 + var(--white_key_width) / 2 + var(--black_key_pad)); }
.piano .key.bl.gSh{ left:calc(var(--white_key_width) *6 + var(--white_key_width) / 2 + var(--black_key_pad)); }
.piano .key:active:before{
height:calc(100% - 2px);
top:1px;
}
.piano .key.wh:active:before{
background:#ffafba;
}
.piano .key.bl:active:before{
background:#7f4b63;
}
/* キーボードで押したときの色 */
.piano .key.wh[note_play]:before{
background:#4169E1;
}
.piano .key.bl[note_play]:before{
background:#1F346D;
}
window.onload = function(){
//vueを使ったピアノ
//web Audio API
var actx = new AudioContext();
var osciillatorNode = null;
var bairitu = Math.pow(2,1/12); //半音の差をあらかじめ計算しておく
var _baseHz = 440; //A(ラ)の音が基準
//音程の名前を配列であらかじめ定義する
var scale_names = {
'0':'a', '1':'aSh', '2':'b', '3':'c', '4':'cSh', '5':'d', '6':'dSh',
'7':'e', '8':'f', '9':'fSh', '10':'g', '11': 'gSh'
}
var scale_keybord = {
'0':'KeyA','1':'KeyW','2':'KeyS','3':'KeyD','4':'KeyR','5':'KeyF','6':'KeyT',
'7':'KeyG','8':'KeyH','9':'KeyU','10':'KeyJ','11':'KeyI',
};
var scale = [];
var keycode4note = [];
//var scale_num = Object.keys(scale_names).length; //音程の種類数
scale = makeScale(scale_names, _baseHz, bairitu);
keycode4note = makeKeycode4note(scale_names, scale_keybord);
/*
var scale = [
{'tone':'a', 'clsnm':'key a wh', 'hz':baseHz},
{'tone':'aSh', 'clsnm':'key aSh bl', 'hz':baseHz * bairitu},
{'tone':'b', 'clsnm':'key b wh', 'hz':baseHz * Math.pow(bairitu, 2)},
{'tone':'c', 'clsnm':'key c wh', 'hz':baseHz * Math.pow(bairitu, 3)},
{'tone':'cSh', 'clsnm':'key cSh bl', 'hz':baseHz * Math.pow(bairitu, 4)},
{'tone':'d', 'clsnm':'key d wh', 'hz':baseHz * Math.pow(bairitu, 5)},
{'tone':'dSh', 'clsnm':'key dSh bl', 'hz':baseHz * Math.pow(bairitu, 6)},
{'tone':'e', 'clsnm':'key e wh', 'hz':baseHz * Math.pow(bairitu, 7)},
{'tone':'f', 'clsnm':'key f wh', 'hz':baseHz * Math.pow(bairitu, 8)},
{'tone':'fSh', 'clsnm':'key fSh bl', 'hz':baseHz * Math.pow(bairitu, 9)},
{'tone':'g', 'clsnm':'key g wh', 'hz':baseHz * Math.pow(bairitu, 10)},
{'tone':'gSh', 'clsnm':'key gSh bl', 'hz':baseHz * Math.pow(bairitu, 11)},
];
*/
var range_snum = {
30:'low',
40:'midi1',
50:'midi2',
60:'hi'
};
//音程を定義する 本当はもう少したくさんあるけどとりあえずこんだけ
var range = [
{'range':'low', 'clsnm':'scale low', 'keys':scale, 'rangerate':0.5},
{'range':'midi1', 'clsnm':'scale midi1', 'keys':scale, 'rangerate':1},
{'range':'midi2', 'clsnm':'scale midi2', 'keys':scale, 'rangerate':2},
{'range':'hi', 'clsnm':'scale hi', 'keys':scale, 'rangerate':4},
];
// {'range':'hihi', 'clsnm':'scale hihi', 'keys':scale, 'rangerate':8}
var piano = new Vue({
el:'#pf',
data:{
'tonename':'',
'ranges':range,
'basehz':_baseHz,
'range_snum':range_snum,
'nowrange':40,
'baserange':40,
'keybord':keycode4note,
'keyplay':false,
'pressing':false,
'pressing_ref':null
},
mounted:function(){
//var l = Object.keys(this.$refs);
//console.log(l);
//console.log(this);
//console.log(this.$refs.midi1_a[0].getAttribute('hz'));
//this.$refs.midi1_a[0].forcus();
},
methods:{
'bshzChange':function(e){ //カポ
//console.log('カポ');
var ojbk = Object.keys(this.ranges);
var l = ojbk.length;
for(var k = 0; k < l; k++){
var range_key = ojbk[k];
this.ranges[range_key]['keys'] = makeScale(scale_names, this.basehz, bairitu);
}
},
'keydown':function(e){
// 音の発音をこちらに集約
this.playNote(e.target);
},
'keyborddown':function(e){
//console.log(this.keyplay)
if(this.keyplay){
//ArrowRight
var Kcode = keybord4notename(this.keybord, e.code);
if(Kcode != null){
//音を鳴らす
if(this.range_snum[this.nowrange]){
var ref_name = this.range_snum[this.nowrange] + '_' + Kcode;
//attrを追加する
this.pressing_ref = ref_name;
this.$refs[ref_name][0].setAttribute('note_play', 'play');
this.playNote(this.$refs[ref_name][0]);
}
}
}
},
'keyborddown_oct8hi':function(e){ //オクターブ上げる
if(this.keyplay){
var range_max = Math.max.apply(null, Object.keys(this.range_snum));
var next_range = this.nowrange + 10;
if(range_max < next_range){
next_range = range_max;
}
this.nowrange = next_range;
}
},
'keyborddown_oct8low':function(e){ //オクターブ下げる
if(this.keyplay){
var range_min = Math.min.apply(null, Object.keys(this.range_snum));
var next_range = this.nowrange - 10;
if(range_min > next_range){
next_range = range_min;
}
this.nowrange = next_range;
}
},
'keybordup':function(e){
this.stopNote();
//console.log(this.keybord[e.code]);
//console.log(e);
},
'keyup':function(e){
this.stopNote();
//this.tonename = ''; //音程名をリセットする
},
'playNote':function(note){
if(!this.pressing){
this.pressing = true;
//console.log('p');
var nm = '';
var hz = note.getAttribute('hz') * note.getAttribute('rangerate');
nm += note.getAttribute('range');
nm += ' ';
nm += note.getAttribute('tone');
nm += ' : ';
nm += hz
this.tonename = nm;
//発音処理
//actx = new AudioContext();
osciillatorNode = actx.createOscillator();
osciillatorNode.frequency.value = hz; //発音周波数を指定する
osciillatorNode.connect(actx.destination);
osciillatorNode.start(); //実際に音を鳴らす
}
},
'stopNote':function(){
if(this.pressing){
osciillatorNode.stop(); //音をストップする
if(this.pressing_ref != null){
this.$refs[this.pressing_ref][0].removeAttribute('note_play');
}
this.pressing_ref = null;
this.pressing = false;
//console.log('s');
}
}
}
});
document.getElementById("pf").focus();
//piano.focus();
}
/*
keybord4notename
*/
function keybord4notename(keybord, keycode){
var name = null;
if(keybord[keycode]){
name = keybord[keycode];
}
return name;
}
/*
音を出す
*/
/*
キーボードに対応する音名を設定していく
*/
function makeKeycode4note(scl_nms, scl_kb){
var k4n = {};
var l = Object.keys(scl_nms).length;
for(var i = 0; i < l; i++){
k4n[scl_kb[i]] = scl_nms[i];
}
return k4n;
}
/*
鍵盤の音階データを作成する
引数の説明
scl_nms:音階の名前
bhz :基準の周波数(通常440)
brt :半音の倍率(12√2)
*/
function makeScale(scl_nms, bhz, brt){
var scl = [];
var scl_num = Object.keys(scl_nms).length; //音程の種類数
//キーボードを描画するためのオブジェクトを動的に生成する
for(var i = 0; i < scl_num; i++){
//割り振られた音の周波数を計算する
var xhz = bhz;
if(i > 1){
xhz = bhz * Math.pow(brt, i);
} else if(i == 1){
xhz = bhz * brt;
}
//表示する鍵盤ボタンのクラス名を生成する
var keycls = 'key';
keycls += ' ';
keycls += scl_nms[i];
keycls += ' ';
if(scl_nms[i].match(/Sh$/)){ //末尾が「Sh」の場合は黒鍵にする
keycls += 'bl';
} else {
keycls += 'wh';
}
scl.push({'tone':scl_nms[i], 'clsnm':keycls, 'hz':xhz});
}
return scl;
}
This Pen doesn't use any external CSS resources.