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

news2025/2/7 8:45:27

引言

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

构建双音轨组合

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

资源选择

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

代码如下:

修改数据源为二维数组

    /// 数据
    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.0
        self.audioCollectionView = buildCollectionView(offsetY: offsetY)
    }
    
    func addMusicCollectionView() {
        let offsetY = CGRectGetMaxY(self.audioCollectionView?.frame ?? CGRect.zero) + 8.0
        self.musicCollectionView = buildCollectionView(offsetY: offsetY)
    }
    
    /// 创建编辑视图
    /// - Parameter :
    /// - offsetY: 偏移量
    ///
    /// - Returns: UICollectionView
    
    func buildCollectionView(offsetY:CGFloat) -> UICollectionView {
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.scrollDirection = .horizontal
        flowLayout.itemSize = item_size
        flowLayout.minimumInteritemSpacing = 0.0
        flowLayout.minimumLineSpacing = 0.0
        let 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 = self
        collectionView.dataSource = self
        self.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 = .red
        if 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.musicItem
            backViewColor = .green
        }
        
        if let editorCell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHEditorCell.self), for: indexPath) as? PHEditorCell {
            if let mediaItem = mediaItem {
                editorCell.titleLabel.text = mediaItem.title
                editorCell.backViewColor = backViewColor
                cell = 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.zero
        let twoSeconds = CMTime(value: 2, timescale: 1)
        let threeSeconds = CMTime(value: 3, timescale: 1)
        let fiveSeconds = CMTime(value: 5, timescale: 1)
        
        // 创建parameters
        let parameters = AVMutableAudioMixInputParameters(track: compositionTrack)
        
        // 设置初始音量 (即使不设置默认也是最大音量1.0)
        parameters.setVolume(1.0, at: zeroSeconds)
        // 2s时音量开始平滑减小3s减小至0.4
        parameters.setVolumeRamp(fromStartVolume: 1.0, toEndVolume: 0.4, timeRange: CMTimeRange(start: twoSeconds, end: threeSeconds))
        // 5s时直接设置音量为0.6
        parameters.setVolume(0.6, at: fiveSeconds)
        
        // 创建audioMix
        let 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.coloradmin.cn/o/1598442.html

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

相关文章

Elastic安装后 postman对elasticsearch进行测试

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

Windows电脑使用Everything+cpolar搭建在线资料库并实现无公网IP管理文件

文章目录 推荐前言1.软件安装完成后,打开Everything2.登录cpolar官网 设置空白数据隧道3.将空白数据隧道与本地Everything软件结合起来总结 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家…

网络安全赛事中开源威胁情报的妙用

写在前面 近期参与了一场网络安全赛事,一些题目的解法涉及到开源网络威胁情报,遂对相关题目及涉及到的知识点进行分析。 什么是OSCTI 开源网络威胁情报 (Open-Source Cyber Threat Intelligence,OSCTI)是详细描述针对某个组织网络安全威胁的数据。OSC…

基于ssm的社区再就业培训管理系统的设计与实现论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本社区再就业培训管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数…

解决微信报错,错误码:80051

方法一:uniapp勾选-->运行时是否压缩代码 工具栏-->运行-->运行到小程序模拟器-->运行时是否压缩代码 方法二:将项目中的静态图片换成网络图片 静态图片非常占用空间 方法三:分包处理 具体方法:http://t.csdnimg.cn…

2022年高教社杯数学建模国赛C题创新多思路详解

C题古代玻璃制品的成分分析与鉴别 题目定位:数据分析题(数据量少,维度较多) 参考往年题目:2021年国赛B题 一、题目背景分析 这几段主要是交代题目的背景,讲解了古代玻璃制品的特征,解释了其化…

低敏猫粮怎么选?看这一篇就够了!

亲爱的朋友们,你们是否曾经为了家中敏感肠胃的小猫咪而犯愁,不知道如何选择适合的猫粮呢?今天,就让我们一起聊聊低敏猫粮与普通猫粮的那些不同之处吧!🐱 1️⃣ **成分差异**:首先,从…

Odoo 17 库存补给报告概览

Odoo 17 的库存补给报告对于优化库存水平和确保供应链顺畅运作至关重要。该报告深入分析库存变动,帮助企业维持理想的库存水平,减少缺货现象,提升整体运营效率。报告主要基于需求预测和历史消费模式来指导库存补充,通过分析销售趋…

eBay跨境商家必读:自养号测评补单的四大优势

eBay的流量并不比亚马逊差,这与eBay在简化买家退货流程,促进批量购买打折产品等方面改善用户购买体验所做的努力是分不开的。那么作为ebay跨境卖家该如何在平台上卖的更好? 1、店铺建设,做好产品的详情页面 1.一般来说&#xff…

WebApis知识总结以及案例(续3)

综合案例 小兔鲜页面注册 分析业务模块 发送验证码模块 用户点击之后,显示05 秒后重新获取 时间到了,自动改为重新获取 //1.发送短信验证码模块const codedocument.querySelector(.code)let flagtrue//通过一个变量来控制 节流阀 // 1.1 点击事件co…

[docker] 核心知识 - 概念和运行

[docker] 核心知识 - 概念和运行 之前 docker 学了个开头就去搞项目去了,不过项目也开展了好久了,前端差不多吃透了,有些新功能需要用 docker 和 k8s……是时候重新学习一下了。 这一部分简单的过一下概念和讲一下怎么运行 docker 镜像和启…

洛谷P1229 遍历问题

洛谷P1229 遍历问题 遍历问题 文章目录 遍历问题题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 正确代码 题目描述 我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它…

windows wireshark抓包rtmp推流出现TCP Retransmission

解决办法:tcp.port1935 && !(tcp.analysis.retransmission)

springcloud-fegin 组件调用

一、Feign 概述 Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。 在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。Feign支持多种…

武汉星起航:亚马逊跨境引领全球贸易新趋势,展现积极影响力

随着全球化浪潮的持续推进,跨境电商行业正迎来前所未有的发展机遇。亚马逊作为全球领先的电商平台,其在跨境电商领域的发展趋势备受瞩目。亚马逊跨境电商不仅扩大了跨境市场的规模,优化了供应链管理,还积极应用科技创新&#xff0…

硬件开源--Model 3C(简称M3)芯片驱动RGB接口86中控屏PCBA原理图

针对市场IOT应用需求,基于启明智显的Model3C芯片(简称M3)设计开发的一款超高性价比的86型中控屏PCBA原理图开源。 Model3C芯片(简称M3)是一款基于 RISC-V 的高性能、国产自主、工业级高清显示与智能控制 MCU,配备强大的 2D 图形加速处理器、PNG/JPEG 解码…

大话设计模式之访问者模式

访问者模式是一种行为设计模式,它允许你在不修改对象结构的前提下定义作用于这些对象结构元素的新操作。 在访问者模式中,有两个核心概念: 访问者(Visitor):定义了对对象结构中每个元素的访问操作。每个访…

STM32的GPIO端口的八种模式解析

目录 STM32的GPIO端口的八种模式解析 一、上拉输入模式 二、下拉输入模式 三、浮空输入模式 四、模拟输入模式 五、推挽输出模式 六、开漏输出模式 七、复用推挽输出模式 八、复用开漏输出模式 STM32的GPIO端口的八种模式解析 在学习STM32的过程中,GPIO端口…

惨痛教训:我的专业赢得了辩论,却用情商丢掉了客户。

某一年,我给某市级部门做了一个3D展示的系统,时间紧,任务重,也是如期完成了。最后到市里去评审的时候,现场来了11个副主任,大家你一言我一语的对系统提出意见和建议。 那个时候我是年轻气盛,知道…

CPLD可运行的最高频率是多少

CPLD可运行的最高频率是多少 AG32 内置CPLD的可运行最高频率 AG32 内置CPLD的可运行最高频率 AG32 MCU 的运行最高频率是248M。而CPLD中没有标准的最高频率。 最大能跑多少MHz,取决于cpld 里的设计。 如果是逻辑电路,则不存在时钟的概念。 如果是时序电路…