Initializing MIDIMetaEvent structure

感情迁移 提交于 2019-12-03 08:18:23

The AudioToolbox framework defines MIDIMetaEvent as

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

where data[1] is actually used as a "variable length array". In (Objective-)C one can just allocate a pointer to a memory block of the actually needed size:

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

Swift is more strict with pointer casts and fixed size arrays are mapped to Swift tuples (which can be cumbersome to handle with).

The following utility class shows how this could be solved:

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

Then you can create an instance with

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

and pass me.metaEventPtr to the Swift functions taking a UnsafePointer<MIDIMetaEvent> argument.


Update for Swift 3/4:

Simply converting a pointer to a different type is no longer possible, it must be "rebound":

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

Create an instance with custom data:

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

Pass to a function taking a UnsafePointer<MIDIMetaEvent> argument:

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

My $0.02

Messing around with pointers just to create a meta event is a pain, but we're stuck with it. It looks like this isn't going to change anytime soon.

I've tried this and it works. I write the MusicSequence to a file and then open it in Sibelius 7. It groks the text, time signature, and key signature events I added. btw., you add the time signature event to the sequence's tempo track. Yeah, ptr[1... isn't explicitly allocated. Haven't hit a problem yet. Any suggestions? (critique welcome)

    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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!