Изменить объем wav-файла в python

У меня есть 2-секундный 16-битный одноканальный 8 кГц WAV-файл, и мне нужно изменить его громкость.

Это должно быть довольно просто, потому что изменение громкости аналогично изменению амплитуды сигнала, и мне просто нужно его ослабить, то есть умножить на число от 0 до 1. Но это не работает: новый звук ниже, но ОЧЕНЬ шумный. Что я делаю неправильно?

Вот мой код:

import wave, numpy, struct

# Open
w = wave.open("input.wav","rb")
p = w.getparams()
f = p[3] # number of frames
s = w.readframes(f)
w.close()

# Edit
s = numpy.fromstring(s, numpy.int16) * 5 / 10  # half amplitude
s = struct.pack('h'*len(s), *s)

# Save
w = wave.open("output.wav","wb")
w.setparams(p)
w.writeframes(s)
w.close()

Спасибо вам, ребята!


person Ricard Bou    schedule 11.11.2012    source источник
comment
Почему вы используете * 5 / 10 вместо / 2?   -  person John Dvorak    schedule 11.11.2012
comment
Если бы мне пришлось угадывать, я бы сказал, что часть * 5 обрезается и переполняется.   -  person Nick ODell    schedule 11.11.2012
comment
Вы читаете файл с правильным порядком байтов? WAV-файлы endian. Использование другого endian уменьшит выборку вдвое и добавит МНОГО шума.   -  person John Dvorak    schedule 11.11.2012
comment
О боже...!! Извините за публикацию этого... Ответ был слишком простым. Делая это: s = numpy.fromstring(s, numpy.int16) * 5 / 10 # половина амплитуды Сигнал насыщен, потому что я умножал целые числа перед их делением. Решение: s = numpy.fromstring(s, numpy.int16) / 10 * 5 # половинная амплитуда Будьте осторожны, это НЕ работает, так как деление дает ноль: s = numpy.fromstring(s, numpy.int16) * (5 / 10) # половина амплитуды Я держу это здесь, если это поможет кому-то еще.   -  person Ricard Bou    schedule 11.11.2012
comment
Ян, потому что они должны быть переменными: 5 - желаемый объем, а 10 - исходный объем.   -  person Ricard Bou    schedule 11.11.2012
comment
Ник, ДА!! Ты прав. Спасибо!   -  person Ricard Bou    schedule 11.11.2012
comment
Вы должны опубликовать свой комментарий в качестве ответа и принять его, когда это возможно (+24 часа IIRC)   -  person John Dvorak    schedule 11.11.2012
comment
Спасибо, Ян, но stackoverflow позволяет мне подождать еще 7 часов, прежде чем я смогу опубликовать ответ... :)   -  person Ricard Bou    schedule 11.11.2012
comment
@JanDvorak Поскольку вы обнаружили проблему, было бы справедливо, если бы вы опубликовали ответ, и ОП принял его. Полное решение, вероятно, будет включать умножение на float(desired_volume) / float(orig_volume).   -  person user4815162342    schedule 11.11.2012
comment
@user4815162342 user4815162342 Жаль, что я плохо разбираюсь в python. Я даже не знал, использует ли python типы float для всего.   -  person John Dvorak    schedule 11.11.2012
comment
Еще одна вещь: оператор * в строке struct.pack('h', *s) преобразует весь массив numpy в кортеж объектов int. Это потенциально очень неэффективно — вместо этого вы должны использовать s.tostring, что эффективно создаст строку непосредственно из содержимого массива.   -  person user4815162342    schedule 11.11.2012
comment
Если он масштабируется с плавающей запятой, простое использование tostring даст неверный результат. Но вместо этого вы могли бы сделать s = (s * (desired/original)).astype(numpy.int16).tostring().   -  person Eryk Sun    schedule 11.11.2012


Ответы (4)


Я написал библиотеку, чтобы упростить такие вещи

Вы можете сделать это так:

from pydub import AudioSegment

song = AudioSegment.from_wav("never_gonna_give_you_up.wav")

# reduce volume by 10 dB
song_10_db_quieter = song - 10

# but let's make him *very* quiet
song = song - 36

# save the output
song.export("quieter.wav", "wav")
person Jiaaro    schedule 05.12.2012
comment
Не могли бы вы вместо этого ослабить г-на Астлея на 36 дБ? - person msw; 06.12.2012
comment
@msw уверен, что это будет song = song - 36 - person Jiaaro; 06.12.2012

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

Проблема была немедленно обнаружена Яном Дворжаком («часть * 5 обрезается и переполняется»), и простое решение было следующим:

s = numpy.fromstring(s, numpy.int16) / 10 * 5

В данном случае это решение было идеальным для меня, просто достаточно хорошим.

Спасибо всем!

person Ricard Bou    schedule 25.12.2012

Это можно сделать с помощью модуля audioop из стандартной библиотеки Python. Таким образом, такие зависимости, как pydub или numpy, не нужны.

import wave, audioop

factor = 0.5

with wave.open('input.wav', 'rb') as wav:
    p = wav.getparams()
    with wave.open('output.wav', 'wb') as audio:
        audio.setparams(p)
        frames = wav.readframes(p.nframes)
        audio.writeframesraw( audioop.mul(frames, p.sampwidth, factor))
person dnknth    schedule 10.01.2021
comment
Это в основном то, что pydub делает под капотом - person droptop; 11.02.2021

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

from pydub import AudioSegment

audio_file = "first.mp3"

song = AudioSegment.from_mp3(audio_file)

new = song.low_pass_filter(1000)

new1 = new.high_pass_filter(1000)

# increae volume by 6 dB
song_6_db_quieter = new1 + 6

# save the output
song_6_db_quieter.export("C://Users//User//Desktop//second.mp3", "mp3")
person Community    schedule 05.06.2020