从文件(八度)进行块流传输时音频失真

问题描述 投票:0回答:2

我正在设置一个简单的音频 IO 系统,该系统通过从已存储在内存中的文件一次调用一个块来模拟“实时块处理”。


目前,我有一个简单的脚本,它从文件中检索数据,然后进入 while 循环,一次提取一个块,并提供 600 Hz 的一阶巴特沃斯低通滤波器(要测试的框架设置)。然后处理每个块并将其添加到在 while 循环范围之外声明的另一个数组中,以便处理后的数据可以在完成后写入波形文件。


为了过滤数据,我使用 Octave signal pkg 生成系数 (butter),然后使用内置 filter 函数来应用 IIR 滤波器。

问题是,如果我不应用任何过滤器影响,即输入=输出,音频听起来完全相同。但是,如果我每次调用块时都应用滤波器,则会产生振铃,从而使信号严重数字失真。


请参阅以下设置脚本(目前仅处理单声道音频)。

# Reset
close all; clear all;

# Audio file path
fileName = 'test.wav';

# Init routines
[x,fs] = audioread(fileName);
xlen = length(x);
[dim1,dim2] = size(x);
y = zeros(dim1,dim2);
[b,a] = butter(1, (600./(fs*0.5)));    
index = 1;
blockSize = 256;

# Enter process loop
while(index + blockSize < xlen)

  # Extract one block
  audioBlock(:,1) = x(index : index + blockSize - 1, 1);

  # Do process
  outAudioBlock = filter(b,a,audioBlock);

  # Store output block
  y(index : index + blockSize - 1, 1) = outAudioBlock(:);

  # Update index 
  index += blockSize;

endwhile

# Write to outputs
audiowrite('processed.wav', y, fs);
audiowrite('processed1.wav', filter(b,a,y), fs);

第二个音频写入只是一个示例,它确认在一次调用中过滤整个音频数据不会产生失真,而块过滤会产生明显的数字失真。


作为旁注:

我还尝试使用不同的滤波技术,包括频域乘法加窗,然后 ifft 返回(使用八度 fftfilt 并仅使用 fft)以及时域卷积并创建重叠相加方法。当应用 FIR 滤波器而不是使用 IIR 系数时,也会出现相同的效果。

我也知道这个例子忽略了音频的最后一个块,但对于这个用例,我不关心最后一个块的零填充。


我不确定我错过了什么;有什么想法吗?

编辑1:这个想法是如果可能的话不使用频域处理(只是时域IIR/FIR滤波),但我研究了频域乘法以查看是否发生了类似的失真结果(它确实发生了)。

matlab audio file-io octave
2个回答
2
投票

这很可能是边缘效应。您将因果 IIR 滤波器应用于

audioBlock
。为了计算第一个样本,状态被初始化为全零。如果我没记错的话,这相当于假设第一个样本之前的信号全为零。这可能会产生不连续性,从而影响块开头的一定数量的样本。由于您使用 IIR 滤波器,因此这种效应可能会持续很长时间。在这方面使用 FIR 滤波器更安全。

我们假设

margin
样本受到影响。您可以按如下方式修改代码,以将信号扩展该量并防止失真:

while(index + blockSize < xlen)

  % Extract one block
  if index==1
    audioBlock = x(index : index + blockSize - 1);
  else
    audioBlock = x(index - margin : index + blockSize - 1);
  end

  % Do process
  outAudioBlock = filter(b,a,audioBlock);

  % Store output block
  if index==1
    y(index : index + blockSize - 1) = outAudioBlock;
  else
    y(index : index + blockSize - 1) = outAudioBlock(margin+1:end);
  end

  % Update index 
  index += blockSize;

end

(免责声明:我这里没有安装octave,而且我的MATLAB副本没有信号处理工具箱,所以我无法测试上面的代码。)


未经请求的建议:

  • 您的数据都是一维的,请使用一维(线性)索引。它效率更高,打字时间也更短。 (请参阅上面的代码。)

  • 提取新的信号位时不要执行

    audioBlock(:,1) =
    操作。只需将结果分配给变量即可。它快得多,并且如果信号大小发生变化并且您忘记重置变量,也不会出现问题。

  • 不要以

    close all; clear all;
    开头。相反,请在脚本顶部写下
    function <filename>
    。这会将脚本转换为函数,这意味着它拥有自己的工作空间。这是一种更安全的工作方式,因为您不会意外地使用脚本中的现有变量,也不会意外地删除基础工作区中的任何内容。

  • 我用

    end
    代替了
    endwhile
    。这是相同的,但也适用于 MATLAB。没有理由不使用最便携的选项。

  • 我用

    %
    代替了
    #
    。同样,相同但便携。请注意 SO 语法突出显示如何与
    %
    配合使用,但不适用于
    #
    ! :)


0
投票

您的滤波器是 IIR,这意味着您需要在帧与帧之间保留它的状态。正是由于这个原因,“过滤器”函数具有状态输入和状态输出参数,但您没有在代码中使用这些参数。这就是为什么在每一帧中,过滤器都从干净状态开始,这必然会产生伪像。解决方法很简单:在进入循环之前将变量“state”初始化为 [],并将其添加到过滤器函数调用中的输入和输出参数中,如下所示:

[outAudioBlock, 状态] = 过滤器(b,a,audioBlock,状态);

© www.soinside.com 2019 - 2024. All rights reserved.