Yohst Yohst - 4 months ago 7x
Swift Question

Initializing MIDIMetaEvent structure

I am struggling to initialize the MIDIMetaEvent structure found in MusicPlayer.h with swift The header file defines the structure as follows:

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

Which seems fairly straightforward up until that 'data' member. Is that a 1 element tuple definition? I can easily initialize all other struct elements but have tried in vain to set 'data' to anything else than a single value. In my code I used an UInt8 array called myData and attempted to init the structure like so:

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

But the compiler is not happy with this and complains about "UnsafeBufferPointer no convertible to UInt8". If I simply set data to a single value but set dataLength to a value more than 1, the resulting MIDIEventData shows that the first value in the event is what I stuck in 'data' followed by gibberish data bytes in accordance with 'dataLength' bytes. So clearly 'data' is seen as some sort of continuous memory.

So how do I set that 'data' element to UInt8 elements from an array?


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:

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.