17

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

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

4

3 に答える 3

28

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

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

サブチャンク記述子 (4 バイト)
    サブチャンク サイズ (4 バイト整数、リトル エンディアン)
    Subchunk Data (サイズは 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 ファイルです。

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

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

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

于 2013-11-15T01:00:26.373 に答える
13

秘訣は、ヘッダーのバイト 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;
        }
}
于 2013-11-15T00:43:15.900 に答える