web-dev-qa-db-fra.com

Android 6.0 (Marshmallow): Comment jouer aux notes midi?

Je crée une application qui génère des sons d'instruments en direct et je prévois d'utiliser la nouvelle API Midi proposée dans Android Marshmallow (version 6.0). J'ai lu le document de présentation du paquet ici http://developer.Android.com/reference/Android/media/midi/package-summary.html } _ et je sais comment générer des notes Midi, mais je suis toujours incertain: comment puis-je jouer ces notes après avoir généré leurs données Midi?

Ai-je besoin d'un programme de synthétiseur pour jouer des notes midi? Si oui, dois-je créer le mien ou est-ce un logiciel fourni par Android ou une tierce partie?

Je suis un novice avec Midi alors soyez le plus descriptif possible avec votre réponse.

Ce que j'ai essayé jusqu'à présent: J'ai créé un objet de gestion Midi et ouvert un port d'entrée

MidiManager m = (MidiManager)context.getSystemService(Context.MIDI_SERVICE); 
MidiInputPort inputPort = device.openInputPort(index);

Ensuite, j'ai envoyé un message de test noteOn au port

byte[] buffer = new byte[32];
int numBytes = 0;
int channel = 3; // MIDI channels 1-16 are encoded as 0-15.
buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
buffer[numBytes++] = (byte)60; // pitch is middle C
buffer[numBytes++] = (byte)127; // max velocity
int offset = 0;
// post is non-blocking
inputPort.send(buffer, offset, numBytes);

J'ai également mis en place une classe pour recevoir les messages de note midi

class MyReceiver extends MidiReceiver {
    public void onSend(byte[] data, int offset,
            int count, long timestamp) throws IOException {
        // parse MIDI or whatever
    }
}
MidiOutputPort outputPort = device.openOutputPort(index);
outputPort.connect(new MyReceiver());

Maintenant, voici où je suis le plus confus. Le cas d'utilisation de mon application est d'être un outil de composition et de lecture tout-en-un pour la création de musique. En d'autres termes, mon application doit contenir ou utiliser un périphérique MIDI virtuel (comme l'intention du synthétiseur midi d'une autre application). À moins que quelqu'un ait déjà créé un tel synthétiseur, je dois en créer un moi-même au cours du cycle de vie de mon application. Comment puis-je convertir une note MIDI reçue () en son provenant de mes haut-parleurs? Je suis particulièrement troublé car il doit également exister un moyen de décider par programme de quel type d’instrument la note doit ressembler: est-ce également le cas dans un synthétiseur?

La prise en charge Midi dans Android Marshmallow est relativement nouvelle et je n’ai donc pas pu trouver de tutoriels ni d’exemples d’applications de synthétiseur en ligne. Toute idée est appréciée.

10
Cody

Je n'ai trouvé aucun moyen "officiel" de contrôler le synthétiseur interne à partir de code Java.

La solution la plus simple consiste probablement à utiliser le pilote MIDI Android du synthétiseur Sonivox

Obtenez-le en tant que package AAR (décompressez le fichier * .Zip) et stockez le fichier * .aar quelque part dans votre espace de travail. Le chemin n'a pas vraiment d'importance et il n'a pas besoin d'être dans la structure de dossiers de votre propre application, mais le dossier "libs" dans votre projet pourrait être un endroit logique.

Avec votre projet Android ouvert dans Android Studio:

Fichier -> Nouveau -> Nouveau module -> Importer le paquet .JAR/.AAR -> Suivant -> Trouver Et sélectionner "MidiDriver-all-release.aar" et changer le nom du sous-projet . si tu veux. -> Terminer

Attendez que Gradle fasse son travail magique, puis allez dans les paramètres de votre module "application" (paramètres de votre propre projet d'application) dans l'onglet "Dépendances" et ajoutez (avec le signe vert "+") le pilote MIDI en tant que dépendance du module. Vous avez maintenant accès au pilote MIDI:

import org.billthefarmer.mididriver.MidiDriver;
   ...
MidiDriver midiDriver = new MidiDriver();

Sans avoir à vous soucier de NDK et de C++, vous disposez des méthodes Java suivantes:

// Not really necessary. Receives a callback when/if start() has succeeded.
midiDriver.setOnMidiStartListener(listener);
// Starts the driver.
midiDriver.start();
// Receives the driver's config info.
midiDriver.config();
// Stops the driver.
midiDriver.stop();
// Just calls write().
midiDriver.queueEvent(event);
// Sends a MIDI event to the synthesizer.
midiDriver.write(event);

Une "preuve de concept" très basique pour jouer et arrêter une note pourrait être quelque chose comme:

package com.example.miditest;

import Android.os.Bundle;
import Android.support.v7.app.AppCompatActivity;
import Android.util.Log;
import Android.view.MotionEvent;
import Android.view.View;
import Android.widget.Button;

import org.billthefarmer.mididriver.MidiDriver;

public class MainActivity extends AppCompatActivity implements MidiDriver.OnMidiStartListener,
        View.OnTouchListener {

    private MidiDriver midiDriver;
    private byte[] event;
    private int[] config;
    private Button buttonPlayNote;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        buttonPlayNote = (Button)findViewById(R.id.buttonPlayNote);
        buttonPlayNote.setOnTouchListener(this);

        // Instantiate the driver.
        midiDriver = new MidiDriver();
        // Set the listener.
        midiDriver.setOnMidiStartListener(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        midiDriver.start();

        // Get the configuration.
        config = midiDriver.config();

        // Print out the details.
        Log.d(this.getClass().getName(), "maxVoices: " + config[0]);
        Log.d(this.getClass().getName(), "numChannels: " + config[1]);
        Log.d(this.getClass().getName(), "sampleRate: " + config[2]);
        Log.d(this.getClass().getName(), "mixBufferSize: " + config[3]);
    }

    @Override
    protected void onPause() {
        super.onPause();
        midiDriver.stop();
    }

    @Override
    public void onMidiStart() {
        Log.d(this.getClass().getName(), "onMidiStart()");
    }

    private void playNote() {

        // Construct a note ON message for the middle C at maximum velocity on channel 1:
        event = new byte[3];
        event[0] = (byte) (0x90 | 0x00);  // 0x90 = note On, 0x00 = channel 1
        event[1] = (byte) 0x3C;  // 0x3C = middle C
        event[2] = (byte) 0x7F;  // 0x7F = the maximum velocity (127)

        // Internally this just calls write() and can be considered obsoleted:
        //midiDriver.queueEvent(event);

        // Send the MIDI event to the synthesizer.
        midiDriver.write(event);

    }

    private void stopNote() {

        // Construct a note OFF message for the middle C at minimum velocity on channel 1:
        event = new byte[3];
        event[0] = (byte) (0x80 | 0x00);  // 0x80 = note Off, 0x00 = channel 1
        event[1] = (byte) 0x3C;  // 0x3C = middle C
        event[2] = (byte) 0x00;  // 0x00 = the minimum velocity (0)

        // Send the MIDI event to the synthesizer.
        midiDriver.write(event);

    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        Log.d(this.getClass().getName(), "Motion event: " + event);

        if (v.getId() == R.id.buttonPlayNote) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                Log.d(this.getClass().getName(), "MotionEvent.ACTION_DOWN");
                playNote();
            }
            if (event.getAction() == MotionEvent.ACTION_UP) {
                Log.d(this.getClass().getName(), "MotionEvent.ACTION_UP");
                stopNote();
            }
        }

        return false;
    }
}

Le fichier de présentation n'a qu'un bouton qui joue la note prédéfinie lorsqu'il est maintenu enfoncé et l'arrête lorsqu'il est relâché

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:paddingBottom="@dimen/activity_vertical_margin"
    Android:paddingLeft="@dimen/activity_horizontal_margin"
    Android:paddingRight="@dimen/activity_horizontal_margin"
    Android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.miditest.MainActivity"
    Android:orientation="vertical">

    <Button
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Play a note"
        Android:id="@+id/buttonPlayNote" />
</LinearLayout>

C'est aussi simple que cela. Le code ci-dessus pourrait bien être un point de départ pour une application pour piano tactile avec 128 instruments sélectionnables, une latence très convenable et une fonctionnalité appropriée "note off" qui manque à de nombreuses applications.

En ce qui concerne le choix de l’instrument: il vous suffira d’envoyer un message MIDI "changement de programme" au canal sur lequel vous souhaitez jouer pour choisir l’un des 128 sons du jeu de sons Général MIDI. Mais cela est lié aux détails de MIDI et non à l'utilisation de la bibliothèque. 

De même, vous voudrez probablement faire abstraction des détails de bas niveau de MIDI afin de pouvoir facilement jouer une note spécifique sur un canal spécifique avec un instrument spécifique à une vélocité spécifique pour une heure et pour laquelle vous pourriez trouver quelques indices de toutes les applications et bibliothèques liées open source Java et MIDI réalisées jusqu'à présent.

Soit dit en passant, cette approche ne nécessite pas Android 6.0. Et pour le moment seulement 4,6% des appareils qui utilisent le Play Store utilisent Android 6.x , votre application n’aura donc pas beaucoup d’audience. 

Bien sûr, si vous souhaitez utiliser le package Android.media.midi, vous pouvez ensuite utiliser la bibliothèque pour implémenter un Android.media.midi.MidiReceiver afin de recevoir les événements MIDI et de les lire sur le synthétiseur interne. Google a déjà un peu de code de démonstration qui joue des notes avec des ondes carrées et sciées . Il suffit de remplacer cela par le synthétiseur interne.

Certaines autres options pourraient être de vérifier l'état du port de FluidSynth sur Android. Je suppose qu'il y a peut-être quelque chose de disponible.

Edit: Autres bibliothèques potentiellement intéressantes:

18
Markus Kauppinen

Ai-je besoin d'un programme de synthétiseur pour jouer des notes midi? Si oui, dois-je créer le mien ou est-ce un logiciel fourni par Android ou une tierce partie?

Non, heureusement, vous n'avez pas besoin de créer votre propre synthétiseur. Android en a déjà un: le synthétiseur audio intégré SONiVOX. États Android dans les docs de SONiVOX JETCreator :

JET fonctionne conjointement avec le synthétiseur audio intégré (EAS) de SONiVOX, qui est le périphérique de lecture MIDI pour Android.

Il n'était pas clair si vous souhaitiez ou non une lecture en temps réel, ou si vous vouliez créer une composition d'abord et la lire plus tard dans la même application. Vous indiquez également que vous voulez jouer des notes midi, pas des fichiers. Mais, pour votre information, la lecture Midi est prise en charge sur les appareils Android . Ainsi, la lecture d'un fichier .mid doit s'effectuer de la même manière que vous le feriez avec un fichier .wav avec MediaPlayer .

Pour être honnête, je n'ai pas utilisé le paquet midi, ni fait de lecture midi, mais si vous pouvez créer un fichier .mid et l'enregistrer sur le disque, vous devriez pouvoir le lire en utilisant straight MediaPlayer.

Maintenant, si vous voulez jouer des fichiers midi notes , non droits, vous pouvez utiliser this mididriver package . En utilisant ce paquet, vous devriez pouvoir écrire des données MIDI sur le synthétiseur intégré:

/**
* Writes midi data to the Sonivox synthesizer. 
* The length of the array should be the exact length 
* of the message or messages. Returns true on success, 
* false on failure.
*/

boolean write(byte buffer[])  

Si vous voulez aller encore plus bas, vous pouvez même jouer directement en PCM en utilisant AudioTrack .

Pour plus d'informations, voici un article blog que j'ai trouvé chez quelqu'un qui semblait avoir des problèmes similaires aux vôtres. Il déclare:

Personnellement, j'ai résolu le problème de génération MIDI dynamique comme suit: générer un fichier MIDI par programme, l'écrire dans la mémoire de stockage de l'appareil, lancer un lecteur multimédia avec le fichier et le laisser jouer. C'est assez rapide si vous avez juste besoin de jouer un son MIDI dynamique. Je doute que cela soit utile pour créer des éléments MIDI contrôlés par l’utilisateur, tels que des séquenceurs, mais c’est génial pour d’autres.

J'espère que j'ai tout couvert.

4
CaptJak

Pour générer du son à l'aide de l'API MIDI d'Android, vous avez besoin d'une application de synthèse qui accepte l'entrée MIDI. Malheureusement, c'est la seule application de ce type que j'ai trouvée sur Google Play: https://play.google.com/store/apps/details?id=com.mobileer.midisynthexample

J'ai pu jouer de la musique en envoyant des notes et des messages inactifs à cette application. Mais le changement de programme a mal fonctionné. À moins que mon code ne soit erroné, il semble que l'application ne comporte que deux instruments.

Cependant, il y a des gars qui travaillent sur d'autres applications de synthétiseur, je m'attends donc à ce que d'autres applications soient bientôt disponibles. Cette application semble prometteuse, même si je ne l'ai pas encore testée: https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fpedrolcl%2Fandroid% 2Ftree% 2Fmaster% 2FNativeGMSynth & sa = D & sntz = 1 & usg = AFQjCNHd-V7MKZQqOxKJwFcwNjP4c-kysA

0
AndroidHobby