查看源音频文件的屏幕截图,检测声级变化的一种简单方法是对样本进行数值积分,以找出特定时间波的“能量”。
一个粗略的算法是:
- 将样本分成几部分
- 计算每个部分的能量
- 取前一个窗口和当前窗口之间的能量比
- 如果该比率超过某个阈值,则确定有突然的巨响。
伪代码
samples = load_audio_samples() // Array containing audio samples
WINDOW_SIZE = 1000 // Sample window of 1000 samples (example)
for (i = 0; i < samples.length; i += WINDOW_SIZE):
// Perform a numerical integration of the current window using simple
// addition of current sample to a sum.
for (j = 0; j < WINDOW_SIZE; j++):
energy += samples[i+j]
// Take ratio of energies of last window and current window, and see
// if there is a big difference in the energies. If so, there is a
// sudden loud noise.
if (energy / last_energy > THRESHOLD):
sudden_sound_detected()
last_energy = energy
energy = 0;
我应该添加一个我没有尝试过的免责声明。
这种方式应该可以在不首先记录所有样本的情况下执行。只要有一定长度的缓冲区(WINDOW_SIZE
在示例中),就可以执行数值积分来计算声音部分的能量。但是,这确实意味着处理过程中会有延迟,具体取决于WINDOW_SIZE
. 确定一段声音的合适长度是另一个问题。
如何分割成多个部分
在第一个音频文件中,似乎关门声音的持续时间是 0.25 秒,所以用于数值积分的窗口应该最多是它的一半,甚至更接近十分之一,所以两者之间的差异即使窗口在静音部分和噪音部分之间重叠,也可以注意到静音和突然的声音。
例如,如果积分窗口为 0.5 秒,第一个窗口覆盖了 0.25 秒的静音和 0.25 秒的关门,第二个窗口覆盖了 0.25 秒的关门和 0.25 秒的静音,则可能会出现两段声音的噪音水平相同,因此不会触发声音检测。我想有一个短窗口会在一定程度上缓解这个问题。
但是,窗口太短将意味着声音的上升可能无法完全适应一个窗口,并且可能会显得相邻部分之间的能量差异很小,这可能会导致声音丢失。
我相信WINDOW_SIZE
和THRESHOLD
都必须根据经验确定要检测的声音。
为了确定该算法需要在内存中保留多少样本,假设WINDOW_SIZE
是关门声音的 1/10,大约为 0.025 秒。在 4 kHz 的采样率下,即 100 个样本。这似乎不是太多的内存要求。使用 200 字节的 16 位样本。
优点缺点
这种方法的优点是,如果源音频以整数形式输入,则可以使用简单的整数运算来执行处理。如前所述,关键是实时处理会有延迟,具体取决于集成部分的大小。
对于这种方法,我可以想到几个问题:
- 如果背景噪音太大,背景噪音和关门的能量差异将不容易区分,并且可能无法检测到关门。
- 任何突然的声音,例如拍手声,都可以视为门正在关闭。
也许,结合其他答案中的建议,例如尝试使用傅里叶分析来分析关门的频率特征,这将需要更多的处理,但会使其不易出错。
在找到解决此问题的方法之前,可能需要进行一些实验。