MACでMIDIを扱うにはCoreMIDIのFrameworkを使う、ということは知っていたのですが、 これまでプログラムを書いたことがありませんでした。そこで、ChatGPTに助けてもらいながら MIDIを受信するプログラムを書いて(コピーして)動作させてみました。
MACでプログラムを開発するためには、Apple StoreからXcodeをインストールする必要があります。 無料です。
新しいProjectを生成します。
Command Line Toolsを選択し、NEXT。
Project Nameをmidiinとし、NEXT。
適当なフォルダで、Create。
midiinという名前のプロジェクトが生成されます。
左のナビゲータ Navigator から、mainを選択する。 内容を全部消して、ChatGPTが教えてくれたプログラムを書き込みます。
プログラムは以下の通りです。
#include <CoreMIDI/CoreMIDI.h>
#include <CoreFoundation/CoreFoundation.h>
#include <stdio.h>
// MIDI の通知コールバック
void MyMIDIReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
for (unsigned int i = 0; i < pktlist->numPackets; i++) {
// MIDI パケットデータを処理します
printf("Received MIDI message with %d bytes:\n", packet->length);
for (unsigned int j = 0; j < packet->length; j++) {
printf("Byte %d: 0x%X\n", j, packet->data[j]);
}
packet = MIDIPacketNext(packet);
}
}
int main() {
MIDIClientRef midiClient;
MIDIPortRef midiInputPort;
OSStatus result;
// MIDI クライアントの作成
result = MIDIClientCreate(CFSTR("MIDI Client"), NULL, NULL, &midiClient);
if (result != noErr) {
printf("Failed to create MIDI client: %d\n", result);
return -1;
}
// MIDI 入力ポートの作成
result = MIDIInputPortCreate(midiClient, CFSTR("Input Port"), MyMIDIReadProc, NULL, &midiInputPort);
if (result != noErr) {
printf("Failed to create MIDI input port: %d\n", result);
return -1;
}
// 接続されているすべての MIDI デバイスを列挙
ItemCount deviceCount = MIDIGetNumberOfDevices();
printf("Number of MIDI devices: %lu\n", deviceCount);
for (ItemCount i = 0; i < deviceCount; i++) {
MIDIDeviceRef device = MIDIGetDevice(i);
// デバイス情報の取得
CFStringRef deviceName;
MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName);
char name[128];
CFStringGetCString(deviceName, name, sizeof(name), kCFStringEncodingUTF8);
printf("Device %lu: %s\n", i, name);
// デバイス内のエンティティを列挙
ItemCount entityCount = MIDIDeviceGetNumberOfEntities(device);
for (ItemCount j = 0; j < entityCount; j++) {
MIDIEntityRef entity = MIDIDeviceGetEntity(device, j);
// エンティティ内のソースを列挙
ItemCount sourceCount = MIDIEntityGetNumberOfSources(entity);
for (ItemCount k = 0; k < sourceCount; k++) {
MIDIEndpointRef source = MIDIEntityGetSource(entity, k);
// ソースに接続
result = MIDIPortConnectSource(midiInputPort, source, NULL);
if (result == noErr) {
printf("Connected to MIDI source %lu\n", k);
} else {
printf("Failed to connect to MIDI source: %d\n", result);
}
}
}
}
// メインループ (実際のアプリケーションでは適切なループ構造を使用します)
printf("Listening for MIDI messages... (Press Ctrl+C to exit)\n");
while (1) {
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, true);
}
// クリーンアップ (実際のアプリケーションではクリーンアップが必要)
MIDIClientDispose(midiClient);
return 0;
}
上のメニューからProduct > Buildを選択することでコンパイルしてみます。 エラーが出ます。
clang: error: linker command failed with exit code 1 (use -v to see invocation) Undefined symbol: _MIDIClientCreate
CoreMIDI.Frameworkをインストールします。 左のNavigatorから、一番上midiinを選択、TARGETからmidiinを選択、上のメニューからGenaralを選択、 Frameworks and librariesから+を選択します。
上のダイアログにCoreMIDIを入力、下に選択子が出ますのでCoreMIDI.Frameworkを選択、Addを押します。
CoreMIDI.Frameworkが追加されています。
一旦プロジェクトを保存、もう一度Buildします。エラーの数は減りますがまだエラーが出ます。
clang: error: linker command failed with exit code 1 (use -v to see invocation) Undefined symbol: _CFRunLoopRunlnMode
ChatGPTに尋ねたところ、CoreFoundation.frameworkを追加でインストールせよとのこと。 インストールします。インストール方法はCoreMIDI.frameeworkと同じです。
これでBuildするとコンパイル、リンクが通ります。MIDI機器を接続し、動作させてみます。 Product > Run を選択します。鍵盤を押すと、MIDIが下のウィンドウに表示されます。
ターミナルで動作させるには実行形式のアプリを生成します。まず、アプリを作成するフォルダを指定します。 TARGET > Build Settings > Build Location > Build Products Pathをクリックします。 $(PROJECT_DIR) と入力しReturnします。
Product > Build For > Profiling を選択します。Xcodeのプロジェクトを保存したmidiinのフォルダの中に Releaseというフォルダーができていると思います。この中にmidiinという実行形式のアプリができています。
ターミナルでReleaseフォルダへ移動し、./midiin (ドット スラッシュ midiin)で実行できます。
コマンドラインプログラムでMIDIを送信するプログラムmidioutを作成します。MIDIメッセージはコマンドラインの引数に以下のように記述します。
> ./midiout 0x90 0x40 0x7F
実行すると、MACにつながっているMIDIデバイスのリストが表示されます。 その後、どのデバイスにMIDIを出力するか、その番号を入力します。
> ./midiout 0x90 0x40 0x7F 90 75 43 Number of MIDI devices: 4 Device 0: ネットワーク Device 1: IACドライバ Device 2: Bluetooth Device 3: nanoKEY2 Select device = 3
プログラムを以下に記載しておきます。コンパイルの方法は前日のMIDI入力と同じです、
#include <CoreMIDI/CoreMIDI.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
MIDIClientRef client;
MIDIPortRef outputPort;
OSStatus result;
if (argc != 4) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return 1;
}
uint8_t status = (uint8_t)strtoul(argv[1], NULL, 0);
uint8_t data1 = (uint8_t)strtoul(argv[2], NULL, 0);
uint8_t data2 = (uint8_t)strtoul(argv[3], NULL, 0);
printf("%02x %02x %02x\n",status,data1,data2);
// MIDI クライアントの作成
result = MIDIClientCreate(CFSTR("MIDI Client"), NULL, NULL, &client);
if (result != noErr) {
fprintf(stderr, "Failed to create MIDI client: %d\n", result);
return 1;
}
// MIDI 出力ポートの作成
result = MIDIOutputPortCreate(client, CFSTR("Output Port"), &outputPort);
if (result != noErr) {
fprintf(stderr, "Failed to create MIDI output port: %d\n", result);
MIDIClientDispose(client);
return 1;
}
// 接続されているすべての MIDI デバイスを列挙
ItemCount deviceCount = MIDIGetNumberOfDevices();
printf("Number of MIDI devices: %lu\n", deviceCount);
// MIDI デバイス名を表示
for (ItemCount i = 0; i < deviceCount; i++) {
MIDIDeviceRef device = MIDIGetDevice(i);
CFStringRef deviceName;
MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName);
char name[128];
CFStringGetCString(deviceName, name, sizeof(name), kCFStringEncodingUTF8);
printf("Device %lu: %s\n", i, name);
}
// デバイスを選択する
int devnum;
fprintf(stderr,"Select device = ");
scanf("%d",&devnum);
// Device番号のデスティネーションを獲得する
MIDIEndpointRef destination = MIDIGetDestination(devnum);
if (destination == 0) {
fprintf(stderr, "No MIDI destination found\n");
MIDIPortDispose(outputPort);
MIDIClientDispose(client);
return 1;
}
// MIDI Dataをパケットにっ変換して送信する
MIDIPacketList packetList;
MIDIPacket *packet = MIDIPacketListInit(&packetList);
uint8_t midiData[3] = { status, data1, data2 };
packet = MIDIPacketListAdd(&packetList, sizeof(packetList), packet, 0, 3, midiData);
result = MIDISend(outputPort, destination, &packetList);
if (result != noErr) {
fprintf(stderr, "Failed to send MIDI message: %d\n", result);
}
// Clean up
MIDIPortDispose(outputPort);
MIDIClientDispose(client);
return 0;
}