web-dev-qa-db-ja.com

Android 6.0(マシュマロ):MIDIノートの演奏方法は?

ライブインストゥルメントサウンドを生成するアプリを作成しています。Android Marshmallow(バージョン6.0)で紹介されている新しいMidi APIを使用する予定です。パッケージの概要ドキュメントはこちらです- http://developer.Android.com/reference/Android/media/midi/package-summary.html と私はミディのノートを生成する方法を知っていますが、私はまだわかりません:これらを実際に再生する方法Midiデータを生成した後のメモ

Midiノートを再生するためにシンセサイザープログラムが必要ですか?その場合、自分で作成する必要がありますか、それともAndroidまたはサードパーティによって提供されたものですか?)

私はミディの初心者ですので、答えはできるだけわかりやすくしてください。

これまでに試したこと:Midiマネージャーオブジェクトを作成し、入力ポートを開きました

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

次に、テストnoteOn midiメッセージをポートに送信しました

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

また、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());

さて、ここで私は最も混乱しています。私のアプリの使用例は、音楽を作成するためのオールインワンの作曲&再生ツールになることです。言い換えると、私のアプリは仮想MIDIデバイスを含むか使用する必要があります(別のアプリのMIDIシンセサイザーのインテントのように)。誰かがすでにそのようなシンセサイザーを作成していない限り、私は自分のアプリのライフサイクル内で自分で作成する必要があります。受信したmidi noteOn()を実際にスピーカーから出力されるサウンドに実際に変換するにはどうすればよいですか?特に、音がどのように聞こえるかをプログラムで決定する方法が必要なため、混乱しています。これもシンセサイザーで行われますか?

AndroidマシュマロのMidiサポートはかなり新しいので、チュートリアルやサンプルシンセサイザーアプリをオンラインで見つけることができませんでした。洞察はありがたいです。

18
Cody

Javaコードから内部シンセサイザを制御するための「公式の」方法は見つかりませんでした。

おそらく、最も簡単なオプションは、Sonivoxシンセサイザー用の Android midiドライバー を使用することです。

入手する AARパッケージとして (* .Zipを解凍)、*。aarファイルをワークスペースのどこかに保存します。パスは実際には重要ではなく、独自のアプリのフォルダー構造内にある必要はありませんが、プロジェクト内の「libs」フォルダーは論理的な場所である可能性があります。

AndroidプロジェクトをAndroid Studioで開きます。

ファイル->新規->新しいモジュール-> .JAR/.AARパッケージのインポート->次へ->「MidiDriver-all-release.aar」を見つけて選択し、必要に応じてサブプロジェクト名を変更します。 ->完了

Gradleが魔法をかけるのを待ってから、「アプリ」モジュールの設定(独自のアプリプロジェクトの設定)の「依存関係」タブに移動し、MIDIドライバーを(緑色の「+」記号で)追加します。モジュールの依存関係。これで、MIDIドライバーにアクセスできます。

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

NDKとC++について何も心配する必要なく、次のJavaメソッドを利用できます。

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

ノートを演奏および停止するための非常に基本的な「概念実証」は次のようなものです。

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

レイアウトファイルには、押したときに定義済みのノートを再生し、リリースすると停止するボタンが1つだけあります。

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

それは実際にはこの単純です。上記のコードは、128の選択可能な楽器、非常にまともなレイテンシ、多くのアプリに欠けている適切な「ノートオフ」機能を備えたタッチピアノアプリの出発点になる可能性があります。

インストゥルメントの選択については、MIDI "プログラム変更"メッセージを再生するチャンネルに送信して、MIDIサウンドセットの128のサウンドから1つを選択するだけです。ただし、これはMIDIの詳細に関連しており、ライブラリの使用方法には関連していません。

同様に、MIDIの低レベルの詳細を抽象化して、特定のチャンネルの特定のノートを特定の楽器で特定の速度で特定の時間、簡単に演奏できるようにすることもできます。これまでに作成されたすべてのオープンソースJavaおよびMIDI関連のアプリケーションとライブラリからの手がかり。

ちなみに、このアプローチはAndroid 6.0を必要としません。現時点では、 Playストアにアクセスするデバイスのわずか4.6%がAndroid 6.x を実行しているため、アプリのオーディエンスは多くありません。

もちろん、Android.media.midiパッケージを使用する場合は、ライブラリを使用してAndroid.media.midi.MidiReceiverを実装し、MIDIイベントを受信して​​内部シンセサイザーで再生できます。グーグルはすでに正方形とのこぎり波でノートを演奏する デモコード を持っています。それを内蔵シンセサイザーに置き換えるだけです。

他のいくつかのオプションは、 FluidSynth をAndroidに移植してステータスを確認することです。何か利用できるかもしれないと思います。

編集:その他の興味深いライブラリ:

26

Midiノートを再生するためにシンセサイザープログラムが必要ですか?その場合、自分で作成する必要がありますか、それともAndroidまたはサードパーティによって提供されたものですか?)

いいえ、幸い、独自のシンセサイザーを作成する必要はありません。 Androidにはすでに1つが組み込まれています:SONiVOX Embedded Audio Syntehesizer。Androidでの状態 docs on SONiVOX JETCreator)

JETは、MIDI再生デバイス)であるSONiVOXの組み込みオーディオシンセサイザー(EAS)と連動します。

リアルタイム再生が必要かどうか、または最初にコンポジションを作成し、後で同じアプリ内で再生するかどうかは明確ではありませんでした。また、ファイルではなくMIDIノートを再生することも指定します。しかし、ご存知のように、Midiの再生は Androidデバイス でサポートされています。したがって、.midファイルの再生は、.wavファイルの再生と同じ方法で行う必要があります。 MediaPlayer を使用します。

正直なところ、私はmidiパッケージを使用したり、midi再生を行ったりしていませんが、.midファイルを作成してディスクに保存できれば、ストレートMediaPlayerを使用して再生できるはずです。 。

ここで、ストレートMIDInotesnotファイルを再生する場合は、-を使用できます このmididriverパッケージ 。このパッケージを使用すると、MIDIデータをEmbedded Synthesizerに書き込むことができるはずです。

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

それよりもさらに低いステップにしたい場合は、 AudioTrack を使用してストレートPCMを再生することもできます。

追加情報については、こちら ブログ投稿 私はあなたと同じような問題を抱えているように見える誰かから見つけました。彼は述べています:

個人的に私は次のように動的なmidi生成の問題を解決しました:プログラムでmidiファイルを生成し、デバイスストレージに書き込み、そのファイルを使用してメディアプレーヤーを開始し、再生させます。ダイナミックなMIDIサウンドを再生するだけの場合は、これで十分です。シーケンサーのようなユーザーコントロールのmidi要素を作成するのに役立つとは思えませんが、他の場合には素晴らしいです。

私はすべてをカバーしたいと思います。

6
CaptJak

Android MIDI APIを使用してサウンドを生成するには、MIDI入力を受け入れるシンセサイザアプリが必要です。残念ながら、これは私がGoogle Playで見つけたアプリのみ: https://play.google.com/store/apps/details?id=com.mobileer.midisynthexample

このアプリにノートオンとノートオフのメッセージを送ることで音楽を再生することができました。しかし、プログラムの変更はうまく機能しませんでした。私のコードで何か間違ったことをしない限り、アプリには2つの計測器しかないようです。

しかし、他のシンセサイザーアプリに取り組んでいる人もいるので、もっと多くのアプリがすぐに利用できるようになると思います。このアプリは有望に見えますが、まだ自分でテストしていません: https://github.com/pedrolcl/Android/tree/master/NativeGMSynth

1
AndroidHobby