2024-01-11 04:09:13 +09:00
|
|
|
|
// @ts-check
|
|
|
|
|
|
2023-03-29 02:16:08 +09:00
|
|
|
|
let midiSelectSlider;
|
2024-01-11 04:09:13 +09:00
|
|
|
|
let midiIn;
|
2023-03-29 02:16:08 +09:00
|
|
|
|
|
|
|
|
|
// for piano visualizer
|
2024-01-11 04:09:13 +09:00
|
|
|
|
const state = State('pianometer/', {
|
|
|
|
|
color: '#2ee5b8',
|
|
|
|
|
rainbowMode: false, // 彩虹模式
|
|
|
|
|
velocityMode: false, // 力度模式
|
|
|
|
|
});
|
|
|
|
|
/** @type {Color} */
|
|
|
|
|
let keyOnColor; // 「按下時」的顏色 [HSB Color Mode]
|
|
|
|
|
/** @type {Color} */
|
|
|
|
|
let pedaledColor; // 「踏板踩住」的顏色 [HSB Color Mode]
|
2023-03-29 02:16:08 +09:00
|
|
|
|
let nowPedaling = false; // is it pedaling?(不要動)
|
|
|
|
|
let isKeyOn = []; // what notes are being pressed (1 or 0)(不要動)
|
|
|
|
|
let isPedaled = []; // what notes are pedaled (1 or 0)(不要動)
|
|
|
|
|
let isBlack = [0, 11, 0, 13, 0, 0, 11, 0, 12, 0, 13, 0]; // 是黑鍵嗎?是的話,相對左方的白鍵位移多少?(default: {0, 11, 0, 13, 0, 0, 11, 0, 12, 0, 13, 0})
|
|
|
|
|
let border = 3; // 左方留空幾個畫素?(default: 3)
|
|
|
|
|
let whiteKeyWidth = 20; // 白鍵多寬?(default: 20)
|
|
|
|
|
let whiteKeySpace = 1; // 白鍵間的縫隙多寬?(default: 1)
|
|
|
|
|
let blackKeyWidth = 17; // 黑鍵多寬?(default: 17)
|
|
|
|
|
let blackKeyHeight = 45; // 黑鍵多高?(default: 45)
|
|
|
|
|
let radius = 5; // 白鍵圓角(default: 5)
|
|
|
|
|
let bRadius = 4; // 黑鍵圓角(default: 4)
|
|
|
|
|
let keyAreaY = 3; // 白鍵從 Y 軸座標多少開始?(default: 3)
|
|
|
|
|
let keyAreaHeight = 70; // 白鍵多高?(default: 70)
|
|
|
|
|
let cc64now = 0; // 現在的踏板狀態
|
|
|
|
|
let cc67now = 0;
|
|
|
|
|
|
|
|
|
|
let sessionStartTime = new Date();
|
|
|
|
|
let sessionTotalSeconds = 0;
|
|
|
|
|
|
2023-03-30 20:21:44 +09:00
|
|
|
|
let flatNames = false;
|
|
|
|
|
|
2023-03-29 02:16:08 +09:00
|
|
|
|
// note counter
|
|
|
|
|
let notesThisFrame = 0;
|
|
|
|
|
let totalNotesPlayed = 0;
|
|
|
|
|
let shortTermTotal = new Array(60).fill(0);
|
|
|
|
|
let legatoHistory = new Array(60).fill(0);
|
2023-03-30 01:48:51 +09:00
|
|
|
|
let notesSMax = 0;
|
2023-03-29 02:16:08 +09:00
|
|
|
|
let totalIntensityScore = 0;
|
|
|
|
|
|
|
|
|
|
// for key pressed counter
|
|
|
|
|
let notePressedCount = 0;
|
|
|
|
|
let notePressedCountHistory = [];
|
|
|
|
|
|
2024-01-11 04:09:13 +09:00
|
|
|
|
WebMidi.enable(function (err) { // check if WebMidi.js is enabled
|
2024-01-11 02:41:58 +09:00
|
|
|
|
if (err) {
|
2024-01-11 04:09:13 +09:00
|
|
|
|
console.log('WebMidi could not be enabled.', err);
|
2024-01-11 02:41:58 +09:00
|
|
|
|
} else {
|
2024-01-11 04:09:13 +09:00
|
|
|
|
console.log('WebMidi enabled!');
|
2024-01-11 02:41:58 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//name our visible MIDI input and output ports
|
2024-01-11 04:09:13 +09:00
|
|
|
|
console.log('---');
|
|
|
|
|
console.log('Inputs Ports: ');
|
|
|
|
|
WebMidi.inputs.forEach(({name}, i) => {
|
|
|
|
|
console.log(i + ': ' + name);
|
|
|
|
|
});
|
2024-01-11 02:41:58 +09:00
|
|
|
|
|
2024-01-11 04:09:13 +09:00
|
|
|
|
console.log('---');
|
|
|
|
|
console.log('Output Ports: ');
|
|
|
|
|
WebMidi.outputs.forEach(({name}, i) => {
|
|
|
|
|
console.log(i + ': ' + name);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
midiSelectSlider = select('#slider');
|
|
|
|
|
midiSelectSlider.attribute('max', WebMidi.inputs.length - 1);
|
2024-01-11 02:41:58 +09:00
|
|
|
|
midiSelectSlider.input(inputChanged);
|
|
|
|
|
midiIn = WebMidi.inputs[midiSelectSlider.value()]
|
|
|
|
|
inputChanged();
|
2023-03-29 02:16:08 +09:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function inputChanged() {
|
2024-01-11 02:41:58 +09:00
|
|
|
|
isKeyOn.fill(0);
|
|
|
|
|
controllerChange(64, 0);
|
|
|
|
|
controllerChange(67, 0);
|
|
|
|
|
|
|
|
|
|
midiIn.removeListener();
|
|
|
|
|
midiIn = WebMidi.inputs[midiSelectSlider.value()];
|
2024-01-11 04:09:13 +09:00
|
|
|
|
midiIn.addListener('noteon', 'all', function (e) {
|
|
|
|
|
console.log('Received \'noteon\' message (' + e.note.number + ', ' + e.velocity + ').');
|
2024-01-11 02:41:58 +09:00
|
|
|
|
noteOn(e.note.number, e.velocity);
|
|
|
|
|
});
|
2024-01-11 04:09:13 +09:00
|
|
|
|
midiIn.addListener('noteoff', 'all', function (e) {
|
|
|
|
|
console.log('Received \'noteoff\' message (' + e.note.number + ', ' + e.velocity + ').');
|
2024-01-11 02:41:58 +09:00
|
|
|
|
noteOff(e.note.number, e.velocity);
|
|
|
|
|
})
|
|
|
|
|
midiIn.addListener('controlchange', 'all', function(e) {
|
2024-01-11 04:09:13 +09:00
|
|
|
|
console.log('Received control change message:', e.controller.number, e.value);
|
2024-01-11 02:41:58 +09:00
|
|
|
|
controllerChange(e.controller.number, e.value)
|
|
|
|
|
});
|
|
|
|
|
console.log(midiIn.name);
|
2024-01-11 04:09:13 +09:00
|
|
|
|
select('#device').html(midiIn.name);
|
2023-03-29 02:16:08 +09:00
|
|
|
|
};
|
|
|
|
|
|
2024-01-11 04:09:13 +09:00
|
|
|
|
/**
|
|
|
|
|
* @param {number} pitch
|
|
|
|
|
* @param {number} velocity
|
|
|
|
|
*/
|
2023-03-29 02:16:08 +09:00
|
|
|
|
function noteOn(pitch, velocity) {
|
2024-01-11 02:41:58 +09:00
|
|
|
|
totalNotesPlayed++;
|
|
|
|
|
notesThisFrame++;
|
|
|
|
|
totalIntensityScore += velocity;
|
|
|
|
|
|
|
|
|
|
// piano visualizer
|
|
|
|
|
isKeyOn[pitch] = velocity;
|
|
|
|
|
if (nowPedaling) {
|
|
|
|
|
isPedaled[pitch] = velocity;
|
|
|
|
|
}
|
2023-03-29 02:16:08 +09:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 04:09:13 +09:00
|
|
|
|
/**
|
|
|
|
|
* @param {number} pitch
|
|
|
|
|
* @param {number} _velocity
|
|
|
|
|
*/
|
2024-01-11 02:41:58 +09:00
|
|
|
|
function noteOff(pitch, _velocity) {
|
|
|
|
|
isKeyOn[pitch] = 0;
|
2023-03-29 02:16:08 +09:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 04:09:13 +09:00
|
|
|
|
/**
|
|
|
|
|
* @param {number} number
|
|
|
|
|
* @param {number} value
|
|
|
|
|
*/
|
2023-03-29 02:16:08 +09:00
|
|
|
|
function controllerChange(number, value) {
|
2024-01-11 02:41:58 +09:00
|
|
|
|
// Receive a controllerChange
|
|
|
|
|
if (number == 64) {
|
|
|
|
|
cc64now = value;
|
|
|
|
|
|
|
|
|
|
if (value >= 64) {
|
|
|
|
|
nowPedaling = true;
|
|
|
|
|
for (let i = 0; i < 128; i++) {
|
|
|
|
|
// copy key on to pedal
|
|
|
|
|
isPedaled[i] = isKeyOn[i];
|
|
|
|
|
}
|
|
|
|
|
} else if (value < 64) {
|
|
|
|
|
nowPedaling = false;
|
|
|
|
|
for (let i = 0; i < 128; i++) {
|
|
|
|
|
// reset isPedaled
|
|
|
|
|
isPedaled[i] = 0;
|
|
|
|
|
}
|
2023-03-29 02:16:08 +09:00
|
|
|
|
}
|
2024-01-11 02:41:58 +09:00
|
|
|
|
}
|
2023-03-29 02:16:08 +09:00
|
|
|
|
|
2024-01-11 02:41:58 +09:00
|
|
|
|
if (number == 67) {
|
|
|
|
|
cc67now = value;
|
|
|
|
|
}
|
2023-04-01 02:35:21 +09:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 04:09:13 +09:00
|
|
|
|
/**
|
|
|
|
|
* @param {HTMLInputElement} cb
|
|
|
|
|
*/
|
2023-04-01 02:35:21 +09:00
|
|
|
|
function toggleRainbowMode(cb) {
|
2024-01-11 04:09:13 +09:00
|
|
|
|
state.rainbowMode = cb.checked;
|
|
|
|
|
if (state.rainbowMode) {
|
2024-01-11 02:41:58 +09:00
|
|
|
|
select('#colorpicker').attribute('disabled', true)
|
2024-01-11 04:09:13 +09:00
|
|
|
|
} else {
|
2024-01-11 02:41:58 +09:00
|
|
|
|
select('#colorpicker').removeAttribute('disabled')
|
2024-01-11 04:09:13 +09:00
|
|
|
|
}
|
2023-04-01 02:35:21 +09:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 04:09:13 +09:00
|
|
|
|
/**
|
|
|
|
|
* @param {HTMLInputElement} cb
|
|
|
|
|
*/
|
2023-04-05 15:40:27 +09:00
|
|
|
|
function toggleVelocityMode(cb) {
|
2024-01-11 04:09:13 +09:00
|
|
|
|
state.velocityMode = cb.checked;
|
2023-04-05 15:40:27 +09:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-01 02:35:21 +09:00
|
|
|
|
function changeColor() {
|
2024-01-11 04:09:13 +09:00
|
|
|
|
keyOnColor = color(state.color = select('#colorpicker').value());
|
|
|
|
|
const darkenedColor = keyOnColor.levels.map(x => Math.floor(x * .7));
|
2024-01-11 02:41:58 +09:00
|
|
|
|
pedaledColor = color(`rgb(${darkenedColor[0]}, ${darkenedColor[1]}, ${darkenedColor[2]})`);
|
|
|
|
|
}
|