如果我使用if
分支中的第一个方法获得MIDIDestination
代码正常工作,并且发送了MIDI数据,则给出以下代码。如果我使用else
分支中的第二种方法,则不会发送任何数据。
var client = MIDIClientRef()
var port = MIDIPortRef()
var dest = MIDIEndpointRef()
MIDIClientCreate("jveditor" as CFString, nil, nil, &client)
MIDIOutputPortCreate(client, "output" as CFString, &port)
if false {
dest = MIDIGetDestination(1)
} else {
var device = MIDIGetExternalDevice(0)
var entity = MIDIDeviceGetEntity(device, 0)
dest = MIDIEntityGetDestination(entity, 0)
}
var name: Unmanaged<CFString>?
MIDIObjectGetStringProperty(dest, kMIDIPropertyDisplayName, &name)
print(name?.takeUnretainedValue() as! String)
var gmOn : [UInt8] = [ 0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7 ]
var pktlist = MIDIPacketList()
var current = MIDIPacketListInit(&pktlist)
current = MIDIPacketListAdd(&pktlist, MemoryLayout<MIDIPacketList>.stride, current, 0, gmOn.count, &gmOn)
MIDISend(port, dest, &pktlist)
在这两种情况下,打印的设备名称都是正确的,每次通话的状态都是noErr
。
我注意到如果我要求kMIDIManufacturerName
属性我得到不同的结果 - 特别是使用第一种方法我得到Generic
,来自MIDI设备所连接的USB MIDI接口,并使用第二种方法,我通过音频MIDI设置应用程序获得Roland
的值。
我想要使用第二种方法的原因是我可以过滤掉没有所需制造商名称的设备,但如上所述,我无法获得工作输出。
任何人都可以解释这两种方法之间的区别,以及为什么后者不起作用,理想情况下提供一个关于如何解决这个问题的建议?
答案 0 :(得分:1)
听起来您只想找到与某个制造商的设备通信的MIDI目标端点。不幸的是,这是不可能的,因为没有协议来发现存在什么MIDI设备,它们的属性是什么,以及它们如何连接到计算机。
(请记住,MIDI是20世纪80年代的原始技术。它甚至不需要双向通信。MIDI设备有完全有效的MIDI设置可以发送数据,但永远不会从,反之亦然。)
计算机知道哪些MIDI接口连接到它(例如,USB-MIDI接口)。 CoreMIDI称这些“设备”。您可以找到有多少端口,每个端口有多少端口等。但是没有办法找到有关物理MIDI设备的任何信息,例如连接到它们的键盘和合成器。
“外部设备”试图解决发现问题。当您按“添加设备”按钮时,它们就是音频MIDI设置中出现的内容。这就是全部!
理想情况下,您的用户会在其设置中为每个物理MIDI设备创建一个外部设备,输入每个设备的所有属性,并以完全镜像其物理MIDI线缆的方式设置所有连接。
不幸的是,实际上:
documentation for MIDIGetDevice()提出了一个很好的建议:
如果客户端遍历系统中的设备和实体,它将不会访问由其他客户端创建的任何虚拟源和目标。此外,设备迭代将返回“离线”(过去存在但当前不存在)的设备,而通过系统的源和目标的迭代将不包括离线设备的端点。
因此,客户端通常应使用MIDIGetNumberOfSources,MIDIGetSource,MIDIGetNumberOfDestinations和MIDIGetDestination,而不是遍历设备和实体来定位端点。
换句话说:使用MIDIGetNumberOfDestinations
和MIDIGetDestination
获取可能的目的地,然后让您的用户选择其中一个。就是这样。
如果确实想要做更多事情:
MIDIEndpointGetEntity
和MIDIEndpointGetDevice
进入MIDI界面。MIDIObjectGetDataProperty
获取属性kMIDIPropertyConnectionUniqueID
的值,该属性是已连接对象的唯一ID数组。然后使用MIDIObjectFindByUniqueID
来访问该对象。 outObjectType
会告诉你它是什么类型的对象。但这很尴尬,而且你不能保证找到任何有用的信息。
答案 1 :(得分:1)
根据Kurt Revis的回答提示,我找到了解决方案。
我需要查找的目标与外部设备的源相关联,并使用该源的kMIDIPropertyConnectionUniqueID
属性找到它们之间的连接。
使用以下代码替换问题中if / else
分支中的代码:
var external = MIDIGetExternalDevice(0)
var entity = MIDIDeviceGetEntity(external, 0)
var src = MIDIEntityGetSource(entity, 0)
var connID : Int32 = 0
var dest = MIDIObjectRef()
var type = MIDIObjectType.other
MIDIObjectGetIntegerProperty(src, kMIDIPropertyConnectionUniqueID, &connID)
MIDIObjectFindByUniqueID(connID, &dest, &type)
属性转储表明连接唯一ID属性实际上是一个数据属性(可能包含多个ID),但结果CFData
似乎是大端格式,因此将其作为整数属性读取似乎工作得很好。