         guard packetDataCount > 0 else { return [] }
         let timeStamp = packetPtr.pointee.timeStamp
 
-        var pendingMessageStatus: UInt8 = 0
-        var pendingDataBytes: [UInt8] = [0, 0]
-        var pendingDataIndex = 0
-        var pendingDataLength = 0
-
+        var pendingMessage = PendingMessage()
         var readingInvalidData: Data?
 
         // Safely getting to the packet data is more difficult than it should be.

        // in the tuple in the struct. There may be more.
        // Do it the hard way instead.
        let rawPacketDataPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(packetPtr) + MemoryLayout.offset(of: \MIDIPacket.data)!, count: packetDataCount)
        return rawPacketDataPtr.enumerated().compactMap { (byteIndex, byte) -> Message? in
            var message: Message?
            var byteIsInvalid = false

            if byte >= 0xF8 {
                // Real Time message
                if let messageType = SystemRealTimeMessage.MessageType(rawValue: byte) {
                    message = SystemRealTimeMessage(timeStamp: timeStamp, type: messageType)
                }
                else {
                    // Byte is invalid
                    byteIsInvalid = true
                }
            }
            else if byte < 0x80 {
                if let data = readingSysExData {
                    readingSysExData?.append(byte)

                    // Tell the delegate we're still reading, every 256 bytes
                    let sysExMessageDataCount = 1 /* for 0xF0 */ + data.count
                    if sysExMessageDataCount % 256 == 0 {
                        delegate?.parserIsReadingSysEx(self, length: sysExMessageDataCount)
                    }
                }
                else if pendingDataIndex < pendingDataLength {
                    pendingDataBytes[pendingDataIndex] = byte
                    pendingDataIndex += 1

                    if pendingDataIndex == pendingDataLength {
                        // This message is now done
                        if let status = SystemCommonMessage.Status(rawValue: pendingMessageStatus) {
                            message = SystemCommonMessage(timeStamp: timeStamp, status: status, data: pendingDataBytes)
                        }
                        else {
                            message = VoiceMessage(timeStamp: timeStamp, statusByte: pendingMessageStatus, data: pendingDataBytes)
                        }
                    }
                }
                else {
                    // Skip this byte -- it is invalid
                    byteIsInvalid = true
                }
            }
            else {
                if readingSysExData != nil {
                    message = finishSysExMessage(validEnd: (byte == 0xF7))
                }

                pendingMessageStatus = byte
                pendingDataLength = 0
                pendingDataIndex = 0

                switch byte & 0xF0 {
                case 0x80,    // Note off
                     0x90,    // Note on
                     0xA0,    // Aftertouch
                     0xB0,    // Controller
                     0xE0:    // Pitch wheel
                    pendingDataLength = 2

                case 0xC0,    // Program change
                     0xD0:    // Channel pressure
                    pendingDataLength = 1

                case 0xF0:
                    // System common message
                    if byte == 0xF0 {
                        // System exclusive
                        readingSysExData = Data()
                        startSysExTimeStamp = timeStamp
                        delegate?.parserIsReadingSysEx(self, length: 1)
                    }
                    else if byte == 0xF7 {
                        // System exclusive ends--already handled above.
                        // But if this is showing up outside of sysex, it's invalid.
                        if message == nil {
                            byteIsInvalid = true
                        }
                    }
                    else if let systemCommonMessageStatus = SystemCommonMessage.Status(rawValue: byte) {
                        let dataLength = systemCommonMessageStatus.otherDataLength
                        if dataLength > 0 {
                            pendingDataLength = dataLength
                        }
                        else {
                            message = SystemCommonMessage(timeStamp: timeStamp, status: systemCommonMessageStatus, data: [])
                        }
                    }
                    else {
                        // Invalid message
                        byteIsInvalid = true
                    }

                default:
                    // This can't happen, but handle it anyway
                    byteIsInvalid = true
                }
            }

            if !ignoresInvalidData {
                if byteIsInvalid {
                    if readingInvalidData == nil {
                        readingInvalidData = Data()
                    }
                    readingInvalidData?.append(byte)
                }

                if let invalidData = readingInvalidData,
                   !byteIsInvalid || byteIndex == packetDataCount - 1 {
                    // We hit the end of a stretch of invalid data.
                    message = InvalidMessage(timeStamp: timeStamp, data: invalidData)
                    readingInvalidData = nil
                }
            }

            message?.originatingEndpoint = originatingEndpoint
            return message
        }
    }
    // swiftlint:enable cyclomatic_complexity function_body_length

    private func finishSysExMessage(validEnd: Bool) -> SystemExclusiveMessage? {
        // NOTE: If we want, we could refuse sysex messages that don't end in 0xF7.

    private var startSysExTimeStamp: MIDITimeStamp = 0
    private var sysExTimeOutTimer: Timer?

    private struct PendingMessage {
        var status: UInt8 = 0
        var data: [UInt8] = []
        var expectedCount: Int = 0
    }

    private func messagesForPacket(_ packetPtr: UnsafePointer<MIDIPacket>) -> [Message] {
        // Split this packet into separate MIDI messages.

