Python3正确修改wav音频数据

Posted

技术标签:

【中文标题】Python3正确修改wav音频数据【英文标题】:Python3 modifying wav audio data correctly 【发布时间】:2021-05-09 02:34:04 【问题描述】:

学习如何使用 Python3 修改不同类型的音频文件,.wav.mp3等使用wave 模块。具体.wav文件格式,在这方面针对这个问题。目前,我知道音频格式有 ISO 标准,对于 .wav 文件格式的音频标准以及附注中的任何关于此主题的参考都非常感谢.

但就我的问题而言,只需使用 Python3 wave 忽略 .wav 文件中的 RIFFFMT 标头模块导入。

有没有更有效的方法可以跳过 RIFF 标头、其他容器,直接进入 data 容器修改其内容?

这个粗略的例子只是将双声道音频 .wav 文件转换为单声道音频 .wav 文件,同时将所有值修改为 (0, 0).

import wave
import struct

# Open Files
inf = wave.open(r"piano2.wav", 'rb')
outf = wave.open(r"output.wav", 'wb')

# Input Parameters
ip = list(inf.getparams())
print('Input Parameters:', ip)
# Example Output: Input Parameters: [2, 2, 48000, 302712, 'NONE', 'not compressed']

# Output Parameters
op = ip[:]
op[0] = 1
outf.setparams(op)

number_of_channels, sample_width, frame_rate, number_of_frames, comp_type, comp_name = ip

format = '<h'.format(number_of_channels)
print('# Channels:', format)

# Read >> Second
for index in range(number_of_frames):
    frame = inf.readframes(1)
    data = struct.unpack(format, frame)

    # Here, I change data to (0, 0), testing purposes
    print('Before Audio Data:', data)
    print('After Modifying Audio Data', (0, 0))

    # Change Audio Data
    data = (0, 0)

    value = data[0]
    value = (value * 2) // 3
    outf.writeframes(struct.pack('<h', value))

# Close In File
inf.close()
# Close Out File
outf.close()

如果只是修改.wav文件的数据段,有没有更好的做法或参考资料?

假设您想在特定时间戳添加声音,这将是更适合我的问题的结果。

【问题讨论】:

【参考方案1】:

性能对比

让我们看看前 3 种读取 WAVE 文件的方法。

最慢的一波模块

您可能已经注意到,wave 模块可能非常缓慢。考虑这段代码:

import wave
import struct

wavefile = wave.open('your.wav', 'r') # check e.g. freesound.org for samples

length = wavefile.getnframes()
for i in range(0, length):
    wavedata = wavefile.readframes(1)
    data = struct.unpack("<h", wavedata)

对于如下定义的 WAVE:

Input File     : 'audio.wav'
Channels       : 1
Sample Rate    : 48000
Precision      : 16-bit
Duration       : 00:09:35.71 = 27634080 samples ~ 43178.2 CDDA sectors
File Size      : 55.3M
Bit Rate       : 768k
Sample Encoding: 16-bit Signed Integer PCM

加载完整音频平均需要 27.7 秒。 wave 模块的另一面是开箱即用,适用于任何系统。

方便的 - 音频文件

一个更方便和更快的解决方案是例如audiofile。根据项目描述,它的重点是阅读速度。

import audiofile as af

signal, sampling_rate = af.read(audio.wav)

这给了我平均 33 毫秒的时间来阅读上述文件。

最快的一个——numpy

​​>

如果我们决定跳过标题(如 OP 所要求的)并只追求速度,numpy 是一个不错的选择:

import numpy as np

byte_length = np.fromfile(filename, dtype=np.int32, count=1, offset=40)[0]
data = np.fromfile(filename, dtype=np.int16, count=byte_length // np.dtype(np.int16).itemsize, offset=44)

标头结构(告诉我们要使用什么offset)定义为here。

该代码的执行大约需要 6 毫秒,比 audioread 少 5 倍。自然它带有一个价格/前提条件:我们需要提前知道数据类型是什么。

修改音频

numpy 数组中获得音频后,您可以随意修改它,也可以决定流式传输文件而不是一次读取所有内容。但请注意:由于声音是一种波,在典型情况下,只需在任意时间t 注入新数据就会导致音频失真(除非它是静音)。

至于写回流,“修改容器”在 Python 中会非常慢。这就是为什么您应该使用数组或切换到更合适的语言(例如 C)。

如果我们使用数组,我们应该注意numpy 对 WAVE 格式一无所知,因此我们必须自己定义标头并写入单独的字节。完全可行的运动,但笨重。幸运的是,scipy 提供了一个方便的函数,它具有numpy 速度的优点(它在下面使用numpy),同时使代码更具可读性:

from scipy.io.wavfile import write

fs = np.fromfile('audio.wav', dtype=np.int32, count=1, offset=24)[0] # we need sample rate

with open('audio_out.wav', 'a') as fout:
    new_data = data.append(np.zeros(2 * fs)) # append 2 seconds of zeros
    write(fout, fs, new_data) 

这可以在一个循环中完成,您使用 numpy / scipy 读取一个块,修改数组 (data) 并写入文件(使用 a 追加)。

【讨论】:

我同意您的帖子内容丰富并回答了我的问题。如果您可以发布一个使用带有数组的numpy 模块和audioread 写入字节的示例,我会很高兴的。这是我遇到困难的部分,因为字节类型和格式对 Python 来说是新的。但感谢这篇内容丰富的帖子。只是替换字节一个简单的例子,如果它不是很麻烦的话。如果不担心,感谢您的详细帖子。 谢谢,我得到了你需要的东西。请注意,使用numpy 编写音频很笨重(这篇文章会长 2 倍)。 numpy 对 WAVE 一无所知,这意味着我们已经定义了标题。在这种情况下,我将使用 scipy write,它是 numpy 的包装器,具有所需的功能并适用于 numpy 数组。你怎么看? @Luckasz 我很欣赏你所做的,并将研究 scipy_write。正如我认为你明白了要点,因为它确实变得笨重,这变得令人困惑。但是,尽管如此。感谢一切。是一个很大的帮助和信息丰富的帖子。再次感谢。此外,感谢您提供更快的时间替代方案,这将在未来非常有帮助。我知道您必须为 .wav 文件的容器写一个struct 我希望额外的解释会有所帮助,当然,你可以做你刚才描述的,但我有理由确定这会使你的代码变得缓慢和复杂(也就是说,如果你自己去struct)。 SciPy 在这里应该很棒,并且使用它而不是纯 numpy 不会受到任何惩罚。祝你好运! 你已经超越了,非常感谢!

以上是关于Python3正确修改wav音频数据的主要内容,如果未能解决你的问题,请参考以下文章

这是读取音频文件 FFT 的正确方法吗? (python + wav)

Python3+叠加两个音频文件,实现混音

解交织 PCM (*.wav) 立体声音频数据

音频 .wav 文件的二进制分类

如何在 Swift 中以 wav 格式录制音频?

如何通过 javascript/html5 播放 wav 音频字节数组?