iOS 录音功能

2020-05-06  本文已影响0人  lq_ios

音频基本知识

介绍几种常用的压缩编码格式。

WAV编码

PCM(通常所说的音频的裸数据格式就是脉冲编码调制(Pulse Code Modulation,PCM)数据),WAV编码的一种实现(有多种实现方式,但是都不会进行压缩操作)就是在PCM数据格式的前面加上44字节,分别用来描述PCM的采样率、声道数、数据格式等信息。

MP3编码

MP3具有不错的压缩比,使用LAME编码(MP3编码格式的一种实现)的中高码率的MP3文件,听感上非常接近源WAV文件,当然在不同的应用场景下,应该调整合适的参数以达到最好的效果。

AAC编码

AAC是新一代的音频有损压缩技术,它通过一些附加的编码技术(比如PS、SBR等),衍生出了LC-AAC、HE-AAC、HE-AAC v2三种主要的编码格式。LC-AAC是比较传统的AAC,相对而言,其主要应用于中高码率场景的编码(≥80Kbit/s);HE-AAC(相当于AAC+SBR)主要应用于中低码率场景的编码(≤80Kbit/s);而新近推出的HE-AACv2(相当于AAC+SBR+PS)主要应用于低码率场景的编码(≤48Kbit/s)。事实上大部分编码器都设置为≤48Kbit/s自动启用PS技术,而>48Kbit/s则不加PS,相当于普通的HE-AAC。

Ogg编码

Ogg是一种非常有潜力的编码,在各种码率下都有比较优秀的表现,尤其是在中低码率场景下。Ogg除了音质好之外,还是完全免费的,这为Ogg获得更多的支持打好了基础。Ogg有着非常出色的算法,可以用更小的码率达到更好的音质,128Kbit/s的Ogg比192Kbit/s甚至更高码率的MP3还要出色。但目前因为还没有媒体服务软件的支持,因此基于Ogg的数字广播还无法实现。Ogg目前受支持的情况还不够好,无论是软件上的还是硬件上的支持,都无法和MP3相提并论。

在info.plist 添加

<key>NSMicrophoneUsageDescription</key>
<string>获取麦克风权限</string>

向系统申请麦克风权限

 AVCaptureDevice.requestAccess(for: AVMediaType.audio) {(granted: Bool) in
 
 }

封装的简单工具类

import AVFoundation

/// 采样率
enum AudioSampleRate:Int {
    case AudioSampleRate8KHZ = 8000
    case AudioSampleRate12KHZ = 12000
    case AudioSampleRate16KHZ = 16000
    case AudioSampleRate24KHZ = 24000
    case AudioSampleRate32KHZ = 32000
}

///声道数
enum AudioNumberOfChannels:Int {
    case AudioNumberOfChannelsOne = 1
    case AudioNumberOfChannelsTwo = 2
}

///量化格式
enum AudioLinearPCMBitDepth:Int {
    case AudioLinearPCMBitDepthKey8 = 8
    case AudioLinearPCMBitDepthKey16 = 16
    case AudioLinearPCMBitDepthKey24 = 24
    case AudioLinearPCMBitDepthKey32 = 32
}



enum AudioAuthorizationStatus: Int, CustomStringConvertible {
    case notDetermined = 0
    case notAuthorized
    case authorized
    public var description: String {
        get {
            switch self {
            case .notDetermined:
                return "用户没有做选择"
            case .notAuthorized:
                return "没有获得权限"
            case .authorized:
                return "获得权限"
            }
        }
    }
}

class AudioManager:NSObject{
    static let shared = AudioManager()
    //采样间隔
    var audioSetting:[String:Any] = [:]
    /// 设置编码格式
    var formatKey:AudioFormatID = kAudioFormatLinearPCM
    /// 抽样率
    var sampleRate:AudioSampleRate = .AudioSampleRate8KHZ
    /// 声道数
    var numberOfChannels:AudioNumberOfChannels = .AudioNumberOfChannelsOne
    /// 位宽(量化格式)
    var linearPCMBitDepth:AudioLinearPCMBitDepth = .AudioLinearPCMBitDepthKey8
    /// 音频权限
    var audioAuthorizationStatus:AudioAuthorizationStatus = .notDetermined
    var audioRecorder:AVAudioRecorder?
    var audioPlayer:AVAudioPlayer?
    
    private override init() {
        super.init()
        self.checkAudioAuthorization()
    }
}


extension AudioManager{
    func checkAudioAuthorization()  {
        let status = AVCaptureDevice.authorizationStatus(for: AVMediaType.audio)
        switch status {
        case .notDetermined:
            self.audioAuthorizationStatus = .notDetermined
        case .restricted:
            self.audioAuthorizationStatus = .notAuthorized
        case .denied:
            self.audioAuthorizationStatus = .notAuthorized
        case .authorized:
            self.audioAuthorizationStatus = .authorized
        @unknown default:
            self.audioAuthorizationStatus = .notDetermined
        }
        
    }
    
    func requestAudioAuthorization(completionHandler:@escaping (AudioAuthorizationStatus)->()){
        AVCaptureDevice.requestAccess(for: AVMediaType.audio) {[weak self] (granted: Bool) in
            self?.audioAuthorizationStatus = granted ? .authorized : .notAuthorized
            DispatchQueue.main.async {
                completionHandler((granted ? .authorized : .notAuthorized))
            }
        }
    }
}


extension AudioManager{
    func startRecorder(voiceUrl:URL) {
        guard self.audioAuthorizationStatus == .authorized else {
            self.requestAudioAuthorization { (status) -> () in}
            return
        }
        do {//[AVSampleRateKey:sampleRate,AVFormatIDKey:formatKey,AVNumberOfChannelsKey:numberOfChannels,AVLinearPCMIsFloatKey:linearPCMBitDepth]
            try audioRecorder = AVAudioRecorder.init(url: voiceUrl, settings:[AVFormatIDKey:formatKey] )
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.record)
            audioRecorder?.delegate = self
            audioRecorder?.isMeteringEnabled = true
            guard let success = audioRecorder?.record()else{
                return
            }
            if success == true{
                print("开始录音")
            }else{
                print("录音失败")
            }
            
        } catch _ {
            print("录音异常")
        }
    }
    
    func pauseRecorderAudio()  {
        audioRecorder?.pause()
        print("暂停录音")
    }
    
    func stopRecorderAudio(){
        audioRecorder?.stop()
        print("停止录音")
    }
}

extension AudioManager{
    func playAudio(voiceUrl:URL){
        do {
            try audioPlayer = AVAudioPlayer.init(contentsOf:voiceUrl)
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
            audioPlayer?.delegate = self
            guard let success = audioPlayer?.play() else {
                return
            }
            if success == true {
                print("开始播放")
            }else{
                print("开始播放失败")
            }
        } catch _{
            print("播放异常")
        }
    }
    
    func audioPause() {
        audioPlayer?.pause()
        print("暂停播放")
    }
    func audioStop()  {
        audioPlayer?.stop()
        print("停止播放")
    }
}

extension AudioManager:AVAudioRecorderDelegate{
    func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) {
        
    }
    func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        
    }
}


extension AudioManager:AVAudioPlayerDelegate{
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        
    }
    func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
        
    }
}


上一篇下一篇

猜你喜欢

热点阅读