如何保留 MIDISourceCreate() 中虚拟 MIDI 源的唯一性?

发布于 2024-12-08 02:10:17 字数 1833 浏览 1 评论 0原文

我正在研究一个小技巧,使用 RtMidi 作为 OS X 上 CoreMIDI 的包装器从应用程序发送 MIDI 消息。我使用 RtMidiOut::openVirtualPort("MyAwesomePort") 所以我可以选择我的应用程序作为DAW 中的输入源。

但是,如果我的程序关闭并再次打开它,我的 DAW 不会将输入设备识别为同一端口,尽管名称相同。

我最初使用的是pyrtmidi,所以直接用RtMidi验证了C++编写的行为。在本例中,“我的 DAW”是 Reaper 4,但我在 Pro Tools、Logic 和 MuLab 中复制了该行为。

我知道可以保留虚拟 midi 端口的一些独特性,因为 MidiKeys 的行为就像我希望我的应用程序能够正常运行:即使 MidiKeys 在我的 DAW 仍在运行时关闭并重新打开,我的 DAW 也会记住它。

因此,我深入研究了 RtMidi 源代码,CoreMIDI 包装器似乎足够简单。 MIDISourceCreate 所要求的只是一个字符串。客户端参数(我在浏览文档后推测)是我的应用程序的标识符,它是 CoreMIDI 服务的客户端。

void RtMidiOut :: openVirtualPort( std::string portName )
{
  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);

  if ( data->endpoint ) {
    errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!";
    error( RtError::WARNING );
    return;
  }

  // Create a virtual MIDI output source.
  MIDIEndpointRef endpoint;
  OSStatus result = MIDISourceCreate( data->client,
                                      CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
                                      &endpoint );
  if ( result != noErr ) {
    errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source.";
    error( RtError::DRIVER_ERROR );
  }

  // Save our api-specific connection information.
  data->endpoint = endpoint;
}

因此,我查看了 MIDISourceCreate 文档,并阅读了以下内容:

创建虚拟源后,最好为其分配与应用程序上次创建它时相同的唯一 ID。 (尽管您应该做好准备,以防万一发生冲突时失败。)这将允许其他客户端更轻松地保留对您的虚拟源的持久引用。

这似乎正是我正在寻找的。但我不知道如何为源分配唯一的 ID。 MIDISourceCreate 的输出参数是 MIDIEndpointRef,根据文档,它只是类型定义为 UInt32。所以我假设也许我应该跟踪这个 UInt32,但这似乎是一个坏主意。

在深入研究了所有这些之后,我感觉自己碰上了一堵砖墙。如何在应用程序运行之间保留 MIDI 端口的唯一性?

I'm working on a little hack sending MIDI messages from an app using RtMidi as a wrapper for CoreMIDI on OS X. I use RtMidiOut::openVirtualPort("MyAwesomePort") so I can select my app as an input source in a DAW.

However, if my program closes and I open it again, my DAW does not recognize the input device as the same port, despite being given the same name.

I was originally using pyrtmidi, so went and verified the behavior writing in C++ directly with RtMidi. "My DAW" in this case is Reaper 4, but I've duplicated the behavior in Pro Tools, Logic, and MuLab.

I know it's possible to retain some uniqueness of a virtual midi port, since MidiKeys behaves just as I'd like my application to behave: my DAWs remember it even if MidiKeys closes and re-opens while my DAW is still running.

So I dug into the RtMidi source, and the CoreMIDI wrapper seemed straightforward enough. All that the MIDISourceCreate asks for is a string. The client parameter is (what I presume after browsing the docs) an identifier for my application, it being a client of the CoreMIDI services.

void RtMidiOut :: openVirtualPort( std::string portName )
{
  CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);

  if ( data->endpoint ) {
    errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!";
    error( RtError::WARNING );
    return;
  }

  // Create a virtual MIDI output source.
  MIDIEndpointRef endpoint;
  OSStatus result = MIDISourceCreate( data->client,
                                      CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
                                      &endpoint );
  if ( result != noErr ) {
    errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source.";
    error( RtError::DRIVER_ERROR );
  }

  // Save our api-specific connection information.
  data->endpoint = endpoint;
}

So I looked at the MIDISourceCreate documentation, and read this:

After creating a virtual source, it's a good idea to assign it the same unique ID it had the last time your application created it. (Although you should be prepared for this to fail in the unlikely event of a collision.) This will permit other clients to retain persistent references to your virtual source more easily.

This seems like exactly what I'm looking for. Except I have no idea how to assign the source a unique ID. The out parameter for MIDISourceCreate is a MIDIEndpointRef, which according to the docs is just typedef'd to a UInt32 down the line. So I hypothesized that maybe I should keep track of this UInt32, but that seems like a bad idea.

After digging through all of this I feel like I'm hitting a bit of a brick wall. How do I retain the uniqueness of my MIDI port in between runs of my application?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

橘亓 2024-12-15 02:10:17

根据 文档

kMIDIPropertyUniqueID

系统为所有对象分配唯一的 ID。虚拟端点的创建者可以在其端点上设置此属性,但如果所选 ID 不唯一,则这样做可能会失败。

所以也许是这样的:

// Try to set the ID if it's saved.
if (savedUniqueId) {
  OSStatus result = MIDIObjectSetIntegerProperty(endpoint, kMIDIPropertyUniqueID, myUniqueId);
  if (result == kMIDIIDNotUnique) {
    savedUniqueId = 0;
  }
}
// If not saved, record the system-assigned ID
if (!savedUniqueId) {
  OSStatus result = MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &savedUniqueId);
  // Handle the error?
}

唯一的 ID 被类型定义为 SInt32。我假设 0 是一个无效的唯一 ID,这至少对于连接来说是正确的(kMIDIPropertyConnectionUniqueID 的文档说它“不存在,如果没有连接则为 0”)。

我不确定如何仅使用 32 位来保持长期唯一性,但希望它足以重新启动您的应用程序。

According to the docs,

kMIDIPropertyUniqueID

The system assigns unique ID's to all objects. Creators of virtual endpoints may set this property on their endpoints, though doing so may fail if the chosen ID is not unique.

So maybe something like this:

// Try to set the ID if it's saved.
if (savedUniqueId) {
  OSStatus result = MIDIObjectSetIntegerProperty(endpoint, kMIDIPropertyUniqueID, myUniqueId);
  if (result == kMIDIIDNotUnique) {
    savedUniqueId = 0;
  }
}
// If not saved, record the system-assigned ID
if (!savedUniqueId) {
  OSStatus result = MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &savedUniqueId);
  // Handle the error?
}

The unique ID is typedefed to a SInt32. I've made the assumption that 0 is an invalid unique ID, which is at least true for connections (the docs for kMIDIPropertyConnectionUniqueID say it's "non-existant or 0 if there is no connection").

I'm not sure how you maintain long-term uniqueness with only 32 bits, but it'll hopefully be sufficient for relaunches of your app.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文