1

I'm working on an application that utilises MIDI equipment. After some fooling around in a playground with CoreMIDI, I found how to get MIDI input signal, so I implemented this:

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)
}

Which works like a charm for using the signal. Now, I want to use the signal to edit the value of an NSSlider, so, naturally, what I came up with was this:

self.slider_one?.integerValue = params[2]

However, when I try to do that, I get the following error:

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

So what I'm wondering is, is there a way to access self from inside of that closure, or is there some other way to use MIDI input in swift?

Thanks.

--- Edit: As asked, my code after 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)

}
sushibrain
  • 2,712
  • 5
  • 33
  • 62
  • This might also be helpful: [How to use instance method as callback for function which takes only func or literal closure](http://stackoverflow.com/questions/33260808/how-to-use-instance-method-as-callback-for-function-which-takes-only-func-or-lit). – Martin R Apr 24 '17 at 13:50
  • @MartinR Tried to do it like this: `UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())` however, I still get the same error – sushibrain Apr 24 '17 at 13:59
  • You must not use `self` inside the closure, you have to reconstruct it from the context pointer, see `let mySelf = Unmanaged ...` in the linked-to answer. – Martin R Apr 24 '17 at 14:02
  • I never directly referenced `self`, I used `let mySelf = Unmanaged...`, after which I tried to use the `mySelf` variable @MartinR – sushibrain Apr 24 '17 at 14:04
  • Can you add the modified code to your question? I am fairly sure that it works. – Martin R Apr 24 '17 at 14:05
  • @MartinR Added the code :) – sushibrain Apr 24 '17 at 14:08
  • You must not use the `observer` variable in the closure, You have to pass it through a C context pointer. Something like `MIDIPortConnectSource(inPort, self.source, observer)` and `let mySelf = Unmanaged.fromOpaque(srcConnRefCon!).takeUnretainedValue()` – Martin R Apr 24 '17 at 14:11
  • @MartinR So far, so good. However, when MIDI signal comes in, I get a bad access exception, problem for another day though. Thanks! – sushibrain Apr 24 '17 at 14:28
  • It perhaps should be `fromOpaque(readProcRefCon)` in my last comment. – Martin R Apr 24 '17 at 14:30
  • @MartinR Still dumps the same issue haha, let the debugging begin! – sushibrain Apr 24 '17 at 14:33
  • @MartinR In case you're interested, it comes forward on both `fromOpaque(readProcRefCon!)` and `fromOpaque(srcConnRefCon!)` and only when accessed, doesn't seem to be a whole lot of info to work with lol – sushibrain Apr 24 '17 at 14:42
  • Discussion moved to chat: http://chat.stackoverflow.com/rooms/142508/discussion-between-wesley-peeters-and-martin-r. – Martin R Apr 24 '17 at 14:45

1 Answers1

0

Usually you can pass some context into C functions, for example:

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

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

then pass it to your C function:

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

and inside your closure function:

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

(written without testing)

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Seems like an awesome solution, however, how would I be able to access `self` from the context variable? – sushibrain Apr 24 '17 at 13:43
  • @WesleyPeeters Actually, you can pass `self` as the context, or you can add it directly to the `struct`. Or you can add a function `getSelf` to the `struct`. My point was that usually you don't need to pass `self`. Instead, you can pass a function/closure that has `self` already wrapped inside, like my `setSliderValue` does. – Sulthan Apr 24 '17 at 13:45