如何在音轨中找到静音部分

Posted

技术标签:

【中文标题】如何在音轨中找到静音部分【英文标题】:How to find silent parts in audio track 【发布时间】:2015-03-17 00:56:00 【问题描述】:

我有以下代码将 wav 文件中的原始音频数据存储在字节缓冲区中:

BYTE header[74];
fread(&header, sizeof(BYTE), 74, inputFile);
BYTE * sound_buffer;
DWORD data_size;

fread(&data_size, sizeof(DWORD), 1, inputFile);
sound_buffer = (BYTE *)malloc(sizeof(BYTE) * data_size);
fread(sound_buffer, sizeof(BYTE), data_size, inputFile);

是否有任何算法来确定音轨何时无声(字面上没有声音)以及何时有一定的声级?

【问题讨论】:

听起来“太宽泛” .... 这个问题的范围太广了?我真的开始认为这个社区是个垃圾。而不是考虑可能的解决方案,你只是写“太宽泛”然后离开。 对我来说听起来并不宽泛。 【参考方案1】:

好吧,您的“声音”将是一个值数组,无论是整数还是实数 - 取决于您的格式。

要使文件静音或“没有声音”,该数组中的值必须为零,或者非常接近零,或者最坏的情况 - 如果音频有偏差 - 值将保持不变左右波动产生声波。

您可以编写一个简单的函数来返回一个范围的增量,换句话说,最大值和最小值之间的差值,增量越小音量越低。

或者,您可以编写一个函数,返回差值低于给定阈值的范围。

为了玩,我写了一个漂亮的类:

template<typename T>
class SilenceFinder 
public:
  SilenceFinder(T * data, uint size, uint samples) : sBegin(0), d(data), s(size), samp(samples), status(Undefined) 

  std::vector<std::pair<uint, uint>> find(const T threshold, const uint window) 
    auto r = findSilence(d, s, threshold, window);
    regionsToTime(r);
    return r;
  

private:
  enum Status 
    Silent, Loud, Undefined
  ;

  void toggleSilence(Status st, uint pos, std::vector<std::pair<uint, uint>> & res) 
    if (st == Silent) 
        if (status != Silent) sBegin = pos;
        status = Silent;
      
    else 
        if (status == Silent) res.push_back(std::pair<uint, uint>(sBegin, pos));
        status = Loud;
      
  

  void end(Status st, uint pos, std::vector<std::pair<uint, uint>> & res) 
    if ((status == Silent) && (st == Silent)) res.push_back(std::pair<uint, uint>(sBegin, pos));
  

  static T delta(T * data, const uint window) 
    T min = std::numeric_limits<T>::max(), max = std::numeric_limits<T>::min();
    for (uint i = 0; i < window; ++i) 
        T c = data[i];
        if (c < min) min = c;
        if (c > max) max = c;
      
    return max - min;
  

  std::vector<std::pair<uint, uint>> findSilence(T * data, const uint size, const T threshold, const uint win) 
    std::vector<std::pair<uint, uint>> regions;
    uint window = win;
    uint pos = 0;
    Status s = Undefined;
    while ((pos + window) <= size) 
        if (delta(data + pos, window) < threshold) s = Silent;
        else s = Loud;
        toggleSilence(s, pos, regions);
        pos += window;
      
    if (delta(data + pos, size - pos) < threshold) s = Silent;
    else s = Loud;
    end(s, pos, regions);
    return regions;
  

  void regionsToTime(std::vector<std::pair<uint, uint>> & regions) 
    for (auto & r : regions) 
        r.first /= samp;
        r.second /= samp;
      
  

  T * d;
  uint sBegin, s, samp;
  Status status;
;

我还没有真正测试过它,但它看起来应该可以工作。但是,它假设一个音频通道,您必须扩展它才能使用和跨多通道音频工作。以下是您的使用方法:

SilenceFinder<audioDataType> finder(audioDataPtr, sizeOfData, sampleRate);
auto res = finder.find(threshold, scanWindow);
// and output the silent regions
for (auto r : res) std::cout << r.first << " " << r.second << std::endl;

还要注意,现在的实现方式,“cut”到静默区域会非常突然,这种“noise gate”类型的过滤器通常带有攻击和释放参数,可以平滑结果。例如,可能有 5 秒的静音,中间只有一个微小的爆裂声,没有攻击和释放参数,你会得到 5 分钟一分为二,爆裂声实际上会保留,但是使用这些你可以实现不同的灵敏度什么时候剪掉。

【讨论】:

所以我想我需要找到静默部分并查看 PCM 数据的样子,对吧?我如何知道哪个数组索引响应轨道中的什么时间? 如果你知道采样率,你可以计算出来。例如,在 48Khz 中,每秒音频将有 48000 个样本。 我明白了,样本数量会受到立体声信号的影响吗? 不,立体声或环绕声——它只会增加通道数,采样率是一样的。 好的,我的视频长度为 1:20:00。我将 4800(我的视频持续时间,以秒为单位)乘以 48000,这实际上是我的采样率,然后乘以 2(因为我的视频是每个样本 16 位 - 所以 2 个字节),我应该得到我的数组的长度?【参考方案2】:

要检查 t1 和 t2 之间的轨道部分是否为“静音”,请计算 t1 和 t2 之间样本的均方根 (RMS)。然后,只需检查 RMS 是否为&lt;= 到您确定构成“静音”的某个阈值。见http://en.wikipedia.org/wiki/Root_mean_square

【讨论】:

RMS 发现交流电源,但不适用于直流电源。他的原始数据可能有DC偏压,而且不需要找到具体的功率水平。 关于直流偏置的好点。考虑到这一点,最好先对数据应用高通滤波器,并使用低截止频率(比如 10 赫兹左右)。之后,RMS 将与功率电平成正比,功率电平将与扬声器发出的声音音量成正比。

以上是关于如何在音轨中找到静音部分的主要内容,如果未能解决你的问题,请参考以下文章

在音轨播放期间更改乐器 - MIDI/Java

在android中播放音轨中的音频

Kdenlive 不会在音轨上显示红色的音频录制按钮

Javascript 从音轨中选择片段

如何让 FFmpeg 在单循环静音视频中自动注入 mp3 音轨?

HTML5视频,如何检测没有音轨?