63 lines
1.9 KiB
Python
63 lines
1.9 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""对比 A/B 两版的双耳空间分离度。
|
||
|
|
|
||
|
|
指标: 左右声道去均值后的相关系数。
|
||
|
|
接近 1.0 = 两耳听到的几乎一样 -> 声音"钉在头中间"(普通耳机听 5.1 的听感)
|
||
|
|
明显偏低 = 两耳有差异 -> 携带了方位信息(HRTF 起了作用)
|
||
|
|
"""
|
||
|
|
import os
|
||
|
|
import struct
|
||
|
|
import wave
|
||
|
|
|
||
|
|
|
||
|
|
def read_stereo(path: str) -> tuple[list[float], list[float]]:
|
||
|
|
"""读 wav 的前两个声道。"""
|
||
|
|
with wave.open(path) as w:
|
||
|
|
channels = w.getnchannels()
|
||
|
|
raw = w.readframes(w.getnframes())
|
||
|
|
frames = struct.unpack("<%dh" % (len(raw) // 2), raw)
|
||
|
|
left: list[float] = []
|
||
|
|
right: list[float] = []
|
||
|
|
for i in range(0, len(frames) - channels + 1, channels):
|
||
|
|
left.append(float(frames[i]))
|
||
|
|
right.append(float(frames[i + 1]) if channels > 1 else float(frames[i]))
|
||
|
|
return left, right
|
||
|
|
|
||
|
|
|
||
|
|
def centered(xs: list[float]) -> list[float]:
|
||
|
|
"""去均值。"""
|
||
|
|
if not xs:
|
||
|
|
return []
|
||
|
|
mean = sum(xs) / len(xs)
|
||
|
|
return [x - mean for x in xs]
|
||
|
|
|
||
|
|
|
||
|
|
def correlation(a: list[float], b: list[float]) -> float:
|
||
|
|
"""皮尔逊相关系数。"""
|
||
|
|
num = sum(x * y for x, y in zip(a, b))
|
||
|
|
da = sum(x * x for x in a) ** 0.5
|
||
|
|
db = sum(y * y for y in b) ** 0.5
|
||
|
|
return num / (da * db) if da and db else 0.0
|
||
|
|
|
||
|
|
|
||
|
|
def rms(xs: list[float]) -> float:
|
||
|
|
"""均方根。"""
|
||
|
|
return (sum(x * x for x in xs) / len(xs)) ** 0.5 if xs else 0.0
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||
|
|
print("%-22s %10s %10s %10s" % ("文件", "相关性", "L 均方根", "R 均方根"))
|
||
|
|
print("-" * 58)
|
||
|
|
for name in ("A-普通下混.wav", "B-HRTF环绕.wav"):
|
||
|
|
if not os.path.isfile(name):
|
||
|
|
continue
|
||
|
|
left, right = read_stereo(name)
|
||
|
|
print("%-22s %10.3f %10.1f %10.1f"
|
||
|
|
% (name, correlation(centered(left), centered(right)), rms(left), rms(right)))
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
raise SystemExit(main())
|