如何在音轨中找到无声部分
How to find silent parts in audio track
我有以下代码将来自 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);
是否有任何算法可以确定音轨何时静音(字面上没有声音)以及何时有一定的音量?
要检查 t1 和 t2 之间的轨迹部分是否 'silent',请计算 t1 和 t2 之间样本的均方根 (RMS)。然后,只需检查 RMS 是否 <=
到您确定构成 'silence' 的某个阈值。参见 http://en.wikipedia.org/wiki/Root_mean_square
那么,您的 "sound" 将是一个值数组,无论是整数还是实数 - 取决于您的格式。
要使文件静音或 "have no sound" 该数组中的值必须为零,或非常接近于零,或者最坏的情况——如果音频有偏差——该值将保持相同而不是四处波动产生声波。
你可以写一个简单的函数,returns一个范围的增量,换句话说,最大值和最小值之间的差值,增量越小,音量越低。
或者,您可以编写一个函数,returns 确定增量低于给定阈值的范围。
为了玩玩,我写了一个漂亮的class:
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"到silent区域会很突兀,这种"noise gate"类型的filer一般都带有attack和release参数,平滑了结果。例如,可能有 5 秒的静音,中间只有一点点爆音,如果没有起音和释放参数,您将把 5 分钟一分为二,爆音实际上会保留下来,但是使用这些您可以实现不同的敏感度什么时候剪掉。
我有以下代码将来自 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);
是否有任何算法可以确定音轨何时静音(字面上没有声音)以及何时有一定的音量?
要检查 t1 和 t2 之间的轨迹部分是否 'silent',请计算 t1 和 t2 之间样本的均方根 (RMS)。然后,只需检查 RMS 是否 <=
到您确定构成 'silence' 的某个阈值。参见 http://en.wikipedia.org/wiki/Root_mean_square
那么,您的 "sound" 将是一个值数组,无论是整数还是实数 - 取决于您的格式。
要使文件静音或 "have no sound" 该数组中的值必须为零,或非常接近于零,或者最坏的情况——如果音频有偏差——该值将保持相同而不是四处波动产生声波。
你可以写一个简单的函数,returns一个范围的增量,换句话说,最大值和最小值之间的差值,增量越小,音量越低。
或者,您可以编写一个函数,returns 确定增量低于给定阈值的范围。
为了玩玩,我写了一个漂亮的class:
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"到silent区域会很突兀,这种"noise gate"类型的filer一般都带有attack和release参数,平滑了结果。例如,可能有 5 秒的静音,中间只有一点点爆音,如果没有起音和释放参数,您将把 5 分钟一分为二,爆音实际上会保留下来,但是使用这些您可以实现不同的敏感度什么时候剪掉。