会员登录 立即注册

搜索

分析验证双耳节拍的存在

[复制链接]
崎山小鹿 发表于 2 小时前 | 显示全部楼层 |阅读模式
崎山小鹿
2 小时前 5 0 看全部
验证方法是:

读取 piano_binaural_10hz.wav
截取中间一段,例如 10 秒附近的 2 秒
分别分析左声道和右声道
对信号加 Hann 窗
做 FFT
绘制 150–250 Hz 局部频谱
在图中明确标出 200 Hz 和 210 Hz
同时打印 200 Hz、210 Hz 附近的幅度
最后比较左右声道:
左声道应该重点看到 200 Hz
右声道应该重点看到 210 Hz

import numpy as np
import soundfile as sf
import matplotlib.pyplot as plt


# ============================================================
# 1. 参数设置
# ============================================================

# 我们要验证的 WAV 文件
audio_file = "piano_binaural_10hz.wav"


# ------------------------------------------------------------
# FFT 分析开始时间
# ------------------------------------------------------------
#
# 不建议从 0 秒开始。
#
# 因为我们之前给双耳信号设置了 3 秒淡入,
# 所以这里从 10 秒开始分析比较合适。
#
start_time = 10.0


# ------------------------------------------------------------
# FFT 分析时长
# ------------------------------------------------------------
#
# 这里使用 2 秒。
#
# 分析时间越长:
#
#     FFT 频率分辨率越高
#
# 例如:
#
#     Fs = 44100
#     T  = 2 秒
#
#     N = 88200
#
#     Δf = Fs / N
#        = 44100 / 88200
#        = 0.5 Hz
#
# 这样就可以比较清楚地区分:
#
#     200 Hz
#     210 Hz
#
analysis_duration = 2.0


# ------------------------------------------------------------
# 重点观察的频率范围
# ------------------------------------------------------------
#
# 我们不看整个频谱。
#
# 只观察:
#
#     150 Hz ~ 250 Hz
#
# 这样 200 Hz / 210 Hz 会非常明显。
#
min_freq = 150
max_freq = 250


# ============================================================
# 2. 读取 WAV
# ============================================================

audio, sample_rate = sf.read(
    audio_file
)


print("========================================")
print("音频基本信息")
print("========================================")

print(
    "采样率:",
    sample_rate,
    "Hz"
)

print(
    "音频数据形状:",
    audio.shape
)


# ============================================================
# 3. 检查是否为立体声
# ============================================================

if audio.ndim != 2:

    raise ValueError(
        "这个 WAV 文件不是立体声文件。"
    )


if audio.shape[1] != 2:

    raise ValueError(
        "这个 WAV 文件必须包含左右两个声道。"
    )


print(
    "声道数量:",
    audio.shape[1]
)


# ============================================================
# 4. 计算音频总时长
# ============================================================

total_samples = len(audio)

duration = (
    total_samples
    / sample_rate
)


print(
    f"音频时长: {duration:.2f} 秒"
)


# ============================================================
# 5. 检查分析时间
# ============================================================

if start_time >= duration:

    raise ValueError(
        "start_time 超过了音频总长度"
    )


# ============================================================
# 6. 截取分析片段
# ============================================================

start_sample = int(
    start_time
    * sample_rate
)

end_sample = int(
    (start_time + analysis_duration)
    * sample_rate
)

end_sample = min(
    end_sample,
    total_samples
)


segment = audio[
    start_sample:end_sample
]


actual_duration = (
    len(segment)
    / sample_rate
)


print()
print("========================================")
print("FFT 分析参数")
print("========================================")

print(
    "开始时间:",
    start_time,
    "秒"
)

print(
    "分析时间:",
    actual_duration,
    "秒"
)

print(
    "分析采样点:",
    len(segment)
)


# ============================================================
# 7. 分离左右声道
# ============================================================

# 左声道
left_signal = segment[:, 0]


# 右声道
right_signal = segment[:, 1]


# ============================================================
# 8. 对左右声道加 Hann 窗
# ============================================================

# 为什么要加 Hann 窗?
#
# 我们截取的是一段有限长度的音频。
#
# 如果直接截断:
#
#     原始信号 ──────────────┐
#                           │
#                           └── 突然变成 0
#
# FFT 会产生比较严重的频谱泄漏。
#
# Hann 窗可以降低这个问题。
#
window = np.hanning(
    len(left_signal)
)


left_windowed = (
    left_signal
    * window
)

right_windowed = (
    right_signal
    * window
)


# ============================================================
# 9. FFT
# ============================================================

left_fft = np.fft.rfft(
    left_windowed
)

right_fft = np.fft.rfft(
    right_windowed
)


# ============================================================
# 10. 计算频率轴
# ============================================================

freqs = np.fft.rfftfreq(
    len(left_windowed),
    d=1 / sample_rate
)


# ============================================================
# 11. 计算 FFT 幅度
# ============================================================

left_magnitude = np.abs(
    left_fft
)

right_magnitude = np.abs(
    right_fft
)


# ============================================================
# 12. 转换为 dB
# ============================================================

# 使用各自声道的最大值进行归一化。
#
# 这样主要是为了方便比较局部频谱。
#
left_magnitude = (
    left_magnitude
    / (left_magnitude.max() + 1e-12)
)

right_magnitude = (
    right_magnitude
    / (right_magnitude.max() + 1e-12)
)


left_db = (
    20
    * np.log10(
        left_magnitude + 1e-12
    )
)

right_db = (
    20
    * np.log10(
        right_magnitude + 1e-12
    )
)


# ============================================================
# 13. 只提取 150 ~ 250 Hz
# ============================================================

freq_mask = (
    (freqs >= min_freq)
    &
    (freqs <= max_freq)
)


display_freqs = freqs[
    freq_mask
]

display_left_db = left_db[
    freq_mask
]

display_right_db = right_db[
    freq_mask
]


# ============================================================
# 14. 找到最接近 200 Hz 的 FFT 点
# ============================================================

target_200_index = np.argmin(
    np.abs(freqs - 200)
)

target_210_index = np.argmin(
    np.abs(freqs - 210)
)


# 实际 FFT 频率
actual_200_freq = freqs[
    target_200_index
]

actual_210_freq = freqs[
    target_210_index
]


# ============================================================
# 15. 打印 200 Hz / 210 Hz 的结果
# ============================================================

print()
print("========================================")
print("200 Hz / 210 Hz 检测结果")
print("========================================")

print()
print(
    f"目标 200 Hz"
)

print(
    f"实际 FFT 频率: "
    f"{actual_200_freq:.2f} Hz"
)

print(
    f"左声道幅度: "
    f"{left_db[target_200_index]:.2f} dB"
)

print(
    f"右声道幅度: "
    f"{right_db[target_200_index]:.2f} dB"
)


print()
print(
    f"目标 210 Hz"
)

print(
    f"实际 FFT 频率: "
    f"{actual_210_freq:.2f} Hz"
)

print(
    f"左声道幅度: "
    f"{left_db[target_210_index]:.2f} dB"
)

print(
    f"右声道幅度: "
    f"{right_db[target_210_index]:.2f} dB"
)


# ============================================================
# 16. 绘制左右声道频谱
# ============================================================

plt.figure(
    figsize=(14, 7)
)


# ------------------------------------------------------------
# 左声道
# ------------------------------------------------------------

plt.plot(
    display_freqs,
    display_left_db,
    label="Left channel"
)


# ------------------------------------------------------------
# 右声道
# ------------------------------------------------------------

plt.plot(
    display_freqs,
    display_right_db,
    label="Right channel"
)


# ------------------------------------------------------------
# 标记 200 Hz
# ------------------------------------------------------------

plt.axvline(
    200,
    linestyle="--",
    label="200 Hz"
)


# ------------------------------------------------------------
# 标记 210 Hz
# ------------------------------------------------------------

plt.axvline(
    210,
    linestyle="--",
    label="210 Hz"
)


# ------------------------------------------------------------
# 图形设置
# ------------------------------------------------------------

plt.xlabel(
    "Frequency (Hz)"
)

plt.ylabel(
    "Magnitude (dB)"
)

plt.title(
    "Piano + 10 Hz Binaural Beat: 150–250 Hz Spectrum"
)

plt.xlim(
    min_freq,
    max_freq
)

plt.ylim(
    -80,
    5
)

plt.grid(
    True,
    alpha=0.3
)

plt.legend()

plt.tight_layout()

plt.show()


# ============================================================
# 17. 单独放大左声道
# ============================================================

plt.figure(
    figsize=(14, 6)
)

plt.plot(
    display_freqs,
    display_left_db
)


# 200 Hz 参考线
plt.axvline(
    200,
    linestyle="--",
    label="Target 200 Hz"
)


# 210 Hz 参考线
plt.axvline(
    210,
    linestyle="--",
    label="Target 210 Hz"
)


plt.xlabel(
    "Frequency (Hz)"
)

plt.ylabel(
    "Magnitude (dB)"
)

plt.title(
    "Left Channel: 200 Hz Verification"
)

plt.xlim(
    150,
    250
)

plt.ylim(
    -80,
    5
)

plt.grid(
    True,
    alpha=0.3
)

plt.legend()

plt.tight_layout()

plt.show()


# ============================================================
# 18. 单独放大右声道
# ============================================================

plt.figure(
    figsize=(14, 6)
)

plt.plot(
    display_freqs,
    display_right_db
)


# 200 Hz
plt.axvline(
    200,
    linestyle="--",
    label="200 Hz"
)


# 210 Hz
plt.axvline(
    210,
    linestyle="--",
    label="Target 210 Hz"
)


plt.xlabel(
    "Frequency (Hz)"
)

plt.ylabel(
    "Magnitude (dB)"
)

plt.title(
    "Right Channel: 210 Hz Verification"
)

plt.xlim(
    150,
    250
)

plt.ylim(
    -80,
    5
)

plt.grid(
    True,
    alpha=0.3
)

plt.legend()

plt.tight_layout()

plt.show()


print()
print("========================================")
print("FFT 分析完成")
print("========================================")

分析结果如下:
在左声道的200Hz处出现波峰,在右声道的210Hz处出现波峰
ScreenShot_2026-08-26_133357_221.png
天不生墨翟,万古如长夜!以墨运商,以商助墨。金双石科技长期招聘科技研发人才!微信:qishanxiaolu   电话:15876572365   公司:深圳市金双石科技有限公司
回复

使用道具 举报

  • 您可能感兴趣
您需要登录后才可以回帖 登录 | 立即注册 |

本版积分规则 返回列表

管理员给TA私信
以墨运商,以商助墨。

查看:5 | 回复:0

  • 分析验证双耳节拍的存在

    验证方法是: 读取 piano_binaural_10hz.wav 截取中间一段,例如 10 秒附近的 2 秒

    阅读:0|2026-08-26
  • 不同的乐器他们的频谱有什么不同2

    使用 FFmpeg 1. 安装 Python 库 pip install numpy soundfile matplotlib 同时电脑需

    阅读:0|2026-08-26
  • USBTreeView

    Windows的设备管理器中可以查看USB设备的信息: 驱动程序供应商:FTDI, 数字签名者:

    阅读:2137|2026-05-23
  • 电子电路仿真网站

    https://velxio.dev/ Arduino、ESP32 和 Raspberry Pi。 直接在您的浏览器中即可使

    阅读:2290|2026-05-19
  • ESP32 板子为什么识别为 CP2102

    你的ESP32开发板被电脑识别为“CP2102”,这并不是一个错误,而是完全正常的现象。这

    阅读:2669|2026-05-09
  • 固件如何加密

    做固件加密,本质上是防止别人读取或复制你的程序。常见做法分为“芯片级保护 + 软件

    阅读:2768|2026-04-06
  • 高端神经反馈脑波检测系统

    高端神经反馈脑波检测系统分为脑波检测、脑波分析、脑波解码三个部分。 前端主程序

    阅读:17|2026-01-06
  • 国内第三方量化平台有哪些有什么特点

    好的,我们来详细了解一下国内主流的第三方量化平台。这些平台极大地降低了个人投资者

    阅读:4779|2025-12-09
  • 为什么脑波要去除伪迹

    1. 伪迹不是大脑信号 EEG设备记录的是头皮上非常微弱的电位变化(微伏级别,μV)。

    阅读:24|2025-12-05
  • 便携可视化语音AI耳机

    项目起源: 初先生的聊天记录: 你那边掌握的技术,有办法做一个有摄像头的,墨者机

    阅读:3181|2025-12-01
金双石科技,软件开发20年,技术行业领先,您的满意,就是我们的目标,认真负责,开拓进取,让成品物超所值
关于我们
公司简介
发展历程
联系我们
本站站务
友情链接
新手指南
内容审核
商家合作
广告合作
商家入驻
新闻合作

手机APP

官方微博

官方微信

联系电话:15876572365 地址:深圳市宝安区西乡街道宝民二路宝民花园 ( 粤ICP备2021100124号-1 ) 邮箱:qishanxiaolu@qq.com
QQ|Powered by Discuz! X3.5 © 2001-2026 Discuz! Team.
快速回复 返回顶部 返回列表