2017-04-24 2 views
0

Je travaille sur une application qui utilise un équipement MIDI. Après quelques niaisage dans une aire de jeux avec CoreMIDI, j'ai trouvé comment obtenir le signal d'entrée MIDI, donc je mis en œuvre cette:Accès à partir d'un pointeur de style 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) 
} 

Ce qui fonctionne comme un charme pour utiliser le signal. Maintenant, je veux utiliser le signal pour modifier la valeur d'un NSSlider, donc, naturellement, ce que je suis venu avec était la suivante:

self.slider_one?.integerValue = params[2] 

Cependant, lorsque je tente de le faire, je reçois l'erreur suivante:

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

donc ce que je me demande est-il un moyen d'accéder self à l'intérieur de cette fermeture, ou est-il une autre façon d'utiliser l'entrée MIDI rapide?

Merci.

--- Edit: Comme demandé, mon code après modification:

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

Cela pourrait aussi être utile: [Comment utiliser la méthode de l'instance comme rappel pour la fonction qui ne prend que la fermeture ou func littérale] (http://stackoverflow.com/questions/33260808/how-to-use-instance-method-as-callback-for-function-which-takes-only-func-ou-allumé). –

+0

@MartinR J'ai essayé de faire comme ceci: 'UnsafeRawPointer (Unmanaged.passUnretained (self) .toOpaque())' Cependant, je reçois toujours la même erreur –

+0

Vous ne devez pas utiliser 'self' dans la fermeture, vous devez le reconstruire à partir du pointeur de contexte, voir 'let mySelf = Unmanaged ...' dans la réponse liée. –

Répondre

0

En général, vous pouvez passer un peu de contexte dans des fonctions C, par exemple:

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

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

passe alors à votre fonction C:

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

et à l'intérieur de votre fonction de fermeture:

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

(écrit sans essai)

+0

Cela me semble une solution géniale, comment pourrais-je accéder à 'self' à partir de la variable de contexte? –

+0

@WesleyPeeters En fait, vous pouvez passer 'self' comme contexte, ou vous pouvez l'ajouter directement dans' struct'. Ou vous pouvez ajouter une fonction 'getSelf' à' struct'. Mon point était que généralement vous n'avez pas besoin de passer «self». Au lieu de cela, vous pouvez passer une fonction/fermeture qui a 'self' déjà enveloppé à l'intérieur, comme le fait mon' setSliderValue'. – Sulthan