如何检测这些音频异常?

19

iOS存在通过某些USB音频设备录音的问题。它无法可靠地重现(每2000-3000次记录中会发生一次,而且会悄无声息地消失),我们目前手动检查音频是否存在任何录制问题。这导致少量样本(1-20个)会被移动一个听起来像“噼啪声”的小数字。

它们看起来像这样:

具有异常的波形

更近一步看:

enter image description here

再接近一点:

enter image description here

同一音频文件中其他地方的另一个单一样本错误:

enter image description here

问题是,在直接访问样本的情况下,如何算法地检测到这些,并避免在具有此类波形的高频音频上触发误报:

enter image description here

加分:确定尽可能多的错误后,如何“修复”音频?

更多加分:在iOS USB音频驱动程序/硬件中可能是什么原因(假设存在该问题)。


你可能会发现http://dsp.stackexchange.com/很有帮助。 - keyboardP
可以跨帖吗? - Alastair Stuart
这很棘手,因为根据http://meta.stackexchange.com/questions/64068/is-cross-posting-a-question-on-multiple-stack-exchange-sites-permitted-if-the-qu的说法,交叉发布似乎有点不被允许。话虽如此,你可以在一个站点上发布(即留在这里或删除此帖并在DSP上发布),如果你没有得到满意的答案,那么就将其删除并在另一个站点上发布。我认为这个问题在这里是有效的,但我建议在DSP上发布,因为它可能有更多的专家在这个领域内。 - keyboardP
正如已经提到的,这是由于时钟偏差所致。如果协议没有解决方案,那么很难在软件中得到正确的结果(也许可以使用PLL和注入训练序列)。简单的解决方法是请求更快的采样率,然后低通滤波并向下采样到所需的级别。仍然需要滤波器(因此不像丢弃样本那么简单),但可以选择滤波带来避免丢失任何音频内容。 - dashesy
我们正在研究许多不同的解决方案来解决未来的问题 - 然而,现有记录(成千上万个文件)的识别问题仍然存在。 - Alastair Stuart
5个回答

14
我认为没有现成的解决方案来查找干扰,但这里有一种(非标准)解决问题的方法。使用此方法,我可以找到大多数间隔,并且只有少量误报,但算法肯定需要进行一些微调。
我的想法是找到偏离样本的起始点和结束点。第一步应该是使这些点更清晰地突出。这可以通过对数据取对数并计算相邻值之间的差异来完成。
在MATLAB中,我加载数据(在本例中使用dirty-sample-other.wav)。
y1 = wavread('dirty-sample-pictured.wav');
y2 = wavread('dirty-sample-other.wav');
y3 = wavread('clean-highfreq.wav');

data = y2;

并使用以下代码:

logdata = log(1+data);
difflogdata = diff(logdata);

所以,不要使用原始数据的这个图表:

original data

我们得到:

diff-log-data

我们正在寻找的区间以正负尖峰形式突出。例如,在对数差异图中放大最大正值,我们得到以下两个图像。一个是原始数据:

Original data zoomed

还有一个关于对数差的:

Diff-log-data zoomed

这个图可以帮助手动找到区域,但理想情况下我们希望使用算法来找到它们。我采用的方法是取大小为6的移动窗口,计算窗口的平均值(除最小值外的所有点),并将其与最大值进行比较。如果最大点是唯一超过平均值且至少是平均值的两倍大的点,则将其视为正极端值。
然后我使用了一个计数阈值,至少有一半的移动窗口在该值上检测到它作为极端值,才能被接受。
将所有点乘以(-1),然后再次运行此算法以检测最小值。
用“o”标记正极端值,“*”标记负极端值,我们得到以下两个图。其中一个是用于对数差异的:

diff-log-data with found extremes

还有一个用于原始数据的:

original data with found extremes

放大图中左侧显示对数差异的部分,我们可以看到大多数极端值位于:

diff-log-data with found extremes zoomed

似乎大多数间隔都被找到了,只有很少的误报。例如,在“clean-highfreq.wav”上运行算法,我只找到一个正极值和一个负极值。
可能可以通过匹配起始点和终止点来筛选出错误分类为极端值的单个值。如果你想替换丢失的数据,可以使用某种插值方法使用周围的数据点进行插值,甚至线性插值也足够好。
以下是我使用的MATLAB代码:
function test20()
clc
clear all

y1 = wavread('dirty-sample-pictured.wav');
y2 = wavread('dirty-sample-other.wav');
y3 = wavread('clean-highfreq.wav');

data = y2;

logdata = log(1+data);
difflogdata = diff(logdata);

figure,plot(data),hold on,plot(data,'.')
figure,plot(difflogdata),hold on,plot(difflogdata,'.')

figure,plot(data),hold on,plot(data,'.'),xlim([68000,68200])
figure,plot(difflogdata),hold on,plot(difflogdata,'.'),xlim([68000,68200])

k = 6;
myData = difflogdata;
myPoints = findPoints(myData,k);

myData2 = -difflogdata;
myPoints2 = findPoints(myData2,k);

figure
plotterFunction(difflogdata,myPoints>=k,'or')
hold on
plotterFunction(difflogdata,myPoints2>=k,'*r')

figure
plotterFunction(data,myPoints>=k,'or')
hold on
plotterFunction(data,myPoints2>=k,'*r')

end

function myPoints = findPoints(myData,k)

iterationVector = k+1:length(myData);
myPoints = zeros(size(myData));
for i = iterationVector
    subVector = myData(i-k:i);
    meanSubVector = mean(subVector(subVector>min(subVector)));
    [maxSubVector, maxIndex] = max(subVector);
    if (sum(subVector>meanSubVector) == 1 && maxSubVector>2*meanSubVector)
        myPoints(i-k-1+maxIndex) = myPoints(i-k-1+maxIndex) +1;
    end
end

end

function plotterFunction(allPoints,extremeIndices,markerType)

extremePoints = NaN(size(allPoints));
extremePoints(extremeIndices) = allPoints(extremeIndices);
plot(extremePoints,markerType,'MarkerSize',15),
hold on
plot(allPoints,'.')
plot(allPoints)

end

编辑 - 恢复原始数据的评论

这是上面第三个图的略微缩小的视图:(干扰在6.8和6.82之间)

Original data zommed out

当我检查数值时,你关于数据被镜像到负值的理论似乎并不完全符合模式。但无论如何,我的想法只是去除差异显然是不正确的。由于周围的点似乎没有受到干扰,我可能会回到最初的想法,即不信任受影响区域内的点,而是使用周围的数据进行某种插值。在大多数情况下,简单的线性插值似乎是一个相当好的近似值。

这是一个非常棒的答案 - 谢谢你详细解释。我不熟悉MATLAB,所以借了一份副本来尝试操作(玩一下)。在确定了这些成对值之后,我该如何进行插值才能输出一个“固定”的wav文件呢? - Alastair Stuart
@AlastairStuart 很高兴您能使用matlab代码!为了设置向量Y的新值,您可以使用语法Y(startIndex:endIndex) = newValues。因此,当您已经确定了这些对时,您应该能够执行类似于Y(stInd:enInd) = a + (b-a)X的操作进行线性插值,其中X是适合区间[0,1]长度的向量。 - user1884905
@AlastairStuart 当我考虑这个问题时,线性插值可能不是你想要做的。如果你看一下我的答案中放大后的图表,似乎新值不应该是线性插值。一个想法是,你可以尝试通过将信号的剩余部分向上或向下移动来消除差异以补偿移位。类似于 X(currentIndex:end) = X(currentIndex:end) - signalShift。我不知道这样做时差异是否会互相抵消。也许最终信号与原始信号会有显著的不同? - user1884905
看你的第三张图,似乎在班次的起点和终点之间,信号在Y轴上被反转了。或者也许我看到了一个不存在的模式。 - Alastair Stuart
@AlastairStuart 或许是这样,但我想看到绘图周围更大一部分的数据才能得出结论。我现在没有访问Matlab的权限,但我可以在周一检查一下。 - user1884905
1
太棒了!我需要一个类似问题的实时解决方案,这给了我一个完美的起点去实现它。 - Tox

9
为什么会发生这种情况呢?USB音频设备和主机不是时钟同步的,也就是说主机无法准确地恢复主机本地时钟与音频接口上ADC/DAC的字时钟之间的关系。各种技术存在,用于时钟恢复,具有不同程度的有效性。问题更加复杂的是,总线时钟可能与两个音频时钟都不相关。虽然你可能认为这对于音频接收来说并不是太大的问题,但是当有数据时,音频捕获回调可能会发生,而且音频接口通常是双向的,主机将以固定的间隔呈现音频,而另一端可能以稍微不同的速率消耗音频。中间有几组缓冲区,可能会出现过度或不足,这似乎就是在这里发生的情况;它发生的时间间隔肯定是正确的。你可能会发现,将USB音频设备更改为围绕不同芯片集(或简单地是不同的局部振荡器)构建的设备可以有所帮助。
作为旁注,IEEE1394音频和MPEG传输流都具有相同的时钟恢复要求。它们都通过以非常可预测的方式将本地时钟参考数据包嵌入串行比特流中来解决问题,从而允许在另一端进行精确的时钟恢复。

1
检查时钟速率漂移是否是问题的一种方法是观察两个设备之间的温差是否会导致变化。例如,在测试之前,将一个设备放在冰箱里,另一个设备放在口袋里。然后交换位置重新测试。 - hotpaw2
确实 - 虽然保持那么冷的温度不到一小时可能是一个挑战。调试这些问题需要很多耐心! - marko

2
我认为下列算法可用于样本以确定潜在的错误结果:
首先,扫描高频量,可以通过将声音块FFT分块(256个值可能),或通过计算连续样本数超过和低于零来实现。后者应该跟踪最大的连续值以下零,最大的连续值以下零,在零周围的小转换数量和块的当前音量(Audacity将其显示为0..1)。然后,如果最大连续值低于5(在44100采样下,零是连续的,而杰出的样本是单独的,5对应于4410Hz频率,这相当高),或者小转换长度之和大于取决于最大连续值的某个值(我认为第一个近似值应为3 * 5 *块大小/两个最大值之间的距离,这大致相当于最响亮的FFT频率的周期。还应该在阈值以上和以下进行测量,因为我们可能会得到一个错误的峰值,这很可能会被主要速度在下限或上限 最大值、也由峰值的标准偏差测得。如果高频率是占主导地位的,则这个块仅适用于零值测试,并且需要特殊手段修复数据。如果高频率显着,即检测到主导低频率,我们可以搜索大于3.0 *高频率音量的峰值,以及此块中异常的零。
此外,你的间隙似乎要么高度扩展要么是纯零,高度扩展为单个错误,零误差范围从1-20。因此,如果有一个零值范围,其绝对值小于0.02,直接被0.15(一个可微调的变量)或更高绝对值的值包围,并且与相同符号,将此点计为一个错误。如果你计算2.0 *(当前样本) -(上一个样本)-(下一个样本),并且它超过了某个阈值(0.1 +高频率音量,或3.0 *高频率音量,无论哪个更大),将其视为一个错误并进行平均处理,就可以检测到突出的单个值。
在发现零缺口时该怎么办——我们可以从FFT块的最显着频率的1个周期向后和向前(平均值)复制值,其中“周期”是最重要的频率的周期。如果“周期”小于间隙(假设我们已检测到声音高亢部分的零间隙),则使用两个或更多周期,使源数据全部有效(在这种情况下,不能进行平均处理,因为可能信号相位从间隙 处向前和向后2个周期)。如果有多个频率的振幅大约相等,我们可以用正确的相位简单采样这些频率,将较不显着的频率全部削减掉。
我认为突出的样本应该只是由周围的2-4个样本平均处理,因为在您的声音文件中似乎只遇到了单个样本。

2
离散小波变换(DWT)可能是您问题的解决方案。
在您的情况下,FFT计算并不是非常有用,因为它是相对频率内容在整个信号持续时间内的平均表示,因此无法检测瞬时变化。离散短时频率变换(STFT)试图通过计算信号的短连续时间块的DFT来解决这个问题,其长度由窗口的长度(和形状)确定,但由于DFT的分辨率取决于数据/块长度,因此在频率或时间上存在分辨率的权衡,并且找到这个神奇的固定窗口大小可能会很棘手!
您需要的是具有高频事件良好时间分辨率和低频事件良好频率分辨率的时频分析方法...进入离散小波变换!
不同应用程序有许多小波变换,正如您所期望的那样,它的计算量很大。DWT可能不是您问题的实际解决方案,但值得考虑。祝您好运。一些星期五晚上的阅读:

http://klapetek.cz/wdwt.html

http://etd.lib.fsu.edu/theses/available/etd-11242003-185039/unrestricted/09_ds_chapter2.pdf

http://en.wikipedia.org/wiki/Wavelet_transform

http://en.wikipedia.org/wiki/Discrete_wavelet_transform


1
您可以尝试以下超级简单的方法(可能已足够):
1. 对于您的波形中的每个点,减去其前任(查看从一个点到下一个点的变化)。
2. 查看这些变化的分布并找到它们的标准差。
3. 如果任何给定的差异超出此标准差的X倍(上面或下面),则将其标记为问题。
4. 通过调整X的最佳值并查看其表现如何来确定最佳值。
5. 大多数“问题”应该作为两个差异对出现,一个向上,一个向下返回。
为了遵循超级简单的方法,您可以通过在线性插值您问题部分之前的最后一个好点和之后的第一个好点之间来修复数据。(确保您不要只删除点,因为这会影响(提高)音频的音高。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接