it-swarm.com.de

Android 6.0 (Marshmallow): Wie spielt man MIDI-Noten?

Ich erstelle eine App, die Live-Instrumentenklänge erzeugt, und plane die Verwendung der neuen Midi-API von Android Marshmallow (Version 6.0). Ich habe das Dokument mit der Paketübersicht hier gelesen http://developer.Android.com/reference/Android/media/midi/package-summary.html und weiß, wie man MIDI-Noten generiert, bin mir aber immer noch nicht sicher : Wie spiele ich diese Noten, nachdem ich ihre Midi-Daten erzeugt habe?

Benötige ich ein Synthesizer-Programm, um MIDI-Noten zu spielen? Wenn ja, muss ich meine eigene erstellen oder wird eine von Android oder einem Drittanbieter bereitgestellt?

Ich bin ein Anfänger bei Midi, bitte beschreibe deine Antwort so genau wie möglich.

Was ich bisher versucht habe: Ich habe ein Midi-Manager-Objekt erstellt und einen Eingabeport geöffnet

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

Dann habe ich eine Testnotiz auf Midi-Nachricht an den Port gesendet

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);

Ich habe auch eine Klasse eingerichtet, um die Midi-Note-Nachrichten zu empfangen

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());

Hier bin ich am verwirrtesten. Der Anwendungsfall meiner App ist es, ein All-in-One-Kompositions- und Wiedergabetool zum Musizieren zu sein. Mit anderen Worten, meine App muss ein virtuelles Midi-Gerät enthalten oder verwenden (wie eine Absicht des Midi-Synthesizers einer anderen App). Sofern nicht bereits jemand einen solchen Synthesizer erstellt hat, muss ich selbst einen innerhalb des Lebenszyklus meiner App erstellen. Wie wandle ich ein empfangenes MIDI-NoteOn () tatsächlich in Sound um, der aus meinen Lautsprechern kommt? Ich bin besonders verwirrt, weil es auch eine Möglichkeit geben muss, programmgesteuert zu entscheiden, von welcher Art von Instrument die Note kommt: Wird dies auch in einem Synthesizer durchgeführt?

Midi-Unterstützung in Android Marshmallow ist ziemlich neu, so dass ich keine Tutorials oder Beispielsynthesizer-Apps online finden konnte. Jeder Einblick wird geschätzt.

10
Cody

Ich habe keine "offizielle" Möglichkeit gefunden, den internen Synthesizer über Java-Code zu steuern.

Die wahrscheinlich einfachste Möglichkeit ist die Verwendung des Android-Midi-Treibers für den Sonivox-Synthesizer .

Holen Sie es als AAR-Paket (entpacken Sie die * .Zip-Datei) und speichern Sie die * .aar-Datei irgendwo in Ihrem Arbeitsbereich. Der Pfad spielt keine Rolle und muss sich nicht in der Ordnerstruktur Ihrer eigenen App befinden, aber der Ordner "libs" in Ihrem Projekt könnte ein logischer Ort sein.

Öffnen Sie Ihr Android-Projekt in Android Studio:

Datei -> Neu -> Neues Modul -> .JAR/.AAR-Paket importieren -> Weiter -> Suchen Sie die Datei "MidiDriver-all-release.aar" und wählen Sie sie aus. Ändern Sie gegebenenfalls den Namen des Unterprojekts. -> Fertig stellen

Warten Sie, bis Gradle es magisch macht, und wechseln Sie dann zu den Einstellungen Ihres "App" -Moduls (den Einstellungen Ihres eigenen App-Projekts) zur Registerkarte "Abhängigkeiten", und fügen Sie (mit dem grünen "+" - Zeichen) den MIDI - Treiber als hinzu Modulabhängigkeit. Jetzt haben Sie Zugriff auf den MIDI - Treiber:

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

Ohne sich um NDK und C++ kümmern zu müssen, stehen Ihnen folgende Java-Methoden zur Verfügung:

// 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);

Ein sehr grundlegender "Proof of Concept" zum Spielen und Stoppen einer Note könnte etwa sein:

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;
    }
}

Die Layout-Datei verfügt nur über eine Schaltfläche, die die vordefinierte Note bei gedrückter Taste wiedergibt und beim Loslassen stoppt:

<?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>

Es ist eigentlich so einfach. Der obige Code könnte ein Ausgangspunkt für eine Touch-Piano-App mit 128 auswählbaren Instrumenten, sehr guter Latenz und einer angemessenen Note-Off-Funktionalität sein, die vielen Apps fehlt.

Zur Auswahl des Instruments: Sie müssen lediglich eine MIDI "Program Change" -Nachricht an den Kanal senden, auf dem Sie spielen möchten, um einen der 128 Sounds im Soundset "General MIDI" auszuwählen. Dies hängt jedoch mit den Details von MIDI zusammen und nicht mit der Verwendung der Bibliothek.

Ebenso möchten Sie wahrscheinlich die Details auf niedriger Ebene von MIDI abstrahieren, damit Sie auf einfache Weise eine bestimmte Note auf einem bestimmten Kanal mit einem bestimmten Instrument mit einer bestimmten Anschlagstärke für eine bestimmte Zeit und für das, was Sie möglicherweise finden, spielen können Einige Hinweise aus allen bisher erstellten Open-Source-Java- und MIDI -bezogenen Anwendungen und Bibliotheken.

Für diesen Ansatz ist übrigens Android 6.0 nicht erforderlich. Und im Moment verwenden nur 4,6% der Geräte, die den Play Store besuchen, Android 6.x , sodass für Ihre App nicht viele Nutzer zur Verfügung stehen.

Wenn Sie das Android.media.midi-Paket verwenden möchten, können Sie in der Bibliothek natürlich einen Android.media.midi.MidiReceiver implementieren, um die MIDI -Ereignisse zu empfangen und auf dem internen Synthesizer abzuspielen. Google hat bereits einige Demo-Code, der Noten mit Rechteck und Sägewellen spielt . Ersetzen Sie das einfach durch den internen Synthesizer.

Einige andere Optionen könnten sein, um herauszufinden, wie der Status beim Portieren von FluidSynth auf Android ist. Möglicherweise ist etwas verfügbar.

Edit: Andere möglicherweise interessante Bibliotheken:

18

Benötige ich ein Synthesizer-Programm, um MIDI-Noten zu spielen? Wenn ja, muss ich meine eigene erstellen oder wird eine von Android oder einem Drittanbieter bereitgestellt?

Nein, zum Glück brauchen Sie keinen eigenen Synthesizer zu bauen. Android hat bereits einen eingebaut: den SONiVOX Embedded Audio Syntehesizer. Android-Status in den Dokumenten zu SONiVOX JETCreator :

JET arbeitet mit dem Embedded Audio Synthesizer (EAS) von SONiVOX zusammen, dem Wiedergabegerät MIDI für Android.

Es war nicht klar, ob Sie eine Echtzeitwiedergabe wünschen oder ob Sie zuerst eine Komposition erstellen und später in derselben App abspielen möchten. Sie geben auch an, dass Sie MIDI-Noten spielen möchten, nicht Dateien. Aber nur damit Sie wissen, ist die Midi-Wiedergabe auf Android-Geräten unterstützt . Das Abspielen einer .mid-Datei sollte also genauso erfolgen wie das Abspielen einer .wav-Datei mit MediaPlayer .

Um ehrlich zu sein, ich habe weder das Midi-Paket noch die Midi-Wiedergabe verwendet. Wenn Sie jedoch eine .mid-Datei erstellen und auf der Festplatte speichern können, sollten Sie sie mit straight MediaPlayer wiedergeben können.

Wenn Sie gerade MIDI Noten , nicht Dateien abspielen möchten, können Sie dieses Mididriver-Paket verwenden. Mit diesem Paket sollten Sie in der Lage sein, MIDI-Daten in den Embedded Synthesizer zu schreiben:

/**
* 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[])  

Wenn Sie noch tiefer gehen möchten, können Sie mit AudioTrack sogar direktes PCM abspielen.

Für zusätzliche Informationen, hier ist ein Blog-Beitrag Ich habe jemanden gefunden, der ähnliche Probleme zu haben schien wie Sie. Er legt fest:

Persönlich habe ich das Problem der dynamischen Midi-Generierung wie folgt gelöst: Programmgesteuert eine Midi-Datei generieren, in den Gerätespeicher schreiben, einen Mediaplayer mit der Datei starten und abspielen lassen. Dies ist schnell genug, wenn Sie nur einen dynamischen Midi-Sound spielen müssen. Ich bezweifle, dass es nützlich ist, benutzergesteuerte MIDI-Inhalte wie Sequenzer zu erstellen, aber in anderen Fällen ist es großartig.

Hoffe ich habe alles abgedeckt.

4
CaptJak

Um Sound mit der Android MIDI API zu erzeugen, benötigen Sie eine Synthesizer-App, die MIDI Eingaben akzeptiert. Leider ist dies die einzige App, die ich bei Google Play gefunden habe: https://play.google.com/store/apps/details?id=com.mobileer.midisynthexample

Ich konnte Musik abspielen, indem ich Notizen an diese App schickte. Aber der Programmwechsel hat schlecht funktioniert. Sofern ich in meinem Code nichts falsch gemacht habe, hat die App anscheinend nur zwei Instrumente.

Es gibt jedoch einige Leute, die an anderen Synthesizer-Apps arbeiten, daher erwarte ich, dass bald weitere Apps verfügbar sein werden. Diese App sieht vielversprechend aus, obwohl ich sie selbst noch nicht getestet habe: 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