Enumerate Midipacketlist In Swift Part 1
by
Enumerate MIDIPacketList in Swift: Part 1
In this post we’re going to look at how we can use Swift 2’s SequenceType and GeneratorType to enumerate the packets of a MIDIPacketList.
- A SequenceType is “a type that can be iterated with a for…in loop.”
- A GeneratorType “encapsulates iteration state and interface for iteration over a sequence.”
But before we dive into the implementation, let’s understand how MIDI packets are represented in Swift.
What is a packet list?
CoreMIDI transports messages via MIDIPacketList, which is essentially a linked list of MIDIPacket structs in that its packets can only be sequentially iterated over.
// Extracted from the CoreMIDI Swift framework
public struct MIDIPacketList {
public var numPackets: UInt32
public var packet: (MIDIPacket)
public init()
public init(numPackets: UInt32, packet: (MIDIPacket))
}
Notice that packet
is a singular MIDIPacket, not an array like you might expect. This is a side effect of the Objective-C struct from which this Swift struct was generated:
// Extracted from the CoreMIDI Objective-C framework header
struct MIDIPacketList
{
UInt32 numPackets;
MIDIPacket packet[1];
};
So how do we iterate over the packets? The answer was introduced in Swift 2: MIDIPacketNext.
MIDIPacketNext is an Objective-C macro made available in Swift 2. Contrary to what the docs imply, MIDIPacketNext works on pre-iOS 9 devices running Swift thanks to the embedded runtime.
A C implementation might look like this:
MIDIPacket *packet = &packetList->packet[0];
for (int i = 0; i < packetList->numPackets; ++i) {
...
packet = MIDIPacketNext(packet);
}
But using Swift extensions we’re going to allow enumeration like this:
let packetList: MIDIPacketList
for packet in packetList {
...
}
MIDIPacketList as a Sequence
Treating MIDIPacketList like a sequence is easy with Swift extensions:
extension MIDIPacketList: SequenceType {
}
Why SequenceType instead of CollectionType?
CollectionType requires subscript access at arbitrary indexes. This isn’t needed when processing a stream of MIDI packets. SequenceType is a more accurate model of how MIDIPackets are consumed. Learn more.
SequenceType requires two things for conformity:
- a
Generator
typealias, and - an implementation of
func generate() -> Generator
.
Classic implementation
Someone coming from Objective-C has been trained to see a protocol and mechanically start implementing its required methods. In this case that would entail defining the Generator type:
public typealias Generator = MIDIPacketListGenerator
and implementing the generate method:
public func generate() -> Generator {
return Generator(packetList: self)
}
MIDIPacketListGenerator must be a GeneratorType, so we’d implement the struct accordingly:
public struct MIDIPacketListGenerator: GeneratorType {
public typealias Element = MIDIPacket
public mutating func next() -> Element? {
// iterator logic
}
}
With a working implementation looking something like this:
public struct MIDIPacketListGenerator : GeneratorType {
public typealias Element = MIDIPacket
init(packetList: MIDIPacketList) {
let ptr = UnsafeMutablePointer<MIDIPacket>.alloc(1)
ptr.initialize(packetList.packet)
self.packet = ptr
self.count = packetList.numPackets
}
public mutating func next() -> Element? {
guard self.packet != nil && self.index < self.count else { return nil }
let lastPacket = self.packet!
self.packet = MIDIPacketNext(self.packet!)
self.index++
return lastPacket.memory
}
// Extracted packet list info
var count: UInt32
var index: UInt32 = 0
// Iteration state
var packet: UnsafeMutablePointer<MIDIPacket>?
}
But this is a pretty long solution in Swift, a language with type inference and a variety of other useful tools. In part 2 of this post we’ll reduce the size of this implementation using Swift’s AnyGenerator type. Read Part 2 now.
View the full implementation of this approach.
References
- Swift 2 and CoreMIDI by Gene De Lisa