You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从PyAudio提取Numpy数组时出现噪音与失真问题

问题描述

我想用支持回环设备的PyAudio补丁录制电脑音频输出,实现实时处理与响应。目前录制音频到文件时音质正常,但转换为Numpy数组后播放却满是噪音,信号严重失真。

改用麦克风输入简化代码后问题依旧,复现代码如下:

import numpy as np
import pyaudiowpatch as pyaudio
import wave
import sounddevice as sd
from matplotlib import pyplot as plt

FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
CHUNK = 1048
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "file.wav"


p = pyaudio.PyAudio()


stream = p.open(format=FORMAT, channels=CHANNELS,
                    rate=RATE, input=True,
                    frames_per_buffer=CHUNK)
print("recording...")
frames = []
fulldata = np.empty(RATE*RECORD_SECONDS*CHANNELS)

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    print(i)
    data = stream.read(CHUNK)
    numpydata = np.frombuffer(data, dtype='int16')
    fulldata[i*CHUNK*CHANNELS:(i+1)*CHUNK*CHANNELS] = numpydata
    frames.append(data)
print("finished recording")
channel0 = fulldata[0::CHANNELS]

print('playing')
sd.play(channel0, samplerate=RATE, blocking=True)


# stop Recording
stream.stop_stream()
stream.close()
p.terminate()

waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(p.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close()

我试过相关方法,录制的文件音质没问题,但转Numpy数组后播放就异常。已经按通道交替排列提取了单通道,先存文件再用librosa加载播放是正常的。注意到录制采样率是48000,但加载WAV时是22050,不确定是否有关。也怀疑过延迟,但不知道怎么设置。想问下有没有其他转换Numpy数组的方法,或者我漏了什么关键点?

解决方案

问题核心出在数据类型不匹配和采样率不一致两个关键点:

  1. 数据类型转换错误
    你用np.empty创建的fulldata默认是float64类型,而从音频流读取的是int16格式的原始音频数据(范围为[-32768, 32767])。直接将int16数值赋值到浮点数组后,sounddevice会把这些数值当成浮点音频样本(正常范围是[-1.0, 1.0]),远超范围的数值会导致严重削波,最终产生噪音。

  2. 采样率不一致的潜在问题
    你代码里配置的录制采样率是44100,但实际录制是48000、加载文件时是22050,这种参数不匹配会导致播放速度、音调异常,甚至加剧失真。必须确保录制、播放环节的采样率完全统一。

修复步骤:

  • 要么将存储数组初始化为int16类型,和原始音频格式匹配;要么将int16数据归一化到[-1.0, 1.0]的浮点范围(推荐,sounddevice对浮点样本支持更稳定)
  • 核对并统一所有环节的采样率参数

修改后的关键代码片段:

# 方案1:用int16类型存储数组,和原始音频格式匹配
fulldata = np.empty(RATE*RECORD_SECONDS*CHANNELS, dtype=np.int16)

# 方案2:归一化到浮点范围(更推荐)
fulldata = np.empty(RATE*RECORD_SECONDS*CHANNELS, dtype=np.float32)
# 读取后转换并归一化
numpydata = np.frombuffer(data, dtype='int16').astype(np.float32) / 32767.0

另外,提取单通道播放时,务必保证sd.play的samplerate参数和实际录制的采样率一致,比如实际录制是48000,就不能用代码里写的44100。

内容的提问来源于stack exchange,提问作者FerdyG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 16:20:05