带有 CoreMIDI 的虚拟环回 MIDI 端口
Virtual loopback MIDI port with CoreMIDI
我正在尝试使用 C 中的 CoreMIDI 在 macOS 上编写简单的环回虚拟 MIDI 端口。首先,我似乎无法理解所有 CoreMIDI 术语。请看下图:
┌─────────────┐
│ MIDI DEVICE │
│ │
└─OUT──────IN─┘
↓ ↑
event event
↓ ↑
──A─────────B── application
↓ ↑
所以我们有一个 MIDI 设备。此设备有 out 端口和 in 端口 从设备的角度来看 。因此,MIDI 设备通过 out 端口发送 MIDI 数据,并通过 in 端口接收数据。
但是现在让我们从应用的角度来看一下这个系统。对于来自 MIDI 设备的应用程序 MIDI 数据,应用程序实际上 收到 (上图中的点 A
)out 端口。来自应用程序的数据由设备通过其 in 端口从应用程序(点 B
)接收。
所以我的第一个问题是API代表什么A
和B
? CoreMIDI中有四个概念:
- 来源
- 目的地
- 输入端口
- 输出端口
那么如果我们想从 MIDI 设备接收 MIDI 数据,我们应该使用什么?我想我们需要这样的东西:
void MyReadProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
{
}
...
MIDIClientRef client;
MIDIClientCreate(CFSTR("CLIENT"), NULL, NULL, &client);
MIDIPortRef inPort;
MIDIInputPortCreate(client, CFSTR("TEST"), MyReadProc, NULL, &inPort);
MIDIPortConnectSource(inPort, srcEndpoint, NULL);
其中 srcEndpoint
是 MIDIEndpointRef
表示 source.
当我们想要将 MIDI 数据发送到设备时,会发生很多有趣的事情。我们应该使用 MIDISend
还是 MIDIReceived
?
现在我需要展示我的环回端口实现:
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreMIDI/CoreMIDI.h>
#include <mach/mach_time.h>
#include <string.h>
void MyReadProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
{
if (readProcRefCon != NULL && srcConnRefCon != NULL)
{
MIDIPortRef portRef = *((MIDIPortRef*)readProcRefCon);
MIDIEndpointRef destRef = *((MIDIEndpointRef*)srcConnRefCon);
OSStatus result = MIDISend(portRef, destRef, pktlist);
printf("B\n");
}
}
...
MIDIClientRef client;
MIDIClientCreate(CFSTR("CLIENT"), NULL, NULL, &client);
MIDIPortRef outPort;
MIDIOutputPortCreate(client, CFSTR("TEST"), &outPort);
MIDIEndpointRef destEndpoint;
MIDIDestinationCreate(client, CFSTR("TEST"), MyReadProc, NULL, &destEndpoint);
MIDIPortRef inPort;
MIDIInputPortCreate(client, CFSTR("TEST"), MyReadProc, &outPort, &inPort);
MIDIEndpointRef srcEndpoint;
MIDISourceCreate(client, CFSTR("TEST"), &srcEndpoint);
MIDIPortConnectSource(inPort, srcEndpoint, &destEndpoint);
好的,我可以创建输入 MIDI 端口,将其连接到源并接收事件。但是环回意味着如果我将数据发送到 TEST out 端口(通过输出端口、目标、源、MIDISend/Received、其他东西??),我想立即从测试 在 端口。
而且我真的不明白环回系统应该如何构建以及用户将如何与之交互?
所以系统中应该有两个端口(或源端口和目标端口?)。用户以某种方式获取对 out 端口(或源或目标?)的引用,通过它发送数据,并通过对 in 端口的引用取回数据(或来源或目的地?)。脑袋都炸了
好的,我终于想通了如何用CoreMIDI建立环回系统。
- Source 从应用的角度来看是一个输入设备
- Destination 从应用的角度来看是一个输出设备
所以我们只需要创建带有回调的目标,我们将在其中通知源(MIDIReceived
)新的 MIDI 数据已到达。因此,我们在 MIDIDestinationCreate
中提供源引用作为回调的参数,并在回调中使用此源。
首先不要使用 MIDIReadProc。它不仅被弃用且不受支持,而且存在问题。参见 https://bradleyross.github.io/ObjectiveC-Examples/Documentation/BRossTools/FunctionalArea.html and https://bradleyross.github.io/ObjectiveC-Examples/Documentation/BRossTools/CoreMidi.html。
客户端是一个虚拟 MIDI 设备,可以是控制器、音序器、合成器等。输入和输出端口是客户端的一部分,源和目标连接到该客户端。当您使用 USB 连接连接 MIDI 设备时,将为这些设备自动创建源和目标。
您还可以使用 MIDIDestinationCreate 和 MIDISourceCreate(具有适当的后缀)为客户端附加虚拟源和目标。因此环回可能涉及具有输出端口和虚拟目的地的客户端。然后输出端口将附加到虚拟目的地。另一种选择是有一个输入端口和虚拟源,源连接到输入端口。
我自己仍在努力理解这一点。
我正在尝试使用 C 中的 CoreMIDI 在 macOS 上编写简单的环回虚拟 MIDI 端口。首先,我似乎无法理解所有 CoreMIDI 术语。请看下图:
┌─────────────┐
│ MIDI DEVICE │
│ │
└─OUT──────IN─┘
↓ ↑
event event
↓ ↑
──A─────────B── application
↓ ↑
所以我们有一个 MIDI 设备。此设备有 out 端口和 in 端口 从设备的角度来看 。因此,MIDI 设备通过 out 端口发送 MIDI 数据,并通过 in 端口接收数据。
但是现在让我们从应用的角度来看一下这个系统。对于来自 MIDI 设备的应用程序 MIDI 数据,应用程序实际上 收到 (上图中的点 A
)out 端口。来自应用程序的数据由设备通过其 in 端口从应用程序(点 B
)接收。
所以我的第一个问题是API代表什么A
和B
? CoreMIDI中有四个概念:
- 来源
- 目的地
- 输入端口
- 输出端口
那么如果我们想从 MIDI 设备接收 MIDI 数据,我们应该使用什么?我想我们需要这样的东西:
void MyReadProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
{
}
...
MIDIClientRef client;
MIDIClientCreate(CFSTR("CLIENT"), NULL, NULL, &client);
MIDIPortRef inPort;
MIDIInputPortCreate(client, CFSTR("TEST"), MyReadProc, NULL, &inPort);
MIDIPortConnectSource(inPort, srcEndpoint, NULL);
其中 srcEndpoint
是 MIDIEndpointRef
表示 source.
当我们想要将 MIDI 数据发送到设备时,会发生很多有趣的事情。我们应该使用 MIDISend
还是 MIDIReceived
?
现在我需要展示我的环回端口实现:
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreMIDI/CoreMIDI.h>
#include <mach/mach_time.h>
#include <string.h>
void MyReadProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
{
if (readProcRefCon != NULL && srcConnRefCon != NULL)
{
MIDIPortRef portRef = *((MIDIPortRef*)readProcRefCon);
MIDIEndpointRef destRef = *((MIDIEndpointRef*)srcConnRefCon);
OSStatus result = MIDISend(portRef, destRef, pktlist);
printf("B\n");
}
}
...
MIDIClientRef client;
MIDIClientCreate(CFSTR("CLIENT"), NULL, NULL, &client);
MIDIPortRef outPort;
MIDIOutputPortCreate(client, CFSTR("TEST"), &outPort);
MIDIEndpointRef destEndpoint;
MIDIDestinationCreate(client, CFSTR("TEST"), MyReadProc, NULL, &destEndpoint);
MIDIPortRef inPort;
MIDIInputPortCreate(client, CFSTR("TEST"), MyReadProc, &outPort, &inPort);
MIDIEndpointRef srcEndpoint;
MIDISourceCreate(client, CFSTR("TEST"), &srcEndpoint);
MIDIPortConnectSource(inPort, srcEndpoint, &destEndpoint);
好的,我可以创建输入 MIDI 端口,将其连接到源并接收事件。但是环回意味着如果我将数据发送到 TEST out 端口(通过输出端口、目标、源、MIDISend/Received、其他东西??),我想立即从测试 在 端口。
而且我真的不明白环回系统应该如何构建以及用户将如何与之交互?
所以系统中应该有两个端口(或源端口和目标端口?)。用户以某种方式获取对 out 端口(或源或目标?)的引用,通过它发送数据,并通过对 in 端口的引用取回数据(或来源或目的地?)。脑袋都炸了
好的,我终于想通了如何用CoreMIDI建立环回系统。
- Source 从应用的角度来看是一个输入设备
- Destination 从应用的角度来看是一个输出设备
所以我们只需要创建带有回调的目标,我们将在其中通知源(MIDIReceived
)新的 MIDI 数据已到达。因此,我们在 MIDIDestinationCreate
中提供源引用作为回调的参数,并在回调中使用此源。
首先不要使用 MIDIReadProc。它不仅被弃用且不受支持,而且存在问题。参见 https://bradleyross.github.io/ObjectiveC-Examples/Documentation/BRossTools/FunctionalArea.html and https://bradleyross.github.io/ObjectiveC-Examples/Documentation/BRossTools/CoreMidi.html。
客户端是一个虚拟 MIDI 设备,可以是控制器、音序器、合成器等。输入和输出端口是客户端的一部分,源和目标连接到该客户端。当您使用 USB 连接连接 MIDI 设备时,将为这些设备自动创建源和目标。
您还可以使用 MIDIDestinationCreate 和 MIDISourceCreate(具有适当的后缀)为客户端附加虚拟源和目标。因此环回可能涉及具有输出端口和虚拟目的地的客户端。然后输出端口将附加到虚拟目的地。另一种选择是有一个输入端口和虚拟源,源连接到输入端口。
我自己仍在努力理解这一点。