AVFoundation学习之简介及音频相关

本文详细介绍AVFoundation框架,涵盖音频播放与录制的基础配置、不同音频会话类别的选择及其实现细节,包括如何初始化AVAudioSession和AVAudioPlayer,处理音频录制流程,以及文字转语音的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title: AVFoundation学习之简介及音频相关
date: 2019-08-20 10:22:52
tags:

一、简介

AVFoundation是可以用它来播放和创建基于时间的视听媒体的几个框架之一

在这里插入图片描述

二、音频播放及录制

1.音频播放

(1)初始化AVAudioSession

let session = AVAudioSession.sharedInstance()
  do {
      try session.setCategory(.playback)
  } catch {
      print("Category error:\(error)")
  }

  do {
      try session.setActive(true, options: .notifyOthersOnDeactivation)
  } catch {
      print("Active error: \(error)")
  }


*setCategory

Ambient 游戏、效率应用程序 AVAudioSessionCategoryAmbient / kAudioSessionCategory_AmbientSound 使用这个分类应用会随着静音键和屏幕关闭而静音
不会终止其他应用播放的声音,可以和其他自带应用如iPod、Safari同时播放声音。
该类别无法在后台播放声音
不支持录音

Solo Ambient(默认) 游戏、效率应用程序 AVAudioSessionCategorySoloAmbient/kAudioSessionCategory_SoloAmbientSound 类似Ambient
使用这个分类应用会随着静音键和屏幕关闭而静音
会终止其它应用播放声音
该类别无法在后台播放声音
不支持录音

Playback 音频和视频播放  AVAudioSessionCategoryPlayback / kAudioSessionCategory_MediaPlayback 用于以音频为主的应用
不会随着静音键和屏幕关闭而静音
默认会 终止其他应用播放的声音
可在后台播放声音。
不支持录音

Record 录音机、视频捕捉 AVAudioSessionCategoryRecord / kAudioSessionCategory_RecordAudio 录音应用,除了来电铃声、闹钟、日历提醒之外的其他系统声音不会被播放。
不会会随着静音键和屏幕关闭而静音
会终止其他应用播放的声音
只提供单纯录音功能。

Play and Record VoIP、语音聊天 AVAudioSessionCategoryPlayAndRecord / kAudioSessionCategory_PlayAndRecord ,如果应用需要用到iPhone上的听筒,这个类别是你唯一的选择,在这个类别下,声音的默认出口为听筒或者耳机。
不会会随着静音键和屏幕关闭而静音
默认不会 终止其他应用播放的声音
提供录音和播放功能

Audio Processing 离线会话和处理 AVAudioSessionCategoryAudioProcessing / kAudioSessionCategory_AudioProcessing 在不播放或录制音频时使用音频硬件编解码器或信号处理器的类别。例如在执行离线音频格式转换时,此类别禁用播放和禁用录音。应用处于后台时,音频处理通常不会继续,但是可以在应用移至后台时,请求更多时间来完成处理。

Multi-Route 使用外部硬件的高级A/V应用程序 AVAudioSessionCategoryMultiRoute 通过可以用的音频辅助设备和内置音频硬件设备,我们可以自定义使用类型
不会随着静音键和屏幕关闭而静音
会终止其他应用播放的声音
既可以录音也可以播放




除了用以上代码设置category之外,还可以设置一个option属性

session.setCategory(.playback, options: .allowAirPlay)

option有以下类型

1、AVAudioSessionCategoryOptionMixWithOthers : 如果确实用的AVAudioSessionCategoryPlayback实现的一个背景音,但是呢,又想和QQ音乐并存,那么可以在AVAudioSessionCategoryPlayback类别下在设置这个选项,就可以实现共存了。

      2、AVAudioSessionCategoryOptionDuckOthers:在实时通话的场景,比如QQ音乐,当进行视频通话的时候,会发现QQ音乐自动声音降低了,此时就是通过设置这个选项来对其他音乐App进行了压制。

      3、AVAudioSessionCategoryOptionAllowBluetooth:如果要支持蓝牙耳机电话,则需要设置这个选项。

      4、AVAudioSessionCategoryOptionDefaultToSpeaker: 如果在VoIP模式下,希望默认打开免提功能,需要设置这个选项。

      5、AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers也是在9.0之后添加的。

      6、AVAudioSessionCategoryOptionAllowBluetoothA2DP、AVAudioSessionCategoryOptionAllowAirPlay是10之后新加的,用来支持蓝牙A2DP耳机和AirPlay。




*setActive激活AVAudioSession,当自己App的Session被激活,其他App的就会解除激活,当options传notifyOthersOnDeactivation的时候,自己的Session解除激活后恢复其他App Session的激活状态

print("\(session.isOtherAudioPlaying)")

*可以通过isOtherAudioPlaying这个属性提前判断当前是否有其他App在播放音频

(2)初始化AVAudioPlayer

do {
            try avAudioPlayer = AVAudioPlayer(contentsOf: urlPath)
        } catch {
            print("AVAudioPlayer init error\(error)")
        }
        
        if avAudioPlayer != nil {
            print("初始化成功")
            print("音频时长\(avAudioPlayer.duration)")
            avAudioPlayer.delegate = self
             // 是否能设置rate属性,只有这个属性设置成YES了才能设置rate属性,并且这些属性都设置在prepareToPlay方法调用之前
            avAudioPlayer.enableRate = true
            // 允许使用立体声播放声音 如果为-1.0则完全左声道,如果0.0则左右声道平衡,如果为1.0则完全为右声道
            avAudioPlayer.pan = 0
            avAudioPlayer.isMeteringEnabled = true
            // 准备播放,这个方法可以不执行,但执行的话可以降低播放器play方法和你听到声音之间的延时
            avAudioPlayer.prepareToPlay()
        } else {
            print("初始化失败")
        }




(3)开始播放语音

@IBAction func playAudio(_ sender: Any) {
        isPlay = !isPlay
        if isPlay {
            avAudioPlayer.play()
            if avAudioPlayer.isPlaying {
                btnPlayAudio.setTitle("暂停播放", for: .normal)
                //开启定时器更新播放进度
                self.cadisplayerLinkStartPlayerAudio()
            }
        } else {
            avAudioPlayer.pause()
            btnPlayAudio.setTitle("播放本地音频", for: .normal)
            
        }
    }
    
    func cadisplayerLinkStartPlayerAudio () {
        if cadisplayerlink == nil {
            cadisplayerlink = CADisplayLink(target: self, selector: #selector(updateProgress))
            cadisplayerlink.preferredFramesPerSecond = 5
            cadisplayerlink.add(to: RunLoop.current, forMode: .common)
        }
    }
    
    @objc func updateProgress() {
        print("currentTime: \(avAudioPlayer.currentTime)")
        print("deviceCurrentTime: \(avAudioPlayer.deviceCurrentTime)")
        
        progressView.setProgress(Float(avAudioPlayer.currentTime / avAudioPlayer.duration), animated: true)
    }




*CADisplayLink 作用相当于NSTimer,可以通过preferredFramesPerSecond来设置每秒刷新次数.preferredFramesPerSecond默认值为屏幕最大帧率(maximumFramesPerSecond),目前是60.

每个计时器对象只能加入到一个runloop中,但是可以被添加到不同的模式中,如果想从所有的模式中移除计时器,需要执行-invalidate()方法.

*可以通过avAudioPlayer.peakPower(forChannel: 0)获取某声道的分贝值,avAudioPlayer.averagePower(forChannel: 0)获取某声道的平均分贝值。这个值的范围是从表示最大分贝的0dB(full scale)到最小分贝或静音的-160dB。获取这两个值之前,要先设置属性meteringEnabled为YES,才能对音频进行测量。另,每当需要读取值时,需要先调用updateMeters方法才能获取最新的值。

2.音频录制

*录音要在infoplist中添加Privacy - Microphone Usage Description

(1)初始化AVAudioSession

let session = AVAudioSession.sharedInstance()
do {
    try session.setCategory(.playAndRecord)
} catch {
    print("session init error:\(error.localizedDescription)")
}

do {
    try session.setActive(true, options: .notifyOthersOnDeactivation)
} catch  {
    print("session active error:\(error.localizedDescription)")
}



(2)初始化AVAudioRecorder

labelState.text = "准备录制"
        filePath = NSTemporaryDirectory() + "memo.caf"
        do {
            self.avAudioRecoder =  try AVAudioRecorder(url: URL(fileURLWithPath: filePath), settings: [AVFormatIDKey:kAudioFormatAppleIMA4,AVSampleRateKey: 44100.0,  AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: kRenderQuality_Medium])
        } catch {
            print("audioRecoder init error:\(error.localizedDescription)")
        }
        
        if self.avAudioRecoder != nil {
            self.avAudioRecoder.delegate = self
            self.avAudioRecoder.isMeteringEnabled = true
            self.avAudioRecoder.prepareToRecord()
        }


/* AVAudioRecorder 录音类这个后面说

可以设置的一些属性 :

<1>AVNumberOfChannelsKey 通道数

<2>AVSampleRateKey 采样率 一般用44100

<3>AVLinearPCMBitDepthKey 比特率 一般设16 32

<4>AVEncoderAudioQualityKey 质量

<5>AVEncoderBitRateKey 比特采样率 一般是128000

<6>AVChannelLayoutKey 通道布局值是一个包含AudioChannelLayout的NSData对象

​ */

*AVFormatIDKey写入内容的音频格式,常用的音频格式支持的值

kAudioFormatLinearPCM

kAudioFormatMPEG4AAC

kAudioFormatAppleLossless

kAudioFormatAppleIMA4

kAudioFormatiLBC

kAudioFormatULaw

kAudioFormatLinearPCM -会将未压缩的音频流写入文件中, 这种格式的保真度最高,相应的文件也最大。

AAC或Apple IMA4的压缩格式会显著缩小文件,还能保证高质量的音频内容。

*AVSampleRateKey --定义录音器采样率。采样率定义了对输入的模拟音频信号每一秒内的采样数。采样率决定音频的质量及最终文件大小。一般标准的采样率:8k、16k、22.5k、44.1k

*AVNumbeOfChannelsKey – 定义记录音频通道数。默认值1,单声道录制。设置2-立体声录制。除非使用外部硬件进行录制,一般应该创建单声道录音

(3)开始录音

if !self.avAudioRecoder.isRecording {
            //开启定时器
            timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
            timer.schedule(deadline: .now(), repeating: 1)
            var time = 0
            
            timer.setEventHandler {
                time += 1
                if time <= 10 {
                    //小于10s更新录制时间和分贝
                    DispatchQueue.main.async {
                        self.updateRecordTimeAndPower(time: time)
                    }
                } else {
                    //停止录音
                    self.stopRecord()
                }
            }
            self.timer.resume()
            self.avAudioRecoder.record()
        }


*更新录制时间和分贝

labelRecordTime.text = "\(time)秒"
        self.avAudioRecoder.updateMeters()
        let peakPower = self.avAudioRecoder.peakPower(forChannel: 0)
        let averPower = self.avAudioRecoder.averagePower(forChannel: 0)
        labelPeakPower.text = "分贝值\(peakPower)"
        labelAveragePower.text = "平均分贝值\(averPower)"


(4)停止录音,保存视频

func stopRecord() {
    self.timer.cancel()
    self.avAudioRecoder.stop()

    self.labelState.text = "录制结束"
    self.saveRecordingName(name: "TestAudio") { (save, error) in
        if save {
            print("保存视频成功")
        } else {
            print("保存视频失败 error: \(error)")
        }
    }
}
    
func saveRecordingName(name: String, block:(_ save: Bool, _ error: String)->()) {
    let timesTamp = NSDate.timeIntervalSinceReferenceDate
    let fileName = "\(name)-\(timesTamp).m4a"

    let docDir = self.documentsDirectory()
    let destPath = docDir + "/\(fileName)"
    print(destPath)
    print(self.filePath)
    let destUrl = URL(fileURLWithPath: destPath)

    var errorStr = ""
    do {
        try FileManager.default.moveItem(at: URL(fileURLWithPath: self.filePath), to: destUrl)
    } catch  {
        errorStr = error.localizedDescription
    }

    if errorStr.isEmpty {
        self.destPath = destUrl
        block(true, errorStr)
        self.avAudioRecoder.prepareToRecord()
    } else {
        block(false, errorStr)
    }
}



三、文字转语音

let synthesizer = AVSpeechSynthesizer()
        let voice = AVSpeechSynthesisVoice(language: "zh-CN")
        let utterance = AVSpeechUtterance(string: "哈哈哈哈哈哈哈")
        utterance.rate = 1//播放速率
        utterance.voice = voice
//        utterance.pitchMultiplier = 0.8// 可在播放待定语句时候改变声调
        utterance.postUtteranceDelay = 0.1// 语音合成器在播放下一条语句的时候有短暂的停顿  这个属性指定停顿的时间
        synthesizer.speak(utterance)
        
        print("目前支持的语音列表:\(AVSpeechSynthesisVoice.speechVoices())")


四、参考链接

https://www.cnblogs.com/zhangxiaoxu/p/8022957.html

https://www.jianshu.com/p/bd333acf62e2 包含播放中断情况的处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值