Как найти немые части в звуковой дорожке

У меня есть следующий код, который хранит необработанные аудиоданные из файла 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);

Есть ли какой-то алгоритм, чтобы определить, когда звуковая дорожка молчит (буквально нет звука), а когда есть какой-то уровень звука?


person SteveS    schedule 17.03.2015    source источник
comment
Звучит слишком широко ....   -  person πάντα ῥεῖ    schedule 17.03.2015
comment
Что слишком широко в этом вопросе? Я действительно начинаю думать, что это сообщество - мусор. Вместо того, чтобы думать о возможном решении, вы просто пишете слишком широко и уходите.   -  person SteveS    schedule 17.03.2015
comment
Мне это не кажется широким.   -  person dtech    schedule 17.03.2015


Ответы (2)


Что ж, ваш «звук» будет массивом значений, будь то целое число или вещественное число — зависит от вашего формата.

Чтобы файл был тихим или «без звука», значения в этом массиве должны быть нулевыми, или очень близкими к нулю, или в худшем случае — если звук имеет смещение — значение останется неизменным, а не колеблется вокруг для создания звуковых волн.

Вы можете написать простую функцию, которая возвращает дельту для диапазона, другими словами, разницу между наибольшим и наименьшим значением, чем меньше дельта, тем ниже громкость звука.

Или, в качестве альтернативы, вы можете написать функцию, которая возвращает вам диапазоны, в которых дельта ниже заданного порога.

Ради игрушек я написал отличный класс:

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
comment
Итак, я думаю, мне нужно найти тихую часть и посмотреть, как выглядят данные PCM, верно? Как узнать, какой индекс массива отвечает за какое время в треке? - person SteveS; 17.03.2015
comment
Вы можете рассчитать это, если знаете частоту дискретизации. Например, в 48 кГц у вас будет 48000 сэмплов на каждую секунду звука. - person dtech; 17.03.2015
comment
Понятно, а количество семплов зависит от стереосигнала? - person SteveS; 17.03.2015
comment
Нет, стерео или объемный - это только увеличивает количество каналов, частота дискретизации одинакова. - person dtech; 17.03.2015
comment
Хорошо, мое видео длится 1:20:00. Я умножаю 4800 (продолжительность моего видео в секундах) на 48000, что на самом деле является моей частотой дискретизации, и умножаю ее на 2 (потому что мое видео имеет 16 бит на сэмпл, то есть 2 байта), и я должен получить длину моего массива? - person SteveS; 17.03.2015
comment
Ну, в основном да, так как несколько каналов обычно переплетаются, все это должно быть втиснуто, так сказать, в один поток. - person dtech; 17.03.2015
comment
Количество каналов, кажется, влияет на массив. На самом деле у меня в два раза больше байтов, чем я посчитал выше. Итак, как это работает? Сочетаются ли звуковые байты в потоке следующим образом: Ch1;Ch2;Ch1;Ch2...? - person SteveS; 17.03.2015
comment
Я работал только с необработанным монофоническим звуком, поэтому не могу сказать вам по опыту, но обычно это идея переплетения, хотя я не уверен, является ли шаг одним байтом на канал. - person dtech; 17.03.2015
comment
Нашел ссылку на это: ">ссылка - person SteveS; 17.03.2015
comment
Давайте продолжим обсуждение в чате. - person SteveS; 17.03.2015
comment
Это много кода, не могли бы вы дать мне более подробное объяснение? - person SteveS; 18.03.2015
comment
@SteveS - это довольно просто, окно - это область, которую вы сканируете и перемещаете по файлу, функция delta() проверяет, молчит окно или нет, и исходя из этого и было ли предыдущее окно молчанием или нет, либо молчание область начинается или заканчивается и добавляется к вектору молчащих областей. Если у вас тихое окно, а предыдущее было громким, это означает, что вы начинаете тишину, если у вас громкое окно, а предыдущее было тихим, это означает, что вы заканчиваете тишину и регистрируете ее с отмеченным началом и текущей позицией. Вот и все. - person dtech; 18.03.2015

Чтобы проверить, является ли часть дорожки между t1 и t2 «молчаливой», вычислите среднеквадратичное значение (RMS) выборок между t1 и t2. Затем просто проверьте, соответствует ли среднеквадратичное значение <= некоторому пороговому значению, которое, по вашему мнению, представляет собой «молчание». См. http://en.wikipedia.org/wiki/Root_mean_square.

person mti2935    schedule 17.03.2015
comment
RMS находит питание переменного тока, но не работает на постоянном токе. Его необработанные данные могут иметь смещение по постоянному току, и, кроме того, нет необходимости находить конкретный уровень мощности. - person Potatoswatter; 17.03.2015
comment
Хорошая мысль о смещении постоянного тока. Чтобы учесть это, было бы неплохо сначала применить к данным фильтр верхних частот с низкой частотой среза (скажем, 10 Гц или около того). После этого среднеквадратичное значение будет пропорционально уровню мощности, которая будет пропорциональна громкости звука, исходящего из динамика. - person mti2935; 17.03.2015