Pydub глюки звука при разделении / присоединении mp3

Я экспериментирую с pydub, который мне очень нравится, однако у меня возникла проблема при разбиении / присоединении файла mp3.

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

В своем доказательстве кода концепции я взял один большой mp3 и разделил его на 1-секундные куски следующим образом:

song = AudioSegment.from_mp3('my.mp3')
song_pos = 0
while song_pos < 100:
    p1 = song_pos * 1000
    p2 = p1 + 1000

    segment = song[p1:p2] # 1 second of audio

    output = StringIO.StringIO()
    segment.export(output, format="mp3")
    client_data = output.getvalue() # send this to client

    song_pos += 1

Значения client_data передаются в браузер через долгоживущее http-соединение:

socket.send("HTTP/1.1 200 OK\r\nConnection: Keep-Alive\r\nContent-Type: audio/mp3\r\n\r\n")

а затем для каждого нового куска аудио

socket.send(client_data)

Может кто-нибудь объяснить глюки, которые я слышу, и предложить способ их устранения?

3 ответа

Решение

Обновление моего комментария до ответа:

Основная проблема заключается в том, что кодеки MP3, используемые ffmpeg, добавляют тишину к концу закодированного звука (и ваш подход заключается в создании нескольких отдельных аудиофайлов).

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

У меня нет окончательного объяснения слышимых артефактов, которые вы слышите, но может случиться так, что вы разделяете звук в точке, где сигнал не равен нулю. Если звук начинается с сэмпла со значением 100 (например), это будет звучать как звук с цифровым треском. Сжатие MP3 также может изменить звук, особенно на более низких скоростях. Если это проблема, появление 1 мс устранит всплеск без заметного слышимого "исчезновения" (хотя потенциально может привести к появлению других артефактов) - более длительное появление (например, 20 или 50 мс позволит избежать странных артефактов в частотной области, но приведет к заметному "исчезать".

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

Вероятно, лучший подход, если это возможно:

Кодируйте весь сигнал как один сжатый файл и отправьте байты (этого одного файла) клиенту порциями для воспроизведения в виде единого потока. Если вы используете постоянное битрейтное кодирование mp3 (CBR), вы можете почти идеально отправлять фрагменты длиной в 1 секунду, просто считая байты. Например, с CBR 256 Кбит / с, просто отправьте 256 КБ за раз.

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

p2 = p1 + 1001

но вам может понадобиться инвертировать процесс объединения, чтобы он работал. Если только вы не добавите дополнительную миллисекунду в конце.

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

Звук - это форма волны, и вы соединяете две волны, которые не совпадают по фазе; так что вы получаете функцию шага, и это делает поп.

Я не знаком с этим программным обеспечением, но кодифицируя предложения Нильса Вернера, вы можете попробовать:

song = AudioSegment.from_mp3('my.mp3')
song_pos = 0

# begin with a millisecond of blank
segment = AudioSegment.silent(duration=1)
# append all your pieces to it
while song_pos < 100:
    p1 = song_pos * 1000
    p2 = p1 + 1000
    #append an item to your segment with several milliseconds of crossfade
    segment.append(song[p1:p2], crossfade=50)
    song_pos += 1

# then pass it on to your client outside of your loop
output = StringIO.StringIO()
segment.export(output, format="mp3")
client_data = output.getvalue() # send this to client

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

Другие вопросы по тегам