[JavaFX] Essayez de créer un clavier MIDI logiciel Partie 2 Faites glisser votre doigt pour changer l'échelle

OpenningKey04.png

Vous pouvez déplacer le vrai piano sur la touche suivante tout en la maintenant enfoncée

Le clavier créé la première fois implémentait un écouteur d'événements pour chaque touche. pour cette raison

Le problème s'est posé. Cette fois, nous améliorerons ce processus afin qu'il puisse s'exprimer davantage comme un vrai piano. Je décrirai également comment obtenir des nœuds enfants à partir du nœud parent.

Flux de solution

Nous allons le résoudre étape par étape. Le premier est le mouvement de l'écouteur d'événement créé la dernière fois.

Déplacer TouchEvents vers le nœud parent

Déplacez les "setOnTouchPressed" et "setOnTouchReleased" créés dans la classe de clé la dernière fois vers le nœud parent. Le parent immédiat est une classe de disposition Notes qui hérite d'AnchorPane, mais compte tenu de l'avenir, nous la déplacerons vers la classe de disposition KeyboardMain supérieure.

Ici, une touche a une note et un récepteur (UraReceiver), et le processus de son et de changement de style est effectué par chaque touche, et uniquement par l'écouteur d'événements qui exécute le processus. J'ai décidé de le transférer chez mes parents.

À l'origine, il a été créé de manière dynamique au lieu de xml, ce type de mouvement était donc étonnamment facile.

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 {
            //Obtenez la clé que vous touchez actuellement
            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)) {
                //Exécute l'écouteur d'événement libéré pour la clé s'il se trouve sur l'objet clé.
                this.touchReleasedKeyboardListen(touchEvent, UraKeyboard.class.cast(targetNode));
            }
            //Seulement lorsque tous les caches sont épuisés, recherchez à nouveau toutes les clés et arrêtez le son.
            this.resetKeyboardNotes(touchEvent);
        } finally {
            touchEvent.consume();
        }
    }
....
    @Override
    public void onTouchPressedListen(TouchEvent touchEvent, Node node) {
        try {
            //Obtenez la clé que vous touchez actuellement
            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)) {
                //Exécuter l'écouteur d'événement Pressed pour la clé s'il se trouve sur l'objet clé
                this.touchPressedKeyboardListen(touchEvent, UraKeyboard.class.cast(targetNode));
            }
        } finally {
            touchEvent.consume();
        }
    }

Quel objet clé est enfoncé

L'événement est passé à une classe supérieure, mais lorsqu'un événement tactile se produit, comment identifiez-vous la clé sur les coordonnées en contact? Bien qu'il s'agisse d'une technique de force brute, il existe également une méthode pour la trouver par rond-point à partir des coordonnées. Sinon, bien que ce ne soit pas la bonne réponse, certaines variables membres permettaient de trouver l'objet parent-enfant cible à partir de l'événement qui a touché la classe javafx.scene.input.TouchEvent. Parmi eux, cette fois nous utiliserons javafx.scene.input.PickResult qui peut être obtenu à partir de javafx.scene.input.TouchPoint. PickedResult sera décrit plus tard, mais il prend également en charge l'événement onTouchMoved de manière flexible, et même si le point se déplace du premier objet clé enfoncé au sommet de l'objet clé suivant, il affichera l'objet de destination.

Par exemple, appuyez sur la touche do (60). Ensuite, l'événement KeyboardMain OnTouchPressed sera exécuté. L'argument de setOnTouchPressed est TouchEvent, qui est l'objet événement du point cible. L'objet TouchPoint à l'intérieur. En outre, l'objet PickeResult obtenu à partir de TouchPoint contient l'objet clé de l'enfant que vous souhaitez cibler. Plus précisément, utilisez-le comme touchEvent.getTouchPoint (). GetPickResult (). GetIntersectedNode ().

Après cela, comme la dernière fois, appuyez simplement sur Note On pour appuyer sur la touche cible et émettre un son.

KeyboarddMain.java


    /**
     *Sonne la touche cible et fait passer l'affichage à l'affichage enfoncé.
     * @param uraKeyboard
     */
    protected synchronized void noteOn(UraKeyboard uraKeyboard) {
        uraKeyboard.uraReceiver().noteOn(uraKeyboard.note());
        uraKeyboard.noteOn(true);
        uraKeyboard.noteOnView();
    }

    /**
     *Désactivez le son de la touche cible et désactivez l'affichage.
     * @param uraKeyboard
     */
    protected synchronized void noteOff(UraKeyboard uraKeyboard) {
        uraKeyboard.uraReceiver().noteOff(uraKeyboard.note());
        uraKeyboard.noteOn(false);
        uraKeyboard.noteOffView();
    }
....
    /**
     *Listener for Pressed events for keys
     * @param touchEvent
     * @param uraKeyboard
     */
    protected void touchPressedKeyboardListen(final TouchEvent touchEvent, final UraKeyboard uraKeyboard) {
     //Sonnez la clé cible
        this.noteOn(touchEvent.getTouchPoint(), uraKeyboard);
    }

Correspond à l'événement de mouvement en appuyant sur

Ensuite, ajoutez un événement TouchMoved afin de pouvoir appuyer sur la touche suivante lorsque vous passez à la touche suivante tout en appuyant sur. Même un petit mouvement s'enflamme, donc pour un traitement non pertinent, il est nécessaire de terminer le traitement dès que possible afin que l'opération ne ralentisse pas. Par exemple, si le contenu de PickedResult n'est pas un objet clé ou s'il est identique à la touche enfoncée, le processus est immédiatement renvoyé à l'appelant.

Lorsque PickedResult devient un autre objet clé (suivant), il est OK si les mêmes contenus écrits dans "setOnTouchPressed" et "setOnTouchReleased" sont exécutés pour un autre objet clé (suivant).

Mais voici le problème.

J'ai pu mettre un autre objet clé (suivant) dans un état sonore, À ce moment, le son de la touche qui sonnait à l'origine doit être remis à l'état arrêté en même temps. TouchEvent ne peut pas obtenir ce qu'était l'objet clé précédent, l'événement TouchMoved précédent, etc.

J'étais troublé.

Gérer la touche cible pendant le toucher

Du coup, j'ai essayé de gérer les objets clés qui sont actuellement touchés pour chaque point, même si c'est un peu forcé ici. Créez un mappage de cache administratif avec ConcurrentMap <Integer, UraKeyboard> et gérez l'objet clé qui a été pressé juste avant.

--Ajouter lorsque vous appuyez sur --Supprimer lorsque vous partez

Je l'ai implémenté pour que

Carte de gestion des objets clés


    /**Carte des objets du clavier appuyée*/
    protected final ConcurrentMap<Integer, UraKeyboard> noteOnKeyCacheMap = newConcurrentHashMap(10, FACTOR.NONE);
....

        synchronized (noteOnKeyCacheMap) {
            //Synchronisez avec la carte de cache au cas où
            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);
....

    /**
     *Si tous les points tactiles ne sont pas sur la touche cible, le son est arrêté. Supprimez-le également du cache.
     * @param touchPoint
     * @param uraKeyboard Target key
     */
    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())) {
                //S'il y a un point tactile sur la touche autre que la cible, continuez à entendre le son.
                LOG.log("DBG XXX oldNoteOnKey in map(oldNoteOnKey=[{}]({}), target=[{}]({}))", CURRENT_TOUCH_ID, uraKeyboard, noteOnEntry.getKey(), noteOnEntry.getValue());
                break;
            }
        }
        if (!isOtherKeyNoteOn) {
            //Arrêtez le son si le point tactile n'est pas trouvé sur la touche cible autre que la cible
            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());
    }

Seul noteOff est spécial. C'est le point que vous avez actuellement, car même si la cible est hors du point, elle continuera de sonner si d'autres points sont sur la même touche.

Résumé

J'ai pu l'améliorer en un clavier fluide. Jusqu'à ce que je trouve le PickedResult pour la première fois, j'ai eu beaucoup de mal à utiliser la méthode de récupération forcée à l'aide de Collections.binarySearch. .. .. (Je suis content que ce soit là.)

La source est également placée ci-dessous https://github.com/syany/u-board/tree/0.2.3

Au fait, je ne peux pas en faire un pot pour l'instant, donc même si vous le prenez, vous ne pouvez pas jouer! vous ne pouvez pas Je dois me lancer dans la tâche Gradle la prochaine fois

La prochaine fois

La date de sortie est indécise, mais j'ajouterai un ruban de pitch bend et de modulation.

Recommended Posts

[JavaFX] Essayez de créer un clavier MIDI logiciel Partie 2 Faites glisser votre doigt pour changer l'échelle
Essayez de créer une tuile de dessin en trois dimensions CS à partir de la tuile Institut géographique
Essayez de faire un simple rappel
Essayez de créer un itérateur qui puisse être vu
Faire une marge à gauche du TextField
La voie de la création d'un service Web (partie 1)