解码ima4音频格式

时间:2010-01-25 07:34:45

标签: iphone file-format ima4

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

我已经阅读了this(wooji-juice.com)关于这个话题的精彩帖子。我遇到了“解码ima4数据包”的麻烦。我看了他们的示例代码,我被卡住了。请帮助w /一些伪代码或示例代码,它们可以指导我正确的方向。

谢谢!

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

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

2 个答案:

答案 0 :(得分:11)

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

文件结构

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

<强>解码

每个34字节的数据包将导致64位16位样本的解压缩。因此,未压缩数据的大小为每个数据包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;
}

答案 1 :(得分: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,则为负;另外,位移可以进行符号扩展。这不在上面的伪代码中处理。