Swift 2 到 Swift 3 转换 Midi Input
Posted
技术标签:
【中文标题】Swift 2 到 Swift 3 转换 Midi Input【英文标题】:Swift 2 to swift 3 conversion Midi Input 【发布时间】:2016-07-08 03:09:43 【问题描述】:我希望有人可以帮助我使用 Xcode 8 和 swift 3 我有一个操场文件 Xcode 7 swift 2,其中涉及 Midi Input 的 Midi 回调,在 7 中一切正常
我尝试转换为 8,但它带来了有关内存的错误和一些我认为不严重的名称更改我还使用 PlaygroundSupport 重新定义了无限循环 但是我无法克服的错误涉及 MyMIDIReadProc 在
MIDIInputPortCreate(midiClient, "MidiTest_InPort", MyMIDIReadProc, nil, &inPort);
错误说 无法将类型“(pktList:UnsafePointer,readProcRefCon:UnsafeMutablePointer,srcConnRefCon:UnsafeMutablePointer)-> Void”的值转换为预期的参数类型“MIDIReadProc”(又名“@convention(c)(UnsafePointer,Optional>,Optional>)->( )')
我的理解是它需要插入一些描述的@convention(c) 包装器。我认为我走在正确的轨道上,因为你可以包装一个函数,但我对把它放在哪里的了解已经用完了。我再次希望有人可以提供建议
感谢阅读 因为我是自学的,所以对任何不好的语言表示歉意
这是原始的 Xcode 7 代码
import Cocoa
import CoreMIDI
import XCPlayground
func getDisplayName(obj: MIDIObjectRef) -> String
var param: Unmanaged<CFString>?
var name: String = "Error";
let err: OSStatus = MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, ¶m)
if err == OSStatus(noErr)
name = param!.takeRetainedValue() as String
return name;
func MyMIDIReadProc(pktList: UnsafePointer<MIDIPacketList>,
readProcRefCon: UnsafeMutablePointer<Void>, srcConnRefCon: UnsafeMutablePointer<Void>) -> Void
let packetList:MIDIPacketList = pktList.memory;
let srcRef:MIDIEndpointRef = UnsafeMutablePointer<MIDIEndpointRef>(COpaquePointer(srcConnRefCon)).memory;
print("MIDI Received From Source: \(getDisplayName(srcRef))");
var packet:MIDIPacket = packetList.packet;
for _ in 1...packetList.numPackets
let bytes = Mirror(reflecting: packet.data).children;
var dumpStr = "";
// bytes mirror contains all the zero values in the ridiulous packet data tuple
// so use the packet length to iterate.
var i = packet.length;
for (_, attr) in bytes.enumerate()
dumpStr += String(format:"$%02X ", attr.value as! UInt8);
--i;
if (i <= 0)
break;
print(dumpStr)
packet = MIDIPacketNext(&packet).memory;
var midiClient: MIDIClientRef = 0;
var inPort:MIDIPortRef = 0;
var src:MIDIEndpointRef = MIDIGetSource(0);
MIDIClientCreate("MidiTestClient", nil, nil, &midiClient);
MIDIInputPortCreate(midiClient, "MidiTest_InPort", MyMIDIReadProc, nil, &inPort);
MIDIPortConnectSource(inPort, src, &src);
// Keep playground running
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true;
这是转换后的 Xcode 8 代码
var str = "Hello, playground"
import Cocoa
import CoreMIDI
import XCPlayground
import PlaygroundSupport
func getDisplayName(obj: MIDIObjectRef) -> String
var param: Unmanaged<CFString>?
var name: String = "Error";
let err: OSStatus = MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, ¶m)
if err == OSStatus(noErr)
name = param!.takeRetainedValue() as String
return name;
func MyMIDIReadProc(pktList: UnsafePointer<MIDIPacketList>,
readProcRefCon: UnsafeMutablePointer<Void>, srcConnRefCon: UnsafeMutablePointer<Void>) -> Void
let packetList:MIDIPacketList = pktList.pointee;
let srcRef:MIDIEndpointRef = UnsafeMutablePointer<MIDIEndpointRef>(OpaquePointer(srcConnRefCon)).pointee;
print("MIDI Received From Source: \(getDisplayName(obj: srcRef))");
var packet:MIDIPacket = packetList.packet;
for _ in 1...packetList.numPackets
let bytes = Mirror(reflecting: packet.data).children;
var dumpStr = "";
var i = packet.length;
for (_, attr) in bytes.enumerated()
dumpStr += String(format:"$%02X ", attr.value as! UInt8);
i -= 1;
if (i <= 0)
break;
print(dumpStr)
packet = MIDIPacketNext(&packet).pointee;
var midiClient: MIDIClientRef = 0;
var inPort:MIDIPortRef = 0;
var src:MIDIEndpointRef = MIDIGetSource(0);
MIDIClientCreate("MidiTestClient", nil, nil, &midiClient);
MIDIInputPortCreate(midiClient, "MidiTest_InPort", MyMIDIReadProc, nil, &inPort);
MIDIPortConnectSource(inPort, src, &src);
PlaygroundPage.current.needsIndefiniteExecution = true
【问题讨论】:
【参考方案1】:指针类型在 Swift 3 中发生了巨大的变化。许多基于 C 的 API 的签名也发生了相应的变化。
手动进行这些更改会很痛苦。只需稍加修改,您就可以让 Swift 为您工作。
尝试更改函数头:
func MyMIDIReadProc(pktList: UnsafePointer<MIDIPacketList>,
readProcRefCon: UnsafeMutablePointer<Void>, srcConnRefCon: UnsafeMutablePointer<Void>) -> Void
到一个闭包声明:
let MyMIDIReadProc: MIDIReadProc = pktList, readProcRefCon, srcConnRefCon in
Swift 以这种风格完美地推断参数类型。
您可能需要修复指针类型转换:
let srcRef:MIDIEndpointRef = UnsafeMutablePointer<MIDIEndpointRef>(OpaquePointer(srcConnRefCon)).pointee;
到这样的事情:
//I'm not sure using `!` is safe here...
let srcRef: MIDIEndpointRef = UnsafeMutablePointer(srcConnRefCon!).pointee
(顺便说一句,你的 Xcode 7 代码中的等效部分有点多余。你不需要在那里使用中间的COpaquePointer
。)
在 Swift 3 中,指针不能为 nil,可空指针用 Optionals 表示。您可能需要进行许多其他修复才能在 Swift 3 中使用基于 C 的 API。
【讨论】:
【参考方案2】:OOPer 正在为您指明(咳咳)正确的方向。这是一篇关于使用 Swift 3 Core MIDI 以及工作 github 存储库的博文。
【讨论】:
【参考方案3】:假设您使用的是 CoreMIDI 1.3 或更高版本,使用MIDIInputPortCreateWithBlock
而不是MIDIInputPortCreate
可能会更幸运。
此方法将 Swift 块作为参数,而不需要 @convention(c)
函数引用,使其更适合在属于 Swift 类的方法中使用,例如:
public func midiReadBlock(ptr: UnsafePointer<MIDIPacketList>, _: UnsafeMutableRawPointer?) -> Void
let list: MIDIPacketList = ptr.pointee
...
您可能还会发现这两个扩展很有用。
这个(源自here)允许您使用for pkt in list
直接迭代MIDIPacketList
:
extension MIDIPacketList: Sequence
public func makeIterator() -> AnyIterator<MIDIPacket>
var iterator: MIDIPacket?
var nextIndex: UInt32 = 0
return AnyIterator
nextIndex += 1
if nextIndex > self.numPackets return nil
if iterator != nil
iterator = withUnsafePointer(to: &iterator!) MIDIPacketNext($0).pointee
else
iterator = self.packet;
return iterator
这个添加了一个方法到 MIDIPacket
以将内容提取为 [UInt8]
而不必使用真正损坏的元组语法:
extension MIDIPacket
public var asArray: [UInt8]
let mirror = Mirror(reflecting: self.data)
let length = Int(self.length)
var result = [UInt8]()
result.reserveCapacity(length)
for (n, child) in mirror.children.enumerated()
if n == length
break
result.append(child.value as! UInt8)
return result
【讨论】:
以上是关于Swift 2 到 Swift 3 转换 Midi Input的主要内容,如果未能解决你的问题,请参考以下文章
使用 Swift 和 CoreMIDI 发送和接收 MIDI
AVAudioEngine MIDI 文件播放(当前进度+MIDI 结束回调)Swift