Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre du Club
    Java.sound.midi pour receptionner, modifier puis envoyé des messages midi - MAO Fruity Loops Akai APC 40
    Bonjour à tous,

    Je suis en train de me chauffer pour un sujet qui me tient à cœur... la musique. Pas n'importe quelle mode de production : la MAO.

    J'utilise actuellement un Akai APC40 (contrôleur Midi) pour contrôler mon logiciel de MAO Fruity Loops.

    L'objet de mon projet est simple. Il est impossible de modifier la configuration de l'APC (le firmware est non modifiable, aucun logiciel d'édition n'est disponible) et le comportement ne correspond pas à l'utilisation que j'aimerais en faire. Bien que je pourrais tout recommencer avec une configuration vide, certains boutons ne seraient pas configurable comme Play, Stop, etc... (la liste et longue). De plus, certains comportements sont très intéressant et j'aimerais les conserver.

    Je me penche donc sur une solution qui ferait office d'intermédiaire entre mon contrôleur et mon logiciel. En gros, mon programme devra être connecté sur les ports midi In et Out du contrôleur, et ils communiquera également avec deux autres ports IN et Out virtuels. Ces derniers seront sélectionnés dans le logiciel de MAO. Ainsi je pourrais laisser passer certains messages et en corriger d'autres.
    L'inspiration de ce projet vient d'un autre projet similaire mais je n'arrive pas à joindre le développeur pour avoir plus d'information sur ses travaux. Je commence donc ma tambouille... en Java.

    Grâce à mes recherches et au travail de certains, j'arrive pour le moment à sélectionner les port In et Out de mon contrôleur et à lire des données provenant des mouvements produits à l'aide des bouton poussoir, potentiomètres rectilignes et rotatifs.

    Comme je ne comprends pas ces valeurs, je suis en train me lancer dans la lecture des signaux midi et la c'est une autre paire de manches.

    Si vous avez des conseils ou des docs / tutos / librairies qui vous aurez déjà servit, je suis preneur!

  2. #2
    Membre du Club
    En fait c'est tout con le MIDI, je baisse ma paire de manches... Et la doc de l'APC est assez claire sur son fonctionnement.

    Après avoir essayé le tuto https://docs.oracle.com/javase/tutor...view-MIDI.html j'ai trouvé une doc concernant le Midi https://docs.oracle.com/javase/8/doc...api/index.html

    Allez, c'est parti pour quelques heures de lecture...

  3. #3
    Membre du Club
    Ca y est, je suis un peu perdu sur deux points...

    J'arrive pour le moment à récupérer les ports midi IN et OUT de l'APC via :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
     
    MidiDevice.Info midiDeviceInfoList[] = javax.sound.midi.MidiSystem.getMidiDeviceInfo();


    Une fois stocké dans un tableau, je boucle/filtre pour récupérer les MidiDeviceInfo IN et OUT de "Akai APC40" via

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
     
    MidiSystem.getMidiDevice(info).getName() == "Akai APC40"


    et un substr() un peu moche pour récupérer le type de connexion Midi que je compare au type de port Midi souhaité

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    String midiType = className.substring(20, className.length() - 6);
    if(midiType .compare('MidiIn')){
    ...
    }


    Ensuite, je récupère les ports d'entrée/sortie que je stock dans des objets via

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
     
    MidiDevice APC40MidiIn = MidiDevice.getMidiDevice(infoIn);
    MidiDevice APC40MidiOut = MidiDevice.getMidiDevice(infoOut);


    Et c'est maintenant que les choses se corses pour moi... J'ai récupérer un code qui fonctionne sur forum mais je le comprends mal. Dans mon Main j'ai ça

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
                apc40.getOutputPort().open(); // Correspond à mon APC40MidiOut.open()
                Receiver myReceiver = new MyReceiver();
                apc40.getOutputPort().getTransmitter().setReceiver(myReceiver);


    La classe MyReceiver :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    import javax.sound.midi.*;
     
    public class MyReceiver implements Receiver {
        Receiver rcvr;
        public MyReceiver() {
            try {
                this.rcvr = MidiSystem.getReceiver();
            } catch (MidiUnavailableException mue) {
                mue.printStackTrace();
            }
        }
     
        @Override
        public void send(MidiMessage message, long timeStamp) {
            byte[] b = message.getMessage();
     
            if (b[0] != (byte)254) {
                System.out.println((0xFF & b[0])+" "+b[1]+ " "+b[2]);
            }
            rcvr.send(message, timeStamp);
        }
     
        @Override
        public void close() {
            rcvr.close();
        }
    }


    1/ A aucun moment je me sers de send pourtant le code marche... Je reçois bien les trois bytes du message Midi... J'ai loupé un truc, en même temps il est tard : j'y verrais peut-être mieux demain

    2 / Je ne comprends pas vraiment le rôle des Transmitter et des Receiver

  4. #4
    Membre du Club
    Finalement j'arrive à avancer. Je viens de comprendre quelques trucs et bidules en recommençant un nouveau POC dont voici le code (pour les intéressés).

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
     
    package POC;
     
    import javax.sound.midi.*;
     
    public class Main {
     
        public static void main(String[] args) throws MidiUnavailableException, InvalidMidiDataException {
     
            MidiDevice.Info[] tableauInfo = MidiSystem.getMidiDeviceInfo();
     
            MidiDevice apcOut;
            MidiDevice apcIn;
     
            apcIn = getDevice("Akai APC40", "MidiOut");
            apcIn.open();
     
    //        System.out.println(apcIn.getMaxTransmitters()); 0
    //        System.out.println(apcIn.getMaxReceivers()); -1
     
     
            apcOut = getDevice("Akai APC40", "MidiIn");
            apcOut.open();
     
    //        System.out.println(apcOut.getMaxTransmitters()); -1
    //        System.out.println(apcOut.getMaxReceivers()); 0
     
            ApcReceiver myApcReceiver = new ApcReceiver(apcIn);
            apcOut.getTransmitter().setReceiver(myApcReceiver);
     
        }
     
        /**
         * name = Akai APC40 || VAkai APC40 In ou Vakai APC40 Out
         * className = MidiOutDeviceInfo ou MidiInDeviceInfo || autre
         * @param name
         * @param className
         * @return
         */
        public static MidiDevice getDevice(String name, String className) throws MidiUnavailableException {
            MidiDevice returnedMidiDevice = null;
            MidiDevice.Info[] tableauInfo = MidiSystem.getMidiDeviceInfo();
     
            for (MidiDevice.Info info : tableauInfo) {
     
                System.out.println(info);
     
                if(info.getName().compareTo(name) == 0 && info.getClass().getSimpleName().contains(className)){
                    returnedMidiDevice = MidiSystem.getMidiDevice(info);
                }
            }
            return returnedMidiDevice;
        }
    }
     
    class ApcReceiver implements Receiver{
     
        MidiDevice apcIn;
        ShortMessage midiMessage = new ShortMessage();
     
        public ApcReceiver(MidiDevice apcIn){
            this.apcIn = apcIn;
        }
     
        @Override
        public void send(MidiMessage message, long timeStamp) {
            byte[] b = message.getMessage();
     
            if (b[0] != (byte)254) {
                System.out.println((0xFF & b[0])+" "+b[1]+ " "+b[2]);
            }
     
            /* Créer le filtre permettant de modifier les messages Midi */
     
            try {
                midiMessage.setMessage((0xFF & b[0]), b[1]+4, b[2]);
                apcIn.getReceiver().send(midiMessage, 1);
            } catch (InvalidMidiDataException e) {
                e.printStackTrace();
            } catch (MidiUnavailableException e) {
                e.printStackTrace();
            }
     
        }
     
        @Override
        public void close() {
     
        }
    }


    Dans ce code, j'arrive à capter les messages midi envoyés par un bouton et à assigner la valeur à un autre bouton (les deux sont chainés en quelques sortes). Je vois le fonctionnement car les boutons sont des potentiomètres infini à LED...
    Next soon

  5. #5
    Modérateur

    Il y'a quelques années j'ai écrit un soft pour l'electribe EMX-1, en full midi (http://renaud.warnotte.be/electribulator), je compte refaire une truc pour une autre machine, un jour.

    Je vais un peu regarder ton sujet, histoire de voir son évolution et si je puis aider.

    Edit : Sinon midi ox tu connais ? http://www.midiox.com
    (Les "ça ne marche pas", même écrits sans faute(s), vous porteront discrédit ad vitam æternam et malheur pendant 7 ans)

    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  6. #6
    Membre du Club
    Salut Wax78!

    Ah ben je commence par un grand merci pour electribulator! C'est une belle contribution qui a du servir à pas mal de monde (dont moi )

    Oui je connais "Midi OX" mais je cherchais à créer une solution qui se met en place facilement et rapidement sur un ordinateurs en faisant le moins d’installations possible, pourquoi pas d'ailleurs émuler les ports Midi virtuels par la suite... et puis faut bien avouer que je cherchais à faire du Java aussi ^^

    En tout cas, merci pour ton message et pour l'intérêt que tu portes à ce projet

    Après dis jours d'arrêt, je m'y remets

  7. #7
    Membre du Club
    Je suis en train de réfléchir au fonctionnalités et plus précisément à la conversion de message Midi.

    Je pensais faire un tableau bidimensionnel contenant en clé les messages midi de l'APC40 et en valeur les messages Midi correspondant à mon APC40 Virtuel.

    Plusieurs classes permettraient de faire ça comme Hashtable par exemple. Elles ont presque toute une méthode get(clé) qui retourne une valeur mais pas de get(valeur) pour récupérer la clé...

    Il faut implémenter Hashtable? Est-ce que vous connaissez une classe qui correspondrait à ce que je recherche?

  8. #8
    Modérateur

    En fait tu peux obtenir la (ou les) clés en fonction de la valeur en parcourant toutes les entrées.

    Genre :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    public class Snippet
    {
     
    	/**
             * @throws InterruptedException
             * @throws IOException
             */
    	public static void main(String[] args)
    	{
    		Hashtable table = new Hashtable();
    		table.put("Sony", "Bravia");
    		table.put("Samsung", "Galaxy");
    		table.put("Noki1", "Lumia");
    		table.put("Nokia", "Lumia");
     
    		Stream keys = keys(table, "Lumia");
    		Object[] l = keys.toArray();
     
    		for (int i = 0; i < l.length; i++)
    		{
    			System.err.println("" + l[i]);
    		}
    	}
     
    	public static <K, V> Stream<K> keys(Map<K, V> map, V value)
    	{
    		return map.entrySet().stream().filter(entry -> value.equals(entry.getValue())).map(Map.Entry::getKey);
    	}
     
    }


    (ou a l'ancienne avec une boucle for, sans stream())

    Une autre solution est de creer une deuxieme map

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    Hashtable table_cle_valeur = new Hashtable();
    table_cle_valeur.put("Sony", "Bravia");
    table_cle_valeur.put("Samsung", "Galaxy");
    table_cle_valeur.put("Noki1", "Lumia");
    table_cle_valeur.put("Nokia", "Lumia");
     
    Hashtable table_valeur_cle = new Hashtable();
    table_valeur_cle.put("Bravia", "Sony");
    table_valeur_cle.put("Galaxy", "Samsung");
    table_valeur_cle.put("Lumia", "Noki1");
    table_valeur_cle.put("Lumia", "Nokia");


    comme ça tu px recuperer la valeur par la clé et vice versa sans faire de boucle qui vont ralentir tout.
    (Les "ça ne marche pas", même écrits sans faute(s), vous porteront discrédit ad vitam æternam et malheur pendant 7 ans)

    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  9. #9
    Membre du Club
    Merci pour tes réponses Wax78!

    Je suis parti sur la deuxième solution du coup

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
     
    package POC;
     
    import java.util.HashMap;
    import java.util.Map;
     
    public class MidiConversion {
     
        private HashMap<Integer, Integer> MaterialToVirtual = new HashMap();
        private HashMap<Integer, Integer> VirtualToMaterial = new HashMap();
     
        public MidiConversion(){
     
            // Todo: Récupération du fichier de conversion des messages Midi
     
            // Pour les boutons "Scene Lauch"
            MaterialToVirtual.put(144,151); // On
            MaterialToVirtual.put(128,135); // Off
     
            MaterialToVirtual.put(145,150);
            MaterialToVirtual.put(129,134);
     
            MaterialToVirtual.put(146,149);
            MaterialToVirtual.put(130,133);
     
            MaterialToVirtual.put(147,148);
            MaterialToVirtual.put(131,132);
     
            MaterialToVirtual.put(148,147);
            MaterialToVirtual.put(132,131);
     
            MaterialToVirtual.put(149,146);
            MaterialToVirtual.put(133,130);
     
            MaterialToVirtual.put(150,145);
            MaterialToVirtual.put(134,129);
     
            MaterialToVirtual.put(151,144);
            MaterialToVirtual.put(135,128);
     
            // Bouger la vue dans la Playlist
            MaterialToVirtual.put(96,97);
            MaterialToVirtual.put(97,96);
     
            this.initHashMapVirtualToMaterial();
        }
     
        /**
         * Initialisation de la table renversée
         */
        private void initHashMapVirtualToMaterial(){
            VirtualToMaterial.clear();
            for (Map.Entry<Integer, Integer> entry: MaterialToVirtual.entrySet()) {
                VirtualToMaterial.put(entry.getValue(), entry.getKey());
            }
        }
     
        /**
         * Converti un message en provenance du controleur midi physique vers le controleur midi virtuel
         * @param key message midi
         * @return messagemidi
         */
        public Integer convertFromMaterialToVirtual(Integer key){
            return MaterialToVirtual.get(key);
        }
     
        /**
         * Converti un message en provenance du controleur midi virtuel vers le controleur midi physique
         * @param key
         * @return
         */
        public Integer convertFromVirtualToMaterial(Integer key){
            return VirtualToMaterial.get(key);
        }
     
        /**
         * Test l'existance d'un clé du HashMap MaterialToVirtual
         * @param key
         * @return
         */
        public boolean isAMaterialKey(Integer key){
            return MaterialToVirtual.containsKey(key);
        }
     
        /**
         * Test l'existance d'un clé du HashMap VirtualToMaterial
         * @param key
         * @return
         */
        public boolean isAVirtualKey(Integer key){
            return VirtualToMaterial.containsKey(key);
        }
    }

###raw>template_hook.ano_emploi###