163 lines
4.5 KiB
JavaScript
163 lines
4.5 KiB
JavaScript
|
/*globals React, App */
|
||
|
|
||
|
(function () {
|
||
|
'use strict';
|
||
|
|
||
|
/**
|
||
|
* Display keyboard
|
||
|
*/
|
||
|
App.components.create('Key', {
|
||
|
displayName: 'Board',
|
||
|
mixins: [React.addons.PureRenderMixin],
|
||
|
clicked: false,
|
||
|
|
||
|
statics: {
|
||
|
offset: 27 + App.MIDI.keyOffset,
|
||
|
keysToNotes: [
|
||
|
65, 50, 90, 51, 69, 82, 53, 84, 54, 89, 55, 85, 73,
|
||
|
57, 79, 48, 80, 87, 83, 88, 68, 67, 70, 86, 66, 72,
|
||
|
78, 74, 188, 190, 76, 191, 77, 223, 192, 16
|
||
|
]
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Set given note on given channel
|
||
|
*
|
||
|
* @param {note: number} Note number
|
||
|
* @param {channel: number} Channel ID
|
||
|
* @param {velocity: number} Note velocity
|
||
|
*/
|
||
|
on: function (note, channel, velocity) {
|
||
|
var key = this.refs['key-' + note];
|
||
|
|
||
|
if (key) {
|
||
|
key.on(channel, velocity);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Check whether given note plays on given channel
|
||
|
*
|
||
|
* @param {note: number} Note number
|
||
|
* @param {channel: number} Channel ID
|
||
|
*/
|
||
|
isOn: function (note, channel) {
|
||
|
var key = this.refs['key-' + note];
|
||
|
return (key.state.channels.indexOf(channel) > -1);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Set given note off given channel
|
||
|
*
|
||
|
* @param {note: number} Note number
|
||
|
* @param {channel: number} Channel ID
|
||
|
*/
|
||
|
off: function (note, channel) {
|
||
|
var key = this.refs['key-' + note];
|
||
|
|
||
|
if (key) {
|
||
|
key.off(channel);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Set all notes off
|
||
|
*/
|
||
|
allOff: function () {
|
||
|
var i, keys = this.refs;
|
||
|
|
||
|
for (i in keys) {
|
||
|
if (keys.hasOwnProperty(i) && i.substr(0, 4) === 'key-') {
|
||
|
keys[i].off();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Events
|
||
|
*/
|
||
|
componentDidMount: function () {
|
||
|
window.addEventListener('keydown', this.keyDown);
|
||
|
window.addEventListener('keyup', this.keyUp);
|
||
|
window.addEventListener('mouseup', this.release);
|
||
|
},
|
||
|
|
||
|
componentWillUnmount: function () {
|
||
|
window.removeEventListener('keydown', this.keyDown);
|
||
|
window.removeEventListener('keyup', this.keyUp);
|
||
|
window.removeEventListener('mouseup', this.release);
|
||
|
},
|
||
|
|
||
|
/* mouse click */
|
||
|
click: function (e) {
|
||
|
if (e.button !== 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.clicked = true;
|
||
|
},
|
||
|
|
||
|
/* mouse release */
|
||
|
release: function () {
|
||
|
this.clicked = false;
|
||
|
},
|
||
|
|
||
|
/* play keys with keyboard (the real one) */
|
||
|
keyDown: function (e) {
|
||
|
var code = e.keyCode, index,
|
||
|
Keyboard = App.components.Key.Board;
|
||
|
|
||
|
// play note
|
||
|
if ((index = Keyboard.keysToNotes.indexOf(code)) > -1) {
|
||
|
index += Keyboard.offset;
|
||
|
|
||
|
if (!this.isOn(index, -1)) {
|
||
|
this.on(index, -1, 127);
|
||
|
}
|
||
|
|
||
|
e.preventDefault();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/* release keys */
|
||
|
keyUp: function (e) {
|
||
|
var index, Keyboard = App.components.Key.Board;
|
||
|
|
||
|
if ((index = Keyboard.keysToNotes.indexOf(e.keyCode)) > -1) {
|
||
|
this.off(index + Keyboard.offset, -1);
|
||
|
|
||
|
e.preventDefault();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Whether keys should be played on mouse over
|
||
|
*/
|
||
|
canPlay: function () {
|
||
|
return this.clicked;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Render keyboard
|
||
|
*/
|
||
|
render: function () {
|
||
|
var i, offset = App.MIDI.keyOffset,
|
||
|
length = 88 + offset, keys = [];
|
||
|
|
||
|
for (i = offset; i < length; i += 1) {
|
||
|
keys.push(App.components.Key.Key({
|
||
|
key: i,
|
||
|
ref: 'key-' + i,
|
||
|
|
||
|
note: i,
|
||
|
canPlay: this.canPlay
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
return React.DOM.p({
|
||
|
className: 'keyboard',
|
||
|
onMouseDown: this.click
|
||
|
}, keys);
|
||
|
}
|
||
|
});
|
||
|
}());
|