Add Velocity Mode
When toggled, the color of each note will be affected by its velocity. Suitable when practicing this kind of stuff: https://wiwi.video/w/uuwkerA14WnDnysFCh6q4z?start=11m1s
This commit is contained in:
parent
3377d1cf35
commit
810bae60b8
4 changed files with 68 additions and 34 deletions
16
globals.js
16
globals.js
|
@ -17,6 +17,7 @@ let bRadius = 4; // 黑鍵圓角(default: 4)
|
||||||
let keyAreaY = 3; // 白鍵從 Y 軸座標多少開始?(default: 3)
|
let keyAreaY = 3; // 白鍵從 Y 軸座標多少開始?(default: 3)
|
||||||
let keyAreaHeight = 70; // 白鍵多高?(default: 70)
|
let keyAreaHeight = 70; // 白鍵多高?(default: 70)
|
||||||
let rainbowMode = false; // 彩虹模式 (default: false)
|
let rainbowMode = false; // 彩虹模式 (default: false)
|
||||||
|
let velocityMode = false; // 力度模式 (default: false)
|
||||||
let cc64now = 0; // 現在的踏板狀態
|
let cc64now = 0; // 現在的踏板狀態
|
||||||
let cc67now = 0;
|
let cc67now = 0;
|
||||||
|
|
||||||
|
@ -92,9 +93,9 @@ function noteOn(pitch, velocity) {
|
||||||
totalIntensityScore += velocity;
|
totalIntensityScore += velocity;
|
||||||
|
|
||||||
// piano visualizer
|
// piano visualizer
|
||||||
isKeyOn[pitch] = 1;
|
isKeyOn[pitch] = velocity;
|
||||||
if (nowPedaling) {
|
if (nowPedaling) {
|
||||||
isPedaled[pitch] = 1;
|
isPedaled[pitch] = velocity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,9 +137,12 @@ function toggleRainbowMode(cb) {
|
||||||
select('#colorpicker').removeAttribute('disabled')
|
select('#colorpicker').removeAttribute('disabled')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleVelocityMode(cb) {
|
||||||
|
velocityMode = cb.checked;
|
||||||
|
}
|
||||||
|
|
||||||
function changeColor() {
|
function changeColor() {
|
||||||
keyOnColor = pedaledColor = color(select('#colorpicker').value());
|
keyOnColor = color(select('#colorpicker').value());
|
||||||
darkenedColor = keyOnColor.levels.map(x => floor(x * .7));
|
let darkenedColor = keyOnColor.levels.map(x => floor(x * .7));
|
||||||
pedaledColor = color(`rgb(${darkenedColor[0]}, ${darkenedColor[1]}, ${darkenedColor[2]})`)
|
pedaledColor = color(`rgb(${darkenedColor[0]}, ${darkenedColor[1]}, ${darkenedColor[2]})`);
|
||||||
console.log(pedaledColor.levels);
|
|
||||||
}
|
}
|
|
@ -31,7 +31,14 @@
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<span style="margin-right: 5px;">彩虹模式</span>
|
<span style="margin-right: 5px;">彩虹模式</span>
|
||||||
<input type="checkbox" id="rainbow-mode-checkbox" onclick="toggleRainbowMode(this)">
|
<input type="checkbox" id="rainbow-mode-checkbox" onclick="toggleRainbowMode(this)">
|
||||||
<label for="rainbow-mode-checkbox">
|
<label for="rainbow-mode-checkbox" class="custom-checkbox">
|
||||||
|
<span class="switch-txt" turnOn="On" turnOff="Off"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<span style="margin-right: 5px;">力度模式</span>
|
||||||
|
<input type="checkbox" id="velocity-mode-checkbox" onclick="toggleVelocityMode(this)">
|
||||||
|
<label for="velocity-mode-checkbox" class="custom-checkbox">
|
||||||
<span class="switch-txt" turnOn="On" turnOff="Off"></span>
|
<span class="switch-txt" turnOn="On" turnOff="Off"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -49,16 +49,28 @@ function drawWhiteKeys() {
|
||||||
for (let i = 21; i < 109; i++) {
|
for (let i = 21; i < 109; i++) {
|
||||||
if (isBlack[i % 12] == 0) {
|
if (isBlack[i % 12] == 0) {
|
||||||
// it's a white key
|
// it's a white key
|
||||||
if (isKeyOn[i] == 1 && !rainbowMode) {
|
if (velocityMode) {
|
||||||
fill(keyOnColor); // keypressed
|
let m = max(isKeyOn[i], isPedaled[i]) * .9 + .1;
|
||||||
} else if (isKeyOn[i] == 1 && rainbowMode) {
|
if ((isKeyOn[i] || isPedaled[i]) && !rainbowMode) {
|
||||||
fill(map(i, 21, 108, 0, 1080) % 360, 100, 100, 100); // rainbowMode
|
let whitenedColor = keyOnColor.levels.map(x => floor(x * m + 255 * (1 - m)));
|
||||||
} else if (isPedaled[i] == 1 && !rainbowMode) {
|
fill(`rgb(${whitenedColor[0]}, ${whitenedColor[1]}, ${whitenedColor[2]})`); // keypressed
|
||||||
fill(pedaledColor); // pedaled
|
} else if ((isKeyOn[i] || isPedaled[i]) && rainbowMode) {
|
||||||
} else if (isPedaled[i] == 1 && rainbowMode) {
|
fill(map(i, 21, 108, 0, 1080) % 360, 100 * m, 100, 100); // rainbowMode
|
||||||
fill(map(i, 21, 108, 0, 1080) % 360, 100, 70, 100); // pedaled rainbowMode
|
} else {
|
||||||
|
fill(0, 0, 100); // white key
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fill(0, 0, 100); // white key
|
if (isKeyOn[i] && !rainbowMode) {
|
||||||
|
fill(keyOnColor); // keypressed
|
||||||
|
} else if (isKeyOn[i] && rainbowMode) {
|
||||||
|
fill(map(i, 21, 108, 0, 1080) % 360, 100, 100, 100); // rainbowMode
|
||||||
|
} else if (isPedaled[i] && !rainbowMode) {
|
||||||
|
fill(pedaledColor); // pedaled
|
||||||
|
} else if (isPedaled[i] && rainbowMode) {
|
||||||
|
fill(map(i, 21, 108, 0, 1080) % 360, 100, 70, 100); // pedaled rainbowMode
|
||||||
|
} else {
|
||||||
|
fill(0, 0, 100); // white key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let thisX = border + wIndex * (whiteKeyWidth + whiteKeySpace);
|
let thisX = border + wIndex * (whiteKeyWidth + whiteKeySpace);
|
||||||
rect(thisX, keyAreaY, whiteKeyWidth, keyAreaHeight, radius);
|
rect(thisX, keyAreaY, whiteKeyWidth, keyAreaHeight, radius);
|
||||||
|
@ -80,16 +92,28 @@ function drawBlackKeys() {
|
||||||
|
|
||||||
if (isBlack[i % 12] > 0) {
|
if (isBlack[i % 12] > 0) {
|
||||||
// it's a black key
|
// it's a black key
|
||||||
if (isKeyOn[i] == 1 && !rainbowMode) {
|
if (velocityMode) {
|
||||||
fill(keyOnColor); // keypressed
|
let m = max(isKeyOn[i], isPedaled[i]) * .9 + .1;
|
||||||
} else if (isKeyOn[i] == 1 && rainbowMode) {
|
if ((isKeyOn[i] || isPedaled[i]) && !rainbowMode) {
|
||||||
fill(map(i, 21, 108, 0, 1080) % 360, 100, 100, 100); // rainbowMode
|
let darkenedColor = keyOnColor.levels.map(x => floor(x * m));
|
||||||
} else if (isPedaled[i] == 1 && !rainbowMode) {
|
fill(`rgb(${darkenedColor[0]}, ${darkenedColor[1]}, ${darkenedColor[2]})`); // keypressed
|
||||||
fill(pedaledColor); // pedaled
|
} else if ((isKeyOn[i] || isPedaled[i]) && rainbowMode) {
|
||||||
} else if (isPedaled[i] == 1 && rainbowMode) {
|
fill(map(i, 21, 108, 0, 1080) % 360, 100, 100 * m, 100); // rainbowMode
|
||||||
fill(map(i, 21, 108, 0, 1080) % 360, 100, 70, 100); // pedaled rainbowMode
|
} else {
|
||||||
|
fill(0, 0, 0); // black key
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fill(0, 0, 0); // white key
|
if (isKeyOn[i] && !rainbowMode) {
|
||||||
|
fill(keyOnColor); // keypressed
|
||||||
|
} else if (isKeyOn[i] && rainbowMode) {
|
||||||
|
fill(map(i, 21, 108, 0, 1080) % 360, 100, 100, 100); // rainbowMode
|
||||||
|
} else if (isPedaled[i] && !rainbowMode) {
|
||||||
|
fill(pedaledColor); // pedaled
|
||||||
|
} else if (isPedaled[i] && rainbowMode) {
|
||||||
|
fill(map(i, 21, 108, 0, 1080) % 360, 100, 70, 100); // pedaled rainbowMode
|
||||||
|
} else {
|
||||||
|
fill(0, 0, 0); // black key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let thisX = border + (wIndex - 1) * (whiteKeyWidth + whiteKeySpace) + isBlack[i % 12];
|
let thisX = border + (wIndex - 1) * (whiteKeyWidth + whiteKeySpace) + isBlack[i % 12];
|
||||||
|
@ -147,7 +171,7 @@ function pushHistories() {
|
||||||
shortTermTotal.push(notesThisFrame);
|
shortTermTotal.push(notesThisFrame);
|
||||||
shortTermTotal.shift();
|
shortTermTotal.shift();
|
||||||
notesThisFrame = 0;
|
notesThisFrame = 0;
|
||||||
legatoHistory.push(isKeyOn.reduce((accumulator, currentValue) => accumulator + currentValue, 0));
|
legatoHistory.push(isKeyOn.reduce((accumulator, currentValue) => accumulator + !!currentValue, 0));
|
||||||
legatoHistory.shift();
|
legatoHistory.shift();
|
||||||
|
|
||||||
|
|
||||||
|
@ -183,8 +207,7 @@ function getPressedKeys(returnString = true) {
|
||||||
let pressedOrPedaled = [];
|
let pressedOrPedaled = [];
|
||||||
|
|
||||||
for (let i = 0; i < isKeyOn.length; i++) {
|
for (let i = 0; i < isKeyOn.length; i++) {
|
||||||
pressedOrPedaled[i] = isKeyOn[i] === 1 || isPedaled[i] === 1 ? 1 : 0;
|
pressedOrPedaled[i] = isKeyOn[i] || isPedaled[i];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let noteNames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; // default if sharp
|
let noteNames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; // default if sharp
|
||||||
|
@ -196,7 +219,7 @@ function getPressedKeys(returnString = true) {
|
||||||
const pressedKeys = [];
|
const pressedKeys = [];
|
||||||
|
|
||||||
for (let i = 0; i < pressedOrPedaled.length; i++) {
|
for (let i = 0; i < pressedOrPedaled.length; i++) {
|
||||||
if (pressedOrPedaled[i] === 1) {
|
if (pressedOrPedaled[i]) {
|
||||||
const noteName = noteNames[i % 12];
|
const noteName = noteNames[i % 12];
|
||||||
const octave = Math.floor(i / 12) - 1;
|
const octave = Math.floor(i / 12) - 1;
|
||||||
pressedKeys.push(`${noteName}${octave}`);
|
pressedKeys.push(`${noteName}${octave}`);
|
||||||
|
|
10
style.css
10
style.css
|
@ -552,7 +552,7 @@ input[type=checkbox] {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
label[for=rainbow-mode-checkbox] {
|
label.custom-checkbox {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
|
@ -563,7 +563,7 @@ label[for=rainbow-mode-checkbox] {
|
||||||
transition: .3s;
|
transition: .3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
label[for=rainbow-mode-checkbox]:after {
|
label.custom-checkbox:after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1.25px;
|
top: 1.25px;
|
||||||
|
@ -575,15 +575,15 @@ label[for=rainbow-mode-checkbox]:after {
|
||||||
transition: .3s;
|
transition: .3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked + label[for=rainbow-mode-checkbox] {
|
input:checked + label.custom-checkbox {
|
||||||
background: #6f42c1;
|
background: #6f42c1;
|
||||||
}
|
}
|
||||||
|
|
||||||
label[for=rainbow-mode-checkbox]:active:after {
|
label.custom-checkbox:active:after {
|
||||||
width: 32.5px;
|
width: 32.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked + label[for=rainbow-mode-checkbox]:after {
|
input:checked + label.custom-checkbox:after {
|
||||||
left: calc(100% - 1.25px);
|
left: calc(100% - 1.25px);
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
}
|
}
|
Loading…
Reference in a new issue