我正在尝试在已启动且正在运行的单一麦克风聊天应用程序中添加对会议聊天的支持(其中一次只能有一个人可以聊天)。客户端和所有内容的流式传输都已完成,语音正在录制并在使用麦克风的计算机上播放得很好但是当第三个人接收到数据包时,音频以非常奇怪的方式播放,我搜索并发现我需要混合两个流,然后将它们作为一个播放。我尝试了一些我在互联网上找到的算法,但是我没有得到我需要的结果。
我在客户端解码输入流后使用speex作为编码器/解码器我尝试通过以下算法混合两个字节的数组/流。
Var Buffer1, Buffer2, MixedBuf: TIdBytes;
Begin
For I := 0 To Length(Buffer1) - 1 Do Begin
If Length(Buffer2) >= I Then
MixedBuf[I] := (Buffer1[I] + Buffer2[I]) / 2
Else
MixedBuf[I] := Buffer1[I];
End;
End;
接收到的缓冲区是492或462字节,因此我检查Buffer2是否小于Buffer1然后混合前462个字节并保持其余字节不变,只需将它们添加到MixedBuff。
使用此算法时会产生大量噪音和失真,只能听到部分声音。
我在Mark Heath
提交的stackoverflow上找到的另一种算法是首先将字节转换为浮点值。
Var Buffer1, Buffer2, MixedBuf: TIdBytes;
samplef1, samplef2, Mixed: Extended;
Begin
For I := 0 To Length(Buffer1) - 1 Do Begin
If Length(Buffer2) >= I Then Begin
samplef1 := Buffer1[I] / 65535;
samplef2 := Buffer2[I] / 65535;
Mixed := samplef1 + samplef2;
if (Mixed > 1.0) Then Mixed := 1.0;
if (Mixed < -1.0) Then Mixed := -1.0;
MixedBuf[I] := Round(Mixed * 65535);
End Else
MixedBuf[I] := Buffer1[I];
End;
End;
该值永远不会低于0,但我仍然会检查该值是否低于-1.0,就像在算法中一样。这种方法工作得更好但仍然存在噪音和失真,来自第二个流的声音总是很微弱,而来自第一个流的声音应该是响亮的,即使第一个人没有说第二个声音很晕。
P.S:哦,有关音频流的一些细节:
录音回放的tWAVEFORMATEX记录详情如下:
FWaveFormat.wFormatTag := WAVE_FORMAT_PCM;
FWaveFormat.nChannels := 1;
FWaveFormat.nSamplesPerSec := WAVESAMPLERATE; // i.e WAVESAMPLERATE = 16000
FWaveFormat.nAvgBytesPerSec := WAVESAMPLERATE*2;
FWaveFormat.nBlockAlign := 2;
FWaveFormat.wBitsPerSample := 16;
FWaveFormat.cbSize := SizeOf(tWAVEFORMATEX);
我希望我能提供所需的所有信息。
答案 0 :(得分:4)
FWaveFormat.wBitsPerSample := 16;
您需要尊重样本为16位宽的事实。您的代码一次以8位运行。你可以这样写:
function MixAudioStreams(const strm1, strm2: TBytes): TBytes;
// assumes 16 bit samples, single channel, common sample rate
var
i: Integer;
n1, n2, nRes: Integer;
p1, p2, pRes: PSmallInt;
samp1, samp2: Integer;
begin
Assert(Length(strm1) mod 2 = 0);
Assert(Length(strm2) mod 2 = 0);
n1 := Length(strm1) div 2;
n2 := Length(strm2) div 2;
nRes := Max(n1, n2);
SetLength(Result, nRes*2);
p1 := PSmallInt(strm1);
p2 := PSmallInt(strm2);
pRes := PSmallInt(Result);
for i := 0 to nRes-1 do begin
if i < n1 then begin
samp1 := p1^;
inc(p1);
end else begin
samp1 := 0;
end;
if i < n2 then begin
samp2 := p2^;
inc(p2);
end else begin
samp2 := 0;
end;
pRes^ := EnsureRange(
(samp1+samp2) div 2,
low(pRes^),
high(pRes^)
);
inc(pRes);
end;
end;
有些人建议按sqrt(2)
进行缩放以维持两个信号的组合功率。这看起来像这样:
pRes^ := EnsureRange(
Round((samp1+samp2) / Sqrt(2.0)),
low(pRes^),
high(pRes^)
);