初始化MIDIMetaEvent结构

Yoh*_*hst 5 macos ios coremidi swift

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

struct MIDIMetaEvent {
  var metaEventType: UInt8
  var unused1: UInt8
  var unused2: UInt8
  var unused3: UInt8
  var dataLength: UInt32
  var data: (UInt8)
}
Run Code Online (Sandbox Code Playgroud)

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

var msg = MIDIMetaEvent(
  metaEventType : UInt8(0x7F),
  unused1       : UInt8(0),
  unused2       : UInt8(0),
  unused3       : UInt8(0),
  dataLength    : UInt32(myData.count),
  data          : UnsafeBufferPointer<UInt8>(start: UnsafePointer<UInt8>(myData), count:myData.count) )
Run Code Online (Sandbox Code Playgroud)

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

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

Mar*_*n R 7

AudioToolbox框架定义MIDIMetaEvent

typedef struct MIDIMetaEvent
{
    UInt8       metaEventType;
    UInt8       unused1;
    UInt8       unused2;
    UInt8       unused3;
    UInt32      dataLength;
    UInt8       data[1];
} MIDIMetaEvent;
Run Code Online (Sandbox Code Playgroud)

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

MIDIMetaEvent *mep = malloc(sizeof(MIDIMetaEvent) + data.count);
Run Code Online (Sandbox Code Playgroud)

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

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

class MyMetaEvent {
    private let size: Int
    private let mem : UnsafeMutablePointer<UInt8>

    let metaEventPtr : UnsafeMutablePointer<MIDIMetaEvent>

    init(type: UInt8, data: [UInt8]) {
        // Allocate memory of the required size:
        size = sizeof(MIDIMetaEvent) + data.count
        mem = UnsafeMutablePointer<UInt8>.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)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以创建一个实例

let me = MyMetaEvent(type: 0x7F, data: myData)
Run Code Online (Sandbox Code Playgroud)

并传递me.metaEventPtr给Swift函数进行UnsafePointer<MIDIMetaEvent> 参数.


Swift 3/4更新:

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

class MyMetaEvent {
    private let size: Int
    private let mem: UnsafeMutablePointer<UInt8>

    init(type: UInt8, data: [UInt8]) {
        // Allocate memory of the required size:
        size = MemoryLayout<MIDIMetaEvent>.size + data.count
        mem = UnsafeMutablePointer<UInt8>.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<MIDIMetaEvent>) -> Void) {
        mem.withMemoryRebound(to: MIDIMetaEvent.self, capacity: 1) { metaEventPtr in
            body(metaEventPtr)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

let me = MyMetaEvent(type: 0x7F, data: ...)
Run Code Online (Sandbox Code Playgroud)

传递给UnsafePointer<MIDIMetaEvent>参数的函数:

me.withMIDIMetaEventPtr { metaEventPtr in
    let status = MusicTrackNewMetaEvent(track, 0, metaEventPtr)
}
Run Code Online (Sandbox Code Playgroud)