2017-04-24 52 views
0

我正在研究利用MIDI設備的應用程序。與CoreMIDI操場一些打打鬧鬧之後,我發現如何讓MIDI輸入信號,所以我實現了這一點:從C風格指針訪問自我

func makeInputSource() { 
    var midiClient : MIDIClientRef = 0 
    var inPort : MIDIPortRef = 0 

    MIDIClientCreate("WobClient" as CFString, nil, nil, &midiClient) 
    MIDIInputPortCreate(midiClient, "WobClient_InPort" as CFString, { 
     (pktList: UnsafePointer<MIDIPacketList>, readProcRefCon: UnsafeMutableRawPointer?, srcConnRefCon: UnsafeMutableRawPointer?) in 
     let packetList : MIDIPacketList = pktList.pointee 
     var packet : MIDIPacket = packetList.packet 

     for _ in 1...packetList.numPackets { 
      let bytes = Mirror(reflecting: packet.data).children 
      var params : [UInt64] = [] 

      var i = packet.length 
      for (_, attr) in bytes.enumerated() { 
       let string = String(format: "%02X ", attr.value as! UInt8) 
       params.append(UInt64(strtoul(string, nil, 16))) 
       i -= 1 

       if (i <= 0) { 
        break 
       } 
      } 

      packet = MIDIPacketNext(&packet).pointee 
     } 
    }, nil, &inPort) 
    MIDIPortConnectSource(inPort, self.source, &self.source) 
} 

這就像一個魅力的作品使用的信號。現在,我想用信號編輯NSSlider的價值,所以,自然而然,我想出了是這樣的:

self.slider_one?.integerValue = params[2] 

然而,當我嘗試這樣做,我得到以下錯誤:

A C function pointer cannot be formed from a closure that captures context 

所以,我想知道是,是否有從該封閉的內部訪問self的方式,或者是有使用MIDI輸入快捷一些其他的方式?

謝謝。

---編輯: 至於問,修改後我的代碼:

func makeInputSource() { 
    var midiClient : MIDIClientRef = 0 
    var inPort : MIDIPortRef = 0 
    var observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()) 

    MIDIClientCreate("WobClient" as CFString, nil, nil, &midiClient) 
    MIDIInputPortCreate(midiClient, "WobClient_InPort" as CFString, { 
     (pktList: UnsafePointer<MIDIPacketList>, readProcRefCon: UnsafeMutableRawPointer?, srcConnRefCon: UnsafeMutableRawPointer?) in 
     let packetList : MIDIPacketList = pktList.pointee 
     var packet : MIDIPacket = packetList.packet 

     for _ in 1...packetList.numPackets { 
      let bytes = Mirror(reflecting: packet.data).children 
      var params : [UInt64] = [] 

      var i = packet.length 
      for (_, attr) in bytes.enumerated() { 
       let string = String(format: "%02X ", attr.value as! UInt8) 
       params.append(UInt64(strtoul(string, nil, 16))) 
       i -= 1 

       if (i <= 0) { 
        break 
       } 
      } 

      let mySelf = Unmanaged<Wob>.fromOpaque(observer).takeUnretainedValue() 
      mySelf.slider_one?.integerValue = 25 // 25 is a test value 
      packet = MIDIPacketNext(&packet).pointee 
     } 

    }, &observer, &inPort) 
    MIDIPortConnectSource(inPort, self.source, &self.source) 

} 
+0

這也可能是有益的:如何使用實例方法作爲回調函數,只需FUNC或立即關閉(http://stackoverflow.com/questions/33260808 /如何使用的實例-方法-AS-回調換功能,其通吃僅-FUNC - 或亮)。 –

+0

@MartinR試圖這樣做:'UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())'然而,我仍然得到相同的錯誤 –

+0

你不能在閉包內使用'self',你必須重構它從上下文指針中,請參閱鏈接到的答案中的let mySelf = Unmanaged ...。 –

回答

0

通常你可以通過一些方面爲C的功能,例如:

struct MyContext { 
    var setSliderValue: (Int) -> Void   
} 

var context = MyContext(setSliderValue: { sliderValue in 
    Dispatch.queue.async { 
     self.slider_one?.integerValue = sliderValue 
    } 
)) 

然後將它傳遞給你的C功能:

MIDIInputPortCreate(midiClient, "WobClient_InPort" as CFString, { ... }, &context, &inPort) 

和裏面你的關閉功能:

let readContext = readProcRefCon!.assumingMemoryBound(to: MyContext.self) 
readContext.pointee.setSliderValue(params[2]) 

(書面未經測試)

+0

看起來像一個很棒的解決方案,但是,我將如何能夠從上下文變量訪問'self'? –

+0

@WesleyPeeters實際上,您可以將'self'作爲上下文,或者直接將其添加到'struct'中。或者你可以在'struct'中添加一個'getSelf'函數。我的觀點是,通常你不需要通過「自我」。相反,你可以傳遞一個已經包裝了'self'的函數/閉包,就像我的'setSliderValue'一樣。 – Sulthan