web-dev-qa-db-ja.com

WAVファイルに44バイトまたは46バイトのヘッダーがあるかどうかを検出するにはどうすればよいですか?

サンプルを開始する前に、すべてのPCMwavオーディオファイルに44バイトのヘッダーデータがあると想定するのは危険であることがわかりました。これは一般的ですが、多くのアプリケーション(ffmpegなど)は46バイトのヘッダーを持つwavを生成し、処理中にこの事実を無視すると、ファイルが破損して読み取り不能になります。しかし、ヘッダーが実際にどれくらいの長さであるかをどのように検出できますか?

明らかにこれを行う方法はありますが、私はこれについて検索してほとんど議論を見つけませんでした。そこにある多くのオーディオプロジェクトは、作者自身のコンテキストに応じて44(または逆に46)を想定しています。

16
Matt J.

秘訣は、ヘッダーのバイト16から始まる4バイト整数である「Subchunk1Size」を調べることです。通常の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;
        }
}
10
Matt J.

すべてのヘッダーデータをチェックして、実際のサイズを確認する必要があります。 Broadcast Wave Formatファイルには、さらに大きな拡張子サブチャンクが含まれます。 Pro ToolsのWAVファイルとAIFFファイルには、オーディオの後のデータだけでなく、文書化されていないさらに多くの拡張チャンクがあります。サンプルデータの開始位置と終了位置を確認したい場合は、実際にデータチャンク(WAVファイルの場合は「data」、AIFFの場合は「SSND」)を探す必要があります。

レビューとして、すべてのWAVサブチャンクは次の形式に準拠しています。

サブチャンク記述子(4バイト)
サブチャンクサイズ(4バイト整数、リトルエンディアン)
サブチャンクデータ(サイズはサブチャンクサイズ)

これは非常に簡単に処理できます。あなたがする必要があるのは記述子を読むことです、それがあなたが探しているものでないならば、データサイズを読んでそして次へスキップしてください。これを行うための単純な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ファイルです。

「RIFF」記述子が見つかりました。
「bext」記述子が見つかりました。
「fmt」記述子が見つかりました。
「minf」記述子が見つかりました。
「Elm1」記述子が見つかりました。 
「データ」記述子が見つかりました。
「regn」記述子が見つかりました。
「ovwf」記述子が見つかりました。
「umid」記述子が見つかりました。
終了ファイルの。

特に、ここでは「fmt」と「data」の両方が他のチャンクの間に合法的に表示されます。これは、 MicrosoftのRIFF仕様 がサブチャンクを任意の順序で表示できることを示しているためです。私が知っているいくつかの主要なオーディオシステムでさえ、これを間違え、それを説明していません。

したがって、特定のチャンクを見つけたい場合は、ファイルをループして、探している記述子が見つかるまで各記述子をチェックします。

26
Radiodef

Radiodefの優れた回答に加えて、明らかではない3つのことを追加したいと思います。

  1. WAVファイルの唯一のルールは、FMTチャンクがDATAチャンクの前に来ることです。それとは別に、最初、DATAチャンクの前、およびその後に、知らないチャンクがあります。次のチャンクを見つけるためにスキップするには、各チャンクのヘッダーを読み取る必要があります。

  2. FMTチャンクは一般に16バイトと18バイトのバリエーションで見られますが、仕様では実際には18バイト以上も許可されています。 FMTチャンクのヘッダーサイズフィールドに16より大きいと表示されている場合、バイト17と18は余分なバイト数も指定するため、両方がゼロの場合、16バイトのものと同じ18バイトのFMTチャンクになります。 FMTチャンクの最初の16バイトだけを読み込んで解析し、それ以上無視しても安全です。なぜこれが重要なのですか? -それほど多くはありませんが、WindowsXPのMediaPlayerは16ビットのWAVファイルを再生できましたが、FMTチャンクが拡張(18バイト以上)バージョンの場合にのみ24ビットのWAVファイルを再生できました。 「Windowsは24ビットのWAVファイルを再生しない」という苦情がたくさんありましたが、18バイトのFMTチャンクがあると、MicrosoftはWindows7の初期の頃にそれを修正しました。 16バイトのFMTファイルを含む24ビットが正常に動作するようになりました。

  3. (新規追加)奇数サイズのチャンクサイズが頻繁に発生します。主に24ビットのモノラルファイルが作成されたときに見られます。仕様からは不明ですが、チャンクサイズは実際のデータ長(奇数値)を指定し、パッドバイト(ゼロ)がチャンクの後、次のチャンクの開始前に追加されます。したがって、チャンクは常に偶数の境界から始まりますが、チャンクのサイズ自体は実際の奇数値として格納されます。

2
Tomuo