0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MIDIFrameworkを使ってMacとシンセで送受信をしてみる

Last updated at Posted at 2025-04-28

ここまでの流れ

前回の投稿で、Standard MIDI Fileを選択するためのファイルブラウザについて書きました。
書いた記事はiOSデバイスのものですが、macOSはNSOutlineViewを使えば簡単に作れるため、他のネット記事や、Qiitaメンバーの記事を参考にしてもらうとして...

MIDIに関連する機能はmacOSとiOSで共通なので、まずは私の開発環境でUSBで常にシンセと繋がっているmacOSでMIDIの送受信をやってみます。

MIDIハードウェア環境

超シンセマニアの私でしたが(笑)、住環境などの問題から徐々に機材が減り、現在はRolandのFANTOM 8だけが鎮座しています。

IMG_0673.jpeg

MacはiMac (Apple Silicon M1)を使っています。

MacとはUSB直結で使っています。
デバイスドライバのタイプはVenderで、MIDIだけでなく、オーディオインターフェースとしても利用できる様になっています。

動作確認の結果、デバイスドライバはVenderでもGenericのどちらでも動作しました。
GenericはmacOS標準ドライバで動作するモードです。

サンプルプロジェクト

GitHubで公開しています。
こちらからダウンロードできます。

MIDIFramework

サンプルプロジェクトにはMIDIFrameworkというフレームワークを取り込んでいます。
これは私がC++, Objective-Cの時代から最新のSwiftまで、長年育ててきたMIDI関連処理をフレームワーク化したものです。
ソースコードで公開するより、このほうがシンプルに使えると思いました。

MIDIの処理はLMIDIというクラスが担当します。

Macに繋がっているMIDIデバイスを調べる

以下の二つのLMIDIクラスプロパティで調べられます。

public static var sourceNames: [String]
public static var destinationNames: [String]

サンプルプログラムでは、

print(LMIDI.sourceNames)
print(LMIDI.destinationNames)

と記述していて、デバッグコンソールにMacに接続されているMIDIデバイスを表示します。

MIDI処理のインスタンスを作る

上記のプロパティでMIDIデバイスを調べ、接続したいデバイスを決めます。
サンプルプログラムでは、MIDI処理のインスタンス名はmidiDeviceという名前にし、ひとまずリスト上一番上のデバイスに接続します。

LMIDIのイニシャライザは送信デバイス、受信デバイスを個別に指定できる様になっています。

private var midiDevice: LMIDI?

midiDevice = LMIDI(sourceName: LMIDI.sourceNames.first!,
              destinationName: LMIDI.destinationNames.first!)

これですぐに送受信できる状態になっています!

受信データの受け取り

サンプルではNotificationを使っています。
MIDIFrameworkは、MIDIデータを受信するとNotificationで通知します。
サンプルでは、

  • すべてのメッセージの受信通知
  • コントロールチェンジの受信通知

を受け取るようにobserverを指定しています。

NotificationCenter.default.addObserver(self,
                    selector: #selector(messageReceived(_:)),
                    name: LMIDI.messageReceivedNotification,
                    object: midiDevice)
NotificationCenter.default.addObserver(self,
                    selector: #selector(controlChangeReceived(_:)),
                    name: LMIDI.controlChangeReceivedNotification,
                    object: midiDevice)

この通知で受け取ったuserInfoの中に、MIDIで受信したデータが格納されています。
サンプルでは、print文を使ってその中身をデバッグコンソールに表示しています。

スクリーンショット 2025-04-28 21.44.34.png

もう一つの受信方法は、LMIDIDelegateというプロトコルを適用すると、

public protocol LMIDIDelegate {
    func messageReceived(receiveData: Data, MIDITimeStamp: MIDITimeStamp)
}

というデリゲートメソッドで受信データを受け取ることができます。
このデリゲートメソッドは受信した時に呼ばれるので、MIDIレコーダーのような受信データを素早く処理したいときに便利です。

タイミング的にはデリゲートメソッドの方がNotificationより早く呼ばれますが、NotificationのUserInfoにもMIDITimeStampを入れてあるので、MIDIデータ受信タイミングはどちらの方法でも正しく得ることができまs。

送信

sendメソッドを使います。
sendメソッドは二つあります。

public func send(packet: Data, timeStamp: MIDITimeStamp)
public func send(packet: Data)

引数にMIDITimeStampがあるメソッドは、指定したタイムスタンプのタイミングでMIDIデータが送信されます。
引数がないメソッドは即時送信されます。

1秒後にノートオフを送るという処理を考えた場合、

  • 引数にタイムスタンプあり〜1秒後のタイムスタンプを計算して、引数に与える
  • 引数にタイムスタンプなし〜タイマーを使って1秒後に送信

という方法が考えられます。
単発でMIDIデータを送信したい場合はタイマーがわかりやすいでしょう。

大量のデータを決まった時間に送るなら、MIDITimeStampを検討するのもいいでしょう。
使う側はMIDITimeStampを指定して送信処理するだけで、あとはmacOSがそのタイミングが来たら外部へ送信してくれます。

送受信ができたら、次はいよいよSMF再生

Standard MIDI Fileを再生するには、基本的に送信処理ができればOKです。
受信処理は必須ではないのですが、受信処理も組み込んでみます。

後日談

iOSのサンプルプロジェクトも追加しました。
macOSとiOSのサンプルは同じフォルダに入れましたので、一緒にダウンロードできます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?