LINUX.ORG.RU

Как порезать аудио файл на куски примерно одного размера с разрезами в местах паузы в речи

 


0

1

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

Попробовал использовать для этого pydub но похоже что он не может работать с большими файлами. 3 часовая запись приводит его к ошибке переполнения памяти.
Небольшие записи режутся но медленно очень.

Может есть еще что-нибудь.

from pydub import AudioSegment 
from pydub.silence import split_on_silence 

def silence_based_conversion(path = "1.wav"):
    chink_size = 50
    output_name = "output"

    # тут стабильно умирает на больших файлах, сжатый звук не помогает
    song = AudioSegment.from_wav(path)

    # это работает медленно даже на не очень больших записях
    chunks = split_on_silence(song,
        min_silence_len = 500,
        silence_thresh = -16
    )

    i = 0
    for chunk in chunks:
....

★★

3 часовая запись приводит его к ошибке переполнения памяти.

Порежьте в audacity на куски по 15 минут

PPP328 ★★★★★
()

ffmpeg наверное такое может, там было что-то типа silencedetect.

да ты и сам можешь читать сэмплы и смотреть на значния, сверяя их с некоторым порогом за определённый период.

anonymous
()

mp3splt, oggsplt, flacsplt - utility for mp3, ogg vorbis and native flac splitting without decoding

trim files using silence detection (see -r option)

sr11
()
rate, data = scipy.io.wavfile.read(path, mmap=True)
song = AudioSegment.from_numpy_array(data, rate)
ei-grad ★★★★★
()

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

ffmpeg -i «input.mov» -af silencedetect=noise=-30dB:d=0.5 -f null - 2> vol.txt

Такой вариант вроде работает. Хотя для варианта с источником на ютубе как то подозрительно быстро отрабатывает.

def find_cut_positions(pauses, desired_chunk_size):
    """
    Находит оптимальные позиции для разрезания аудио на основе пауз и желаемой длины фрагмента.

    Args:
        pauses (list): Список пауз в формате [(начало, конец, длительность), ...].
        desired_chunk_size (float): Желаемая длительность фрагмента в секундах.

    Returns:
        list: Список кортежей (позиция разреза, длительность фрагмента).
    """
    cut_positions = []
    current_position = 0
    total_duration = pauses[-1][1] if pauses else current_position

    while current_position < total_duration:
        # Найдем паузу, наиболее близкую к текущей позиции + desired_chunk_size
        suitable_cut = None
        for start, end, duration in pauses:
            if start >= current_position + desired_chunk_size:
                suitable_cut = (start, end, duration)
                break

        if suitable_cut:
            cut_position = suitable_cut[0]
            chunk_length = cut_position - current_position
        else:
            # Если не нашли подходящей паузы, сделаем разрез принудительно
            cut_position = current_position + desired_chunk_size
            chunk_length = desired_chunk_size

        cut_positions.append((cut_position, chunk_length))
        current_position = cut_position

    cut_positions.insert(0, (0, cut_positions[0][0]))
    return cut_positions


def find_silence_points(audio_file: str) -> list:
    """
    Находит моменты тишины в аудиофайле и возвращает список точек для разрезания на основе этих моментов и желаемой длины фрагмента.

    Args:
        audio_file (str): Путь к аудиофайлу (поддерживаются любые форматы, распознаваемые ffmpeg, и ссылки на YouTube).

    Returns:
        list: Список кортежей (позиция разреза, длительность фрагмента).
    """
    if '/youtu.be/' in audio_file or 'youtube.com/' in audio_file:
        proc = subprocess.run([YT_DLP, '-x', '-g', audio_file], stdout=subprocess.PIPE)
        audio_file = proc.stdout.decode('utf-8', errors='replace').strip()

    proc = subprocess.run([FFMPEG, '-i', audio_file, '-af', 'silencedetect=noise=-30dB:d=0.5', '-f', 'null', '-'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output = proc.stdout.decode('utf-8', errors='replace').strip()
    error = proc.stderr.decode('utf-8', errors='replace').strip()


    pattern_start = re.compile(r'silence_start:\s+(\d+\.\d+)')
    pattern_end_duration = re.compile(r'silence_end:\s+(\d+\.\d+)\s+\|\s+silence_duration:\s+(\d+\.\d+)')
    
    silences = []
    current_start = None

    for line in error.splitlines():
        start_match = pattern_start.search(line)
        end_duration_match = pattern_end_duration.search(line)

        if start_match:
            current_start = float(start_match.group(1))
        elif end_duration_match and current_start is not None:
            end = float(end_duration_match.group(1))
            duration = float(end_duration_match.group(2))
            silences.append((current_start, end, duration))
            current_start = None

    return find_cut_positions(silences, 50)


if __name__ == '__main__':
    pass
    # print(find_silence_points('1.opus'))
    print(find_silence_points('https://www.youtube.com/watch?v=HqQOpmv1How'))

theurs ★★
() автор топика
Ответ на: комментарий от theurs

Да, у тебя вот такие условия:

for start, end, duration in pauses:
    if start >= current_position + desired_chunk_size:
        suitable_cut = (start, end, duration)
        break
...
if suitable_cut:
    ...
else:
    # Если не нашли подходящей паузы, сделаем разрез принудительно

То есть сперва ты перебираешь все паузы и ищешь ту, которая находится на желаемом месте разреза, либо дальше его. А потом, почему-то, пишешь else в следующем условии, проверяющем нашлась ли пауза (вообще из всех). Такое условие (else) может выполнится лишь в самом конце аудиозаписи, когда после перебора всех пауз окажется, что их расположение не дотягивает до того, чтобы сделать на их месте разрез.

Получается, что все куски кроме последнего у тебя будут больше, чем desired_chunk_size.

В моём скрипте тоже могут быть куски больше указанной длительности, но только тогда, когда в таком куске нет пауз. У меня для разреза берётся не та пауза, которая >= desired_chunk_size, а предыдущая, если размер куска уже перерос desired_chunk_size.

anonymous
()
Ответ на: комментарий от theurs
$ yt-dlp -x --audio-format wav 'https://www.youtube.com/watch?v=HqQOpmv1How' -o krasnodar.wav
...
$ time ./silence.py krasnodar.wav -l 50 --simulate
Saving "./00000.wav", 49.579 s ... done.
Saving "./00001.wav", 47.193 s ... done.
Saving "./00002.wav", 48.570 s ... done.
Saving "./00003.wav", 49.951 s ... done.
Saving "./00004.wav", 28.120 s ... done.
Saving "./00005.wav", 43.222 s ... done.
Saving "./00006.wav", 48.798 s ... done.
Saving "./00007.wav", 30.042 s ... done.
Saving "./00008.wav", 49.883 s ... done.
Saving "./00009.wav", 46.971 s ... done.
Saving "./00010.wav", 48.236 s ... done.
Saving "./00011.wav", 48.616 s ... done.
Saving "./00012.wav", 46.304 s ... done.
Saving "./00013.wav", 33.291 s ... done.

real	0m0.172s
user	0m0.150s
sys	0m0.021s
anonymous
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.