gpt4 book ai didi

python - 解包单 channel 波形数据并将其存储在数组中

转载 作者:行者123 更新时间:2023-12-03 00:16:42 25 4
gpt4 key购买 nike

我正在尝试使用 struct.unpack 从单 channel WAVE 文件中解压缩数据.我想将数据存储在一个数组中并能够对其进行操作(例如通过添加给定方差的噪声)。我提取了标题数据并将其存储在字典中,如下所示:

stHeaderFields['ChunkSize'] = struct.unpack('<L', bufHeader[4:8])[0]
stHeaderFields['Format'] = bufHeader[8:12]
stHeaderFields['Subchunk1Size'] = struct.unpack('<L', bufHeader[16:20])[0]
stHeaderFields['AudioFormat'] = struct.unpack('<H', bufHeader[20:22])[0]
stHeaderFields['NumChannels'] = struct.unpack('<H', bufHeader[22:24])[0]
stHeaderFields['SampleRate'] = struct.unpack('<L', bufHeader[24:28])[0]
stHeaderFields['ByteRate'] = struct.unpack('<L', bufHeader[28:32])[0]
stHeaderFields['BlockAlign'] = struct.unpack('<H', bufHeader[32:34])[0]
stHeaderFields['BitsPerSample'] = struct.unpack('<H', bufHeader[34:36])[0]

当我传入一个文件时,我得到以下输出:
NumChannels: 1
ChunkSize: 78476
BloackAlign: 0
Filename: foo.wav
ByteRate: 32000
BlockAlign: 2
AudioFormat: 1
SampleRate: 16000
BitsPerSample: 16
Format: WAVE
Subchunk1Size: 16

然后我尝试通过执行 struct.unpack('<h', self.bufHeader[36:])[0] 来获取数据但是这样做会返回一个简单的整数值 24932 .我不允许使用 wave图书馆或任何其他与波浪有关的东西,因为我必须将其适应其他类型的信号。如何存储和操作实际的波浪数据?

编辑:
while chunk_reader < stHeaderFields['ChunkSize']:
data.append(struct.unpack('<H', bufHeader[chunk_reader:chunk_reader+stHeaderFields['BlockAlign']]))

最佳答案

好的,我会尝试写一个完整的演练。

首先,将 WAV(或更可能是 RIFF)文件视为线性结构是一个常见的错误。它实际上是一棵树,每个元素都有一个 4 字节标记、4 字节长度的数据和/或子元素,以及内部的某种数据。

WAV 文件通常只有两个子元素('fmt' 和 'data'),但它也可能包含带有一些子元素('INAM'、'IART'、'ICMT')的元数据('LIST')等)或其他一些元素。此外,对于 block 没有实际的顺序要求,因此认为“数据”跟随“fmt”是不正确的,因为元数据可能会夹在两者之间。

那么让我们看一下RIFF文件:

'RIFF'
|-- file type ('WAVE')
|-- 'fmt '
| |-- AudioFormat
| |-- NumChannels
| |-- ...
| L_ BitsPerSample
|-- 'LIST' (optional)
| |-- ... (other tags)
| L_ ... (other tags)
L_ 'data'
|-- sample 1 for channel 1
|-- ...
|-- sample 1 for channel N
|-- sample 2 for channel 1
|-- ...
|-- sample 2 for channel N
L_ ...

那么,您应该如何读取 WAV 文件呢?好吧,首先你需要从文件的开头读取 4 个字节并确保它是 RIFFRIFX标记,否则它不是有效的 RIFF 文件。 RIFF 之间的区别和 RIFX是前者使用小端编码(并且在所有地方都支持),而后者使用大端编码(几乎没有人支持它)。为简单起见,假设我们只处理 little-endian RIFF 文件。

接下来,您读取根元素长度(以文件字节顺序)和以下文件类型。如果文件类型不是 WAVE ,它不是 WAV 文件,因此您可能会放弃进一步处理。读取根元素后,您开始读取所有子元素并处理您感兴趣的那些。

阅读 fmt header 非常简单,您实际上已经在代码中完成了它。

数据样本通常表示为 1、2、3 或 4 个字节(同样,在文件字节序中)。最常见的格式是所谓的 s16_le (您可能已经在 ffmpeg 等一些音频处理实用程序中看到过这样的命名),这意味着样本以小端序的有符号 16 位整数呈现。其他可能的格式是 u8 (8 位样本是无符号数!), s24_le , s32_le .数据样本是交错的,因此即使对于多声道音频,也很容易找到流中的任意位置。注意:这仅对未压缩的 WAV 文件有效,如 AudioFormat == 1 所示。对于其他格式,数据样本可能有其他布局。

那么让我们来看一个简单的WAV阅读器:
stHeaderFields = dict()
rawData = None

with open("file.wav", "rb") as f:
riffTag = f.read(4)
if riffTag != 'RIFF':
print 'not a valid RIFF file'
exit(1)

riffLength = struct.unpack('<L', f.read(4))[0]
riffType = f.read(4)
if riffType != 'WAVE':
print 'not a WAV file'
exit(1)

# now read children
while f.tell() < 8 + riffLength:
tag = f.read(4)
length = struct.unpack('<L', f.read(4))[0]

if tag == 'fmt ': # format element
fmtData = f.read(length)
fmt, numChannels, sampleRate, byteRate, blockAlign, bitsPerSample = struct.unpack('<HHLLHH', fmtData)
stHeaderFields['AudioFormat'] = fmt
stHeaderFields['NumChannels'] = numChannels
stHeaderFields['SampleRate'] = sampleRate
stHeaderFields['ByteRate'] = byteRate
stHeaderFields['BlockAlign'] = blockAlign
stHeaderFields['BitsPerSample'] = bitsPerSample

elif tag == 'data': # data element
rawData = f.read(length)

else: # some other element, just skip it
f.seek(length, 1)

现在我们知道了文件格式信息及其样本数据,因此我们可以对其进行解析。如前所述,样本可能有任何大小,但现在让我们假设我们只处理 16 位样本:
blockAlign = stHeaderFields['BlockAlign']
numChannels = stHeaderFields['NumChannels']

# some sanity checks
assert(stHeaderFields['BitsPerSample'] == 16)
assert(numChannels * stHeaderFields['BitsPerSample'] == blockAlign * 8)

for offset in range(0, len(rawData), blockAlign):
samples = struct.unpack('<' + 'h' * numChannels, rawData[offset:offset+blockAlign])

# now samples contains a tuple with sample values for each channel
# (in case of mono audio, you'll have a tuple with just one element).
# you may store it in the array for future processing,
# change and immediately write to another stream, whatever.

因此,现在您拥有 rawData 中的所有样本,您可以随意访问和修改它。使用 Python 的 array() 可能会很方便有效地访问和修改数据(但在 24 位音频的情况下不会这样做,您需要编写自己的序列化和反序列化)。

完成数据处理后(可能涉及放大或缩小每个样本的位数、更改 channel 数、声级操作等),您只需编写一个新的 RIFF具有正确数据长度的 header (通常可以使用简化公式 36 + len(rawData) 计算),更改后的 fmt标题和 data溪流。

希望这可以帮助。

关于python - 解包单 channel 波形数据并将其存储在数组中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27370180/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com