2017-02-17 94 views
1

如果我使用if分支中的第一個方法獲取MIDIDestination代碼,並且發送了MIDI數據,則給定以下代碼。如果我使用else分支中的第二種方法,則不會發送數據。對CoreMIDI目標的混淆

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,從USB MIDI接口,MIDI設備連接,並用第二種方法,我得到通過音頻MIDI設置應用程序配置的值Roland

我想用第二種方法是專門這樣我就可以過濾掉不具有所期望的製造商名稱設備,但如上述,我不能再獲得工作輸出的原因。

誰能解釋這兩種方法之間的區別,爲什麼後者不起作用,最好提供一個建議,我怎麼能解決呢?

+0

你確定你正在得到正確的'實體'嗎?那麼'MIDIDeviceGetNumberOfEntities'呢? – Sulthan

+0

@Sulthan是的。只有一個「實體」,根據上面的說法,顯示的名稱是相同的(並且是正確的)。但是,返回的唯一ID不一樣。 – Alnitak

+0

好的,有趣的 - 如果我用'MIDIGetDevice(4)'而不是'MIDIGetExternalDevice(1)'代碼實際工作,並且我獲得相同的唯一ID,除非我仍然得到錯誤的製造商ID(即,一個用於實際的接口,而不是連接到它的單元)。該文檔不清楚「外部」設備和普通設備之間的差異,這裏:( – Alnitak

回答

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看起來是以big-endian格式,所以讀取它作爲一個整數屬性反而似乎工作正常。

+0

很高興你得到它的工作!該屬性的值可能是*單個整數(在這種情況下,您的代碼工作)或包含多個唯一ID的'CFData'。在後一種情況下,MIDIObjectGetIntegerProperty應該返回一個非零的錯誤。 –

+0

@KurtRevis似乎可以讀取一個'CFData',它只包含一個整數作爲一個整數而沒有任何問題 - 我懷疑反序列化代碼並不在意,但我應該試着從編程上弄清楚哪一個是'得到。文檔確實說了「從CoreMIDI 1.3開始,這個屬性也可以是CFDataRef,它包含一個大端的SInt32數組,允許指定一個驅動對象連接到多個外部對象(通過MIDI通過或分割)_」 – Alnitak

1

這聽起來像你想找到只MIDI目的地端點談某製造商的設備。不幸的是,這是不可能的,因爲沒有發現MIDI設備存在的協議,它們的屬性是什麼以及它們如何連接到計算機。 (記住,MIDI是原始的1980s技術,它甚至不需要雙向通信。有MIDI設備的完全有效的MIDI設置,你可以發送數據但永遠不能接收數據,反之亦然。)

計算機知道MIDI接口連接到它(例如,一個USB-MIDI接口)。 CoreMIDI稱這些「設備」。您可以找出有多少個端口,每個端口有多少個端口等等。但是沒有辦法找到有關物理MIDI設備的任何信息,例如連接到它們的鍵盤和合成器。

「外部設備」是企圖繞過發現問題。當您按下「添加設備」按鈕時,它們出現在音頻MIDI設置中。就這樣!

理想的情況下你的用戶將創造他們設置每個物理MIDI設備的外部設備,進入每一個的所有屬性,並建立在完全反映其物理MIDI線的方式全部連接。

不幸的是,在現實中:

  • 可能沒有任何外部設備。在「音頻MIDI設置」中創建它們並沒有多大好處,而且這是很多無聊的數據輸入,所以大多數人都不會感到困擾。
  • 如果有外部設備,則不能相信用戶添加的任何信息。例如,製造商可能不對,或者可能拼錯了。
  • 在用戶使用軟件之前強制用戶在Audio MIDI Setup中進行設置是非常不友好的。因此,沒有應用程序可以這麼做......因此沒有人在「音頻MIDI設置」中設置任何內容。這是一個雞與雞蛋的問題。
  • 即使有外部設備,用戶可能也想將MIDI發送到其他端點(如其他應用程序創建的虛擬端點),這些端點並未明顯連接到外部設備。你應該讓他們做他們想做的。

documentation for MIDIGetDevice()做一個很好的建議:

如果通過系統中的設備和實體客戶端迭代,它永遠不會訪問任何虛擬源和其他客戶端創建的目標。此外,設備迭代將返回「脫機」(過去存在但目前不存在)的設備,而通過系統源和目標的迭代將不包括脫機設備的端點。

因此,客戶端通常應該使用MIDIGetNumberOfSources,MIDIGetSource,MIDIGetNumberOfDestinations和MIDIGetDestination,而不是遍歷設備和實體來定位端點。

換句話說:使用MIDIGetNumberOfDestinationsMIDIGetDestination以獲得可能的目的地,然後讓你的用戶選擇其中之一。就這樣。

如果你真的希望做更多:

  • 給定一個目標端點,可以使用MIDIEndpointGetEntityMIDIEndpointGetDevice去的MIDI接口。
  • 給定任何MIDI對象,你可以找到它與其他對象的連接。使用MIDIObjectGetDataProperty可以獲取屬性kMIDIPropertyConnectionUniqueID的值,該值是連接對象的唯一ID的數組。然後使用MIDIObjectFindByUniqueID到達對象。 outObjectType會告訴你它是什麼樣的對象。

但是,這是相當尷尬,你不能保證找到任何有用的信息。

+0

我得到你來自哪裏,但這是針對不需要虛擬設備的MIDI補丁編輯器的,我希望通過Audio MIDI Setup應用程序配置的信息不僅可以獲得製造商,還可以獲得SysEx設備ID。通過屬性檢索的連接唯一ID是否採用big-endian格式?但是,如果我直接要求這個屬性是一個整數,他們看起來是正確的。 – Alnitak

+0

我已經在我的系統上傾倒了所有目的地,設備和外部設備的樹。目的地中的唯一連接ID與任何外部設備的連接ID之間沒有明顯的重疊。 http://pastebin.com/93xhZCCJ – Alnitak

+0

是的,唯一的ID應該是big-endian。不幸的是,從'NSData'中提取它們是一件痛苦的事情。我從來沒有在Swift中做過這個,所以橋接可能有問題嗎? [這是我在Objective-C中的做法](https://github.com/krevis/MIDIApps/blob/master/Frameworks/SnoizeMIDI/SMEndpoint.m#L306)。 –