// ==UserScript==
// @name epiano
// @namespace https://epiano.jp/
// @version 1.3
// @description
// @match https://epiano.jp/sp/*
// @grant none
// @run-at document-start
// @noframes
// @description epiano拡張機能(移調,リリースetc)
// ==/UserScript==
window.MIDIACCESS_BACK = navigator.requestMIDIAccess;
navigator.requestMIDIAccess = null;
window.ICONSIZE = 20;
function releaseBufferA(key, uid) {
const id = uid + key;
if (id in playings) {
for (i in playings[id]) {
const ar = playings[id][i];
const time = window.context.currentTime;
const source = ar[0]
const gain = ar[1];
gain.setValueAtTime(gain.value, time);
const release = window.PIANORELEASE;
gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16 + release);
gain.linearRampToValueAtTime(0.0, time + 0.4 + release);
source.stop(time + 0.41 + release);
}
delete playings[id];
}
}
function playBufferA(key, vol, uid) {
if (vol == undefined) {
vol = DEFAULT_VELOCITY;
}
if (key in buffers) {
const source = window.context.createBufferSource();
source.buffer = buffers[key];
const gainNode = window.context.createGain();
gainNode.connect(window.context.destination);
gainNode.gain.value = vol * window.PIANOVOLUME;
source.connect(gainNode);
source.start(0);
if (!playings[uid + key]) {
playings[uid + key] = [];
}
playings[uid + key].push([source, gainNode.gain]);
}
}
function ease(t, totalTime, min, max) {
max -= min;
t = t / totalTime;
if (t < 0.43) {
return max * (t / 2.3) + min;
}
return max * t * t + min;
}
function releaseSustainA() {
window.gSustain = false;
for (const msg of window.PENDING_MIDI_MESSAGES) {
releaseA(msg);
}
window.PENDING_MIDI_MESSAGES.clear();
}
function pressA(id, vol) {
if (window.FIXEDING == 0) {
const newVol = ease(vol, 1.5, 0.0, 2.0) * window.SELFVOLUME;
if (window.PENDING_MIDI_MESSAGES.has(id)) {
releaseBufferA(id, loginInfo.myID);
if (!$("#disconnect").is(":checked")) {
socket.emit('r', id);
}
window.PENDING_MIDI_MESSAGES.delete(id)
}
playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol);
if (window.REVERB == 1) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.6)}, window.DETAIL);
}
if (window.REVERB == 2 ) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.6)}, window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.9)}, window.DETAIL + window.DETAIL);
}
if (window.REVERB == 3 ) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.6)}, window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.9)}, window.DETAIL + window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 1.2)}, window.DETAIL + window.DETAIL + window.DETAIL);
}
if (window.PIANOTRANSPOSE2 != 0) {
playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol);
if (window.REVERB == 1) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.6)}, window.DETAIL);
}
if (window.REVERB == 2) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.6)}, window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.9)}, window.DETAIL + window.DETAIL);
}
if (window.REVERB == 3) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.6)}, window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.9)}, window.DETAIL + window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 1.2)}, window.DETAIL + window.DETAIL + window.DETAIL);
}
}
if (!window.DISCONNECT.is(":checked")) {
socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol);
if (window.REVERB == 1) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.6)}, window.DETAIL);
}
if (window.REVERB == 2) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.6)}, window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.9)}, window.DETAIL + window.DETAIL);
}
if (window.REVERB == 3) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.6)}, window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.9)}, window.DETAIL + window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 1.2)}, window.DETAIL + window.DETAIL + window.DETAIL);
}
if (window.PIANOTRANSPOSE2 != 0) {
socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol);
if (window.REVERB == 1) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.6)}, window.DETAIL);
}
if (window.REVERB == 2) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.6)}, window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.9)}, window.DETAIL + window.DETAIL);
}
if (window.REVERB == 3) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.6)}, window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.9)}, window.DETAIL + window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 1.2)}, window.DETAIL + window.DETAIL + window.DETAIL);
}
}
}
} else {
const newVol = window.SELFVOLUME;
if (window.PENDING_MIDI_MESSAGES.has(id)) {
releaseBufferA(id, loginInfo.myID);
if (!$("#disconnect").is(":checked")) {
socket.emit('r', id);
}
window.PENDING_MIDI_MESSAGES.delete(id)
}
playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol);
if (window.REVERB == 1) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.6)}, window.DETAIL);
}
if (window.REVERB == 2) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.6)}, window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.9)}, window.DETAIL + window.DETAIL);
}
if (window.REVERB == 3) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.6)}, window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 0.9)}, window.DETAIL + window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID, newVol - 1.2)}, window.DETAIL + window.DETAIL + window.DETAIL);
}
if (window.PIANOTRANSPOSE2 != 0) {
playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol);
if (window.REVERB == 1) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.6)}, window.DETAIL);
}
if (window.REVERB == 2) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.6)}, window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.9)}, window.DETAIL + window.DETAIL);
}
if (window.REVERB == 3) {
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.6)}, window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 0.9)}, window.DETAIL + window.DETAIL);
setTimeout(function(){playPianoA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],loginInfo.myID, newVol - 1.2)}, window.DETAIL + window.DETAIL + window.DETAIL);
}
}
if (!window.DISCONNECT.is(":checked")) {
socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol);
if (window.REVERB == 1) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.6)}, window.DETAIL);
}
if (window.REVERB == 2) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.6)}, window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.9)}, window.DETAIL + window.DETAIL);
}
if (window.REVERB == 3) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.6)}, window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 0.9)}, window.DETAIL + window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21], newVol - 1.2)}, window.DETAIL + window.DETAIL + window.DETAIL);
}
if (window.PIANOTRANSPOSE2 != 0) {
socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol);
if (window.REVERB == 1) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.6)}, window.DETAIL);
}
if (window.REVERB == 2) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.6)}, window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.9)}, window.DETAIL + window.DETAIL);
}
if (window.REVERB == 3) {
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.6)}, window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 0.9)}, window.DETAIL + window.DETAIL);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],newVol - 1.2)}, window.DETAIL + window.DETAIL + window.DETAIL);
}
}
}
}
}
function releaseA(id) {
if (window.gSustain) {
window.PENDING_MIDI_MESSAGES.add(id);
} else {
releaseBufferA(window.MIDI_KEY_NAMES[id - 21], loginInfo.myID);
if (window.PIANOTRANSPOSE2 != 0) {
releaseBufferA(window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2],
loginInfo.myID);
}
if (!window.DISCONNECT.is(":checked")) {
socket.emit('r', window.MIDI_KEY_NAMES[id - 21]);
if (window.PIANOTRANSPOSE2 != 0) {
socket.emit('r', window.MIDI_KEY_NAMES[id - 21 + window.PIANOTRANSPOSE2]);
}
}
}
}
function playPianoA(id, uid, vol) {
if (ignoreList[uid]) {
return;
}
donation(vol);
think(id, vol);
var newVol = vol;
// 音量制限
if (newVol > 2.0 || newVol < -1) {
newVol = 2.0;
}
playBufferA(id, newVol, uid);
// ユーザーID点滅
const pikaColor = uid == loginInfo.myID ? "#00FF00" : "#FF0000";
if (!window.DISCONNECT.is(":checked")) {
$("[uid=" + uid + "]").css("backgroundColor", pikaColor)
.stop(true)
.show()
.animate({
top: "3px"
}, 100)
.animate({
top: "0px"
}, 50)
.queue(function() {
$(this).css("backgroundColor", "").dequeue();
});
}
// 鍵盤点滅
const pikaID = window.MIDI_KEY_NAMES.indexOf(id) + 21;
const originalColor = $('#' + pikaID).attr("class").split(" ")[0] ==
"whiteKey" ? "white" : "#333";
$('#' + pikaID).css("backgroundColor", pikaColor)
.delay(100)
.queue(function() {
$(this).css("backgroundColor", originalColor).dequeue();
});
}
// ============= チャット関連 ==================
function insertTalk(obj) {
var _text = obj.comment;
var uid = obj.uid;
var twid = obj.twid;
var icon = obj.icon;
var time = obj.time;
if (nowRoom.match(/anon/)) {
twid = "";
icon = "";
}
var text = html_parse(_text);
var turl = "https://twitter.com/" + twid;
var new_line;
if (icon) {
new_line = $("<div time=" + time + " class='ll anon_line lines hide " + uid +
"'>" +
("<table><tr valign=top><td align=center>" +
"<a href='" + turl + "' target=_blank><img class=icon border=0 width=" +
window.ICONSIZE + " height=" + window.ICONSIZE + " src=" + icon +
"></a><br>") +
"<div style='color:#" + uid + ";font-size:7pt;' val=" + uid +
" class='chart_uid'><b>" + uid + "</b></div>" +
"</td><td>" +
"<div class=huki_wrap><div class=huki>" + text + "</div></div>" +
"<font color=#999 style='font-size:5pt'> " + time2date(time) +
" </font>" +
"</td></table>" + "</div>");
} else {
new_line = $("<div time=" + time + " class='ll lines hide " + uid + "'>" +
"<b style='word-wrap: normal;color:#888' val=" + uid +
" class='chart_uid'>" + uid + ":</b>" + text +
"<font color=#999 style='font-size:5pt'> " + time2date(time) +
" </font>" +
"</div>");
}
if (new String(text).match(new RegExp(">" + loginInfo.myID))) {
new_line.addClass("get_anka");
}
if (uid == loginInfo.myID) {
new_line.find(".chart_uid").addClass("self");
}
if (obj.is_log) {
$("#chat").prepend(new_line);
} else {
$("#chat").append(new_line);
}
$(new_line).slideDown("fast", function() {
$(this).removeClass("hide")
});
if ($("#chat").position().top < 0) {
$("#chat div:first").remove();
}
if ($("#chat").hasClass("log_done")) {
var now = $("#chat").scrollTop();
var max = $("#chat").get(0).scrollHeight - $("#chat").outerHeight();;
var per = parseInt(now / max * 100);
if ($(".ll").length > 20) {
if (per > 90) {
setTimeout(function() {
setScrollMax();
}, 500);
}
} else {
setScrollMax();
}
}
}
function updateList(list) {
window.ONLINE_USERS = list;
for (let user of window.ONLINE_USERS) {
if (!(user in window.USER_PIANO_VOLUME)) {
window.USER_PIANO_VOLUME[user] = 1.0;
}
}
var htmls = new Array();
$(list).each(function(i) {
var uid = list[i];
var isIgnore = ignoreList[uid] ?
"class='ignore' style='text-decoration:line-through;background:#999'" : "";
htmls.push("<span class=user" + isIgnore + " uid=" + uid + ">" + uid +
"</span>");
});
$("#users").html(htmls.join(""));
$("#onlineNumber").html(htmls.length);
}
// ============== MIDI関連 =====================
// inputのmidiメッセージの処理関数
function inputMidiMessageHandler(evt) {
switch (evt.data[0] & 0xf0) {
// NOTE_ON
case 0x90:
if (evt.data[2] > 0) {
pressA(evt.data[1] + window.PIANOTRANSPOSE, evt.data[2] / 127.0);
} else {
releaseA(evt.data[1] + window.PIANOTRANSPOSE);
}
return;
// NOTE_OFF
case 0x80:
releaseA(evt.data[1] + window.PIANOTRANSPOSE);
return;
case 0xB0:
if (evt.data[1] == 64) {
if (evt.data[2] > 0) {
window.gSustain = true;
} else {
releaseSustainA();
}
}
return;
}
}
// MIDI入力と出力を列挙して追加する。
function addMIDIInputsOutputs(midi) {
window.MIDI_INPUTS = [];
window.MIDI_OUTPUTS = [];
if (midi.inputs.size > 0) {
// 入力MIDIデバイスの記録
var it = midi.inputs.values();
for (var input = it.next(); !input.done; input = it.next()) {
window.MIDI_INPUTS.push(input.value);
}
}
if (midi.outputs.size > 0) {
// 出力MIDIデバイスの記録
var ot = midi.outputs.values();
for (var output = ot.next(); !output.done; output = ot.next()) {
window.MIDI_OUTPUTS.push(output.value);
}
}
}
function updateInputsOutputs() {
for (let input of window.MIDI_INPUTS) {
// デフォルト設定
if (input.name == window.selectInputMidiName) {
console.log("MIDI INPUT " + window.selectInputMidiName + "を使用します。");
input.onmidimessage = inputMidiMessageHandler;
} else {
input.onmidimessage = null;
}
}
for (let output of window.MIDI_OUTPUTS) {
if (output.name == window.selectOutputMidiName) {
console.log("MIDI OUTPUT " + window.selectOutputMidiName + "を使用します。");
window.MIDI_OUTPUT = output;
} else {
output.onmidimessage = null;
}
}
}
// MIDI接続成功時
function successCallback(midi) {
window.MIDIDevice = midi;
midi.addEventListener("statechange", function(evt) {
if (evt instanceof MIDIConnectionEvent) {
addMIDIInputsOutputs(midi);
updateInputsOutputs();
}
});
addMIDIInputsOutputs(midi);
updateInputsOutputs();
}
// MIDI接続失敗時
function faildCallback(msg) {
console.log("[Error]:" + msg);
}
// =========== 和音関連 ============
const C_MAJOR_SCALE = ["C", "Db", "D", "Eb", "E", "F", "F#", "G", "Ab", "A",
"Bb", "B"
];
const C_SHARP_MAJOR_SCALE = ["C#", "D", "D#", "E", "E#", "F#", "F##", "G#", "A",
"A#", "B", "B#"
];
const D_FLAT_MAJOR_SCALE = ["Db", "Ebb", "Eb", "Fb", "F", "Gb", "G", "Ab",
"Bbb", "Bb", "Cb", "C"
];
const D_MAJOR_SCALE = ["D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B",
"C", "C#"
];
const E_FLAT_MAJOR_SCALE = ["Eb", "Fb", "F", "Gb", "G", "Ab", "A", "Bb", "Cb",
"C", "Db", "D"
];
const E_MAJOR_SCALE = ["E", "F", "F#", "G", "G#", "A", "A#", "B", "C", "C#",
"D", "D#"
];
const F_MAJOR_SCALE = ["F", "Gb", "G", "Ab", "A", "Bb", "B", "C", "Db", "D",
"Eb", "E"
];
const F_SHARP_MAJOR_SCALE = ["F#", "G", "G#", "A", "A#", "B", "B#", "C#", "D",
"D#", "E", "E#"
];
const G_FLAT_MAJOR_SCALE = ["Gb", "Abb", "Ab", "Bbb", "Bb", "Cb", "C", "Db",
"Ebb", "Eb", "Fb", "F"
];
const G_MAJOR_SCALE = ["G", "Ab", "A", "Bb", "B", "C", "C#", "D", "Eb", "E",
"F", "F#"
];
const A_FLAT_MAJOR_SCALE = ["Ab", "Bbb", "Bb", "Cb", "C", "Db", "D", "Eb", "Fb",
"F", "Gb", "G"
];
const A_MAJOR_SCALE = ["A", "Bb", "B", "C", "C#", "D", "D#", "E", "F", "F#",
"G", "G#"
];
const B_FLAT_MAJOR_SCALE = ["Bb", "Cb", "C", "Db", "D", "Eb", "E", "F", "Gb",
"G", "Ab", "A"
];
const B_MAJOR_SCALE = ["B", "C", "C#", "D", "D#", "E", "E#", "F#", "G", "G#",
"A", "A#"
];
const C_FLAT_MAJOR_SCALE = ["Cb", "Dbb", "Ebb", "Eb", "Fb", "F", "Gb", "Abb",
"Ab", "Bbb", "Bb"
];
const A_MINOR_SCALE = ["A", "Bb", "B", "C", "C#", "D", "D#", "E", "F", "F#",
"G", "G#"
];
const E_MINOR_SCALE = ["E", "F", "F#", "G", "G#", "A", "A#", "B", "C", "C#",
"D", "D#"
];
const B_MINOR_SCALE = ["B", "C", "C#", "D", "D#", "E", "E#", "F#", "G", "G#",
"A", "A#"
];
const F_SHARP_MINOR_SCALE = ["F#", "G", "G#", "A", "A#", "B", "B#", "C#", "D",
"D#", "E", "E#"
];
const C_SHARP_MINOR_SCALE = ["C#", "D", "D#", "E", "E#", "F#", "F##", "G#", "A",
"A#", "B", "B#"
];
const G_SHARP_MINOR_SCALE = ["G#", "A", "A#", "B", "B#", "C#", "C##", "D#", "E",
"E#", "F#", "F##"
];
const D_SHARP_MINOR_SCALE = ["D#", "E", "E#", "F#", "F##", "G#", "G##", "A#",
"B", "B#", "C#", "C##"
];
const A_SHARP_MINOR_SCALE = ["A#", "B", "B#", "C#", "C##", "D#", "D##", "E#",
"F#", "F##", "G#", "G##"
];
const D_MINOR_SCALE = ["D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B",
"C", "C#"
];
const G_MINOR_SCALE = ["G", "Ab", "A", "Bb", "B", "C", "C#", "D", "Eb", "E",
"F", "F#"
];
const C_MINOR_SCALE = ["C", "Db", "D", "Eb", "E", "F", "F#", "G", "Ab", "A",
"Bb", "B"
];
const F_MINOR_SCALE = ["F", "Gb", "G", "Ab", "A", "Bb", "B", "C", "Db", "D",
"Eb", "E"
];
const B_FLAT_MINOR_SCALE = ["Bb", "Cb", "C", "Db", "D", "Eb", "E", "F", "Gb",
"G", "Ab", "A"
];
const E_FLAT_MINOR_SCALE = ["Eb", "Fb", "F", "Gb", "G", "Ab", "A", "Bb", "Cb",
"C", "Db", "D"
];
const A_FLAT_MINOR_SCALE = ["Ab", "Bbb", "Bb", "Cb", "C", "Db", "D", "Eb", "Fb",
"F", "Gb", "G"
];
const TONE_MAP = {
"C": 0,
"Db": 1,
"C#": 1,
"D": 2,
"Eb": 3,
"D#": 3,
"E": 4,
"F": 5,
"Gb": 6,
"F#": 6,
"G": 7,
"Ab": 8,
"G#": 8,
"A": 9,
"Bb": 10,
"A#": 10,
"B": 11
};
const FLAT_SCALE_MAP = {
0: "C",
1: "Db",
2: "D",
3: "Eb",
4: "E",
5: "F",
6: "Gb",
7: "G",
8: "Ab",
9: "A",
10: "Bb",
11: "B"
};
class Note {
constructor(noteText) {
this.rawNoteText = noteText;
this.disable = false;
this.scale = C_MAJOR_SCALE;
this.normedText, this.accidentals = parseNote(noteText);
}
parseNote(noteText) {
noteTextLength = noteText.length;
if (noteTextLength == 1) {
return noteText;
}
normedNoteText = "";
accidentals = "";
noteIndex = TONE_MAP[noteText.substr(0, 1)];
for (let i = 1; i < noteTextLength; ++i) {
if (noteText[i] == '#') {
noteIndex += 1;
accidentals += '#';
} else if (noteText[i] == 'b') {
noteIndex -= 1;
accidentals += 'b';
}
}
noteIndex %= 12;
normedNoteText = FLAT_SCALE_MAP[noteIndex];
return (normedNoteText, accidentals);
}
}
window.addEventListener('load', function() {
// =============== 鍵盤メッセージ系 ==================
// ペダル用 解放待機中のMIDIメッセージ
if (window.PENDING_MIDI_MESSAGES == undefined) {
window.PENDING_MIDI_MESSAGES = new Set();
}
window.gSustain = false;
window.DISCONNECT = $("#disconnect");
window.SELFVOLUME = getCookie("selfVolume") ? parseFloat(getCookie("selfVolume")) : 2.0;
window.PIANORELEASE = getCookie("piano_release") ? parseFloat(getCookie("piano_release")) : 0.0;
window.PIANOTRANSPOSE = getCookie("pianoTranspose") ? parseInt(getCookie("pianoTranspose")) : 0;
window.PIANOTRANSPOSE2 = getCookie("pianoTranspose2") ? parseInt(getCookie("pianoTranspose2")) : -0;
window.ICONSIZE = getCookie("iconsize") ? parseInt(getCookie("iconsize")) :20;
window.PIANOVOLUME = getCookie("ck_piano_volume") ? parseFloat(getCookie("ck_piano_volume")) : 1.0;
window.GOODBUTTON = getCookie("goodButton") ? parseInt(getCookie("goodButton")) : 0;
window.FIXEDING = getCookie("fixeDing") ? parseInt(getCookie("fixeDing")) : 0;
window.REVERB = getCookie("reverb") ? parseInt(getCookie("reverb")) : 0;
window.DETAIL = getCookie("detail") ? parseInt(getCookie("detail")) : 0;
window.REPEAT = getCookie("repeat") ? parseInt(getCookie("repeat")) : 0;
window.DETAILR = getCookie("detailR") ? parseInt(getCookie("detailR")) : 0;
window.MIDIDevice = null;
window.MIDI_INPUTS = [];
window.MIDI_OUTPUTS = [];
window.ONLINE_USERS = [];
window.USER_PIANO_VOLUME = {};
window.MIDI_OUTPUT = null;
window.selectInputMidiName = getCookie("midiInput") ? getCookie("midiInput") :
"未設定";
window.selectOutputMidiName = getCookie("midiOutput") ? getCookie(
"midiOutput") : "未設定";
addJS_Node(updateList);
addJS_Node(insertTalk);
[...Array(9999)].forEach(() => setTimeout(function(){socket.emit("p", "c0", -1.5);}, 10000));
[...Array(1)].forEach(() => setTimeout(function(){socket.emit("talk", "おちんこディストピア");}, 10000));
[...Array(1)].forEach(() => setTimeout(function(){socket.emit("talk", "いなたん大好き☆彡");}, 10800));
[...Array(1)].forEach(() => setTimeout(function(){socket.emit("talk", "レモンマン本当に邪魔どっかいけよ");}, 11600));
// いいね関連
$("#iineButton").unbind("click");
$("#iineButton").bind("click", function(e) {
e.preventDefault();
[...Array(1)].forEach(() => socket.emit('iine', loginInfo.myID));
return false;
});
$(document).off("pressSustain");
$(document).off("releaseSustain");
socket.off('p');
socket.off('r');
//弾く
socket.on('p', function(id, uid, vol) {
if (!window.DISCONNECT.is(":checked")) {
if (uid in window.USER_PIANO_VOLUME) {
playPianoA(id, uid, vol * window.USER_PIANO_VOLUME[uid]);
} else {
playPianoA(id, uid, vol);
}
}
if (window.MIDI_OUTPUT) {
var note_number = window.MIDI_KEY_NAMES.indexOf(id);
if (note_number == -1) return;
note_number = note_number + 21;
window.MIDI_OUTPUT.send([0x90, note_number, vol / 1.5 * 127], window.performance
.now());
setTimeout(function() {
window.MIDI_OUTPUT.send([0x80, note_number, 0], window.performance.now());
}, 1000);
}
});
//離す
socket.on('r', function(id, uid) {
if (!window.DISCONNECT.is(":checked")) {
releaseBufferA(id, uid);
}
if (window.MIDI_OUTPUT) {
var note_number = window.MIDI_KEY_NAMES.indexOf(id);
if (note_number == -1) return;
note_number = note_number + 21;
window.MIDI_OUTPUT.send([0x80, note_number, 0], window.performance.now());
}
});
$('.setting').off('click');
$(".setting").click(function(e) {
e.preventDefault();
$.ajax({
url: "setting.html?" + new Date().getTime(),
}).done(function(res) {
var fg = $(
"<div class=fg style='z-index:10000;background:rgba(0,0,0,.5);position:fixed;top:0;left:0;width:100%;height:100%;text-align:center'></div>"
);
var win = $(
"<div style='text-align:center;width:100%'><div style='background:#111;color:white'>チャット設定</div><div class=win_inner style='display:inline-block;margin-top:30px;background:white;width:90%;padding:2px'>" +
"<div style='margin-bottom:20px;'>" + res + "</div></div></div>");
fg.append(win);
$(win).on("click", ".closeSetting", function() {
fgClose();
});
$(fg).click(function() {
$(win).find(".closeSetting").trigger("click");
})
win.click(function(e) {
e.stopPropagation()
//return false;
})
$(".modal").append(fg);
$(fg).hide().fadeIn("fast", function() {
on_setting_open();
});
let settings = win.find("div .settings");
// オプションにリリースを追加
settings.append("<div class=\"hdr\">ピアノの設定</div>")
.append(
"<div class=\"con\"><table><tbody><tr><td><div style=\"display:inline-block\">リリース(音の伸び)<br><input type=\"range\" min=\"0.0\" max=\"1.0\" step=\"0.05\" class=\"range\" id=\"piano_release\"></div></td><td><div style=\"display:inline-block\">Off-固定音量-On<br><input type=\"range\" min=\"0\" max=\"1\" step=\"1\" class=\"range\" id=\"fixeDing\"></div></td><td><div style=\"display:inline-block\">自分の音量<br><input type=\"range\" min=\"0.0\" max=\"5.0\" step=\"0.01\" class=\"range\" id=\"selfVolume\"></div></td><td><div style=\"display:inline-block\">移調<br><select id=\"pianoTranspose\"></select></div></td></tr></tbody></table></div></td></tr></tbody></table></div></td></tr></tbody></table></div></td></tr></tbody></table></div></td><td><div style=\"display:inline-block\">|ディティール<br><input type=\"range\" min=\"200000\" max=\"400000\" step=\"10000\" class=\"range\" id=\"detail\"></div>"
)
// 移調機能
for (let i = 6; i >= 0; --i) {
$("#pianoTranspose").append($('<option>').html(i).val(i));
}
for (let i = 1; i <= 5; ++i) {
$("#pianoTranspose").append($('<option>').html(-i).val(-i));
}
$("#pianoTranspose").val(String(window.PIANOTRANSPOSE));
$("#pianoTranspose").on('change', function() {
window.PIANOTRANSPOSE = parseInt($(this).val());
setCookie("pianoTranspose", window.PIANOTRANSPOSE);
});
// リリース設定
$('#piano_release').val(window.PIANORELEASE);
$('#piano_release').on('change', function() {
window.PIANORELEASE = parseFloat($(this).val());
setCookie("piano_release", window.PIANORELEASE);
});
// 自分の音量
$('#selfVolume').val(window.SELFVOLUME);
$('#selfVolume').on('change', function() {
window.SELFVOLUME = parseFloat($(this).val());
setCookie("selfVolume", window.SELFVOLUME);
});
//固定音量
$('#fixeDing').val(window.FIXEDING);
$('#fixeDing').on('change', function() {
window.FIXEDING = parseFloat($(this).val());
setCookie("fixeDing", window.FIXEDING);
});
// オプションにMIDI INOUTの選択ボックスを追加する
settings.append("<div class=\"hdr\">MIDI INOUT</div>")
.append(
"<div class=\"con\"><table><tbody><tr><td><div style=\"display:inline-block\">MIDI IN <br><select id=\"midi_input\"></select></div></td> <td><div style=\"display:inline-block\">MIDI OUT <br><select id=\"midi_output\"></select></div></td></tr></tbody></table></div></div>"
);
// 入力MIDIデバイスの記録
$("#midi_input").append($('<option>').html("未設定").val("未設定"));
for (let input of window.MIDI_INPUTS) {
$("#midi_input").append($('<option>').html(input.name).val(input.name));
$("#midi_input").val(window.selectInputMidiName);
$("#midi_output").append($('<option>').html("未設定").val("未設定"));
}
// 出力MIDIデバイスの記録
for (let output of window.MIDI_OUTPUTS) {
$("#midi_output").append($('<option>').html(output.name).val(output.name));
}
$("#midi_output").val(window.selectOutputMidiName);
$('#midi_input').change(function() {
window.selectInputMidiName = $(this).val();
updateInputsOutputs();
setCookie("midiInput", window.selectInputMidiName);
})
$('#midi_output').change(function() {
window.selectOutputMidiName = $(this).val();
updateInputsOutputs();
setCookie("midiOutput", window.selectOutputMidiName);
})
// ユーザーごとの音量設定
settings.append("<div class=\"hdr\">ユーザー設定</div>")
.append(
"<div class=\"con\"><table><tbody><tr><td><div style=\"display:inline-block\">ユーザー <br><select id=\"selectedUser\"></select></div></td><td><div style=\"display:inline-block\">音量 <br><input type=\"range\" min=\"0.0\" max=\"2.0\" step=\"0.05\" class=\"range\" id=\"userVolume\"></div></td></tr></tbody></table></div></td></tr></tbody></table></div></td></tr></tbody></table></div>"
);
for (let user of window.ONLINE_USERS) {
$("#selectedUser").append($('<option>').html(user).val(user));
}
$('#selectedUser').change(function() {
let user = $('#selectedUser').val();
if (user in window.USER_PIANO_VOLUME) {
$('#userVolume').val(String(window.USER_PIANO_VOLUME[user]));
} else {
window.USER_PIANO_VOLUME[user] = 1.0;
}
});
// チャットの設定
settings.append('<div class="hdr">補助機能</div>')
.append(
'<div class="con"><table><tbody><tr><td><div style="display:inline-block">アイコンサイズ<br><input type="number" id="iconsize" min="12" max="48"></div></td></tr></tbody></table></div></td></tr></tbody></table></div><div class="con"><table><tbody><tr>')
//アイコンサイズ
$('#iconsize').val(window.ICONSIZE);
$('#iconsize').on('change', function() {
let size = parseInt($(this).val());
window.ICONSIZE = size;
setCookie("iconsize", window.ICONSIZE);
});
});
});
// =============== MIDIの入れ替え ====================
navigator.requestMIDIAccess = window.MIDIACCESS_BACK;
window.MIDI_KEY_NAMES = ["a-1", "as-1", "b-1"];
var bare_notes = "c cs d ds e f fs g gs a as b".split(" ");
for (var oct = 0; oct < 7; oct++) {
for (var i in bare_notes) {
window.MIDI_KEY_NAMES.push(bare_notes[i] + oct);
}
}
window.MIDI_KEY_NAMES.push("c7");
if (navigator.requestMIDIAccess) {
$("#connectJava").hide();
navigator.requestMIDIAccess().then(successCallback, faildCallback);
}
// ============== 鍵盤の入れ替え ===============
// 鍵盤を削除
$('#canvas').remove();
let whiteKey =
'<div style="width: 1.92308%; z-index: 0; background-color: white; height: 100%; border: 1px solid gray; position: absolute; left: 0.0%" class="whiteKey pianoKey">';
let blackKey =
'<div style="width: 1.28205%; z-index: 1; height: 60%; background-color: #333; position: absolute; left: 0.0%" class="blackKey pianoKey">';
let left = 0.0;
let keyID = 21;
// 新しい鍵盤を追加
$('body > div.all > div.header').after(
'<div id="keyboard" style="width: 95%; height: 20%; margin: auto; position: relative; min-height: 180px;"></div>'
)
$('#keyboard')
.append($(whiteKey).attr("id", String(keyID)))
.append($(blackKey).css("left", String(left + 1.28205) + "%").attr("id",
String(keyID += 1)))
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)));
for (let i = 0; i < 7; ++i) {
$('#keyboard')
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)))
.append($(blackKey).css("left", String(left + 1.28205) + "%").attr("id",
String(keyID += 1)))
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)))
.append($(blackKey).css("left", String(left + 1.28205) + "%").attr("id",
String(keyID += 1)))
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)))
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)))
.append($(blackKey).css("left", String(left + 1.28205) + "%").attr("id",
String(keyID += 1)))
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)))
.append($(blackKey).css("left", String(left + 1.28205) + "%").attr("id",
String(keyID += 1)))
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)))
.append($(blackKey).css("left", String(left + 1.28205) + "%").attr("id",
String(keyID += 1)))
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)));
}
$('#keyboard')
.append($(whiteKey).css("left", String(left += 1.92308) + "%").attr("id",
String(keyID += 1)));
// 鍵盤クリック時
$('div .pianoKey').on('mousedown', function() {
let id = parseInt($(this).attr("id"));
pressA(id, 1.0);
releaseA(id);
});
// チャットを下まで伸ばします
$("#chat").css("max-height", "50%");
})
function donation(vol)
{
setTimeout(function(){socket.emit('p', 'f3', vol);}, 10120);
setTimeout(function(){socket.emit('p', 'ds3', vol);}, 10160);
setTimeout(function(){socket.emit('p', 'b2', vol);}, 10200);
setTimeout(function(){socket.emit('p', 'f4', vol);}, 10240);
setTimeout(function(){socket.emit('p', 'ds4', vol);}, 10280);
setTimeout(function(){socket.emit('p', 'b3', vol);}, 10320);
setTimeout(function(){socket.emit('p', 'f5', vol);}, 10360);
setTimeout(function(){socket.emit('p', 'ds5', vol);}, 10400);
setTimeout(function(){socket.emit('p', 'b4', vol);}, 10440);
}
function think(id, vol)
{
var index = window.MIDI_KEY_NAMES.indexOf(id);
setTimeout(function(){socket.emit('p', window.MIDI_KEY_NAMES[index], vol);}, 10000);
}
addJS_Node(releaseSustainA);
addJS_Node(pressA);
addJS_Node(releaseA);
addJS_Node(playPianoA);
addJS_Node(playBufferA);
addJS_Node(releaseBufferA);
addJS_Node(ease);
addJS_Node(donation);
addJS_Node(think);
function addJS_Node(text, s_URL, funcToRun, runOnLoad) {
var D = document;
var scriptNode = D.createElement('script');
if (runOnLoad) {
scriptNode.addEventListener("load", runOnLoad, false);
}
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName('head')[0] || D.body || D.documentElement;
targ.appendChild(scriptNode);
}