OneHundredNotes MIDI Input Player

Plays back MIDI events from some sort of MIDI player (like MIDI-OX or nanoMIDIPlayer)

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         OneHundredNotes 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.");