Initializing MIDIMetaEvent structure

≯℡__Kan透↙ 提交于 2019-12-21 02:45:12

问题


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?


回答1:


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



回答2:


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)


来源:https://stackoverflow.com/questions/27724055/initializing-midimetaevent-structure

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