I am looking at some of the examples of midi output using core midi.
Specifically this question
and this
I have code that works based on these in objC and I now want to try to translate that to swift.
the line I least understand is this one: MIDIPacketList* pktList = (MIDIPacketList*) pktBuffer;
I read this as declaring a pointer pktlist of type MIDIPacketList and assigning it the value of pktBuffer, cast to the type MIDIPacketList
I'm new to C and objC and this makes no sense to me.
MIDIPacketList is a struct defined here:
in what way does it make sense to cast a bytearray to the struct type MIDIPacketList and what is this trying to achieve? I guess it is trying to define the size of the packet list, but why do we need to do that here, and how does this do it anyway? why is it not sufficient to do it with MIDIPacketListAdd as happens a few lines later?
here is my attempt at a midi output class in swift - can anyone see what is going wrong? this code gives no errors in Xcode until it is run. I have a working version of this in objC, but i can't get the size of the packet list defined in swift. (at least I think that's the problem)
import Foundation
import CoreMIDI
class MidiOutClass {
var midiClient = MIDIClientRef()
var midiSource = MIDIEndpointRef()
func openOutput() {
MIDIClientCreate("MIDI client", nil, nil, &midiClient)
MIDISourceCreate(midiClient, "MIDI Source",&midiSource)
println("midi out opened")//should only do this if successful
}
func noteOn(channel: Int, note: Int, velocity:Int) {
midisend((0x90+channel), note: note, value: velocity)
}
func polyAfter(channel: Int, note: Int, value:Int) {
midisend((0xA0+channel), note: note, value: value)
}
func noteOff(channel: Int, note: Int) {
midisend((0x90+channel), note: note, value: 0 )
}
func midisend(status:Int, note: Int, value:Int) {
var packet: UnsafeMutablePointer<MIDIPacket> = nil
//var buffer = [Byte](count:1024, repeatedValue: 0)
//this is the array I'm trying to use in a similar way to the obj C.
var packetList: UnsafeMutablePointer<MIDIPacketList> = nil
let midiDataToSend:[Byte] = [Byte(status), Byte(note), Byte(value)];
packet = MIDIPacketListInit(packetList);
packet = MIDIPacketListAdd(packetList, 1024, packet, 0, 3, midiDataToSend);
if (packet == nil ) {
println("failed to send the midi.")
} else {
MIDIReceived(midiSource, packetList)
println("sent some stuff")
}
}
}//end MidiOutClass
I found an answer at this question
the byte array in the objC version is a nasty hack to allocate some memory to the packetList
here's my revised code, which now works.
func midisend(status:Int, note: Int, value:Int) {
var packet = UnsafeMutablePointer<MIDIPacket>.alloc(1)
var packetList = UnsafeMutablePointer<MIDIPacketList>.alloc(1)
let midiDataToSend:[Byte] = [Byte(status), Byte(note), Byte(value)];
packet = MIDIPacketListInit(packetList);
packet = MIDIPacketListAdd(packetList, 1024, packet, 0, 3, midiDataToSend);
if (packet == nil ) {
println("failed to send the midi.")
} else {
MIDIReceived(midiSource, packetList)
println("sent some stuff")
}
packet.destroy()
//packet.dealloc(1)
packetList.destroy()
packetList.dealloc(1)
}
However, the commented out dealloc seems to be unnecessary, I'm not exactly sure why yet. Does MIDIReceived take care of it?
If anyone has a better solution - maybe without using pointers at all, please post it!
This is how I ended up going about it. I used the example of the byte buffer as found in various C / ObjC examples online, but I found I had to allocate a little bit more space for the MIDIPacketList struct itself.
A MidiEvent is just a UInt8 array. For usual MIDI events, it has a length of 3 bytes: (Status, Data, Data).
/// A UInt8 array, usually 3 bytes long
public typealias MidiEvent = [UInt8]
extension MIDIPacketList {
init(midiEvents: [MidiEvent]) {
let timestamp = MIDITimeStamp(0) // do it now
let totalBytesInAllEvents = midiEvents.reduce(0) { total, event in
return total + event.count
}
// Without this, we'd run out of space for the last few MidiEvents
let listSize = MemoryLayout<MIDIPacketList>.size + totalBytesInAllEvents
// CoreMIDI supports up to 65536 bytes, but in practical tests it seems
// certain devices accept much less than that at a time. Unless you're
// turning on / off ALL notes at once, 256 bytes should be plenty.
assert(totalBytesInAllEvents < 256,
"The packet list was too long! Split your data into multiple lists.")
// Allocate space for a certain number of bytes
let byteBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: listSize)
// Use that space for our MIDIPacketList
self = byteBuffer.withMemoryRebound(to: MIDIPacketList.self, capacity: 1) { packetList -> MIDIPacketList in
var packet = MIDIPacketListInit(packetList)
midiEvents.forEach { event in
packet = MIDIPacketListAdd(packetList, listSize, packet, timestamp, event.count, event)
}
return packetList.pointee
}
byteBuffer.deallocate() // release the manually managed memory
}
}
So if you're just sending one midi note, it'd look something like this. This example would send a NoteOn message to the Middle C on Channel 1 with a velocity of 100. You should use helper functions to make these MidiEvents though, rather than hard coding them ;)
var packets = MIDIPacketList(midiEvents: [[0x90, 60, 100]])
MIDISend(clientOutputPort, destination, &packetList)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With