2010-03-16 106 views
6

我正在處理一個wav文件的幅度和縮放一些十進制的因素。我試圖圍繞如何以有效的內存方式讀取和重寫文件,同時試圖解決語言的細微差別(我是C的新手)。該文件可以是8位或16位格式。我認爲這樣做的方法是首先將header data讀入一些預定義的結構中,然後在循環中處理實際數據,在該循環中我將讀取一塊數據到緩衝區中,執行所需的任何操作,以及然後將其寫入輸出。處理音頻wav文件與C

#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; 
} 

我在最後得到不同的文件大小(以1K左右,對於一個40MB的文件),我懷疑這是由於這樣的事實,我正在寫一整個緩衝區輸出,即使該文件在填充整個緩衝區大小之前可能已經終止。另外,輸出文件會搞砸 - 不會播放或打開 - 所以我可能會做錯整個事情。任何關於我搞砸的提示都會很棒。謝謝!

+1

當你說的輸入和輸出文件具有不同的大小,是輸出文件大於還是小於輸入? – bta 2010-03-16 19:51:21

+1

輸出較大 – sa125 2010-03-16 20:07:35

回答

8

你正在讀取字節,而不是16位的樣品在這個else分支:

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

縮放時,您不飽和值,例如原來的16位採樣= 32000和係數= 1.5將環繞整數值,而不是它夾到最高的32767

你不會在RIFF和其它頭看看所有的。在WAV文件中,音頻數據後面可能會出現一些信息頁腳或其他標題。換句話說:您的header結構太靜態。你也應該從文件中讀取WAV格式,而不是有一個參數說它是8位樣本。

這是不會發生的:

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

8位/ 16位的值永遠不會大於三萬二千七百六十八分之二百五十五除非您的計算機中插入一些魔術位到內存時的整數溢出:P

音頻樣本被簽名,所以範圍是-128; 127和-32768; 32767。在乘法表達式中必須進行溢出檢查。您還正在對浮點到整數舍入模式進行假設,這是可配置的,應予以考慮。可能是類似if(roundf(factor * inbuff16[i]) > 32767 || roundf(factor * inbuff16[i]) < -32768)的東西。

您不存儲fread的結果,因此您會向輸出文件寫入太多樣本。

作爲最後一點,您正在重新發明輪子。只要這是爲了學習,沒關係。否則你應該使用現有的庫。

+1

我認爲你抓住了大部分東西 - 我會補充說sizeof(元)是錯誤的時候寫出頭爲元是一個指針 - 需要是sizeof(header)或sizeof(* meta)。 – Dipstick 2010-03-16 20:04:24

+0

這是很好的反饋,我會嘗試這些事情 – sa125 2010-03-16 20:08:01

+0

回覆:第3項,你也不能根據你最喜歡的波形編輯器的輸出做出假設,因爲它們都有怪癖。很顯然,libsndfile中的大部分工作都是在各種波形編輯器中處理奇怪的問題。如果你對libsndfile許可證感到滿意,所有這些都會減少到幾個sf_read_floats和sf_write_floats,並引入了一些初始化的東西。 – kibibu 2010-03-16 23:49:58

1

我建議在十六進制編輯器中查看原始文件和輸出文件,看看您是否正確地重新寫入數據。如果生成的文件不能播放或打開,則輸出文件的標題可能不正確。

另一種選擇是刪除音頻處理邏輯,只需將源文件讀入內部緩衝區並將其寫入文件即可。如果您的代碼可以以這種方式生成有效的工作輸出文件,那麼您可以將問題縮小到處理代碼。

您可能還想從小於40Mb的文件開始。如果沒有其他,請複製該輸入文件並將其縮減爲幾秒鐘的音頻。較小的文件將更容易手動檢查。請致電fread()fwrite()驗證其返回值。這些函數返回讀取或寫入的元素數量,如果對任何一個函數的調用返回的值比預期的小,那麼這可能是文件大小差異的根源。

此外,fread的第二個參數是以字節爲單位。因此,如果您想讀取填充整個緩衝區,則需要說些更像fread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);的內容。當前的代碼只能讀取BUFSIZE字節(它適用於8位情況,但爲了清晰起見,我建議更改它)。

+0

你是對的文件大小,它絕對是值得收縮只是爲了看看它是否工作 – sa125 2010-03-16 20:09:08

5

使用庫來讀取和寫入聲音文件要好得多。例如。 libsndfile。該網頁有一個「其他類似項目」的列表,你也可以看看。 sndfile-tools可能是學習如何使用該庫的很好的代碼示例。

0

如果可能的話,您可能希望查看與C不同的語言,除非它專門用於C應用程序。

  • 例如python有一個很好的wav包,它可以很容易地讀取ww文件&。
  • 對於更多專業或學術用途,第一個指導是MATLAB,它也很容易讀取wav文件(直接導入然後作爲單個表達式運行的矢量)。
1

也無需用於讀取WAV頭此以下行(使頭48個字節長,而不是「標準」 44):

short int extra_param_size;