iOS 录音功能实现

音频基本知识

  • 采样率(sampleRate)
    所谓采样就是在时间轴上对信号进行数字化。根据奈奎斯特定理(也称为采样定理),按比声音最高频率高2倍以上的频率对声音进行采样(也称为AD转换),对于高质量的音频信号,其频率范围(人耳能够听到的频率范围)是20Hz~20kHz,所以采样频率一般为44.1kHz,这样就可以保证采样声音达到20kHz也能被数字化,从而使得经过数字化处理之后,人耳听到的声音质量不会被降低。而所谓的44.1kHz就是代表1秒会采样44100次。

  • 量化格式(sampleFormat)
    量化是指在幅度轴上对信号进行数字化,比如用16比特的二进制信号来表示声音的一个采样,而16比特(一个short)所表示的范围是[-32768,32767],共有65536个可能取值,因此最终模拟的音频信号在幅度上也分为了65536层

  • 声道数(channel)
    声道(Sound Channel) 是指声音在录制或播放时在不同空间位置采集或回放的相互独立的音频信号,所以声道数也就是声音录制时的音源数量或回放时相应的扬声器数量。

  • PCM
    通常所说的音频的裸数据格式就是脉冲编码调制(Pulse Code Modulation,PCM)数据。描述一段PCM数据一般需要以下几个概念:量化格式(sampleFormat)、采样率(sampleRate)、声道数(channel)。以CD的音质为例:量化格式(有的地方描述为位深度)为16比特(2字节),采样率为44100,声道数为2,这些信息就描述了CD的音质。而对于声音格式,还有一个概念用来描述它的大小,称为数据比特率,即1秒时间内的比特数目,它用于衡量音频数据单位时间内的容量大小。而对于CD音质的数据,比特率为多少呢?计算如下:
    44100 * 16 * 2 = 1378.125kbps

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

WAV编码

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

  • 特点:音质非常好,大量软件都支持。
  • 适用场合:多媒体开发的中间文件、保存音乐和音效素材。

MP3编码

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

  • 特点:音质在128Kbit/s以上表现还不错,压缩比比较高,大量软件和硬件都支持,兼容性好。
  • 适用场合:高比特率下对兼容性有要求的音乐欣赏。

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。

  • 特点:在小于128Kbit/s的码率下表现优异,并且多用于视频中的音频编码。
  • 适用场合:128Kbit/s以下的音频编码,多用于视频中音频轨的编码。

Ogg编码

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

  • 特点:可以用比MP3更小的码率实现比MP3更好的音质,高中低码率下均有良好的表现,兼容性不够好,流媒体特性不支持。
  • 适用场合:语音聊天的音频消息场景。

在info.plist 添加

<key>NSMicrophoneUsageDescriptionkey>
<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?) {
        
    }
}


你可能感兴趣的:(ios,ios,swift)