四.音视频编辑-音频混合-概述

引言

当我们在前两篇博客中成功地构建了一个媒体组合,并且略过了音频部分时,我们意识到了我们需要对这个项目进行更详细的探讨。在本篇博客中,我们将会展示如何创建一个包含视频轨道、配音音频轨道以及背景音频轨道的完整媒体组合。更进一步,我们将探讨当两个音频轨道同时竞争空间时的音频混合方案。

构建双音轨组合

在前面博客的基础上我们需要进行一些调整以构建新的媒体组合。

资源选择

首先是资源选择器的调整,原本的资源选择器只支持单一的视频媒体资源选择,现在我们将其分为三组,分别为视频资源,配音音频资源,背景音乐资源。

代码如下:

修改数据源为二维数组

    /// 数据var dataArray:[[PHResource]] = [[PHResource]]()

初始化数据

    override func initData() {super.initData()//添加视频资源var videoArray = [PHResource]()let resource_breckiehill = PHResource(resource_name: "01_nebula", resource_ext: "mp4", resource_type: .video)videoArray.append(resource_breckiehill)let resource_dkglitch = PHResource(resource_name: "04_quasar", resource_ext: "mp4", resource_type: .video)videoArray.append(resource_dkglitch)dataArray.append(videoArray)//添加音频资源var audioArray = [PHResource]()let resource_john_kennedy = PHResource(resource_name: "John F. Kennedy", resource_ext: "m4a", resource_type: .audio)audioArray.append(resource_john_kennedy)let resource_ronald_reagen = PHResource(resource_name: "Ronald Reagan", resource_ext: "m4a", resource_type: .audio)audioArray.append(resource_ronald_reagen)dataArray.append(audioArray)//添加背景音乐资源var bgmArray = [PHResource]()let resource_keep_going = PHResource(resource_name: "02 Keep Going", resource_ext: "m4a", resource_type: .music)bgmArray.append(resource_keep_going)let resource_star_gazing = PHResource(resource_name: "01 Star Gazing", resource_ext: "m4a", resource_type: .music)bgmArray.append(resource_star_gazing)dataArray.append(bgmArray)

列表改为分组样式,显示结果如下图:

资源选择器

资源编辑

资源编辑页面需要将三个轨道都显示到页面上,为此我们需要创建三个不同的横向列表来显示不同的媒体资源轨道。

    /// 数据var timeLine = PHTimeLine()/// 视频编辑视图var videoCollectionView:UICollectionView?/// 音频编辑视图var audioCollectionView:UICollectionView?/// 背景音乐编辑视图var musicCollectionView:UICollectionView?
    func addCollectionView() {addVideoCollectionView()addAudioCollectionView()addMusicCollectionView()}func addVideoCollectionView() {self.videoCollectionView = buildCollectionView(offsetY: 50.0)}func addAudioCollectionView() {let offsetY = CGRectGetMaxY(self.videoCollectionView?.frame ?? CGRect.zero) + 8.0self.audioCollectionView = buildCollectionView(offsetY: offsetY)}func addMusicCollectionView() {let offsetY = CGRectGetMaxY(self.audioCollectionView?.frame ?? CGRect.zero) + 8.0self.musicCollectionView = buildCollectionView(offsetY: offsetY)}/// 创建编辑视图/// - Parameter :/// - offsetY: 偏移量////// - Returns: UICollectionViewfunc buildCollectionView(offsetY:CGFloat) -> UICollectionView {let flowLayout = UICollectionViewFlowLayout()flowLayout.scrollDirection = .horizontalflowLayout.itemSize = item_sizeflowLayout.minimumInteritemSpacing = 0.0flowLayout.minimumLineSpacing = 0.0let collectionView = UICollectionView(frame: CGRect(x: 0.0, y: offsetY, width: self.bounds.size.width, height: item_size.height), collectionViewLayout: flowLayout)collectionView.contentInset = UIEdgeInsets(top: 0.0, left: self.bounds.width*0.5, bottom: 0.0, right: self.bounds.width*0.5)collectionView.delegate = selfcollectionView.dataSource = selfself.addSubview(collectionView)collectionView.register(PHEditorCell.self, forCellWithReuseIdentifier: NSStringFromClass(PHEditorCell.self))return collectionView}

每个collectionView加载timeline中的不同数据。

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {var cell:UICollectionViewCell?var mediaItem:PHMediaItem?var backViewColor:UIColor = .redif collectionView == self.videoCollectionView {mediaItem = timeLine.videoItmes[indexPath.section]backViewColor = .red}if collectionView == self.audioCollectionView {mediaItem = timeLine.audioItems[indexPath.section]backViewColor = .blue}if collectionView == self.musicCollectionView {mediaItem = timeLine.musicItembackViewColor = .green}if let editorCell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHEditorCell.self), for: indexPath) as? PHEditorCell {if let mediaItem = mediaItem {editorCell.titleLabel.text = mediaItem.titleeditorCell.backViewColor = backViewColorcell = editorCell}}if cell == nil {assert(false, "cell is nil")}return cell!}

最终的实现的页面显示效果如下:

混合音频

这次组合媒体看起来还不错,场景切换也十分清晰,但仔细感受我们会发现在音频上有一些小瑕疵,首先一个比较严重的问题是我们的两个音频轨道发生了冲突,在开始播放时几乎听不见画外音,背景音乐的音量已经完全覆盖了它。与其让着两个音频轨道发生冲突,倒不如使用一种名为闪避的处理方案,在画外音持续的时间内将背景音乐的音量调低,并保持这个音量直到画外音结束之后再恢复到原来的音量。

另外一个小问题时当播放结束时音乐戛然而止,如果声音可以渐渐减小,会带来更好的用户体验。

框架提供了一个AVAudioMix类处理上面的问题,它是用来在组合的音频轨道中进行自定义音频的处理。

AVAudioMix所具有的音频处理方法是由它的输入参数集定义的,它的参数是AVAudioMixInputParameters类型的对象。AVAudioMixInputParameters的实例关联组合中的单独音频轨道,并在添加到音频混合时定义基于轨道的处理方法。AVAudioMix和其相关联的AVAudioMixInputParameters集合都是不可变对象,意味着它们适用于为AVPlayerItem和AVAssetExportSession之类的客户端提供相关数据,不过它们不能操作其状态。当我们需要创建一个自定义音频混合时,需要改用它们在AVMutableAudioMix和AVMutableAudioMixInputParameters中的可变子类。

AVAudioMix及其相关类的示意图如下:

AVAudioMix及其相关类

自动调节音量

当一个组合资源播放或导出时,默认行为是以最大音量或正常音量。只有一个单音轨道时这样的方法才可能比较容易接受,不过当一个组合资源包含多个音频源时就会出现问题。对于多音频轨道的情况,每个声音都在争夺空间,这就不可避免会导致一些声音可能无法被听到。

AV Foundation把音量定义为了一个标准化的浮点型数值,数值范围从0.0~1.0。音频轨道的默认音量为1.0,不过可以使用AVMutableAudioMixInputParameters实例修改这个值。这个对象允许在一个指定时间点或给定的时间范围自动调节音量。

AVMutableAudioMixInputParameters提供了两个方法来调节音量:

1.在指定时间点立即调节音量。音量在音轨持续时间内会保持不变,直到有另一个音量调节出现。

setVolume(_ volume: Float, at time: CMTime)

volume:表示目标音量。

time:起始时间点。

2.在一个给定时间范围内平滑地将音量从一个值调节到另外一个值。当需要在一个时间范围内调整音量时,音量会立即变为指定值的初始值音量并在持续时间内逐渐调整为指定的结束值。

setVolumeRamp(fromStartVolume startVolume: Float, toEndVolume endVolume: Float, timeRange: CMTimeRange)

starVolume:起始音量。

endVolume:目标音量。

timeRange:变化持续时间范围。

简单示例

我们来使用上面的知识来实现一下下面的小示例,8秒的音频资源,开始播放时默认音量为1.0,当播放到2秒的时候开始设置音量平滑的减小到3秒时音量为0.4,而到第5秒音量直接调整为0.6。

        // 创建一个音频轨道let composition = AVMutableComposition()let compositionTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)// 设置时间let zeroSeconds = CMTime.zerolet twoSeconds = CMTime(value: 2, timescale: 1)let threeSeconds = CMTime(value: 3, timescale: 1)let fiveSeconds = CMTime(value: 5, timescale: 1)// 创建parameterslet parameters = AVMutableAudioMixInputParameters(track: compositionTrack)// 设置初始音量 (即使不设置默认也是最大音量1.0)parameters.setVolume(1.0, at: zeroSeconds)// 2s时音量开始平滑减小3s减小至0.4parameters.setVolumeRamp(fromStartVolume: 1.0, toEndVolume: 0.4, timeRange: CMTimeRange(start: twoSeconds, end: threeSeconds))// 5s时直接设置音量为0.6parameters.setVolume(0.6, at: fiveSeconds)// 创建audioMixlet audioMix = AVMutableAudioMix()audioMix.inputParameters = [parameters]
  1. 首先需要有一个音频轨道。
  2. 定义设置音量变化的时间。
  3. 创建一个新的与要操作的轨道关联的AVMutableAudioMixInputParameters实例。
  4. 默认音量为1.0,在2s时设置音量平滑过渡到0.4持续时间为1s,在5秒时直接设置音量为0.6。
  5. 定义好所有参数后就可以创建AVMutableAudioMix了,将参数添加到数组中,并将数组赋给音频混合对象的inputParameters属性。

示例中创建了一个全格式的音频混合,可以被设置为AVPlayerItem或AVAssetExportSession的audioMix属性进行播放或导出。

结语

在本文中,我们深入探讨了使用AVFoundation进行音频混合的相关类和方案。我们介绍了核心概念,包括AVAudioMix,AVMutableAudioMix,AVAudioMixInputParameters,AVMutableAudioMixInputParameters等类的使用场景。

通过提供一个简单的实例,我们演示了如何在实际项目中应用这些技术。

希望本文能为您提供清晰的指导,并激发您在自己的应用程序中尝试音频混合的想法。在下一篇博客中,我们将继续探讨如何将这些概念整合到实际项目中,为您展示更多高级技术和实用技巧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/819668.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Antd:在文本框中展示格式化JSON

要想将对象转换为格式化 JSON 展示在文本框中,需要用到 JSON.stringify JSON.stringify 方法接受三个参数: value:必需,一个 JavaScript 值(通常为对象或数组)要转换为 JSON 字符串。replacer&#xff1a…

MySql 安装,小白也可以学会成功安装的保姆级教程

MySql 安装 文章目录 MySql 安装1.Mysql下载1.1 访问下载链接1.2 选择合适版本1.3 下载安装包 2.MySql安装3.安装成功检测验证3.1 mysql自带控制台验证3.2 win系统控制台进入验证 4. mysql 配置path5. navicat 连接 mysql 1.Mysql下载 1.1 访问下载链接 MySQL Downloads 这里…

【自由看门狗配置及计算】

一、看门狗使用场景 看门狗是一个硬件计时电路,用来监测由软件故障导致的系统故障。 片上有两个看门狗定时器外设,自由看门狗定时器(FWDGT也叫独立看门狗)和窗口看门狗定时器(WWDGT)。 当嵌入式程序在运…

Linux程序调试优化(1)——内存占用详解及优化思路

文章目录 1.free查看总体的内存占用2./proc/$PID/status 查看某进程状态 linux开发最重要的两个参数,分别是内存以及CPU使用率,若内存出现严重不足,则在需要使用内存时,可能出现申请不到的情况,导致 OOM,L…

不出天府锋巢直播产业基地,即可激活电商直播产业、产教融合及人才培训服务

天府锋巢直播产业基地打造直播产业产教融合及人才培训服务新模式,携手政府、企业、高校,促进直播产业与创新人才双向奔赴,推进教学与实战深度融合,推动实习与就业无缝衔接。 各方资讯一应俱全 直播产业产教融合及人才培训服务全套…

DFS专题:力扣岛屿问题(持续更新)

DFS专题:力扣岛屿问题 开篇 每次做到DFS相关的题目都是直接跳过。蓝桥杯过后痛定思痛,好好学习一下DFS和BFS。先从DFS开始吧。 参考题解:nettee:岛屿类问题的通用解法、DFS 遍历框架 一、岛屿数量 题目链接: 200.岛屿数量 题…

Vue3学习05 一些API

Vue3-API 其它 API【shallowRef 与 shallowReactive 】shallowRefshallowReactive总结 【readonly 与 shallowReadonly】readonlyshallowReadonly 【toRaw 与 markRaw】toRawmarkRaw 【customRef】 Vue3新组件【Teleport】【Suspense】【全局API转移到应用对象】【其他】 其它 …

BackTrader 中文文档(一)

原文:www.backtrader.com/ 主页 欢迎来到 backtrader! 原文:www.backtrader.com/ 一个功能丰富的 Python 框架,用于回测和交易 backtrader允许您专注于编写可重复使用的交易策略、指标和分析器,而不必花时间构建基础…

Android T多屏多显——应用双屏间拖拽移动功能(更新中)

功能以及显示效果简介 需求:在双屏显示中,把启动的应用从其中一个屏幕中移动到另一个屏幕中。 操作:通过双指按压应用使其移动,如果移动的距离过小,我们就不移动到另一屏幕,否则移动到另一屏。 功能分析…

振弦式渗压计的安装与防护:在水工建筑物中的关键应用

振弦式渗压计,作为一种高效的孔隙水压力或液体液位测量工具,广泛应用于水工建筑物、基岩内、测压管、钻孔、堤坝、管道和压力容器内。其安装和防护工作至关重要,直接关系到测量数据的准确性和仪器的使用寿命。本文将重点探讨振弦式渗压计在填…

RabbitMQ实战教程(1)

RabbitMQ 一、RabbitMQ介绍 1.1 现存问题 服务调用:两个服务调用时,我们可以通过传统的HTTP方式,让服务A直接去调用服务B的接口,但是这种方式是同步的方式,虽然可以采用SpringBoot提供的Async注解实现异步调用&…

Ue不消耗輸入

1、介紹 我們都知道ue裏面使用輸入時,都是在PlayerController裏面進行獲取, 使用官方的操作映射,軸映射,以及目前最新的增强型輸入 但是我們發現了一個問題 那就是輸入會被消耗 就是儅我鼠標按在一個按鈕上時 你另一個地方接受…

Elastic安装后 postman对elasticsearch进行测试

一、创建索引和mapping //id 字段自增id //good_sn 商品SKU //good_name 商品名称 //good_introduction 商品简介 //good_descript 商品详情 PUT http://IP:9200/shop { "mappings":{ "good":{ "properties":{ …

LabVIEW光学探测器板级检测系统

LabVIEW光学探测器板级检测系统 特种车辆乘员舱的灭火抑爆系统广泛采用光学探测技术来探测火情。光学探测器作为系统的关键部件,其探测灵敏度、响应速度和准确性直接关系到整个系统的运行效率和安全性。然而,光学探测器在长期使用过程中可能会因为灰尘污…

牛客Linux高并发服务器开发学习第一天

Linux开发环境搭建 安装Xshell 7远程连接虚拟机的Ubuntu 安装Xftp 7可以传输文件(暂时还没使用) 安装VMware Tools可以直接从Windows系统向虚拟机Linux系统拖拽文件实现文件交互。 安装CScode可以远程连接Linux系统进行代码的编写。(Windows系统与Linxu系统公钥…

ActiveMQ 任意文件上传漏洞复现

一、使用弱口令登陆 ​ 访问 http://ip:8161/admin/ 进入admin登陆页面,使用弱口令登陆,账号密码皆为 admin,登陆成功后,headers中会出现验证信息 ​ 如: Authorization: Basic YWRtaW46YWRtaW4 # 二、利用PUT协议上…

STL体系结构与各容器基本介绍

STL体系结构与各容器基本介绍 STL体系结构基本容器序列式关联式&#xff08;查找更快&#xff09;其他&#xff08;不常用&#xff09;使用分配器 STL体系结构 六大模块 容器算法迭代器适配器仿函数分配器 基本容器 序列式 array c11新标准array<类型&#xff0c;大小&…

Linux-管道

目录 无名管道关闭未使用的管道文件描述符 管道对应的内存大小与shell命令进行通信&#xff08;popen&#xff09;命名管道FIFO创建FIFO文件打开FIFO文件 无名管道 管道是最早出现的进程间通信的手段。 管道的作用是在有亲缘关系的进程之间传递消息。所谓有亲缘关系&#xff…

MySQL中的SQL高级语句[二]

使用语言 MySQL 使用工具 Navicat Premium 16 代码能力快速提升小方法&#xff0c;看完代码自己敲一遍&#xff0c;十分有用 拖动表名到查询文件中就可以直接把名字拉进来以下是使用脚本方法&#xff0c;也可以直接进行修改中括号&#xff0c;就代表可写可不写 有些地方的代…

IO——标准IO

1.1概念 标准IO&#xff1a;是在C库中定义的一组专门用于输入输出的函数。 1.2特点 &#xff08;1&#xff09;通过缓冲机制减少系统调用&#xff0c;提高效率 &#xff08;2&#xff09;围绕流操作&#xff0c;用FILE*描述 &#xff08;3&#xff09;标准IO默认打开三个流&a…