epiano

epiano拡張機能(移調,リリースetc)

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==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'>&nbsp;&nbsp;" + 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'>&nbsp;" + time2date(time) +
			" </font>" +
			"</div>");
	}

	if (new String(text).match(new RegExp("&gt;" + 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);
}