您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Plays back MIDI events from some sort of MIDI player (like MIDI-OX or nanoMIDIPlayer)
// ==UserScript== // @name OneHundredNotes.com MIDI Input Player // @namespace http://tampermonkey.net/ // @version 1.0.3 // @description Plays back MIDI events from some sort of MIDI player (like MIDI-OX or nanoMIDIPlayer) // @author zackiboiz // @match *://onehundrednotes.vercel.app/* // @icon https://icons.duckduckgo.com/ip2/vercel.app.ico // @license MIT // @grant none // @require https://cdn.jsdelivr.net/npm/[email protected]/client-dist/socket.io.min.js // ==/UserScript== class MIDIController { constructor(socket) { this.socket = socket; this.sustain = false; this.auto_sustain = false; this.sustained_notes = {}; } pressSustain() { this.sustain = true; } releaseSustain() { this.sustain = false; if (!this.auto_sustain) { for (const [name, note] of Object.entries(this.sustained_notes)) { if (this.sustained_notes[name] && !this.held_notes[name]) { this.releaseNote(note.name, note.channel, null, true); this.sustained_notes[name] = false; } } } } setupMIDIEvents() { console.log("[DEBUG] Requesting MIDI access..."); if (navigator.requestMIDIAccess) { navigator.requestMIDIAccess().then((res) => { console.log("[DEBUG] MIDI access granted."); res.inputs.forEach((input) => { input.onmidimessage = this.handleMIDIMessage.bind(this); }); }).catch(() => { console.log("[DEBUG] MIDI access denied."); }); } } handleMIDIMessage(message) { const [status, note_num, velocity] = message.data; const channel = status & 0x0f; if (status >= 0x90 && status <= 0x9f && velocity > 0) { const { note, octave } = this.getNoteName(note_num + 9); this.pressNote(note, octave); } else if ((status >= 0x80 && status <= 0x8f) || (status >= 0x90 && status <= 0x9f && velocity === 0)) { const { note, octave } = this.getNoteName(note_num + 9); this.releaseNote(note, octave); } else if (status >= 0xb0 && status <= 0xbf && note_num === 64) { if (velocity > 0) { this.pressSustain(); } else { this.releaseSustain(); } } } getNoteName(midi_note) { const steps = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; const octave = Math.floor((midi_note - 21) / 12); const note = steps[(midi_note - 21) % 12]; return { note, octave }; } pressNote(note, octave) { console.log(`[DEBUG] Pressing ${note + octave}.`); this.socket.emit("note-start", { letter: note, octave: octave }); } releaseNote(note, octave) { console.log(`[DEBUG] Releasing ${note + octave}.`); this.socket.emit("note-stop", { letter: note, octave: octave }); } } const socket = window.io("https://onehundrednotes.onrender.com", { origin: "https://onehundrednotes.vercel.app", autoConnect: false, }); socket.on("connect", () => { console.log("[DEBUG] Connected to OneHundredNotes.com socket."); const controller = new MIDIController(socket); controller.setupMIDIEvents(); }); socket.connect(); console.log("[DEBUG] OneHundredNotes.com MIDI Input Player connected.");