初始化MIDIMetaEvent结构

时间:2014-12-31 17:00:31

标签: ios macos swift coremidi

我正在努力用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<UInt8>(start: UnsafePointer<UInt8>(myData), count:myData.count) )

但编译器对此并不满意并抱怨&#34; UnsafeBufferPointer无法转换为UInt8&#34;。如果我只是将数据设置为单个值但将dataLength设置为大于1的值,则生成的MIDIEventData会显示事件中的第一个值是我所处理的数据&#39;其次是根据&quot; dataLength&#39;的乱码数据字节。字节。这么清楚&#39;数据&#39;被视为某种连续的记忆。

那么如何设置数据&#39;元素到数组的UInt8元素?

2 个答案:

答案 0 :(得分:6)

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

然后您可以使用

创建实例
let me = MyMetaEvent(type: 0x7F, data: myData)

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

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

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

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

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

答案 1 :(得分:0)

我的$ 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)