[JavaFX] Versuchen Sie, ein Software-MIDI-Keyboard zu erstellen. Teil 2 Schieben Sie Ihren Finger, um die Skalierung zu ändern

OpenningKey04.png

Sie können das echte Klavier zur nächsten Taste bewegen, während Sie es gedrückt halten

Die beim ersten Mal erstellte Tastatur implementierte für jede Taste einen Ereignis-Listener. aus diesem Grund

Das Problem trat auf. Dieses Mal werden wir diesen Prozess verbessern, damit er eher wie ein echtes Klavier ausgedrückt werden kann. Ich werde auch beschreiben, wie untergeordnete Knoten vom übergeordneten Knoten abgerufen werden.

Lösungsfluss

Wir werden es Schritt für Schritt lösen. Die erste ist die Bewegung des zuletzt erstellten Ereignis-Listeners.

Verschieben Sie TouchEvents auf den übergeordneten Knoten

Verschieben Sie die zuletzt in der Schlüsselklasse erstellten "setOnTouchPressed" und "setOnTouchReleased" auf den übergeordneten Knoten. Das unmittelbare übergeordnete Element ist eine Notes-Layoutklasse, die AnchorPane erbt. In Zukunft werden wir es jedoch in die höhere KeyboardMain-Layoutklasse verschieben.

Hier hat eine Taste eine Note und einen Empfänger (UraReceiver), und der Vorgang des tatsächlichen Klingens und Änderns des Stils wird von jeder Taste und nur vom Ereignis-Listener ausgeführt, der den Prozess ausführt. Ich beschloss, es meinen Eltern zu übertragen.

Ursprünglich wurde es dynamisch anstelle von XML erstellt, sodass diese Art der Bewegung überraschend einfach war.

KeyboardMain.java



public class KeyboardMain extends VBox implements UraOnTouchMovedListener, UraOnTouchReleasedListener, UraOnTouchPressedListener {
....

        this.setOnTouchReleased(touchEvent -> {
            onTouchReleasedListen(touchEvent, this);
        });
        this.setOnTouchPressed(touchEvent -> {
            onTouchPressedListen(touchEvent, this);
        });
....
 @Override
    public void onTouchReleasedListen(TouchEvent touchEvent, Node node) {
        try {
            //Holen Sie sich den Schlüssel, den Sie gerade berühren
            final Node targetNode = touchEvent.getTouchPoint().getPickResult().getIntersectedNode();
            if (targetNode == null) {
                return;
            }

            LOG.log("DBG Released Before [{}] getTouchPoint=({}), target=({})", touchEvent.getTouchPoint().getId(), touchEvent.getTouchPoint(), touchEvent.getTarget());
            if (UraKeyboard.class.isInstance(targetNode)) {
                //Führt den freigegebenen Ereignis-Listener für den Schlüssel aus, wenn er sich auf dem Schlüsselobjekt befindet.
                this.touchReleasedKeyboardListen(touchEvent, UraKeyboard.class.cast(targetNode));
            }
            //Suchen Sie erst dann, wenn alle Caches erschöpft sind, alle Tasten erneut und stoppen Sie den Sound.
            this.resetKeyboardNotes(touchEvent);
        } finally {
            touchEvent.consume();
        }
    }
....
    @Override
    public void onTouchPressedListen(TouchEvent touchEvent, Node node) {
        try {
            //Holen Sie sich den Schlüssel, den Sie gerade berühren
            final Node targetNode = touchEvent.getTouchPoint().getPickResult().getIntersectedNode();
            if (targetNode == null) {
                return;
            }

            LOG.log("DBG Pressed Before [{}] getTouchPoint=({}), target=({})", touchEvent.getTouchPoint().getId(), touchEvent.getTouchPoint(), touchEvent.getTarget());
            if (UraKeyboard.class.isInstance(targetNode)) {
                //Führen Sie den Listener für gedrückte Ereignisse für den Schlüssel aus, wenn er sich auf dem Schlüsselobjekt befindet
                this.touchPressedKeyboardListen(touchEvent, UraKeyboard.class.cast(targetNode));
            }
        } finally {
            touchEvent.consume();
        }
    }

Welches Tastenobjekt wird gedrückt?

Das Ereignis wurde in eine höhere Klasse verschoben. Wenn jedoch ein Berührungsereignis auftritt, wie identifizieren Sie die Taste an den Berührungskoordinaten? Obwohl es sich um eine Brute-Force-Technik handelt, gibt es auch eine Methode, um sie durch Round-Robin anhand der Koordinaten zu finden. Obwohl dies nicht die richtige Antwort ist, gab es alternativ einige Mitgliedsvariablen, die das Eltern-Kind-Zielobjekt aus dem Ereignis fanden, das die Klasse javafx.scene.input.TouchEvent berührte. Diesmal verwenden wir unter anderem javafx.scene.input.PickResult, das unter javafx.scene.input.TouchPoint erhältlich ist. PickedResult wird später beschrieben, unterstützt aber auch flexibel das Ereignis onTouchMoved. Selbst wenn sich der Punkt vom ersten gedrückten Tastenobjekt zum oberen Rand des nächsten Tastenobjekts bewegt, wird das Zielobjekt angezeigt.

Berühren Sie beispielsweise die Taste do (60). Anschließend wird das Ereignis KeyboardMain OnTouchPressed ausgeführt. Das Argument von setOnTouchPressed ist TouchEvent, das Ereignisobjekt des Zielpunkts. Das TouchPoint-Objekt im Inneren. Darüber hinaus enthält das von TouchPoint erhaltene PickeResult-Objekt das Schlüsselobjekt des Kindes, auf das Sie abzielen möchten. Verwenden Sie es insbesondere wie touchEvent.getTouchPoint (). GetPickResult (). GetIntersectedNode ().

Schalten Sie danach wie beim letzten Mal einfach Note On ein, um die Zieltaste zu drücken und einen Ton zu erzeugen.

KeyboarddMain.java


    /**
     *Ertönt die Zieltaste und wechselt die Anzeige in die gedrückte Anzeige.
     * @param uraKeyboard
     */
    protected synchronized void noteOn(UraKeyboard uraKeyboard) {
        uraKeyboard.uraReceiver().noteOn(uraKeyboard.note());
        uraKeyboard.noteOn(true);
        uraKeyboard.noteOnView();
    }

    /**
     *Schalten Sie den Ton der Zieltaste stumm und lassen Sie die Anzeige nicht drücken.
     * @param uraKeyboard
     */
    protected synchronized void noteOff(UraKeyboard uraKeyboard) {
        uraKeyboard.uraReceiver().noteOff(uraKeyboard.note());
        uraKeyboard.noteOn(false);
        uraKeyboard.noteOffView();
    }
....
    /**
     *Listener für Gedrückte Ereignisse für Tasten
     * @param touchEvent
     * @param uraKeyboard
     */
    protected void touchPressedKeyboardListen(final TouchEvent touchEvent, final UraKeyboard uraKeyboard) {
     //Klingeln Sie den Zielschlüssel
        this.noteOn(touchEvent.getTouchPoint(), uraKeyboard);
    }

Entspricht dem Bewegungsereignis beim Drücken

Fügen Sie als Nächstes ein TouchMoved-Ereignis hinzu, damit Sie die nächste Taste drücken können, wenn Sie beim Drücken zur nächsten Taste wechseln. Selbst eine kleine Bewegung wird sich entzünden. Für eine nicht verwandte Verarbeitung ist es daher erforderlich, die Verarbeitung so schnell wie möglich zu beenden, damit der Vorgang nicht verlangsamt wird. Wenn der Inhalt von PickedResult beispielsweise kein Schlüsselobjekt ist oder mit dem gedrückten Schlüssel identisch ist, wird der Prozess sofort an den Aufrufer zurückgegeben.

Wenn PickedResult ein anderes (nächstes) Schlüsselobjekt wird, ist es in Ordnung, wenn der gleiche Inhalt, der in "setOnTouchPressed" und "setOnTouchReleased" geschrieben wurde, für ein anderes (nächstes) Schlüsselobjekt ausgeführt wird.

Aber hier ist das Problem.

Ich konnte ein anderes (nächstes) Schlüsselobjekt in einen klingenden Zustand versetzen. Zu diesem Zeitpunkt muss der Ton der ursprünglich klingelnden Taste gleichzeitig in den gestoppten Zustand zurückversetzt werden. TouchEvent kann nicht abrufen, was das vorherige Schlüsselobjekt, das vorherige TouchMoved-Ereignis usw. war.

Ich war beunruhigt.

Verwalten Sie die Zieltaste während der Berührung

Also habe ich versucht, die Schlüsselobjekte zu verwalten, die derzeit für jeden Punkt berührt werden, obwohl dies hier etwas erzwungen ist. Erstellen Sie mit ConcurrentMap <Integer, UraKeyboard> eine administrative Cache-Map und verwalten Sie das Schlüsselobjekt, das bis kurz zuvor gedrückt wurde.

Ich habe es so implementiert

Key Object Management Map


    /**Tastaturobjektkarte gedrückt (ertönt)*/
    protected final ConcurrentMap<Integer, UraKeyboard> noteOnKeyCacheMap = newConcurrentHashMap(10, FACTOR.NONE);
....

        synchronized (noteOnKeyCacheMap) {
            //Synchronisieren Sie für alle Fälle mit der Cache-Zuordnung
            Integer CURRENT_TOUCH_ID = touchEvent.getTouchPoint().getId();
            final UraKeyboard oldNoteOnKey = noteOnKeyCacheMap.get(CURRENT_TOUCH_ID);
....
 
        if (!uraKeyboard.isNoteOn()) {
            LOG.log("DBG Pressed The event already Hover({}), ({}).", uraKeyboard, uraKeyboard);
            this.noteOn(uraKeyboard);
        }
        noteOnKeyCacheMap.putIfAbsent(touchPoint.getId(), uraKeyboard);
....

    /**
     *Wenn sich nicht alle Berührungspunkte auf der Zieltaste befinden, wird der Ton gestoppt. Löschen Sie es auch aus dem Cache.
     * @param touchPoint
     * @param uraKeyboard Zielschlüssel
     */
    protected void noteOff(final TouchPoint touchPoint, final UraKeyboard uraKeyboard) {
        final Integer CURRENT_TOUCH_ID = touchPoint.getId();
        boolean isOtherKeyNoteOn = false;
        for (final Map.Entry<Integer, UraKeyboard> noteOnEntry : noteOnKeyCacheMap.entrySet()) {
            if (CURRENT_TOUCH_ID.equals(noteOnEntry.getKey())) {
                continue;
            }
            if (isOtherKeyNoteOn = uraKeyboard.equals(noteOnEntry.getValue())) {
                //Wenn sich auf der Taste außer dem Ziel ein Berührungspunkt befindet, bleibt der Ton erhalten.
                LOG.log("DBG XXX oldNoteOnKey in map(oldNoteOnKey=[{}]({}), target=[{}]({}))", CURRENT_TOUCH_ID, uraKeyboard, noteOnEntry.getKey(), noteOnEntry.getValue());
                break;
            }
        }
        if (!isOtherKeyNoteOn) {
            //Stoppen Sie den Ton, wenn der Berührungspunkt nicht auf der Zieltaste außer dem Ziel gefunden wird
            LOG.log("DBG Move&Pressed The event did not Hover({}), ({}).", touchPoint, uraKeyboard);
            this.noteOff(uraKeyboard);
        }
        noteOnKeyCacheMap.remove(CURRENT_TOUCH_ID);
        LOG.log("DBG Move DELETE({}), __CACHE__({}).", uraKeyboard, noteOnKeyCacheMap.entrySet());
    }

Nur noteOff ist etwas Besonderes. Dies ist der Punkt, den Sie derzeit haben, denn selbst wenn sich das Ziel außerhalb des Punkts befindet, ertönt es weiterhin, wenn sich andere Punkte auf derselben Taste befinden.

Zusammenfassung

Ich konnte es zu einer fließenden Tastatur verbessern. Bis ich PickedResult zum ersten Mal fand, hatte ich große Probleme mit der Methode des gewaltsamen Abrufs mit Collections.binarySearch. .. .. (Ich bin froh, dass es da war.)

Die Quelle befindet sich ebenfalls unten https://github.com/syany/u-board/tree/0.2.3

Übrigens kann ich es noch nicht in ein Glas schaffen, also kannst du nicht spielen, selbst wenn du es aufhebst! du kannst nicht Ich muss beim nächsten Mal in die Gradle-Aufgabe einsteigen

Nächstes Mal

Das Erscheinungsdatum steht noch nicht fest, aber ich werde ein Pitch Bend & Modulation Ribbon hinzufügen.

Recommended Posts

[JavaFX] Versuchen Sie, ein Software-MIDI-Keyboard zu erstellen. Teil 2 Schieben Sie Ihren Finger, um die Skalierung zu ändern
Versuchen Sie, aus der Kachel des Geographical Institute eine dreidimensionale CS-Zeichnungskachel zu erstellen
Versuchen Sie, einen einfachen Rückruf zu tätigen
Versuchen Sie, einen Iterator zu erstellen, der einen Blick darauf werfen kann
Machen Sie einen Rand links vom TextField
Der Weg zum Erstellen eines Webdienstes (Teil 1)