<commit_before>//
//  MIDIPacket+SequenceType.swift
//  AudioKit
//
//  Created by <NAME>, revision history on Github.
//  Copyright © 2018 AudioKit. All rights reserved.
//

/**
 Allows a MIDIPacket to be iterated through with a for statement.
 This is necessary because MIDIPacket can contain multiple midi events,
 but Swift makes this unnecessarily hard because the MIDIPacket struct uses a tuple
 for the data field. Grrr!

 Example usage:
 let packet: MIDIPacket
 for message in packet {
 // message is a Message
 }
 */
extension MIDIPacket: Sequence {
    /// Generate a MIDI packet
    public func makeIterator() -> AnyIterator<AKMIDIEvent> {
        let generator = generatorForTuple(self.data)
        var index: UInt16 = 0

        return AnyIterator {
            if index >= self.length {
                return nil
            }

            func pop() -> MIDIByte {
                assert(index < self.length)
                index += 1
                guard let byte = generator.next() as? MIDIByte else {
                    return 0 // Is this right?
                }
                return byte
            }
            let status = pop()
            if AudioKit.midi.isReceivingSysex {
                return AKMIDIEvent.appendIncomingSysex(packet: self) //will be nil until sysex is done
            } else if AKMIDIEvent.isStatusByte(status) {
                var data1: MIDIByte = 0
                var data2: MIDIByte = 0
                guard var mstat = AKMIDIStatusType.from(byte: status) else {
                    return AKMIDIEvent(packet: self)
                }

                let chan = status & 0xF

                switch  mstat {

                case .noteOff, .noteOn, .polyphonicAftertouch, .controllerChange, .pitchWheel:
                    data1 = pop(); data2 = pop()
                    if mstat == .noteOn && data2 == 0 {
                        // turn noteOn with velocity 0 to noteOff
                        mstat = .noteOff
                    }
                    return AKMIDIEvent(status: mstat, channel: chan, byte1: data1, byte2: data2)

                case .programChange, .channelAftertouch:
                    data1 = pop()
                    return AKMIDIEvent(status: mstat, channel: chan, byte1: data1, byte2: data2)

                case .systemCommand:
                    guard let cmd = AKMIDISystemCommand(rawValue: status) else {
                        return AKMIDIEvent(packet: self)
                    }
                    switch  cmd {
                    case .sysex:
                        index = self.length
                        return AKMIDIEvent(packet: self)
                    case .songPosition:
                        //the remaining event generators need to be tested and tweaked to the specific messages
                        data1 = pop()
                        data2 = pop()
                        return AKMIDIEvent(command: cmd, byte1: data1, byte2: data2)
                    case .songSelect:
                        data1 = pop()
                        return AKMIDIEvent(command: cmd, byte1: data1, byte2: data2)
                    default:
                        return AKMIDIEvent(packet: self)
                    }
                }
            } else {
                return nil
            }
        }
    }
}

typealias AKRawMIDIPacket = (
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,<commit_msg>cleanup midi sequence packet type to work w new akmidievent<commit_after>//
//  MIDIPacket+SequenceType.swift
//  AudioKit
//
//  Created by <NAME>, revision history on Github.
//  Copyright © 2018 AudioKit. All rights reserved.
//

/**
 Allows a MIDIPacket to be iterated through with a for statement.
 This is necessary because MIDIPacket can contain multiple midi events,
 but Swift makes this unnecessarily hard because the MIDIPacket struct uses a tuple
 for the data field. Grrr!

 Example usage:
 let packet: MIDIPacket
 for message in packet {
 // message is a Message
 }
 */
extension MIDIPacket: Sequence {
    /// Generate a MIDI packet
    public func makeIterator() -> AnyIterator<AKMIDIEvent> {
        let generator = generatorForTuple(self.data)
        var index: UInt16 = 0

        return AnyIterator {
            if index >= self.length {
                return nil
            }

            func pop() -> MIDIByte {
                assert(index < self.length)
                index += 1
                guard let byte = generator.next() as? MIDIByte else {
                    return 0 // Is this right?
                }
                return byte
            }
            let status = pop()
            if AudioKit.midi.isReceivingSysex {
                return AKMIDIEvent.appendIncomingSysex(packet: self) //will be nil until sysex is done
            } else if AKMIDIEvent.isStatusByte(status) {
                var data1: MIDIByte = 0
                var data2: MIDIByte = 0
                guard var mstat = AKMIDIStatusType.from(byte: status) else {
                    return AKMIDIEvent(packet: self)
                }

                switch  mstat {

                case .noteOff, .noteOn, .polyphonicAftertouch, .controllerChange, .pitchWheel:
                    data1 = pop(); data2 = pop()
                    if mstat == .noteOn && data2 == 0 {
                        // turn noteOn with velocity 0 to noteOff
                        mstat = .noteOff
                    }
                    return AKMIDIEvent(data: [status, data1, data2])

                case .programChange, .channelAftertouch:
                    data1 = pop()
                    return AKMIDIEvent(data: [status, data1])

                case .systemCommand:
                    guard let cmd = AKMIDISystemCommand(rawValue: status) else {
                        return AKMIDIEvent(packet: self)
                    }
                    switch  cmd {
                    case .sysex:
                        index = self.length
                        return AKMIDIEvent(packet: self)
                    case .songPosition:
                        //the remaining event generators need to be tested and tweaked to the specific messages
                        data1 = pop()
                        data2 = pop()
                        return AKMIDIEvent(data: [status, data1, data2])
                    case .songSelect:
                        data1 = pop()
                        return AKMIDIEvent(data: [status, data1])
                    default:
                        return AKMIDIEvent(packet: self)
                    }
                }
            } else {
                return nil
            }
        }
    }
}

typealias AKRawMIDIPacket = (
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
    MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte, MIDIByte,
{'fraction_non_alphanumeric': 0.07447207297263526, 'fraction_numerical': 0.00837186055229289, 'mean_word_length': 1.5728061716489874, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6984678', 'n_tokens_mistral': 2479, 'n_tokens_neox': 2164, 'n_words': 658}