AIFF和AIFF-C音频交换文件格式的简单介绍
正文
AIFF,全称 Audio Interchange File Format,可简写为 Audio IFF 或 AIFF,是苹果公司推出的一种音频文件格式。
AIFF-C,是 AIFF 的扩充,C 意为 Compressed,说明这是一种可以存储压缩数据的格式,由苹果公司进行扩展,可简写为AIFC。
由此可知,AIFF 和 AIFF-C 主要用于苹果公司推出的设备和系统,但由于文件交换的需要,在 Windows 下偶尔还是会有使用的需求。
关于这个两个格式的详细文档,可以从 这个页面 下载,本文将基于这两份文档,进行一些简单的介绍。
关于AIFF格式和相关的代码,可在https://gitcode.net/PeaZomboss/learnaudios找到(include和source文件夹)。
AIFF
由于 AIFF 是未压缩的格式,所以只能用来保存未经压缩的 PCM 编码格式,关于这种格式,本文不做介绍,需要的仔细搜索即可。
在具体介绍之前,先了解一些 AIFF 文件所用到的特殊数据结构:
ID
,与 WAV 格式的 Four Char Code 类似,使用四个可打印 ASCII 字符组成,其本质就是一个 long intpstring
,Pascal 风格的字符串类型,由一个表示长度的字节和若干个字符构成,最大长度为 255extended
,扩展精度的浮点数类型,是 IEEE 754 标准之一,用 80 bit 表示的浮点数,占用10个字节,是 Pascal 语言的基础类型,但在x86下使用存在明显问题,稍后详解
注意:AIFF 的所有数据都按大端序存储,在 Intel、AMD、ARM 等小端序的 CPU 中读取需要进行转换
如果对 WAV 格式比较熟悉的话,那么下面的内容也不难理解
首先介绍几个基本 ID
#define AIFFIDFORM 'FORM'
#define AIFFIDAIFF 'AIFF'
#define AIFFIDCommon 'COMM'
#define AIFFIDSoundData 'SSND'
之所以说基本,是因为每个 AIFF 文件至少要用到这四个 ID,其余格式请查阅文档
AIFF 文件按块进行存储,每个块都由块头和块数据组成,其中块头的定义如下:
typedef struct {long ID; // 这个是前面提到的 ID,记住是大端存储的,在小端 CPU 下不能直接使用上面的定义long Size; // 块数据的大小,不包括块头
} ChunkHeader;
注意:所有块都必须按2字节对齐,即使Size可能是奇数
接着介绍 AIFF 文件头定义
typedef struct {long ID; // 这个必须是 'FORM'long Size; // 文件大小减去 Size 和 ID 的大小,即文件字节数减8long Type; // 这个必须是 'AIFF'
} AIFFHeader;
与 WAV 格式的 RIFF 定义十分相似,这个主要是用来标识文件类型用的
然后我们来看看 Common 块的定义
typedef struct {long id; // 必须是 'COMM'long size; // 必须是 18short channels; // 声道数,比如 1 代表单声道,2 代表立体声unsigned long SampleFrames; // 采样帧数,每一个帧的大小取决于声道数和位深度short SampleSize; // 位深度,即每个采样的大小,用位表示,常见的有 16,24 等extended SampleRate; // 采样率,常见的如 44100,48000 等
} CommonChunk;
- 和 WAV 一样,音频数据是交错存储的,这意味着每个采样帧都包含所有声道的数据,所以总采样数据大小等于采样帧数*声道数*位深度/8
- 位深度可能不为8的整数倍,但实际存储按字节大小整数倍确定,比如位深度为12,实际每个采样占用2个字节,其余位补零
- 由于c/c++语言并不支持
extended
类型,所以实际使用中需要将其转化为单精度的float
或者双精度的double
最后说一下 SoundData 块的定义
typedef struct {long id; // 必须是 'SSND'long size;unsigned long offset;unsigned long BlockSize;// 按 PCM 编码的数据
} SoundDataChunk;
- offset 和 BlockSize 主要用来对齐,一般情况下为0,很少遇到非0的,具体请参阅官方文档
AIFC
AIFC 是 AIFF 的扩展,主要是添加了一个新块 FormatVersion 块,同时扩充了原来的 Common 块
先介绍下新增的常量
#define AIFCVersion1 0xA2805140
#define AIFCIDAIFC 'AIFC' // 替换文件头的 'AIFF'
#define AIFCIDFVER 'FVER'
新的 FormatVersion 块
typedef struct {long ID; // 必须是 'FVER'long Size; // 4unsigned long TimeStamp; // AIFCVersion1,目前只有这个
} FormatVersionChunk;
扩展的 Common 块
typedef struct {long id; // 必须是 'COMM'long size; // 22 + CompressionName 大小short channels; // 和 AIFF 一样unsigned long SampleFrames; // 和 AIFF 一样short SampleSize; // 和 AIFF 一样extended SampleRate; // 和 AIFF 一样long CompressionType;// pstring CompressionName; // 可选
} AIFCCommonChunk;
- CompressionType 有许多,可以在开头的链接找到,下面介绍几个常见的
- ‘NONE’,未经压缩的,与 AIFF 完全一致
- ‘sowt’,数据是交换端序存储的,对于小端 CPU 来说就不用转换了
- ‘fl32’,数据是按照 IEEE 32位浮点数存储的
- ‘fl64’,数据是按照 IEEE 64位浮点数存储的
- ‘FL32’,与 ‘fl32’ 相同,可以理解为别名
- CompressionName 可选,用于指示压缩方法名,即使有一般也可忽略
除了sowt类型,其余全部为大端
文件的读取方法
- 首先读取文件头12个字节,判断文件类型是 AIFF 或 AIFC,否则退出
- 读取8个字节,判断 ID 和 Size,确定是否需要该块,不需要则文件指针前进 Size 个字节
- 读取需要的信息,进行相应的保存(比如Common块的内容,音频数据的文件偏移量等)
- 重复2-3,直到文件末尾
- 如果文件相关信息不齐全,则退出
- 根据先前保存的相关信息,读取音频数据
- 所有的内容信息都要进行大小端转换
- 音频数据的大小端转换取决于每个采样的字节数
其他细节内容
苹果曾经主要是使用 Pascal 语言进行开发的,后来才换了 C 系的语言,而 Pascal 原生支持 extended 浮点数类型,且在 x86 架构(包括64位)下得到完美的支持(除了Windows x64,这是由于微软ABI的限制,故编译器默认不支持,但可以使用汇编手动调用x87 FPU进行处理)。
但是C/C++(大多数语言)不支持extended类型,所以我们需要手动处理,这里介绍两种方法,一个如何在x86架构的cpu进行操作,因为其余架构本人并不熟悉,另一个是通用解决方法,也就是根据浮点数的定义手动处理转换。
我们首先定义 extended 类型结构体
typedef struct {unsigned char data[10];
} extended;
关于大小端的转换此处不再赘述,类似字符串的逆置
然后写两个函数,一个是 ExtendedToFloat,一个是 FloatToExtended,使用 double 同理,只需简单的修改
以下内容只能在 MinGW-gcc 下编译,在 MSVC 下需要修改,但原理一致,都是利用 x86 架构自带的 x87 FPU,这个方法并不推荐,且有较大的局限性,故推荐看后面的通用方案
void ExtendedToFloat(const extended *e, const float *s)
{#ifdef _WIN32
#ifdef _WIN64// Windows x64 下,前两个参数分别在 rcx 和 rdx 中// 如果是 Linux x64,则前两个参数分别在 rdi 和 rsi 中asm("fldt (%rcx)\n\t""fstps (%rdx)\n\t"); // AT&T 汇编风格下,如果是 double 则使用 fstpq
#elseasm("movl 4(%esp), %ecx\n\t" // esp+4 是第一个参数"movl 8(%esp), %edx\n\t" // esp+8 是第二个参数"fldt (%ecx)\n\t""fstps (%edx)\n\t");
#endif
#else
#error "Do not support now"
#endif
}void FloatToExtended(const float *s, const extended *e)
{#ifdef _WIN32
#ifdef _WIN64asm("flds (%rcx)\n\t" // AT&T 汇编风格下,如果是 double 则使用 fldq"fstpt (%rdx)\n\t");
#elseasm("movl 4(%esp), %ecx\n\t""movl 8(%esp), %edx\n\t""flds (%ecx)\n\t""fstpt (%edx)\n\t");
#endif
#else
#error "Do not support now"
#endif
通用方法如下(注意这里的尾数是截断的,没有舍入):
void ExtendedToFloat(const extended *e, const float *s)
{unsigned char *ps = (unsigned char *)s;unsigned char *pe = (unsigned char *)e;unsigned short exponent;exponent = ((pe[9] & 0x7f) << 8 | pe[8]) - 16383 + 127;ps[3] = pe[9] & 0x80 | exponent >> 1;ps[2] = exponent << 7 | (pe[7] & 0x7f); // 实际上尾数的第一位被舍弃了ps[1] = pe[6];ps[0] = pe[5];
}void FloatToExtended(const float *s, const extended *e)
{unsigned char *ps = (unsigned char *)s;unsigned char *pe = (unsigned char *)e;unsigned short exponent;exponent = ((ps[3] & 0x7f) << 1 | (ps[2] >> 7)) - 127 + 16383;pe[9] = ps[3] & 0x80 | exponent >> 8;pe[8] = exponent;pe[7] = 0x80 | (ps[2] & 0x7f); // 同理,尾数第一位要置位1pe[6] = ps[1];pe[5] = ps[0];pe[4] = 0;*((long *)pe) = 0;
}void ExtendedToDouble(const extended *e, const double *d)
{unsigned char *pd = (unsigned char *)&d;unsigned char *pe = (unsigned char *)&e;unsigned short exponent = (((pe[9] & 0x7f) << 8) | pe[8]) - 16383 + 1023;pd[7] = pe[9] & 0x80 | exponent >> 4;pd[6] = exponent << 4 | ((pe[7] >> 3) & 0xf); // 同理pd[5] = pe[7] << 5 | pe[6] >> 3;pd[4] = pe[6] << 5 | pe[5] >> 3;pd[3] = pe[5] << 5 | pe[4] >> 3;pd[2] = pe[4] << 5 | pe[3] >> 3;pd[1] = pe[3] << 5 | pe[2] >> 3;pd[0] = pe[2] << 5 | pe[1] >> 3;
}void DoubleToExtended(const double *d, const extended *e)
{unsigned char *pd = (unsigned char *)&d;unsigned char *pe = (unsigned char *)&e;unsigned short exponent = (((pd[7] & 0x7f) << 4) | (pd[6] >> 4)) - 1023 + 16383;pe[9] = pd[7] & 0x80 | exponent >> 8;pe[8] = exponent;pe[7] = 0x80 | pd[6] << 3 | pd[5] >> 5; // 同理pe[6] = pd[5] << 3 | pd[4] >> 5;pe[5] = pd[4] << 3 | pd[3] >> 5;pe[4] = pd[3] << 3 | pd[2] >> 5;pe[3] = pd[2] << 3 | pd[1] >> 5;pe[2] = pd[1] << 3 | pd[0] >> 5;pe[1] = pd[0] << 3;pe[0] = 0;
}
实际上这个方法在某种程度上也并不通用,因为不同平台对80位浮点数的表示方法不一样,但是由于Intel的x87 FPU出现时间较早,其使用的80位浮点数表示方式(即尾数第一位固定为1,而没有省略)至少在AIFF文件已经成为了一种事实上的标准,几乎所有的AIFF(AIFC)文件都是按照这种方式的。不过关于80位浮点数和x87协处理器的处理已经属于历史遗留问题了,没有必要讨论好坏,所以目前我们只需要用特点的代码将其转换到double类型使用即可。
有兴趣可以看看这个链接https://blog.sina.com.cn/s/blog_6449050e0100p85z.html,对比了x86和MIPS的区别。
水平有限,敬请理解
更新记录
2023-01-22:修正了关于extended浮点数的错误表述,修正了错误的代码,添加了double类型的转换,添加了代码库。
AIFF和AIFF-C音频交换文件格式的简单介绍相关推荐
- EDID文件格式转换工具介绍
EDID文件格式转换工具介绍 1 EDID文件 简单来说,EDID文件主要内容就是128个字节数据(V1.3版本)或是256个字节数据(V1.4版本). 但是不同的EDID的编辑工具支持的文件 ...
- 【Oboe——Android低延迟音频应用开发库使用介绍】
Oboe--Android低延迟音频应用开发库使用介绍 一. 背景 Oboe是一个C++库,是Google于2018年开发用来为Android打造高性能的互动音频体验,可在99%的安卓设备上实现最低可 ...
- 蓝牙音频双剑客(二)--高质量音频分布协议(A2DP) 概念介绍
零. 概述 主要介绍下蓝牙协议栈(bluetooth stack)传统蓝牙音频协议之高质量音频分布协议(A2DP) 概念介绍,包括在协议栈中的架构,角色以及Source到Sink的发送Audio流程 ...
- aiff转mp3及其它各种文件格式转换
一大清早,同事拿了硕大的移动硬盘来找我,说是有个aiff格式的文件想转成mp3,我没见过这个格式,暗想是否是高清的?或是什么.结果自己见识少,人家直接告诉我,是苹果机里的.我气得鼻子差点歪了,在苹果机 ...
- 音频数据文件格式(PCM,WAV,MIDI)简记
PCM(Pulse Code Modulation):脉冲编码调制 把声源数据按一定的频率进行脉冲调制进行存储的数据格式,简单来说就是对模拟声音信号的数字 转换. WAV WAV是一种无损音频数据格式 ...
- 音频wav文件格式分析
一.音频文件 /usr/share/sounds/deepin/stereo/desktop-login.wav 二.文件信息 syli@syli-PC:~/work/repo/Demo/pa$ ...
- linux 设置交换文件格式,使用linux的mkswap命令建立和设置SWAP交换分区
使用linux的mkswap命令建立和设置SWAP交换分区 发布时间:2020-07-06 16:18:21 来源:亿速云 阅读:112 作者:清晨 栏目:服务器 小编给大家分享一下使用linux的m ...
- 常见音乐文件格式简单介绍(转)
爱听音乐的人很多,大家也都想听到高音质的音乐.如果注意一些小问题,就可以较明显地提升音质.我们平常听音乐的第一个可控环节就是音频文件的选择,源文件可谓是起决定性的因素,一切都从这里开始,其重要性不言而 ...
- 音频格式DTS 和 AC3 和 AAC简单介绍及HDTV
DTS:全称为Digital Theater Systems(数字影院系统),是一种有损多声道家庭影院音频格式,但它用了很高的码率进行编码,通常为768-1536kbps,能够营造出比AC3更好的影院 ...
最新文章
- Conway#39;s law(康威定律)
- 让数据中台飞起来—— Quick BI性能优化解决方案及实践
- koa2 mysql增删改查_koa2对mongodb的增删改查
- 通过CISA的一些经验分享(原文写于09年)
- Python学习笔记:使用Python操作数据库
- JavaScript高级程序设计学习(二)之基本概念
- sqliteman安装错误
- 车来了:精准实时公交
- java小游戏超级玛丽:07.第三关的设计
- 前端类库开发最佳实践
- J276-删除链表中重复的节点
- 计算机二级MS-Office真题及答案-历年汇总
- CSS filter有哪些用途
- 武林外传—阿沅,这是依赖传递呀!
- Java(老白再次入门) - IO流
- 2021计算智能期末复习
- 布隆,牛逼!布谷鸟,牛逼!
- PHP用户自定义函数的语法结构,调用自定义函数(PHP语法)
- 【51单片机】代码实例
- Linux学习:Linux启动过程的问题解决