Что ж, ваш «звук» будет массивом значений, будь то целое число или вещественное число — зависит от вашего формата.
Чтобы файл был тихим или «без звука», значения в этом массиве должны быть нулевыми, или очень близкими к нулю, или в худшем случае — если звук имеет смещение — значение останется неизменным, а не колеблется вокруг для создания звуковых волн.
Вы можете написать простую функцию, которая возвращает дельту для диапазона, другими словами, разницу между наибольшим и наименьшим значением, чем меньше дельта, тем ниже громкость звука.
Или, в качестве альтернативы, вы можете написать функцию, которая возвращает вам диапазоны, в которых дельта ниже заданного порога.
Ради игрушек я написал отличный класс:
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;
Также обратите внимание, что то, как это реализовано прямо сейчас, «разрез» с тихими областями будет очень резким, такие фильтры типа «нойзгейт» обычно поставляются с параметрами атаки и освобождения, которые сглаживают результат. Например, может быть 5 секунд тишины с небольшим хлопком в середине, без параметров атаки и восстановления, вы получите 5 минут, разделенных на две части, а хлоп на самом деле останется, но с их помощью вы можете реализовать различную чувствительность к когда его отрезать.
person
dtech
schedule
17.03.2015