【问题标题】:Virtual loopback MIDI port with CoreMIDI带有 CoreMIDI 的虚拟环回 MIDI 端口
【发布时间】:2021-09-10 03:51:31
【问题描述】:

我正在尝试使用 C 语言中的 CoreMIDI 在 macOS 上编写简单的环回虚拟 MIDI 端口。首先,我似乎无法理解所有 CoreMIDI 术语。请看下图:

┌─────────────┐
│ MIDI DEVICE │
│             │
└─OUT──────IN─┘
  ↓         ↑
event     event
  ↓         ↑
──A─────────B──  application
  ↓         ↑

所以我们有一个 MIDI 设备。此设备具有输出端口和输入端口从设备的角度来看。所以通过 out 端口 MIDI 设备发送 MIDI 数据并通过 in 端口接收数据。

但现在让我们从应用程序的角度来看这个系统。对于来自 MIDI 设备的应用程序 MIDI 数据,实际上是由应用程序从设备 out 端口接收(上图中的 A 点)。来自应用程序的数据由设备通过其 in 端口从应用程序接收(点 B)。

所以我的第一个问题是 AB 代表什么 API? 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);

其中srcEndpointMIDIEndpointRef 代表来源

当我们想向设备发送 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 等等??),我想立即从 TEST in 接收该数据 端口。

我真的不明白该环回系统应该如何构建以及用户将如何与之交互?

所以系统中应该有两个端口(或源和目标?)。用户以某种方式获取对 out 端口(或源或目标?)的引用,通过它发送数据,并通过对 in 端口(或源或目标?)的引用获取数据.好像我的头要炸了。

【问题讨论】:

    标签: c macos midi loopback coremidi


    【解决方案1】:

    好的,我终于想通了如何使用 CoreMIDI 构建环回系统。

    1. 从应用的角度来看,来源是一种输入设备
    2. 从应用的角度来看,目标是一个输出设备

    所以我们只需要创建带有回调的目的地,我们将在其中通知源(MIDIReceived)新的 MIDI 数据到达。因此,我们在MIDIDestinationCreate 中提供源引用作为回调的参数,并在回调中使用此源。

    【讨论】:

      【解决方案2】:

      首先不要使用 MIDIReadProc。它不仅已弃用且不受支持,而且存在问题。见https://bradleyross.github.io/ObjectiveC-Examples/Documentation/BRossTools/FunctionalArea.htmlhttps://bradleyross.github.io/ObjectiveC-Examples/Documentation/BRossTools/CoreMidi.html

      客户端是一个虚拟 MIDI 设备,可以是控制器、音序器、合成器等。输入和输出端口是客户端的一部分,源和目标连接到客户端。当您通过 USB 连接连接 MIDI 设备时,将自动为这些设备创建源和目标。

      您还可以使用 MIDIDestinationCreate 和 MIDISourceCreate(带有适当的后缀)为客户端附加虚拟源和目标。因此,环回可能涉及具有输出端口和虚拟目的地的客户端。然后将输出端口连接到虚拟目的地。另一种选择是有一个输入端口和一个虚拟源,源连接到输入端口。

      我自己还在努力理解这一点。

      【讨论】:

      • 感谢您的回答!我创建了chat room 来回复你
      猜你喜欢
      • 2012-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-02
      • 2019-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多