初始化MIDIMetaEvent结构

我正在努力用swift初始化MusicPlayer.h中的MIDIMetaEvent结构头文件定义结构如下:

struct MIDIMetaEvent { var metaEventType: UInt8 var unused1: UInt8 var unused2: UInt8 var unused3: UInt8 var dataLength: UInt32 var data: (UInt8) } 

直到那个’数据’成员,这似乎相当简单。 这是1元素元组定义吗? 我可以很容易地初始化所有其他结构元素但是徒劳地试图将“数据”设置为除单个值之外的任何其他内容。 在我的代码中,我使用了一个名为myData的UInt8数组,并试图像这样初始化结构:

 var msg = MIDIMetaEvent( metaEventType : UInt8(0x7F), unused1 : UInt8(0), unused2 : UInt8(0), unused3 : UInt8(0), dataLength : UInt32(myData.count), data : UnsafeBufferPointer(start: UnsafePointer(myData), count:myData.count) ) 

但编译器对此并不满意,并抱怨“UnsafeBufferPointer无法转换为UInt8”。 如果我只是将数据设置为单个值但将dataLength设置为大于1的值,则生成的MIDIEventData显示事件中的第一个值是我在’data’中停留的值,后跟根据’dataLength’字节的乱码数据字节。 很明显,“数据”被视为某种连续的记忆。

那么如何将’data’元素设置为数组中的UInt8元素?

AudioToolbox框架将MIDIMetaEvent定义为

 typedef struct MIDIMetaEvent { UInt8 metaEventType; UInt8 unused1; UInt8 unused2; UInt8 unused3; UInt32 dataLength; UInt8 data[1]; } MIDIMetaEvent; 

其中data[1]实际上用作“可变长度数组”。 在(Objective-)C中,可以只指定一个指向实际所需大小的内存块的指针:

 MIDIMetaEvent *mep = malloc(sizeof(MIDIMetaEvent) + data.count); 

Swift对指针转换更严格,固定大小的数组映射到Swift元组(处理起来可能很麻烦)。

以下实用程序类显示了如何解决此问题:

 class MyMetaEvent { private let size: Int private let mem : UnsafeMutablePointer let metaEventPtr : UnsafeMutablePointer init(type: UInt8, data: [UInt8]) { // Allocate memory of the required size: size = sizeof(MIDIMetaEvent) + data.count mem = UnsafeMutablePointer.alloc(size) // Convert pointer: metaEventPtr = UnsafeMutablePointer(mem) // Fill data: metaEventPtr.memory.metaEventType = type metaEventPtr.memory.dataLength = UInt32(data.count) memcpy(mem + 8, data, UInt(data.count)) } deinit { // Release the allocated memory: mem.dealloc(size) } } 

然后你可以创建一个实例

 let me = MyMetaEvent(type: 0x7F, data: myData) 

并将me.metaEventPtr传递给Swift函数,该函数采用UnsafePointer参数。


Swift 3/4更新:

简单地将指针转换为不同类型是不可能的,它必须是“反弹”:

 class MyMetaEvent { private let size: Int private let mem: UnsafeMutablePointer init(type: UInt8, data: [UInt8]) { // Allocate memory of the required size: size = MemoryLayout.size + data.count mem = UnsafeMutablePointer.allocate(capacity: size) mem.initialize(to: 0, count: size) // Fill data: mem.withMemoryRebound(to: MIDIMetaEvent.self, capacity: 1) { metaEventPtr in metaEventPtr.pointee.metaEventType = type metaEventPtr.pointee.dataLength = UInt32(data.count) memcpy(&metaEventPtr.pointee.data, data, data.count) } } deinit { // Release the allocated memory: mem.deallocate(capacity: size) } func withMIDIMetaEventPtr(body: (UnsafePointer) -> Void) { mem.withMemoryRebound(to: MIDIMetaEvent.self, capacity: 1) { metaEventPtr in body(metaEventPtr) } } } 

使用自定义数据创建实例:

 let me = MyMetaEvent(type: 0x7F, data: ...) 

传递给一个采用UnsafePointer参数的函数:

 me.withMIDIMetaEventPtr { metaEventPtr in let status = MusicTrackNewMetaEvent(track, 0, metaEventPtr) } 

我的0.02美元

只是为了创建一个元事件而乱用指针是一种痛苦,但我们仍然坚持使用它。 看起来这不会很快改变。

我试过这个并且它有效。 我将MusicSequence写入文件,然后在Sibelius 7中打开它。它会记录我添加的文本,时间签名和关键签名事件。 顺便说一句,你将时间签名事件添加到序列的速度轨道。 是的,ptr [1 …没有明确分配。 尚未遇到问题。 有什么建议么? (欢迎评论)

  let text = "Piano" let data = [UInt8](text.utf8) var metaEvent = MIDIMetaEvent() metaEvent.metaEventType = 3 // track or sequence name metaEvent.dataLength = UInt32(data.count) withUnsafeMutablePointer(&metaEvent.data, { ptr in for i in 0 ..< data.count { ptr[i] = data[i] } }) let status = MusicTrackNewMetaEvent(track, 0, &metaEvent)