正文

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 int
  • pstring,Pascal 风格的字符串类型,由一个表示长度的字节和若干个字符构成,最大长度为 255
  • extended,扩展精度的浮点数类型,是 IEEE 754 标准之一,用 80 bit 表示的浮点数,占用10个字节,是 Pascal 语言的基础类型,但在x86下使用存在明显问题,稍后详解

注意:AIFF 的所有数据都按大端序存储,在 Intel、AMD、ARM 等小端序的 CPU 中读取需要进行转换

如果对 WAV 格式比较熟悉的话,那么下面的内容也不难理解

首先介绍几个基本 ID

  1. #define AIFFIDFORM 'FORM'
  2. #define AIFFIDAIFF 'AIFF'
  3. #define AIFFIDCommon 'COMM'
  4. #define AIFFIDSoundData 'SSND'

之所以说基本,是因为每个 AIFF 文件至少要用到这四个 ID,其余格式请查阅文档


AIFF 文件按块进行存储,每个块都由块头和块数据组成,其中块头的定义如下:

  1. typedef struct {
  2. long ID; // 这个是前面提到的 ID,记住是大端存储的,在小端 CPU 下不能直接使用上面的定义
  3. long Size; // 块数据的大小,不包括块头
  4. } ChunkHeader;

注意:所有块都必须按2字节对齐,即使Size可能是奇数


接着介绍 AIFF 文件头定义

  1. typedef struct {
  2. long ID; // 这个必须是 'FORM'
  3. long Size; // 文件大小减去 Size 和 ID 的大小,即文件字节数减8
  4. long Type; // 这个必须是 'AIFF'
  5. } AIFFHeader;

与 WAV 格式的 RIFF 定义十分相似,这个主要是用来标识文件类型用的


然后我们来看看 Common 块的定义

  1. typedef struct {
  2. long id; // 必须是 'COMM'
  3. long size; // 必须是 18
  4. short channels; // 声道数,比如 1 代表单声道,2 代表立体声
  5. unsigned long SampleFrames; // 采样帧数,每一个帧的大小取决于声道数和位深度
  6. short SampleSize; // 位深度,即每个采样的大小,用位表示,常见的有 16,24 等
  7. extended SampleRate; // 采样率,常见的如 44100,48000 等
  8. } CommonChunk;
  • 和 WAV 一样,音频数据是交错存储的,这意味着每个采样帧都包含所有声道的数据,所以总采样数据大小等于采样帧数*声道数*位深度/8
  • 位深度可能不为8的整数倍,但实际存储按字节大小整数倍确定,比如位深度为12,实际每个采样占用2个字节,其余位补零
  • 由于c/c++语言并不支持extended类型,所以实际使用中需要将其转化为单精度的float或者双精度的double

最后说一下 SoundData 块的定义

  1. typedef struct {
  2. long id; // 必须是 'SSND'
  3. long size;
  4. unsigned long offset;
  5. unsigned long BlockSize;
  6. // 按 PCM 编码的数据
  7. } SoundDataChunk;
  • offset 和 BlockSize 主要用来对齐,一般情况下为0,很少遇到非0的,具体请参阅官方文档

AIFC

AIFC 是 AIFF 的扩展,主要是添加了一个新块 FormatVersion 块,同时扩充了原来的 Common 块

先介绍下新增的常量

  1. #define AIFCVersion1 0xA2805140
  2. #define AIFCIDAIFC 'AIFC' // 替换文件头的 'AIFF'
  3. #define AIFCIDFVER 'FVER'

新的 FormatVersion 块

  1. typedef struct {
  2. long ID; // 必须是 'FVER'
  3. long Size; // 4
  4. unsigned long TimeStamp; // AIFCVersion1,目前只有这个
  5. } FormatVersionChunk;

扩展的 Common 块

  1. typedef struct {
  2. long id; // 必须是 'COMM'
  3. long size; // 22 + CompressionName 大小
  4. short channels; // 和 AIFF 一样
  5. unsigned long SampleFrames; // 和 AIFF 一样
  6. short SampleSize; // 和 AIFF 一样
  7. extended SampleRate; // 和 AIFF 一样
  8. long CompressionType;
  9. // pstring CompressionName; // 可选
  10. } AIFCCommonChunk;
  • CompressionType 有许多,可以在开头的链接找到,下面介绍几个常见的

    • 'NONE',未经压缩的,与 AIFF 完全一致
    • 'sowt',数据是交换端序存储的,对于小端 CPU 来说就不用转换了
    • 'fl32',数据是按照 IEEE 32位浮点数存储的
    • 'fl64',数据是按照 IEEE 64位浮点数存储的
    • 'FL32',与 'fl32' 相同,可以理解为别名
  • CompressionName 可选,用于指示压缩方法名,即使有一般也可忽略

除了sowt类型,其余全部为大端

文件的读取方法

  1. 首先读取文件头12个字节,判断文件类型是 AIFF 或 AIFC,否则退出
  2. 读取8个字节,判断 ID 和 Size,确定是否需要该块,不需要则文件指针前进 Size 个字节
  3. 读取需要的信息,进行相应的保存(比如Common块的内容,音频数据的文件偏移量等)
  4. 重复2-3,直到文件末尾
  5. 如果文件相关信息不齐全,则退出
  6. 根据先前保存的相关信息,读取音频数据
  • 所有的内容信息都要进行大小端转换
  • 音频数据的大小端转换取决于每个采样的字节数

其他细节内容

苹果曾经主要是使用 Pascal 语言进行开发的,后来才换了 C 系的语言,而 Pascal 原生支持 extended 浮点数类型,且在 x86 架构(包括64位)下得到完美的支持(除了Windows x64,这是由于微软ABI的限制,故编译器默认不支持,但可以使用汇编手动调用x87 FPU进行处理)。

但是C/C++(大多数语言)不支持extended类型,所以我们需要手动处理,这里介绍两种方法,一个如何在x86架构的cpu进行操作,因为其余架构本人并不熟悉,另一个是通用解决方法,也就是根据浮点数的定义手动处理转换。

我们首先定义 extended 类型结构体

  1. typedef struct {
  2. unsigned char data[10];
  3. } extended;

关于大小端的转换此处不再赘述,类似字符串的逆置

然后写两个函数,一个是 ExtendedToFloat,一个是 FloatToExtended,使用 double 同理,只需简单的修改

以下内容只能在 MinGW-gcc 下编译,在 MSVC 下需要修改,但原理一致,都是利用 x86 架构自带的 x87 FPU,这个方法并不推荐,且有较大的局限性,故推荐看后面的通用方案

  1. void ExtendedToFloat(const extended *e, const float *s)
  2. {
  3. #ifdef _WIN32
  4. #ifdef _WIN64
  5. // Windows x64 下,前两个参数分别在 rcx 和 rdx 中
  6. // 如果是 Linux x64,则前两个参数分别在 rdi 和 rsi 中
  7. asm("fldt (%rcx)\n\t"
  8. "fstps (%rdx)\n\t"); // AT&T 汇编风格下,如果是 double 则使用 fstpq
  9. #else
  10. asm("movl 4(%esp), %ecx\n\t" // esp+4 是第一个参数
  11. "movl 8(%esp), %edx\n\t" // esp+8 是第二个参数
  12. "fldt (%ecx)\n\t"
  13. "fstps (%edx)\n\t");
  14. #endif
  15. #else
  16. #error "Do not support now"
  17. #endif
  18. }
  19. void FloatToExtended(const float *s, const extended *e)
  20. {
  21. #ifdef _WIN32
  22. #ifdef _WIN64
  23. asm("flds (%rcx)\n\t" // AT&T 汇编风格下,如果是 double 则使用 fldq
  24. "fstpt (%rdx)\n\t");
  25. #else
  26. asm("movl 4(%esp), %ecx\n\t"
  27. "movl 8(%esp), %edx\n\t"
  28. "flds (%ecx)\n\t"
  29. "fstpt (%edx)\n\t");
  30. #endif
  31. #else
  32. #error "Do not support now"
  33. #endif

通用方法如下(注意这里的尾数是截断的,没有舍入):

  1. void ExtendedToFloat(const extended *e, const float *s)
  2. {
  3. unsigned char *ps = (unsigned char *)s;
  4. unsigned char *pe = (unsigned char *)e;
  5. unsigned short exponent;
  6. exponent = ((pe[9] & 0x7f) << 8 | pe[8]) - 16383 + 127;
  7. ps[3] = pe[9] & 0x80 | exponent >> 1;
  8. ps[2] = exponent << 7 | (pe[7] & 0x7f); // 实际上尾数的第一位被舍弃了
  9. ps[1] = pe[6];
  10. ps[0] = pe[5];
  11. }
  12. void FloatToExtended(const float *s, const extended *e)
  13. {
  14. unsigned char *ps = (unsigned char *)s;
  15. unsigned char *pe = (unsigned char *)e;
  16. unsigned short exponent;
  17. exponent = ((ps[3] & 0x7f) << 1 | (ps[2] >> 7)) - 127 + 16383;
  18. pe[9] = ps[3] & 0x80 | exponent >> 8;
  19. pe[8] = exponent;
  20. pe[7] = 0x80 | (ps[2] & 0x7f); // 同理,尾数第一位要置位1
  21. pe[6] = ps[1];
  22. pe[5] = ps[0];
  23. pe[4] = 0;
  24. *((long *)pe) = 0;
  25. }
  26. void ExtendedToDouble(const extended *e, const double *d)
  27. {
  28. unsigned char *pd = (unsigned char *)&d;
  29. unsigned char *pe = (unsigned char *)&e;
  30. unsigned short exponent = (((pe[9] & 0x7f) << 8) | pe[8]) - 16383 + 1023;
  31. pd[7] = pe[9] & 0x80 | exponent >> 4;
  32. pd[6] = exponent << 4 | ((pe[7] >> 3) & 0xf); // 同理
  33. pd[5] = pe[7] << 5 | pe[6] >> 3;
  34. pd[4] = pe[6] << 5 | pe[5] >> 3;
  35. pd[3] = pe[5] << 5 | pe[4] >> 3;
  36. pd[2] = pe[4] << 5 | pe[3] >> 3;
  37. pd[1] = pe[3] << 5 | pe[2] >> 3;
  38. pd[0] = pe[2] << 5 | pe[1] >> 3;
  39. }
  40. void DoubleToExtended(const double *d, const extended *e)
  41. {
  42. unsigned char *pd = (unsigned char *)&d;
  43. unsigned char *pe = (unsigned char *)&e;
  44. unsigned short exponent = (((pd[7] & 0x7f) << 4) | (pd[6] >> 4)) - 1023 + 16383;
  45. pe[9] = pd[7] & 0x80 | exponent >> 8;
  46. pe[8] = exponent;
  47. pe[7] = 0x80 | pd[6] << 3 | pd[5] >> 5; // 同理
  48. pe[6] = pd[5] << 3 | pd[4] >> 5;
  49. pe[5] = pd[4] << 3 | pd[3] >> 5;
  50. pe[4] = pd[3] << 3 | pd[2] >> 5;
  51. pe[3] = pd[2] << 3 | pd[1] >> 5;
  52. pe[2] = pd[1] << 3 | pd[0] >> 5;
  53. pe[1] = pd[0] << 3;
  54. pe[0] = 0;
  55. }

实际上这个方法在某种程度上也并不通用,因为不同平台对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音频交换文件格式的简单介绍的更多相关文章

  1. Dockerfile文件格式的简单介绍

    # This dockerfile uses the ubuntu image # VERSION 2 - EDITION 1 # Author: docker_user # Command form ...

  2. PCM文件格式简单介绍

    PCM文件格式简单介绍 PCM文件:模拟音频信号经模数转换(A/D变换)直接形成的二进制序列,该文件没有附加的文件头和文件结束标志.Windows的Convert工具能够把PCM音频格式的文件转换成M ...

  3. FreeSWITCH第三方库(音频)的简单介绍(一)

    FreeSWITCH使用了大量的第三方库,本文档主要介绍音频相关库的信息: 视频相关库的信息介绍参考:http://www.cnblogs.com/yoyotl/p/5488890.html 其他相关 ...

  4. iOS开发之音频播放AVAudioPlayer 类的介绍

    主要提供以下了几种播放音频的方法: 1. System Sound Services System Sound Services是最底层也是最简单的声音播放服务,调用 AudioServicesPla ...

  5. 【Android】播放音频的几种方式介绍

    接下来笔者介绍一下Android中播放音频的几种方式,android.media包下面包含了Android开发中媒体类,当然笔者不会依次去介绍,下面介绍几个音频播放中常用的类: 1.使用MediaPl ...

  6. SD卡FAT32获得高速的文件格式(图文介绍)

    说明: MBR :Master Boot Record ( 主引导记录) DBR :DOS Boot Record ( 引导扇区) FAT :File Allocation Table ( 文件分配表 ...

  7. JSON 在Ajax数据交换中的简单运用

    随着浏览器内核更新,原先的json.js在最新的谷歌浏览下不管用了,运行报错,特此修改下代码,不使用json.js,使用Object自带的json转换方法,修改时间,2016年10月26日. 首先需要 ...

  8. 什么是AIFF?

    AIFF是音频交换文件格式(Audio Interchange File Format)的英文缩写,是Apple公司开发的一种声音文件格式,被Macintosh平台及其应用程序所支持,Netscape ...

  9. IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包

    IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 文章目录 IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 1. IKEv2 协商总体框架 2. 第二包流程图 3. ...

  10. IKEv2协议协商流程: (IKE-SA-INIT 交换)第一包

    文章目录 1. IKEv2 协商总体框架 2. 第一包流程图 3. openswan源码学习 3.1 ikev2parent_outI1() 3.2 ikev2parent_outI1_withsta ...

随机推荐

  1. Gorm源码学习-数据库连接

    1 前言 gorm源码地址: Gorm , 本文基于commit:cef3de694d9615c574e82dfa0b50fc7ea2816f3e 官方入门指南: Dosc 2 连接数据库代码示例 目 ...

  2. 说一下 ArrayList 和 LinkedList 的区别?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在上一篇文章里,我们聊到了基于动态数组 ArrayList 线性表,今天我们来讨论 ...

  3. orcl rollup 分组小计、合计

    表数据: select * from group_test; 分组小计.合计: select group_id, decode(concat(job, group_id), null, '合计', g ...

  4. 100以内能被7整除的前五个数-Java

    import java.util.HashSet; import java.util.Set; public class Demo { //100以内能够被7整除的前五个数 public static ...

  5. 【Spark】Day05-内核解析:组件、流程、部署、运行模式、通讯架构、任务调度(Stage、task级)、两种Shuffle机制、内存管理、核心组件

    一.内核概述 内核:核心组件的运行机制.任务调度.内存管理.运行原理 1.核心组件 (1)Driver驱动器节点:执行main方法,将程序转化为作业job,在executor中调度任务task,跟踪并 ...

  6. Yearning建立流程和数据源进行测试

    1.前提说明 前面已经搭建好了平台,并且接入了LDAP.邮箱和钉钉,现在就是建立一下数据源和流程来进行测试,如果有什么疑问可以看上一篇文章安装Yearning审核平台 2.建立流程 2.1 新建流程 ...

  7. STM32按键控制LED亮灭的代码

    led.c #include "led.h" void LED_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2P ...

  8. python 实现AES加解密

    AES 只是个基本算法,实现 AES 有几种模式,主要有 ECB.CBC.CFB 和 OFB  CTR,直接上代码,此处为AES加密中的CBC模式,EBC模式与CBC模式相比,不需要iv. impor ...

  9. golang在win10安装、环境配置 和 goland(IDE开发golang配置)

    前言 本人在使用goland软件开发go时,对于goland软件配置网上资料少,为了方便自己遗忘.也为了希望和我一样的小白能够更好的使用,所以就写下这篇博客,废话不多说开考. 一.查看自己电脑系统版本 ...

  10. sqli-laba靶场搭建

    windows下安装sqli-laba 环境:windows10 安装phpstudy 1.下载并安装小皮面板phpstudy(傻瓜式安装) https://www.xp.cn/windows-pan ...