2012-08-23 32 views
6

我試圖混合在一起2 16位線性PCM音頻流,我似乎無法克服噪聲問題。我認爲當混合樣品時它們來自溢流。混合16位線性PCM流並避免削波/溢出

我有以下功能...

short int mix_sample(short int sample1, short int sample2) 
{ 
    return #mixing_algorithm#; 
} 

...這是我曾嘗試爲#mixing_algorithm#

sample1/2 + sample2/2 
2*(sample1 + sample2) - 2*(sample1*sample2) - 65535 
(sample1 + sample2) - sample1*sample2 
(sample1 + sample2) - sample1*sample2 - 65535 
(sample1 + sample2) - ((sample1*sample2) >> 0x10) // same as divide by 65535 

他們中的一些已經產生比別人更好的結果,但即使是最好的結果包含了相當多的噪音。

任何想法如何解決呢?

+0

你能寫出完整的算法,我看不到任何作業! – perilbrain

+0

當您將sample1和sample2除以2時,您會得到誤差範圍1. –

回答

7

這裏是一個描述性的實現:

short int mix_sample(short int sample1, short int sample2) { 
    const int32_t result(static_cast<int32_t>(sample1) + static_cast<int32_t>(sample2)); 
    typedef std::numeric_limits<short int> Range; 
    if (Range::max() < result) 
     return Range::max(); 
    else if (Range::min() > result) 
     return Range::min(); 
    else 
     return result; 
} 

混合,它只是添加和夾!

爲了避免剪切失真,您將需要使用飽和度或限制器。理想情況下,你將有一個小的int32_t緩衝區和少量的lookahead。這會引入延遲。

比任何地方限制更常見,是在你的信號中留下一些「空間」的價值。

+0

該解決方案運行良好。謝謝! – Ragnar

+0

@Ragnar太棒了 - 不客氣:) – justin

+1

避免限幅的唯一「正確」方法是除以二。在「失真和噪聲」部分中有一些說明性代碼:http://blog.bjornroche.com/2013/05/the-abcs-of-pcm-uncompressed-digital.html –

0

我認爲他們應該是功能映射[MIN_SHORT, MAX_SHORT] -> [MIN_SHORT, MAX_SHORT],他們顯然不是(除了第一個),所以溢出發生。

如果開卷的主張是行不通的,你也可以嘗試:

((long int)(sample1) + sample2)/2 
+0

雖然添加信號是正確的,用簡單的*歸一化*來保持範圍,一個信號會不希望地影響另一個信號。例如,如果'sample1'總是爲零(無聲),你只想* sample2',但是你得到'sample2/2' - 即輸出更安靜。 – Clifford

+0

是的,你是完全正確的。但解決了溢出和裁剪的問題。 (s1,s2)* s1 +(1-w(s1,s2))* s2'其中'w(s1,s2)'是當s1!= 0 && s2!= 0時有'w(s1,0)= 1','w(0,s2)= 0'和'0

-2

由於您處於時間範圍內,所以連續採樣之間的頻率信息處於區別,當您除以二時,會損壞該信息。這就是爲什麼添加和裁剪效果更好。當然,剪切會增加非常高的頻率噪聲,可能會被濾除。

+0

我期望OP聽到的噪音是由包裝值造成的,而不是像丟失分辨率的單個細節那樣微妙的東西 – Will

9

我發現的最佳解決方案是given by Viktor Toth。他提供了8位無符號PCM的溶液中,並改變爲16位有符號PCM,產生這樣的:

int a = 111; // first sample (-32768..32767) 
int b = 222; // second sample 
int m; // mixed result will go here 

// Make both samples unsigned (0..65535) 
a += 32768; 
b += 32768; 

// Pick the equation 
if ((a < 32768) || (b < 32768)) { 
    // Viktor's first equation when both sources are "quiet" 
    // (i.e. less than middle of the dynamic range) 
    m = a * b/32768; 
} else { 
    // Viktor's second equation when one or both sources are loud 
    m = 2 * (a + b) - (a * b)/32768 - 65536; 
} 

// Output is unsigned (0..65536) so convert back to signed (-32768..32767) 
if (m == 65536) m = 65535; 
m -= 32768; 

使用這個算法意味着幾乎不存在需要夾子輸出作爲它只有一個值在範圍之內。與直線平均不同,即使其他信號源無聲,也不會降低一個信號源的音量。

+0

「安靜」是什麼意思? - 這通常意味着*低幅值*(*接近中間值),但是在這裏您表示*負值*(低於中間值),而「大聲」等式在*一個或兩個都爲正值時執行*(在移位之前 - 即添加直流偏壓))。除此之外*音量*是對*信號*的感知,而不是單個樣本 - 「大聲」的聲音將在整個範圍內具有樣本。 – Clifford

+0

@Clifford:中間是可用範圍的中間,所以如果值介於0和65535之間,那麼中間是32767.最好在鏈接到Viktor Toth頁面的鏈接中解釋。 – Malvineous

+0

我意識到 - 我的問題是修辭 - 在這種情況下,「安靜」和「大聲」這兩個詞語是不準確和誤導的。 – Clifford

1

這是我在我最近的合成器項目上做的。

int* unfiltered = (int *)malloc(lengthOfLongPcmInShorts*4); 
int i; 
for(i = 0; i < lengthOfShortPcmInShorts; i++){ 
    unfiltered[i] = shortPcm[i] + longPcm[i]; 
} 
for(; i < lengthOfLongPcmInShorts; i++){ 
    unfiltered[i] = longPcm[i]; 
} 

int max = 0; 
for(int i = 0; i < lengthOfLongPcmInShorts; i++){ 
    int val = unfiltered[i]; 
    if(abs(val) > max) 
     max = val; 
} 

short int *newPcm = (short int *)malloc(lengthOfLongPcmInShorts*2); 
for(int i = 0; i < lengthOfLongPcmInShorts; i++){ 
    newPcm[i] = (unfilted[i]/max) * MAX_SHRT; 
} 

我將所有PCM數據添加到一個整數數組中,這樣我就可以不經過濾所有數據。

這樣做後,我查找整數數組中的絕對最大值。

最後,我將整數數組放入一個短整型數組中,方法是將每個元素除以該最大值,然後乘以最大短整型值。

這樣您就可以獲得適合數據所需的最小「淨空」量。

您可能可以對整數數組做一些統計並整合一些裁剪,但是對於我所需要的最小量的頭頂空間對我來說足夠好。