// @ts-check let midiSelectSlider; let midiIn; // for piano visualizer const state = State('pianometer/', { color: '#2ee5b8', rainbowMode: false, // 彩虹模式 velocityMode: false, // 力度模式 }); /** @type {Color} */ let keyOnColor; // 「按下時」的顏色 [HSB Color Mode] /** @type {Color} */ let pedaledColor; // 「踏板踩住」的顏色 [HSB Color Mode] 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; let flatNames = false; // note counter let notesThisFrame = 0; let totalNotesPlayed = 0; let shortTermTotal = new Array(60).fill(0); let legatoHistory = new Array(60).fill(0); let notesSMax = 0; let totalIntensityScore = 0; // for key pressed counter let notePressedCount = 0; let notePressedCountHistory = []; WebMidi.enable(function (err) { // check if WebMidi.js is enabled if (err) { console.log('WebMidi could not be enabled.', err); } else { console.log('WebMidi enabled!'); } //name our visible MIDI input and output ports console.log('---'); console.log('Inputs Ports: '); WebMidi.inputs.forEach(({name}, i) => { console.log(i + ': ' + name); }); 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); midiSelectSlider.input(inputChanged); midiIn = WebMidi.inputs[midiSelectSlider.value()] inputChanged(); }); function inputChanged() { isKeyOn.fill(0); controllerChange(64, 0); controllerChange(67, 0); midiIn.removeListener(); midiIn = WebMidi.inputs[midiSelectSlider.value()]; midiIn.addListener('noteon', 'all', function (e) { console.log('Received \'noteon\' message (' + e.note.number + ', ' + e.velocity + ').'); noteOn(e.note.number, e.velocity); }); midiIn.addListener('noteoff', 'all', function (e) { console.log('Received \'noteoff\' message (' + e.note.number + ', ' + e.velocity + ').'); noteOff(e.note.number, e.velocity); }) midiIn.addListener('controlchange', 'all', function(e) { console.log('Received control change message:', e.controller.number, e.value); controllerChange(e.controller.number, e.value) }); console.log(midiIn.name); select('#device').html(midiIn.name); }; /** * @param {number} pitch * @param {number} velocity */ function noteOn(pitch, velocity) { totalNotesPlayed++; notesThisFrame++; totalIntensityScore += velocity; // piano visualizer isKeyOn[pitch] = velocity; if (nowPedaling) { isPedaled[pitch] = velocity; } } /** * @param {number} pitch * @param {number} _velocity */ function noteOff(pitch, _velocity) { isKeyOn[pitch] = 0; } /** * @param {number} number * @param {number} value */ function controllerChange(number, value) { // 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; } } } if (number == 67) { cc67now = value; } } /** * @param {HTMLInputElement} cb */ function toggleRainbowMode(cb) { state.rainbowMode = cb.checked; if (state.rainbowMode) { select('#colorpicker').attribute('disabled', true) } else { select('#colorpicker').removeAttribute('disabled') } } /** * @param {HTMLInputElement} cb */ function toggleVelocityMode(cb) { state.velocityMode = cb.checked; } function changeColor() { keyOnColor = color(state.color = select('#colorpicker').value()); const darkenedColor = keyOnColor.levels.map(x => Math.floor(x * .7)); pedaledColor = color(`rgb(${darkenedColor[0]}, ${darkenedColor[1]}, ${darkenedColor[2]})`); }