Найти звуковой эффект внутри аудиофайла - PullRequest
0 голосов
/ 30 сентября 2018

У меня загружается 3-часовой MP3-файл, и каждые ~ 15 минут воспроизводится отдельный 1-секундный звуковой эффект, который сигнализирует о начале новой главы.

Можно ли каждый раз идентифицировать этотзвуковой эффект воспроизводится, поэтому я могу отметить смещения по времени?

Звуковой эффект всегда одинаков, но, поскольку он был закодирован в формате с потерями, будет небольшое отклонение.

Смещения времени будут сохранены в метаданных фрейма главы ID3 .


Пример источника , где звуковой эффект воспроизводится дважды.

ffmpeg -ss 0.9 -i source.mp3 -t 0.95 sample1.mp3 -acodec copy -y

ffmpeg -ss 4.5 -i source.mp3 -t 0.95 sample2.mp3 -acodec copy -y


Я очень новичок в обработке звука, но моей первоначальной мыслью было извлечь образец1 секунды звукового эффекта, затем используйте librosa в python для извлечения временных рядов с плавающей запятой для обоих файлов, округлитечисла с плавающей запятой и попытайтесь найти совпадение.

import numpy
import librosa

print("Load files")

source_series, source_rate = librosa.load('source.mp3') # 3 hour file
sample_series, sample_rate = librosa.load('sample.mp3') # 1 second file

print("Round series")

source_series = numpy.around(source_series, decimals=5);
sample_series = numpy.around(sample_series, decimals=5);

print("Process series")

source_start = 0
sample_matching = 0
sample_length = len(sample_series)

for source_id, source_sample in enumerate(source_series):

    if source_sample == sample_series[sample_matching]:

        sample_matching += 1

        if sample_matching >= sample_length:

            print(float(source_start) / source_rate)

            sample_matching = 0

        elif sample_matching == 1:

            source_start = source_id;

    else:

        sample_matching = 0

Это не работает с файлами MP3, описанными выше, но работает с версией MP4 - где он смог найти образец, который я извлек, ноэто был только один образец (не все 12).

Я также должен отметить, что этот сценарий занимает чуть более 1 минуты для обработки 3-часового файла (который включает 237 426 624 образца).Поэтому я могу себе представить, что какое-то усреднение в каждом цикле может привести к тому, что это займет значительно больше времени.

Ответы [ 4 ]

0 голосов
/ 04 января 2019

Чтобы ответить на ответы @jonnor и @ paul-john-leonard, они оба верны, с помощью фреймов (FFT) я смог обнаружить аудио событие.

Я написалполный исходный код:

https://github.com/craigfrancis/audio-detect

Некоторые примечания:

  • Для создания шаблонов я использовал ffmpeg:

    ffmpeg -ss 13.15 -i source.mp4 -t 0.8 -acodec copy -y templates/01.mp4;

  • Я решил использовать librosa.core.stft, но мне нужно было сделать собственную реализацию этой функции stft для 3-часового файла, который я анализирую,так как он слишком велик для хранения в памяти.

  • При использовании stft я сначала попытался использовать hop_length 64, а не значение по умолчанию (512), так как предполагал, что это будетдайте мне больше данных для работы ... теория могла бы быть верной, но 64 был слишком подробным, и большую часть времени приводил к его сбою.

  • Я до сих пор понятия не имеюкак получить взаимную корреляцию между фреймом и шаблоном для работы (через numpy.correlate) ... вместо этого я взял результаты на кадр (1025 сегментов, а не 1024, которые я бelieve относятся к найденным частотам в Гц) и сделали очень простую проверку средней разности, затем удостоверились, что среднее было выше определенного значения (мой тестовый пример работал на 0,15, основные файлы, которые я использую для этого, требовали 0,55 - предположительно потому, что основнойфайлы были сжаты немного больше):

    hz_score = abs(source[0:1025,x] - template[2][0:1025,y])
    hz_score = sum(hz_score)/float(len(hz_score))

  • При проверке этих результатов действительно полезно показать их награф.Я часто использовал что-то вроде следующего:

    import matplotlib.pyplot as plt
    plt.figure(figsize=(30, 5))
    plt.axhline(y=hz_match_required_start, color='y')

    while x < source_length:
    debug.append(hz_score)
    if x == mark_frame:
    plt.axvline(x=len(debug), ymin=0.1, ymax=1, color='r')

    plt.plot(debug)
    plt.show()

  • Когда вы создаете шаблон, вам нужно обрезать любую ведущую тишину (чтобы избежатьплохое соответствие) и дополнительные ~ 5 кадров (кажется, что процесс сжатия / перекодирования изменяет это) ... аналогично, удалите последние 2 кадра (я думаю, что кадры содержат немного данных из своего окружения, гдепоследний, в частности, может быть немного отключен).

  • Когда вы начнете находить совпадение, вы можете обнаружить, что это нормально для первых нескольких кадров, а затем не получается ... вы, вероятно, будетенужно повторить кадр или два позже.Я обнаружил, что проще иметь процесс, который поддерживает несколько шаблонов (небольшие вариации звука), и проверял их первый тестируемый (например, 6-й) кадр, и, если это совпадало, помещал их в список возможных совпадений.Затем, по мере продвижения к следующим кадрам источника, он может сравнивать его со следующими кадрами шаблона, пока все кадры в шаблоне не будут сопоставлены (или не пройдены).

0 голосов
/ 06 октября 2018

Попытка прямого сопоставления выборок сигналов во временной области не является хорошей идеей.Сигнал mp3 сохранит свойства восприятия, но вполне вероятно, что фазы частотных компонентов будут смещены, поэтому значения выборки не будут совпадать.

Вы можете попытаться сопоставить объемные огибающие вашего эффекта и сэмпла.Это менее вероятно повлияет на процесс mp3.

Сначала нормализуйте ваш семпл, чтобы встроенные эффекты были того же уровня, что и эталонный эффект.Построение новых осциллограмм на основе эффекта и выборки с использованием среднего значения пиковых значений по временным кадрам, которые достаточно коротки для захвата соответствующих объектов.Лучше все же использовать перекрывающиеся рамки.Затем используйте взаимную корреляцию во временной области.

Если это не сработает, вы можете проанализировать каждый кадр, используя БПФ, это даст вам вектор признаков для каждого кадра.Затем вы пытаетесь найти совпадения последовательности элементов вашего эффекта с образцом.Аналогично предложению https://stackoverflow.com/users/1967571/jonnor.MFCC используется в распознавании речи, но поскольку вы не обнаруживаете речь, FFT, вероятно, в порядке.

Я предполагаю, что эффект воспроизводится сам по себе (без фонового шума), и он добавляется к записи электронным способом (в отличие от записи через микрофон).Если это не так, проблема становится более сложной.

0 голосов
/ 14 октября 2018

Это может быть не ответ, это именно то, куда я попал, прежде чем я начну исследовать ответы @jonnor и @ paul-john-leonard.

Я смотрел на спектрограммы, которые вы можете получить, используяlibrosa stft и amplitude_to_db и, думая, что если я возьму данные, которые входят в графики, с небольшим округлением, я потенциально могу найти 1 воспроизводимый звуковой эффект:

https://librosa.github.io/librosa/generated/librosa.display.specshow.html

Код, который я написал ниже, вид работ;хотя он:

  1. Возвращает довольно много ложных срабатываний, которые могут быть исправлены путем настройки параметров того, что считается совпадением.

  2. Мне нужно заменить функции librosa чем-то, что может анализировать, округлять и выполнять проверки совпадений за один проход;поскольку трехчасовой аудиофайл приводит к нехватке памяти python на компьютере с 16 ГБ ОЗУ через ~ 30 минут, прежде чем он даже достигнет значения бита округления.


import sys
import numpy
import librosa

#--------------------------------------------------

if len(sys.argv) == 3:
    source_path = sys.argv[1]
    sample_path = sys.argv[2]
else:
    print('Missing source and sample files as arguments');
    sys.exit()

#--------------------------------------------------

print('Load files')

source_series, source_rate = librosa.load(source_path) # The 3 hour file
sample_series, sample_rate = librosa.load(sample_path) # The 1 second file

source_time_total = float(len(source_series) / source_rate);

#--------------------------------------------------

print('Parse Data')

source_data_raw = librosa.amplitude_to_db(abs(librosa.stft(source_series, hop_length=64)))
sample_data_raw = librosa.amplitude_to_db(abs(librosa.stft(sample_series, hop_length=64)))

sample_height = sample_data_raw.shape[0]

#--------------------------------------------------

print('Round Data') # Also switches X and Y indexes, so X becomes time.

def round_data(raw, height):

    length = raw.shape[1]

    data = [];

    range_length = range(1, (length - 1))
    range_height = range(1, (height - 1))

    for x in range_length:

        x_data = []

        for y in range_height:

            # neighbours = []
            # for a in [(x - 1), x, (x + 1)]:
            #     for b in [(y - 1), y, (y + 1)]:
            #         neighbours.append(raw[b][a])
            #
            # neighbours = (sum(neighbours) / len(neighbours));
            #
            # x_data.append(round(((raw[y][x] + raw[y][x] + neighbours) / 3), 2))

            x_data.append(round(raw[y][x], 2))

        data.append(x_data)

    return data

source_data = round_data(source_data_raw, sample_height)
sample_data = round_data(sample_data_raw, sample_height)

#--------------------------------------------------

sample_data = sample_data[50:268] # Temp: Crop the sample_data (318 to 218)

#--------------------------------------------------

source_length = len(source_data)
sample_length = len(sample_data)
sample_height -= 2;

source_timing = float(source_time_total / source_length);

#--------------------------------------------------

print('Process series')

hz_diff_match = 18 # For every comparison, how much of a difference is still considered a match - With the Source, using Sample 2, the maximum diff was 66.06, with an average of ~9.9

hz_match_required_switch = 30 # After matching "start" for X, drop to the lower "end" requirement
hz_match_required_start = 850 # Out of a maximum match value of 1023
hz_match_required_end = 650
hz_match_required = hz_match_required_start

source_start = 0
sample_matched = 0

x = 0;
while x < source_length:

    hz_matched = 0
    for y in range(0, sample_height):
        diff = source_data[x][y] - sample_data[sample_matched][y];
        if diff < 0:
            diff = 0 - diff
        if diff < hz_diff_match:
            hz_matched += 1

    # print('  {} Matches - {} @ {}'.format(sample_matched, hz_matched, (x * source_timing)))

    if hz_matched >= hz_match_required:

        sample_matched += 1

        if sample_matched >= sample_length:

            print('      Found @ {}'.format(source_start * source_timing))

            sample_matched = 0 # Prep for next match

            hz_match_required = hz_match_required_start

        elif sample_matched == 1: # First match, record where we started

            source_start = x;

        if sample_matched > hz_match_required_switch:

            hz_match_required = hz_match_required_end # Go to a weaker match requirement

    elif sample_matched > 0:

        # print('  Reset {} / {} @ {}'.format(sample_matched, hz_matched, (source_start * source_timing)))

        x = source_start # Matched something, so try again with x+1

        sample_matched = 0 # Prep for next match

        hz_match_required = hz_match_required_start

    x += 1

#--------------------------------------------------
0 голосов
/ 06 октября 2018

Это проблема обнаружения аудио-событий.Если звук всегда одинаковый и в то же время нет других звуков, его, вероятно, можно решить с помощью метода сопоставления с шаблоном.По крайней мере, если нет других звуков с другими значениями, которые звучат одинаково.

Самый простой вид сопоставления с шаблоном - это вычисление взаимной корреляции между вашим входным сигналом и шаблоном.

  1. Вырежьте пример звука для обнаружения (используя Audacity).Бери как можно больше, но избегай начала и конца.Сохраните его как файл .wav
  2. Загрузите шаблон .wav с помощью librosa.load ()
  3. Разделите входной файл на ряд пересекающихся кадров.Длина должна соответствовать вашему шаблону.Может быть сделано с помощью librosa.util.frame
  4. Итерировать по кадрам и вычислять взаимную корреляцию между рамкой и шаблоном, используя numpy.correlate .
  5. Высокие значения взаимной корреляции указывают на хорошее совпадение.Порог может быть применен, чтобы решить, что является событием или нет.И номер кадра можно использовать для расчета времени события.

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

Если громкость записей непоследовательна, вы должны нормализовать это перед запуском обнаружения.

Если взаимная корреляция во временной области не работает, вы можете вычислить мельспектрограмму или MFCCособенности и взаимно коррелируют это.Если это также не дает хороших результатов, модель машинного обучения можно обучить с использованием контролируемого обучения, но для этого необходимо пометить группу данных как событие / не-событие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...