8

私はwavファイルの振幅を処理し、それを小数でスケーリングすることに取り組んでいます。私は、言語のニュアンスに取り組みながら、メモリ効率の良い方法でファイルを読み書きする方法に頭を悩ませようとしています (私は C が初めてです)。ファイルは、8 ビットまたは 16 ビット形式のいずれかです。私がこれを行うと考えた方法は、最初にヘッダーデータを事前に定義された構造体に読み取り、次にループ内で実際のデータを処理して、データのチャンクをバッファーに読み取り、必要なことは何でも行います。そしてそれを出力に書き込みます。

#include <stdio.h>
#include <stdlib.h>


typedef struct header 
{
    char chunk_id[4];
    int chunk_size;
    char format[4];
    char subchunk1_id[4];
    int subchunk1_size;
    short int audio_format;
    short int num_channels;
    int sample_rate;
    int byte_rate;
    short int block_align;
    short int bits_per_sample;
    short int extra_param_size;
    char subchunk2_id[4];
    int subchunk2_size;
} header;

typedef struct header* header_p;

void scale_wav_file(char * input, float factor, int is_8bit)
{
    FILE * infile = fopen(input, "rb");
    FILE * outfile = fopen("outfile.wav", "wb");

    int BUFSIZE = 4000, i, MAX_8BIT_AMP = 255, MAX_16BIT_AMP = 32678;

    // used for processing 8-bit file
    unsigned char inbuff8[BUFSIZE], outbuff8[BUFSIZE];

    // used for processing 16-bit file
    short int inbuff16[BUFSIZE], outbuff16[BUFSIZE];

    // header_p points to a header struct that contains the file's metadata fields
    header_p meta = (header_p)malloc(sizeof(header));

    if (infile)
    {

        // read and write header data
        fread(meta, 1, sizeof(header), infile);
        fwrite(meta, 1, sizeof(meta), outfile);

        while (!feof(infile))
        {
            if (is_8bit)
            {
                fread(inbuff8, 1, BUFSIZE, infile);   
            } else {
                fread(inbuff16, 1, BUFSIZE, infile);      
            }

            // scale amplitude for 8/16 bits
            for (i=0; i < BUFSIZE; ++i)
            {
                if (is_8bit)
                {
                    outbuff8[i] = factor * inbuff8[i];
                    if ((int)outbuff8[i] > MAX_8BIT_AMP)
                    {
                        outbuff8[i] = MAX_8BIT_AMP;
                    }
                } else {
                    outbuff16[i] = factor * inbuff16[i];
                    if ((int)outbuff16[i] > MAX_16BIT_AMP)
                    {
                        outbuff16[i] = MAX_16BIT_AMP;
                    } else if ((int)outbuff16[i] < -MAX_16BIT_AMP) {
                        outbuff16[i] = -MAX_16BIT_AMP;
                    }
                }
            }

            // write to output file for 8/16 bit
            if (is_8bit)
            {
                fwrite(outbuff8, 1, BUFSIZE, outfile);
            } else {
                fwrite(outbuff16, 1, BUFSIZE, outfile);
            }
        }
    }

    // cleanup
    if (infile) { fclose(infile); }
    if (outfile) { fclose(outfile); }
    if (meta) { free(meta); }
}

int main (int argc, char const *argv[])
{
    char infile[] = "file.wav";
    float factor = 0.5;
    scale_wav_file(infile, factor, 0);
    return 0;
}

最後にファイルサイズが異なります(40Mbファイルの場合、1k程度)。これは、ファイルが終了している可能性があるにもかかわらず、バッファ全体を出力に書き込んでいることが原因であると思われますバッファサイズ全体を満たす前に。また、出力ファイルがめちゃくちゃです - 再生も開くこともできません - だから私はおそらくすべて間違っています。私が台無しにしている場所に関するヒントは素晴らしいでしょう。ありがとう!

4

5 に答える 5

9

1この else ブランチでは、16 ビット サンプルではなくバイトを読み取っています。

while (!feof(infile))
    {
        if (is_8bit)
        {
            fread(inbuff8, 1, BUFSIZE, infile);   
        } else {
            fread(inbuff16, 1, BUFSIZE, infile); // <-- should be BUFSIZE*2     
        }

2スケーリング時に値を飽和させません。たとえば、元の 16 ビット サンプル = 32000 および係数 = 1.5 は、整数値を最大 32767 にクランプするのではなく、ラップします。

3 RIFF やその他のヘッダーをまったく見ていません。WAV ファイルでは、オーディオ データの後に何らかの情報フッターが続くか、追加のヘッダーが前にある可能性があります。言い換えれば、あなたのheader構造体はあまりにも静的です。また、8 ビット サンプルであるというパラメーターを使用する代わりに、ファイルから WAV 形式を読み取る必要があります。

4これは起こりません:

                outbuff16[i] = factor * inbuff16[i];
                if ((int)outbuff16[i] > MAX_16BIT_AMP)

8 ビット/16 ビット値は、整数がオーバーフローしたときにコンピュータがメモリにいくつかのマジック ビットを挿入する場合を除いて、255/32768 を超えることはありません :P

また、オーディオ サンプルは署名されているため、範囲は -128;127 および -32768;32767 です。オーバーフロー チェックは、乗算式で発生する必要があります。また、浮動小数点から整数への丸めモードについても想定しています。これは構成可能であり、考慮する必要があります。のようなものif(roundf(factor * inbuff16[i]) > 32767 || roundf(factor * inbuff16[i]) < -32768)かもしれません。

5の結果を保存しないfreadため、出力ファイルに書き込むサンプルが多すぎます。

6最後に、車輪の再発明です。学習用であれば大丈夫です。それ以外の場合は、既存のライブラリを使用する必要があります。

于 2010-03-16T19:57:11.340 に答える
5

ライブラリを使用してサウンド ファイルを読み書きする方がはるかに優れています。例libsndfile。そのウェブページには、あなたも見ることができる「他の同様のプロジェクト」のリストがあります。ライブラリのsndfile-tools使用方法を学ぶための良いコード例です。

于 2010-03-16T23:45:00.760 に答える
1

次の行も、WAV ヘッダーを読み取るためには必要ありません (「標準」の 44 バイトではなく、ヘッダーを 48 バイトにします)。

short int extra_param_size;
于 2011-11-16T09:33:08.610 に答える
1

元のファイルと出力ファイルを 16 進エディタで見て、データが正しく書き直されているかどうかを確認することをお勧めします。結果のファイルが再生されない、または開かない場合は、出力ファイルのヘッダーが正しくない可能性があります。

もう 1 つのオプションは、オーディオ処理ロジックを削除し、ソース ファイルを内部バッファーに読み込んでファイルに書き出すことです。コードがこの方法で有効で機能する出力ファイルを生成できる場合は、問題を処理コードに絞り込むことができます。

40Mb より小さいファイルから始めることもできます。他に何もない場合は、その入力ファイルのコピーを作成し、数秒のオーディオにトリミングします。ファイルが小さいほど、手動で検査しやすくなります。

編集:と への呼び出しは、戻り値を検証する必要がありますfread()fwrite()これらの関数は、読み取りまたは書き込みされた要素の数を返します。いずれかの関数の呼び出しが予想よりも少ない値を返す場合、これがファイル サイズの違いの原因である可能性があります。

また、 の 2 番目のパラメータfreadはバイト単位です。したがって、バッファ全体を読み取りで埋めたい場合は、fread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);. 現在のコードはバイト単位でしか読み取れませんBUFSIZE(これは偶然にも 8 ビットの場合に機能しますが、わかりやすくするためにも変更することをお勧めします)。

于 2010-03-16T19:45:06.950 に答える
0

可能であれば、特に C アプリケーション用でない限り、C 以外の言語を検討することをお勧めします。

  • たとえば、pythonには、wavファイルを簡単に読み書きできる優れたwavパッケージがあります。
  • より専門的または学術的に使用する場合、最初に頼りになるのは MATLAB です。これは、wav ファイルを非常に簡単に読み取ります (ベクトルに直接変換し、単一の式として操作します)。
于 2011-08-18T05:19:47.167 に答える