如何检测WAV文件是否具有44或46字节的标头?

时间:2013-11-15 00:43:16

标签: audio wav

我发现在样本开始之前假设所有PCM wav音频文件都有44个字节的标题数据是危险的。虽然这很常见,但许多应用程序(例如ffmpeg)将生成带有46字节标头的wav,并忽略此事实,而处理将导致文件损坏且不可读。但是,如何判断标题实际存在多长时间?

显然有一种方法可以做到这一点,但我搜索并发现很少有关于此的讨论。根据作者自己的背景,很多音频项目假设44(或相反,46)。

3 个答案:

答案 0 :(得分:26)

您应该检查所有标题数据以查看实际大小。广播波格式文件将包含更大的扩展子块。来自Pro Tools的WAV和AIFF文件具有更多未扩展的扩展块以及音频后的数据。如果您想确定样本数据的开始和结束位置,您需要实际查找数据块(WAV文件的“数据”和AIFF的“SSND”)。

作为评论,所有WAV子块都符合以下格式:

Subchunk Descriptor (4 bytes)
    Subchunk Size (4 byte integer, little endian)
    Subchunk Data (size is Subchunk Size)

这很容易处理。您需要做的就是读取描述符,如果它不是您要查找的描述符,请读取数据大小并跳到下一个。一个简单的Java例程就是这样的:

//
// Quick note for people who don't know Java well:
// 'in.read(...)' returns -1 when the stream reaches
// the end of the file, so 'if (in.read(...) < 0)'
// is checking for the end of file.
//
public static void printWaveDescriptors(File file)
        throws IOException {
    try (FileInputStream in = new FileInputStream(file)) {
        byte[] bytes = new byte[4];

        // Read first 4 bytes.
        // (Should be RIFF descriptor.)
        if (in.read(bytes) < 0) {
            return;
        }

        printDescriptor(bytes);

        // First subchunk will always be at byte 12.
        // (There is no other dependable constant.)
        in.skip(8);

        for (;;) {
            // Read each chunk descriptor.
            if (in.read(bytes) < 0) {
                break;
            }

            printDescriptor(bytes);

            // Read chunk length.
            if (in.read(bytes) < 0) {
                break;
            }

            // Skip the length of this chunk.
            // Next bytes should be another descriptor or EOF.
            int length = (
                  Byte.toUnsignedInt(bytes[0])
                | Byte.toUnsignedInt(bytes[1]) << 8
                | Byte.toUnsignedInt(bytes[2]) << 16
                | Byte.toUnsignedInt(bytes[3]) << 24
            );
            in.skip(Integer.toUnsignedLong(length));
        }

        System.out.println("End of file.");
    }
}

private static void printDescriptor(byte[] bytes)
        throws IOException {
    String desc = new String(bytes, "US-ASCII");
    System.out.println("Found '" + desc + "' descriptor.");
}

例如,这里有一个随机的WAV文件:

Found 'RIFF' descriptor.
Found 'bext' descriptor.
Found 'fmt ' descriptor.
Found 'minf' descriptor.
Found 'elm1' descriptor.
Found 'data' descriptor.
Found 'regn' descriptor.
Found 'ovwf' descriptor.
Found 'umid' descriptor.
End of file.

值得注意的是,这里'fmt'和'data'合法地出现在其他块之间,因为Microsoft's RIFF specification表示子块可以按任何顺序出现。甚至我所知道的一些主要音频系统也会出错,并没有考虑到这一点。

因此,如果要查找某个块,请遍历文件检查每个描述符,直到找到您要查找的那个。

答案 1 :(得分:10)

诀窍是查看“Subchunk1Size”,它是从头的字节16开始的4字节整数。在正常的44字节wav中,此整数将为16 [10,0,0,0]。如果它是一个46字节的标题,如果有额外的可扩展元数据,那么这个整数将是18 [12,0,0,0]甚至更高(罕见?)。

额外数据本身(如果存在)从字节36开始。

因此,检测标头长度的简单C#程序如下所示:

static void Main(string[] args)
{
    byte[] bytes = new byte[4];
    FileStream fileStream = new FileStream(args[0], FileMode.Open, FileAccess.Read);
    fileStream.Seek(16, 0);
    fileStream.Read(bytes, 0, 4);
    fileStream.Close();
    int Subchunk1Size = BitConverter.ToInt32(bytes, 0);

    if (Subchunk1Size < 16)
        Console.WriteLine("This is not a valid wav file");
    else
        switch (Subchunk1Size)
        {
            case 16:
                Console.WriteLine("44-byte header");
                break;
            case 18:
                Console.WriteLine("46-byte header");
                break;
            default:
                Console.WriteLine("Header contains extra data and is larger than 46 bytes");
                break;
        }
}

答案 2 :(得分:2)

除了Radiodef的优秀回复之外,我还想添加3件不太明显的东西。

  1. WAV文件的唯一规则是FMT块在DATA块之前。除此之外,你会发现在开始时,在DATA块之前和之后都不知道的块。您必须读取每个块的标题才能跳过以查找下一个块。

  2. FMT块通常在16字节和18字节变体中找到,但规范实际上也允许超过18个字节。 如果FMT大块&#39;标题大小字段大于16,字节17和18还指定了多少额外字节,因此如果它们都为零,则最终得到一个18字节的FMT块,与16字节的字节相同。 只读取FMT块的前16个字节并解析它们是安全的,而忽略了它们。 为什么这很重要? - 不多了,但Windows XP的媒体播放器能够播放16位WAV文件,但只有当FMT块是扩展(18+字节)版本时才能播放24位WAV文件。曾经有很多抱怨说Windows不能播放我的24位WAV文件&#34;但是如果它有一个18字节的FMT块,它会...微软在某些时候修复了在Windows 7的早期阶段,所以24位的16字节FMT文件现在工作正常。

  3. (新增)奇数尺寸的块尺寸经常发生。主要是在制作24位单声道文件时看到的。从规范中不清楚,但是块大小指定了实际数据长度(奇数值),并且在块之后和下一个块开始之前添加了填充字节(零)。所以块总是从偶数边界开始,但块大小本身存储为实际的奇数值。