Android 6.0 (Marshmallow): Как играть в миди?

Я создаю приложение, которое генерирует звуки живых инструментов, и я планирую использовать новый Midi API, представленный в Android Marshmallow (версия 6.0). Я прочитал документ обзора пакета здесь 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); 

Затем я отправил тестовую заметку на сообщение 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 другого приложения). Если кто-то уже не сделал такой синтезатор, я должен создать его самостоятельно в течение жизненного цикла моего приложения. Как я фактически конвертирую полученную миди noteOn () в звук, выходящий из моих динамиков? Я особенно смущен, потому что также должен быть способ программно решить, на каком типе инструмента звучит примечание, как будто это происходит: это также сделано в синтезаторе?

Поддержка Midi в Android Marshmallow довольно новая, поэтому я не смог найти какие-либо учебники или примеры приложений для синтезаторов онлайн. Любое понимание очень ценится.

Я не нашел никакого «официального» способа управления внутренним синтезатором из Java-кода.

Вероятно, самым простым вариантом является использование midi-драйвера Android для синтезатора Sonivox .

Получите его как пакет 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; } } 

В файле макета есть только одна кнопка, которая воспроизводит предопределенное примечание при удерживании и останавливает его при отпускании:

 <?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 выбираемыми инструментами, очень приличной латентностью и надлежащей функциональностью «заметки», которой не хватает многим приложениям.

Что касается выбора инструмента: вам просто нужно отправить сообщение «Program Change» MIDI на канал, на котором вы собираетесь играть, чтобы выбрать один из 128 звуков в звуковом сигнале General MIDI. Но это связано с деталями MIDI, а не с использованием библиотеки.

Аналогично, вы, вероятно, захотите абстрагироваться от деталей низкого уровня MIDI, чтобы вы могли легко воспроизводить конкретную заметку на определенном канале с помощью специального инструмента с определенной скоростью в течение определенного времени, и для этого вы могли бы найти некоторые подсказки от всех Приложения Java и MIDI, связанные с открытым исходным кодом, и библиотеки, созданные до сих пор.

Этот подход, кстати, не требует Android 6.0. И на данный момент только 4,6% устройств, посещающих Play Store, работают под управлением Android 6.x, поэтому для вашего приложения не будет много аудитории.

Конечно, если вы хотите использовать пакет android.media.midi , вы можете использовать библиотеку для реализации android.media.midi.MidiReceiver для приема MIDI-событий и воспроизведения их на внутреннем синтезаторе. У Google уже есть демо-код, который воспроизводит ноты с квадратными и волновыми волнами . Просто замените его на встроенный синтезатор.

Некоторые другие варианты могут состоять в том, чтобы проверить, каков статус с переносом FluidSynth на Android. Наверное, может быть что-то доступное.

Изменить: Другие, возможно, интересные библиотеки:

  • Порт Java javax.sound.midi для абстрагирования технических характеристик MIDI низкого уровня
  • USB MIDI Driver для подключения к цифровому пианино / клавиатуре с USB-разъемом USB
  • MIDI over Bluetooth LE для беспроводного подключения к цифровому пианино / клавиатуре, поддерживающего MIDI через Bluetooth LE (например, некоторые недавние цифровые пианино Roland и Dexibell)
  • Порт библиотеки музыки JFugue для Android для дальнейшего абстрагирования деталей MIDI и, вместо этого, мышления с точки зрения теории музыки

Нужна ли мне программа синтезатора для воспроизведения примечаний Midi? Если да, то я должен сделать свой собственный или один, предоставленный Android или сторонним лицом?

Нет, к счастью, вам не нужно создавать свой собственный синтезатор. Android уже имеет встроенный встроенный синтезатор SONiVOX. Android заявляет в документах на SONiVOX JETCreator :

JET работает совместно с встроенным аудио синтезатором SONIVOX (EAS), который является устройством воспроизведения MIDI для Android.

Было неясно, хотите ли вы воспроизведение в реальном времени или хотите сначала создать композицию и воспроизвести ее в одном приложении. Вы также заявляете, что хотите играть MIDI-заметки, а не файлы. Но, как вы знаете, воспроизведение на Midi поддерживается на устройствах Android . Поэтому воспроизведение файла .mid должно выполняться таким же образом, как и в WAV-файле с использованием MediaPlayer .

Честно говоря, я не использовал midi-пакет или не делал midi-воспроизведение, но если вы можете создать файл .mid и сохранить его на диск, то вы сможете воспроизвести его с помощью прямого MediaPlayer .

Теперь, если вы хотите играть в обычные MIDI- заметки , а не файлы, вы можете использовать этот пакет 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[]) 

Если вы хотите сделать шаг ниже, вы можете даже сыграть в прямом PCM с помощью AudioTrack .

Для получения дополнительной информации, это сообщение в блоге, которое я нашел у кого-то, у кого, похоже, были подобные неприятности. Он утверждает:

Лично я решил проблему динамического midi поколения следующим образом: программно генерировать midi-файл, записывать его в память устройства, инициировать медиаплеер с файлом и позволять ему играть. Это достаточно быстро, если вам просто нужно воспроизвести динамический миди-звук. Я сомневаюсь, что это полезно для создания управляемых пользователем MIDI-файлов, таких как секвенсоры, но для других случаев это здорово.

Надеюсь, я все покрыл.

Для создания звука с использованием Android MIDI API вам потребуется приложение для синтезатора, которое принимает вход MIDI. К сожалению, это единственное приложение, которое я нашел в Google Play: https://play.google.com/store/apps/details?id=com.mobileer.midisynthexample

Я смог воспроизвести музыку, отправив сообщение и отменив сообщение в этом приложении. Но смена программы работала плохо. Если я не сделал что-то неправильно в своем коде, кажется, что приложение имеет только два инструмента.

Тем не менее, есть некоторые ребята, работающие над другими приложениями для синтезаторов, поэтому я ожидаю, что в ближайшее время появятся новые приложения. Это приложение выглядит многообещающим, хотя я еще не тестировал его сам: https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fpedrolcl%2Fandroid%2Ftree%2Fmaster%2FNativeGMSynth&sa=D&sntz = 1 & USG = AFQjCNHd-V7MKZQqOxKJwFcwNjP4c-kysA