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:
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)
来源:https://stackoverflow.com/questions/27724055/initializing-midimetaevent-structure