import AVFoundation /** The Audio Specific Config is the global header for MPEG-4 Audio - seealso: http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config - seealso: http://wiki.multimedia.cx/?title=Understanding_AAC */ package struct AudioSpecificConfig: Equatable { package static let adtsHeaderSize: Int = 7 package enum AudioObjectType: UInt8 { case unknown = 0 case aacMain = 1 case aacLc = 2 case aacSsr = 3 case aacLtp = 4 case aacSbr = 5 case aacScalable = 6 case twinqVQ = 7 case celp = 8 case hxvc = 9 init?(objectID: MPEG4ObjectID?) { switch objectID { case .aac_Main?: self = .aacMain case .AAC_LC?: self = .aacLc case .AAC_SSR?: self = .aacSsr case .AAC_LTP?: self = .aacLtp case .AAC_SBR?: self = .aacSbr case .aac_Scalable?: self = .aacScalable case .twinVQ?: self = .twinqVQ case .CELP?: self = .celp case .HVXC?: self = .hxvc case .none: return nil @unknown default: return nil } } } enum SamplingFrequency: UInt8 { case hz96000 = 0 case hz88200 = 1 case hz64000 = 2 case hz48000 = 3 case hz44100 = 4 case hz32000 = 5 case hz24000 = 6 case hz22050 = 7 case hz16000 = 8 case hz12000 = 9 case hz11025 = 10 case hz8000 = 11 case hz7350 = 12 var sampleRate: Float64 { switch self { case .hz96000: return 96000 case .hz88200: return 88200 case .hz64000: return 64000 case .hz48000: return 48000 case .hz44100: return 44100 case .hz32000: return 32000 case .hz24000: return 24000 case .hz22050: return 22050 case .hz16000: return 16000 case .hz12000: return 12000 case .hz11025: return 11025 case .hz8000: return 8000 case .hz7350: return 7350 } } init?(sampleRate: Float64) { switch Int(sampleRate) { case 96000: self = .hz96000 case 88200: self = .hz88200 case 64000: self = .hz64000 case 48000: self = .hz48000 case 44100: self = .hz44100 case 32000: self = .hz32000 case 24000: self = .hz24000 case 22050: self = .hz22050 case 16000: self = .hz16000 case 12000: self = .hz12000 case 11025: self = .hz11025 case 8000: self = .hz8000 case 7350: self = .hz7350 default: return nil } } } enum ChannelConfiguration: UInt8 { case definedInAOTSpecificConfig = 0 case frontCenter = 1 case frontLeftAndFrontRight = 2 case frontCenterAndFrontLeftAndFrontRight = 3 case frontCenterAndFrontLeftAndFrontRightAndBackCenter = 4 case frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRight = 5 case frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRightLFE = 6 case frontCenterAndFrontLeftAndFrontRightAndSideLeftAndSideRightAndBackLeftAndBackRightLFE = 7 var channelCount: UInt32 { switch self { case .definedInAOTSpecificConfig: return 0 case .frontCenter: return 1 case .frontLeftAndFrontRight: return 2 case .frontCenterAndFrontLeftAndFrontRight: return 3 case .frontCenterAndFrontLeftAndFrontRightAndBackCenter: return 4 case .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRight: return 5 case .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRightLFE: return 6 case .frontCenterAndFrontLeftAndFrontRightAndSideLeftAndSideRightAndBackLeftAndBackRightLFE: return 8 } } var audioChannelLayoutTag: AudioChannelLayoutTag? { switch self { case .definedInAOTSpecificConfig: return nil case .frontCenter: return nil case .frontLeftAndFrontRight: return nil case .frontCenterAndFrontLeftAndFrontRight: return kAudioChannelLayoutTag_MPEG_3_0_B case .frontCenterAndFrontLeftAndFrontRightAndBackCenter: return kAudioChannelLayoutTag_MPEG_4_0_B case .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRight: return kAudioChannelLayoutTag_MPEG_5_0_D case .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRightLFE: return kAudioChannelLayoutTag_MPEG_5_1_D case .frontCenterAndFrontLeftAndFrontRightAndSideLeftAndSideRightAndBackLeftAndBackRightLFE: return kAudioChannelLayoutTag_MPEG_7_1_B } } var audioChannelLayout: AVAudioChannelLayout? { guard let audioChannelLayoutTag else { return nil } return AVAudioChannelLayout(layoutTag: audioChannelLayoutTag) } init?(channelCount: UInt32) { switch channelCount { case 0: self = .definedInAOTSpecificConfig case 1: self = .frontCenter case 2: self = .frontLeftAndFrontRight case 3: self = .frontCenterAndFrontLeftAndFrontRight case 4: self = .frontCenterAndFrontLeftAndFrontRightAndBackCenter case 5: self = .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRight case 6: self = .frontCenterAndFrontLeftAndFrontRightAndBackLeftAndBackRightLFE case 8: self = .frontCenterAndFrontLeftAndFrontRightAndSideLeftAndSideRightAndBackLeftAndBackRightLFE default: return nil } } } let type: AudioObjectType let frequency: SamplingFrequency let channelConfig: ChannelConfiguration let frameLengthFlag = false package var bytes: [UInt8] { var bytes = [UInt8](repeating: 0, count: 2) bytes[0] = type.rawValue << 3 | (frequency.rawValue >> 1) bytes[1] = (frequency.rawValue & 0x1) << 7 | (channelConfig.rawValue & 0xF) << 3 return bytes } package init?(bytes: [UInt8]) { guard let type = AudioObjectType(rawValue: bytes[0] >> 3), let frequency = SamplingFrequency(rawValue: (bytes[0] & 0b00000111) << 1 | (bytes[1] >> 7)), let channel = ChannelConfiguration(rawValue: (bytes[1] & 0b01111000) >> 3) else { return nil } self.type = type self.frequency = frequency self.channelConfig = channel } init(type: AudioObjectType, frequency: SamplingFrequency, channel: ChannelConfiguration) { self.type = type self.frequency = frequency self.channelConfig = channel } package init?(formatDescription: CMFormatDescription?) { guard let streamDescription = formatDescription?.audioStreamBasicDescription, let type = AudioObjectType(objectID: MPEG4ObjectID(rawValue: Int(streamDescription.mFormatFlags))), let frequency = SamplingFrequency(sampleRate: streamDescription.mSampleRate), let channelConfig = ChannelConfiguration(channelCount: streamDescription.mChannelsPerFrame) else { return nil } self.type = type self.frequency = frequency self.channelConfig = channelConfig } func encode(to data: inout Data, length: Int) { let fullSize: Int = Self.adtsHeaderSize + length data[0] = 0xFF data[1] = 0xF9 data[2] = (type.rawValue - 1) << 6 | (frequency.rawValue << 2) | (channelConfig.rawValue >> 2) data[3] = (channelConfig.rawValue & 3) << 6 | UInt8(fullSize >> 11) data[4] = UInt8((fullSize & 0x7FF) >> 3) data[5] = ((UInt8(fullSize & 7)) << 5) + 0x1F data[6] = 0xFC } package func makeAudioFormat() -> AVAudioFormat? { var audioStreamBasicDescription = makeAudioStreamBasicDescription() if let audioChannelLayoutTag = channelConfig.audioChannelLayoutTag { return AVAudioFormat( streamDescription: &audioStreamBasicDescription, channelLayout: AVAudioChannelLayout(layoutTag: audioChannelLayoutTag) ) } return AVAudioFormat(streamDescription: &audioStreamBasicDescription) } private func makeAudioStreamBasicDescription() -> AudioStreamBasicDescription { AudioStreamBasicDescription( mSampleRate: frequency.sampleRate, mFormatID: kAudioFormatMPEG4AAC, mFormatFlags: UInt32(type.rawValue), mBytesPerPacket: 0, mFramesPerPacket: frameLengthFlag ? 960 : 1024, mBytesPerFrame: 0, mChannelsPerFrame: channelConfig.channelCount, mBitsPerChannel: 0, mReserved: 0 ) } } extension AudioSpecificConfig: CustomDebugStringConvertible { // MARK: CustomDebugStringConvertible package var debugDescription: String { Mirror(reflecting: self).debugDescription } }