簡體   English   中英

C ++讀取16位Wav文件

[英]C++ reading 16bit Wav file

我在讀取16位.wav文件時遇到問題。 我已閱讀標題信息,但轉換似乎不起作用。

例如,如果在Matlab中讀取波形文件,則會得到以下類型的數據:

 -0.0064, -0.0047,  -0.0051, -0.0036, -0.0046, -0.0059,  -0.0051

但是,在我的C ++程序中,返回以下內容:

0.960938, -0.00390625, -0.949219, -0.00390625, -0.996094, -0.00390625

我需要以相同的方式表示數據。 現在,對於8 bit .wav文件,我執行了以下操作:

uint8_t c;

for(unsigned i=0; (i < size); i++)
{
    c = (unsigned)(unsigned char)(data[i]);
    double t = (c-128)/128.0;
    rawSignal.push_back(t);
}

但是,當我為16bit執行此操作時,這很有用:

uint16_t c;

for(unsigned i=0; (i < size); i++)
{
   c = (signed)(signed char)(data[i]);
   double t = (c-256)/256.0;
   rawSignal.push_back(t);
}

不起作用,並顯示輸出(上方)。

我遵循這里找到的標准

我的datachar數組,而rawSignalstd::vector<double>我可能只是在處理轉換錯誤,但似乎找不到位置。 有人有什么建議嗎?

謝謝

編輯:

這是現在顯示的內容(在圖中):

在此輸入圖像描述

這是它應該顯示的內容:

在此輸入圖像描述

這里有一些問題:

  • 8位wavs是無符號的,但是16位wavs是有符號的。 因此,Carl和Jay的答案中給出的減法步驟是不必要的。 我認為他們只是從你的代碼中復制,但他們錯了。
  • 16位波的范圍從-32,768到32,767,而不是-256到255,這使得你使用的乘法無論如何都是錯誤的。
  • 16位wavs是2個字節,因此你必須讀取兩個字節來制作一個樣本,而不是一個。 您似乎一次只能閱讀一個角色。 讀取字節時,如果您的本地字節序不是little-endian,則可能必須交換它們。

假設是一個低端架構,您的代碼將看起來像這樣(非常接近卡爾的答案):

for (int i = 0; i < size; i += 2)
{
    int c = (data[i + 1] << 8) | data[i];
    double t = c/32768.0;
    rawSignal.push_back(t);
}

對於大端架構:

for (int i = 0; i < size; i += 2)
{
    int c = (data[i] << 8) | data[i+1];
    double t = c/32768.0;
    rawSignal.push_back(t);
}

該代碼未經測試,請LMK如果不起作用。

(首先關於little-endian / big-endian-ness。WAV只是一種容器格式,其中編碼的數據可以是無數格式。大多數編解碼器都是無損的(MPEG Layer-3又稱MP3,是的,流可以“打包”成WAV,各種CCITT和其他編解碼器。。你假設你處理某種PCM格式,你看到RAW格式的實際波形,沒有進行無損轉換。字節順序取決於在編解碼器上生成流。 是否在RIFF WAV文件中保證格式參數的字節順序?

這也是一個問題,如果一個PCM樣本是線性比例采樣整數或者它背后有一些縮放,對數比例或其他轉換。 我遇到的常規PCM wav文件是簡單的線性比例樣本,但是我不在音頻錄制或制作行業工作。

所以你的解決方案的路徑:

  1. 確保您正在處理常規的16位PCM編碼RIFF WAV文件。
  2. 在讀取流時,始終一次讀取兩個字節(char)並將兩個字符轉換為16位短。 人們在我面前展示了這一點
  3. 您顯示的波形清楚地表明您要么沒有很好地估計頻率 (或者您只有一個單聲道而不是立體聲 )。 因為采樣率(44.1kHz,22KHz,11KHz,8kHz等)與分辨率(8位,16位,24位等)一樣重要。 也許在第一種情況下,您有立體聲數據。 您可以單聲道閱讀它,您可能不會注意到它。 在第二種情況下,如果您具有單聲道數據,則在讀取數據的一半過程中將用盡樣本。 根據您的圖表,這似乎就是這種情況。 談論另一個原因:較低的采樣分辨率(以及16位也較低)通常與較低的采樣率配對。 因此,如果您的輸入數據是錄制時間,並且您認為您有22kHz的數據,但它實際上只有11kHz,那么您將從實際樣本中途耗盡並讀入內存垃圾。 所以這些中的任何一個。

確保您正確解釋和處理循環迭代器變量和大小。 看起來大小告訴您有多少字節。 你只有一半的短整數樣本。 注意,正因為如此,Bjorn的解將i正確地增加了2。

16位的數量范圍為-32,768至32,767,而不是-256至255(僅為9位)。 采用:

for (int i = 0; i < size; i += 2)
{
    c = (data[i + 1] << 8) + data[i]; // WAV files are little-endian
    double t = (c - 32768)/32768.0;
    rawSignal.push_back(t);
}

你可能想要更像這樣的東西:

uint16_t c;
for(unsigned i=0; (i < size); i++)
{
   // get a 16 bit pointer to the array
   uint16_t* p = (uint16_t*)data;
   // get the i-th element
   c = *( p + i );
   // convert to signed? I'm guessing this is what you want
   int16_t cs = (int16_t)c;
   double t = (cs-256)/256.0;
   rawSignal.push_back(t);
}

您的代碼將8位值轉換為有符號值,然后將其寫入無符號變量。 你應該看一下,看看它是不是你想要的。

我的工作代碼是

int8_t* buffer = new int8_t[size];
/*
  HERE buffer IS FILLED
*/
for (int i = 0; i < size; i += 2)
{
    int16_t c = ((unsigned char)buffer[i + 1] << 8) | (unsigned char)buffer[i];
    double t = c/32768.0;
    rawSignal.push_back(t);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM