iOS 音频开发

 

音频基础知识

组成

音频文件的组成:文件格式(或者音频容器) + 数据格式(或者音频编码)。

文件格式(或音频容器)是用于形容文件本身的格式。

我们可以通过多种不同的方法为真正的音频数据编码。例如CAF文件便是一种文件格式,它能够包含MP3格式,线性PCM以及其它数据格式的音频。

数据格式(或音频编码)

我们将从音频编码开始阐述(而不是文件格式),因为编码是最重要的环节。

线性PCM:

这是表示线性脉冲编码调制,主要是描写用于将模拟声音数据转换成数字格式的技术。简单地说也就是未压缩的数据。因为数据是未压缩的,所以我们便可以最快速地播放出音频,而如果空间不是问题的话这便是iPhone音频的优先代码选择。

音频文件计算大小


声卡对声音的处理质量可以用三个基本参数来衡量,即采样频率、采样位数和声道数。

采样频率:

是指单位时间内的采样次数。采样频率越大,采样点之间的间隔就越小,数字化后得到的声音就越逼真,但相应的数据量就越大。声卡一般提供11.025kHz、22.05kHz和44.1kHz等不同的采样频率。

采样位数:

是记录每次采样值数值大小的位数。采样位数通常有8bits或16bits两种,采样位数越大,所能记录声音的变化度就越细腻,相应的数据量就越大。

声道数

是指处理的声音是单声道还是立体声。单声道在声音处理过程中只有单数据流,而立体声则需要左、右声道的两个数据流。显然,立体声的效果要好,但相应的数据量要比单声道的数据量加倍。

声音数据量的计算公式为:

数据量(字节/秒)= (采样频率(Hz)× 采样位数(bit) × 声道数)/ 8

单声道的声道数为1,立体声的声道数为2。

【例1】请计算对于5分钟双声道、16位采样位数、44.1kHz采样频率声音的不压缩数据量是多少?
根据公式:数据量=(采样频率×采样位数×声道数×时间)/8
得,数据量(MB)=[44.1×1000×16×2×(5×60)] /(8×1024×1024)=50.47MB
计算时要注意几个单位的换算细节:
时间单位换算:1分=60秒
采样频率单位换算:1kHz=1000Hz
数据量单位换算:1MB=1024×1024=1048576B

【例2】请计算对于双声道立体声、采样频率为44.1kHz、采样位数为16位的激光唱盘(CD-A),用一个650MB的CD-ROM可存放多长时间的音乐?
已知音频文件大小的计算公式如下:
文件的字节数/每秒=采样频率(Hz)X采样位数(位)X声道数/8
根据上面的公式计算一秒钟时间内的不压缩数据量:(44.1×1000×16×2)/8=0.168MB/s
那么,一个650MB的CD-ROM可存放的时间为:(650/0.168)/(60×60)=1.07小时。


iOS 音频转码

音频转码使用的框架为:AudioToolBox

内存转码:

使用函数: AudioConverterFillComplexBuffer
    - (void)handleAudioPackets:(const void *)inputData numberOfBytes:(UInt32)numberOfBytes numberOfPackets:(UInt32)numberOfPackets packetDescriptions:(AudioStreamPacketDescription *)packetDescriptions { if (!_audioFileStream || !_parseAudioHeader || !_decodeConverterRef) return; AudioConvertInfo convertInfo = (AudioConvertInfo){ .done = NO, .numberOfPackets = numberOfPackets, .packetDescriptions = packetDescriptions, .audioBuffer = (AudioBuffer){ .mData = (void *)inputData, .mDataByteSize = numberOfBytes, .mNumberChannels = _sourceAsbd.mChannelsPerFrame } }; AudioBufferList decodedData; decodedData.mNumberBuffers = 1; decodedData.mBuffers[0].mNumberChannels = _canonicalAsbd.mChannelsPerFrame; decodedData.mBuffers[0].mDataByteSize = _decodeBufferSize; decodedData.mBuffers[0].mData = _decodeBuffer; UInt32 ioOutputDataPackets1, ioOutputDataPackets2; OSStatus decodingStatus, encodingStatus; while (1) { ioOutputDataPackets1 = numberOfPackets; decodingStatus = AudioConverterFillComplexBuffer(_decodeConverterRef, AudioConverterCallback, (void*)&convertInfo, &ioOutputDataPackets1, &decodedData, NULL); if (decodingStatus == OS_STATUS_DONE || decodingStatus == 0) { if (ioOutputDataPackets1 > 0) { // Start encoding AudioConvertInfo encodeConvertInfo = (AudioConvertInfo){ .done = NO, .numberOfPackets = ioOutputDataPackets1, .packetDescriptions = NULL, .audioBuffer = (AudioBuffer){ .mData = decodedData.mBuffers[0].mData, .mDataByteSize = decodedData.mBuffers[0].mDataByteSize, .mNumberChannels = _canonicalAsbd.mChannelsPerFrame } }; AudioBufferList encodedData; encodedData.mNumberBuffers = 1; encodedData.mBuffers[0].mNumberChannels = _destinationAsbd.mChannelsPerFrame; encodedData.mBuffers[0].mDataByteSize = _encodeBufferSize; encodedData.mBuffers[0].mData = _encodeBuffer; while (1) { ioOutputDataPackets2 = _encodePacketsPerBuffer; encodingStatus = AudioConverterFillComplexBuffer(_encodeConverterRef, AudioConverterCallback, (void*)&encodeConvertInfo, &ioOutputDataPackets2, &encodedData, _encodePacketDescriptions); if (encodingStatus == OS_STATUS_DONE || encodingStatus == 0) { //一个buffer 转码成功 } else { [self failureOccurred]; return; } if (encodingStatus == OS_STATUS_DONE) { break; } } // End encoding } } else { [self failureOccurred]; return; } if (decodingStatus == OS_STATUS_DONE) { break; } } }

文件转码:

使用函数 ExtAudioFileRead
void startConvert(ExtAudioConverterSettings* settings){ //Determine the proper buffer size and calculate number of packets per buffer //for CBR and VBR format UInt32 sizePerBuffer = 32*1024;//32KB is a good starting point UInt32 framesPerBuffer = sizePerBuffer/sizeof(SInt16); // allocate destination buffer SInt16 *outputBuffer = (SInt16 *)malloc(sizeof(SInt16) * sizePerBuffer); while (1) { AudioBufferList outputBufferList; outputBufferList.mNumberBuffers = 1; outputBufferList.mBuffers[0].mNumberChannels = settings->outputFormat.mChannelsPerFrame; outputBufferList.mBuffers[0].mDataByteSize = sizePerBuffer; outputBufferList.mBuffers[0].mData = outputBuffer; UInt32 framesCount = framesPerBuffer; CheckError(ExtAudioFileRead(settings->inputFile, &framesCount, &outputBufferList), "ExtAudioFileRead failed"); if (framesCount==0) { printf("Done reading from input file\n"); return; } CheckError(ExtAudioFileWrite(settings->outputFile, framesCount, &outputBufferList), "ExtAudioFileWrite failed"); } }

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

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

相关文章

【ArcGIS微课1000例】0045:ArcGIS制图模板的自定义与使用方法

怎样在ArcGIS中保存地图模板以在地图制图与打印之前使用呢? 文章目录 一、地图模板简介二、地图模板创建1. 创建模板2. 创建缩略图3. 保存模板三、地图模板使用一、地图模板简介 使用ArcMap打开一个已有的地图模板,【文件】→【新建】,任选一个模板,这里选择一个传统模板。…

api 接口开发理论 在php中调用接口以及编写接口

如:http://localhost/openUser.php?actget_user_list&typejson 在这里openUser.php相当于一个接口,其中get_user_list 是一个API(获取用户列表),讲求返回的数据类型为JSON格式。 你只需要在你PHP代码中执行这条链…

怎么样的框架对于开发者是友好的?

云原生离.NET开发到底有多远?云原生的概念由来不久,故事从“上云”开始,伴随dorker、k8s等技术的推出,以及CNCF与各大云厂商的共同加持,云原生逐渐被大家所熟知。云原生不依赖具体的云,不管公有云还是私有云…

JS 烧脑面试题大赏

本文精选了20多道具有一定迷惑性的js题,主要考察的是类型判断、作用域、this指向、原型、事件循环等知识点,每道题都配有详细傻瓜式的解析,偏向于初学者,大佬请随意。 第1题 let a 1 function b(a) {a 2console.log(a) } b(a)…

苹果ios用js的Date()出现NaN问题解决办法

原文:苹果ios用js的Date()出现NaN问题解决办法ios使用如下方法获得NaN,安卓手机则是正常计算,解决方法是换个这个时间的格式 new Date("2017-04-28 23:59:59").getTime() 换成如下方式就正常了,就是‘-’换成…

Thinkphp 验证码、文件上传

一、验证码 验证码参数 例题&#xff1a;登录时验证下验证码 LoginController.class.php <?php namespace Home\Controller; use Think\Controller; class LoginController extends Controller {public function Login(){if(empty($_POST)){$this->display(); } e…

ArcGIS实验教程——实验四十七:数据驱动页工具批量制作甘肃省各地级市人口七普专题图集

本实验详细讲解利用ArcGIS数据驱动页工具,制作甘肃省各地级市人口七普专题图集。 文章目录 1. 数据驱动页工具简介2. 甘肃省各地级市人口七普专题图集2.1 符号化及标注2.2 数据驱动页的创建2.3 数据驱动页面文本操作2.4 数据驱动页的导出1. 数据驱动页工具简介 数据驱动页面是…

为什么Java有GC调优而没听说过有CLR的GC调优?

前言在很多的场合我都遇到过一些群友提这样的一些问题&#xff1a;为什么Java有GC调优而CLR没有听说过有GC调优呢&#xff1f;到底是Java的JVM GC比较强还是C#使用的.NET CLR的GC比较强呢&#xff1f;其实业内已经有几位大佬的高赞文章和大家分享一下&#xff0c;主要讨论JVM和…

Ubuntu16.04 - 安装RabbitVCS,linux下的TortoiseSVN!!!

RabbitVCS 官网&#xff1a;http://rabbitvcs.org/ 1&#xff0c;添加PPA源。在shell里面执行下面命令&#xff1a; sudo add-apt-repository ppa:rabbitvcs/ppa 这个命令执行完毕后&#xff0c;查看执行结果看是否密钥导入成功&#xff0c;成功截图&#xff1a; 如果导入密钥失…

8 种最坑的SQL错误用法

1、LIMIT 语句 2、隐式转换 3、关联更新、删除 4、混合排序 5、EXISTS语句 6、条件下推 7、提前缩小范围 8、中间结果集下推 总结 sql语句的执行顺序&#xff1a; FROM <left_table>ON <join_condition><join_type>JOIN <right_table>WHERE &…

Vue根据菜单json数据动态按需加载路由Vue-router

每个菜单项对应一个页面组件&#xff0c;根据菜单项动态按需加载路由 路由配置的正确写法&#xff1a; /*router/index.js*/ import Vue from vue import Router from vue-router import url from ./url import store from ../storeVue.use(Router)const router new Router({/…

【ArcGIS微课1000例】0047:制图表达(2)---河流渐变效果的实现

当我们在ArcMap中加载河流数据时,得到的效果往往如图所示,仅仅是表示河流位置的线要素,既无法真实地反映河流的实际情况,同时在出图的时候也远没有任何美化效果。 文章目录 1.创建制图表达2.添加几何效果3.使用制图规则4.使用制图表达属性覆盖警告:这些操作会对您的数据库…

操作系统思考 第二章 进程

第二章 进程 作者&#xff1a;Allen B. Downey 原文&#xff1a;Chapter 2 Processes 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 2.1 抽象和虚拟化 在我们谈论进程之前&#xff0c;我打算先定义几个东西&#xff1a; 抽象&#xff08;Abstraction&#xff09;&…

1 句代码,搞定 ASP.NET Core 绑定多个源到同一个类

问题有群友希望将路由中的信息绑定到一个Dto对象中&#xff1a;public class DDDDDto {[FromRoute(Name "collectionId")]public Guid collectionId { get; set; }[BindProperty(Name "relativeUrl")]public string relativeUrl { get; set; } }这样就不用…

redux中间件的用法

1.定义 中间件就是一个函数&#xff0c;对store.dispatch方法进行了改造&#xff0c;在发出 Action 和执行 Reducer 这两步之间&#xff0c;添加了其他功能。 2.举例 日志中间件 import { applyMiddleware, createStore } from redux; import createLogger from redux-logger; …

设置git自动补全功能(windows版本)

目录 下载 Git 的源代码 在目录中 git/contrib/completion/ 中找到 git-completion.bash 文件 将 git-completion.bash 文件改名为 .git-completion.bash 找到本机git安装目录 将.git-completion.bash文件复制到git安装目录下的etc文件夹 打开同目录下的 bash.bashrc 文件&…

用Vue搭建一个应用盒子(二):datetime-picker

接着上次的进度&#xff0c;我们已经实现了一个todo-list。它已经具备了基本的功能&#xff0c;可以新建、编辑、删除任务。但是美中不足的是&#xff0c;它的时间设定上只能通过输入一段字符串来设定&#xff0c;很不社会。我们应该完成的效果是一个time-picker&#xff0c;日…

ArcGIS实验教程——实验四十八:ArcGIS制图表达入门及案例教程

文章目录 1. 制图表达的概念1.1 什么是地图表达1.2 使用制图表达改善要素外观1.3 制图表达的优点2. 使用制图表达2.1 创建制图表达2.2 使用制图表达来符号化图层2.3 使用制图表达规则3. 地图表达实战案例1.创建制图表达2.添加几何效果3.使用制图规则4.使用制图表达属性覆盖1. 制…

PAT (Advanced Level) 1070. Mooncake (25)

简单贪心。先买性价比高的。 #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<map> #include<stack> #include<queue> #include<string> #include<algorithm> using namespace std;doub…

[转]Java 18 还未用上,Java 19 最新两大特性曝光

铁打的 Java&#xff0c;流水的版本。 不久前&#xff0c;Java 18 才正式发布&#xff0c;遵循 Oracle 六个月发一版本的频率&#xff0c;Java 19 将在今年 9 月出炉。这不&#xff0c;还没等众多开发者用上 Java 18&#xff0c;关于 Java 19 最新的两个目标功能就被披露了出…