Python3 正确修改wav音频数据

Python3 modifying wav audio data correctly

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

但就我的问题而言,只需忽略 RIFFFMT headers,在 [=28= .wav 文件使用 Python3 wave 模块导入。

有没有更高效的方式跳过RIFFheaders,其他容器,直接进入data容器修改其内容?

这个粗略的例子只是将 two-channel 音频 .wav 文件转换为 single-channel 音频 .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文件的数据段,是否有更好的做法或参考material?

性能比较

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

最慢的一波模块

正如您可能已经注意到的那样,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

如果我们决定跳过 header(如 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)

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

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

修改音频

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

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

如果我们使用数组,我们应该注意 numpy 对 WAVE 格式一无所知,因此我们必须自己定义 header 并写入单独的字节。完全可行的练习,但笨重。幸运的是,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 for追加)。