10

为了减少 iPhone 应用程序的下载大小,我正在压缩一些音频文件。具体来说,我在命令行上使用 afconvert 将 .wav 格式更改为 .caf 格式 w/ima4 压缩。

我读过这篇(wooji-juice.com)关于这个确切主题的精彩帖子。我在“解码 ima4 数据包”步骤时遇到问题。我查看了他们的示例代码,但我被卡住了。请提供一些伪代码或示例代码来帮助我朝着正确的方向前进。

谢谢!

附加信息:这是我已经完成的内容以及我遇到问题的地方......我可以在模拟器和手机上播放 .wav 文件。我可以在命令行上使用 afconvert 将 .wav 文件压缩为 .caf w/ima4 压缩。我正在使用带有 CrashLanding 的 SoundEngine(我修复了一个内存泄漏)。我修改了 SoundEngine 代码以查找 mFormatID 'ima4'。

我不明白上面链接的博客文章以“计算解压缩数据的大小”开头。为什么我需要这样做?另外,术语“数据包”指的是什么?我对任何类型的音频编程都很陌生。

4

2 回答 2

12

Wooji-JuiceMultimedia WikiApple收集所有数据后,这是我的建议(可能需要一些实验):

文件结构

  • Apple IMA4 文件由 34 字节的数据包组成。这是用于构建文件的数据包单元。
  • 每个 34 字节的数据包有两部分:
    • 前 2 个字节包含前导码:初始预测器和步长索引
    • 剩下的 32 个字节包含声音半字节(4 位半字节用于检索 16 位样本)
  • 每个数据包有 32 个字节的压缩数据,代表 64 个 16 位的样本。
  • 如果声音文件是立体声,则数据包是交错的(一个用于左侧,一个用于右侧);必须有偶数个数据包。

解码

每个 34 字节的数据包将导致 16 位的 64 个样本的解压缩。所以未压缩数据的大小是每个数据包 128 字节。

解码伪代码如下:

int[] ima_index_table = ... // Index table from [Multimedia Wiki][2]
int[] step_table = ... // Step table from [Multimedia Wiki][2]
byte[] packet = ... // A packet of 34 bytes compressed
short[] output = ... // The output buffer of 128 bytes
int preamble = (packet[0] << 8) | packet[1];
int predictor = preamble && 0xFF80; // See [Multimedia Wiki][2]
int step_index = preamble && 0x007F; // See [Multimedia Wiki][2]
int i;
int j = 0;
for(i = 2; i < 34; i++) {
    byte data = packet[i];
    int lower_nibble = data && 0x0F;
    int upper_nibble = (data && 0xF0) >> 4;

    // Decode the lower nibble
    step_index += ima_index_table[lower_nibble];
    diff = ((signed)nibble + 0.5f) * step / 4;
    predictor += diff;
    step = ima_step_table[step index];

    // Clamp the predictor value to stay in range
    if (predictor > 65535)
        output[j++] = 65535;
    else if (predictor < -65536)
        output[j++] = -65536;
    else
        output[j++] = (short) predictor;

    // Decode the uppper nibble
    step_index += ima_index_table[upper_nibble];
    diff = ((signed)nibble + 0.5f) * step / 4;
    predictor += diff;
    step = ima_step_table[step index];

    // Clamp the predictor value to stay in range
    if (predictor > 65535)
        output[j++] = 65535;
    else if (predictor < -65536)
        output[j++] = -65536;
    else
        output[j++] = (short) predictor;
}
于 2010-02-03T17:09:12.643 回答
1

术语“数据包”是指一组带有标头的压缩音频样本。您需要标头来解码紧随其后的数据。如果您认为您的 ima4 文件是一本书,那么每个数据包就是一页。顶部是解码该页面所需的值,然后是压缩音频。

这就是为什么您需要计算解压缩数据的大小(然后为其腾出空间)——因为它是压缩的,所以您需要将数据从压缩音频转换为未压缩音频,然后才能输出。为了分配输出缓冲区,您需要知道它必须有多大(注意:您可能需要一次输出大于单个数据包的块)。

根据前面的“概述”部分,看起来典型的结构是 64 个样本集,每个 16 位(即 128 个字节)被转换为一个 2 字节的标头和一个 32 字节的压缩样本集(34 个字节)在所有)。因此,在典型情况下,您可以通过获取输入数据大小,除以 34 得到数据包数,然后乘以 128 字节来获得每个数据包的未压缩音频,从而产生预期的输出数据大小。

不过,你不应该那样做。看起来您应该改为查询 kAudioFilePropertyDataFormat 来获取 mBytesPerPacket——这是上面的“34”值,而 mFramesPerPacket——这是上面的 64,乘以 2(对于 16 字节样本)得到 128 字节的输出。

然后,对于每个数据包,您将需要运行帖子中描述的解码。在稍长的伪 C 代码中,假设您正在获取字节数组来处理标头:

packet = GetPacket();
Header = (packet[0] << 8) | packet[1]; //Big-endian 16-bit value
step_index = Header & 0x007f; //Lower seven bits
predictor = Header & 0xff80; //Upper nine bits
for (i = 2; i < mBytesPerPacket; i++)
{
    nibble = packet[i] & 0x0f; //Low Nibble
    process that nibble, per the blogpost -- be careful on sign-extension!
    nibble = (packet[i] & 0xf0) >> 4; //High Nibble
    process that nibble, per the blogpost -- be careful on sign-extension!
}

上面的符号扩展是指帖子涉及以无符号和有符号方式处理每个半字节的事实。如果半字节的高位(第 3 位)为 1,则为负;此外,位移可能会进行符号扩展。这在上面的伪代码中没有处理。

于 2010-02-02T17:35:57.507 回答