Android 6.0(棉花糖):如何演奏Midi音符?

我正在创建一个可产生现场乐器声音的应用程序,并且计划使用Android棉花糖(6.0版)中的新Midi

API。我已经在http://developer.android.com/reference/android/media/midi/package-

summary.html上阅读了软件包概述文档,并且我知道如何生成Midi笔记,但是我仍然不确定:我该怎么办在生成它们的Midi数据后还可以演奏这些音符?

我需要一个合成器程序来演奏Midi音符吗?如果是这样,我必须自己制作,还是由Android或第三方提供?

我是Midi的新手,所以请尽可能对您的回答进行描述。

创建一个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 Marshmallow中的Midi支持是相当新的,因此我无法在线找到任何教程或示例合成器应用程序。任何见解均表示赞赏。

回答:

我还没有找到任何“正式”的方式来从Java代码控制内部合成器。

可能最简单的选择是为Sonivox合成器使用Android

midi驱动程序。

以AAR软件包的形式获取(解压

.zip)并将 .aar文件存储在工作区中的某个位置。路径并不重要,也不必位于您自己的应用程序的文件夹结构中,但是项目内部的“

libs”文件夹可能是一个合乎逻辑的地方。

Android Studio中打开您的Android项目:

文件->新建->新建模块->导入.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;

}

}

布局文件只有一个按钮,当按下该按钮时,它会播放预定义的音符,而在释放时会停止:

<?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种声音之一。但这与MIDI的细节有关,与库的使用无关。

同样,您可能希望抽象出MIDI的低级细节,以便您可以在特定时间内使用特定乐器以特定速度轻松地在特定通道上播放特定音符,并且可能从中找到一些线索。到目前为止,开源的Java和MIDI相关应用程序和库。

顺便说一下,这种方法不需要Android 6.0。目前,访问Play商店的设备中只有4.6%运行Android

6.x,因此您的应用不会有太多的受众。

当然,如果您想使用android.media.midi包,则可以使用该库来实现android.media.midi.MidiReceiver来接收MIDI事件并在内部合成器上播放它们。Google已经有一些演示代码,可以播放带有正方形和锯齿状的音符。只需将其替换为内部合成器即可。

其他一些选择可能是检查将FluidSynth移植到Android 的状态。我想可能有可用的东西。

其他可能有趣的库:

  • Java的javax.sound.midi包的端口,用于抽象底层MIDI技术细节
  • USB MIDI驱动程序,用于通过USB MIDI接口连接到数码钢琴/键盘
  • MIDI over Bluetooth LE驱动程序,用于无线连接到通过Bluetooth LE支持MIDI的数码钢琴/键盘(例如,一些最新的Roland和Dexibell数码钢琴)
  • 适用于Android的JFugue音乐库端口,用于进一步抽象MIDI详细信息,并根据音乐理论进行思考

以上是 Android 6.0(棉花糖):如何演奏Midi音符? 的全部内容, 来源链接: utcz.com/qa/424494.html

回到顶部