在Jetson Nano B01 SUB上实现了3D音频的输出。

软件方案为:python3.9+pyaudio或pyglet。

硬件方案为:立体声耳机+USB声卡(用于耳机3.5mm美标线转接Jeston Nano的USB口)

基于pyglet实现的3D音频可以应用于实时CV图像处理的输出。见:

【开源/在PC和Jeston上实现YOLO+SGBM+3D音频】:高实时性双目摄像机目标检测+深度测量+音频输出程序及硬件搭配方案源码分享(可运行于PC和Jeston Nano B01上)

硬件注意事项

Jetson Nano B01 SUB的优点:

  1. 好买,官方版经常断货,只有板子没有开发套件
  2. 功耗低(移动电源/microUSB模式5W, DC5V/4A的圆口供电是可开启MaxN,约10W)
  3. Jetson家族最便宜的(开发套件¥1200)

但是它的性能也是最低的(只有0.5TOPS),而且Jetpack5以上不支持,意味着cuda版本不能超过10, 导致和pytorch可能配合不好

如果要运行AI,建议加价1000元购买Jetson Orin Nano,或者其它更近期的Jetson产品。

详见:【Jeston点亮教程】Jeston nano B01 SUB(国产版)新手注意事项(开机、兼容性等)

如果用蓝牙模块连接,需注意蓝牙输出会不会丢失立体声信息。

选择USB声卡时,注意有些声卡可能把立体声混响为单声道输出。

pyglet介绍及源码分享

pyglet是一个基于OpenAL的游戏级多媒体播放库,支持3D音频。

它的功能也可以用pyopenal实现,见:pyopenal官方文档

pyglet的Github页

pyglet的官方文档

注意:3D音频播放的wav文件必须是单声道的。播放的source需要对应listener。需要创建window!

HRTF可以提升3D感,建议打开;

在windows上,可能没有OpenAL驱动,需要去下载,官方下载地址

最简单的方式:下载soft_oal.dll,放在代码同目录。(方便linux找到)

把soft_oal.dll改名OpenAL32.dll复制一份,放在代码同目录。(方便Windows找到)

import os
# 打开 HRTF(必须在 import pyglet 前设置)
os.environ["ALSOFT_HRTF"] = "1"
import pyglet
import math
pyglet.options['audio'] = ('openal',)
print(pyglet.media.get_audio_driver())
window = pyglet.window.Window(visible=False)

source = pyglet.media.load("sounds/test_mono.wav", streaming=False)

listener = pyglet.media.get_audio_driver().get_listener()
listener.position = (0, 0, 0)
listener.forward_orientation = (0, 0, -1)
listener.up_orientation = (0, 1, 0)


player = pyglet.media.Player()
player.queue(source)
player.loop = True
player.play()

radius = 10.0
angle = 0.0
speed = 1.0

def update(dt):
    global angle
    angle += speed * dt
    x = radius * math.cos(angle)
    y = radius * math.sin(angle)
    player.position = (x, y, 0)
    print(f"Sound at ({x:.2f}, {y:.2f})")

pyglet.clock.schedule_interval(update, 0.05)
def stop(dt):
    print("Stopping pyglet app...")
    pyglet.app.exit()

pyglet.clock.schedule_once(stop, 10)
pyglet.app.run()

Pyaudio版本测试代码

可以用这个基于pyaudio的代码测试你的耳机有没有立体声效果。

import pyaudio
import wave
import numpy as np
import math

# 配置
WAV_FILE = "test_mono.wav"  # 单声道 WAV 文件
CHUNK = 1024
RADIUS = 5.0  # 声音绕圈半径
SPEED = 2.0  # 绕圈速度
MAX_DISTANCE = RADIUS  # 音量衰减最大距离

# 打开 WAV
wf = wave.open(WAV_FILE, "rb")
if wf.getnchannels() != 1:
    raise ValueError("请使用单声道 WAV 文件!")

p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=2,  # 输出双声道
                rate=wf.getframerate(),
                output=True)

def get_stereo_chunk(samples, x, y):
    """根据x,y坐标计算左右声道音量"""
    distance = math.sqrt(x ** 2 + y ** 2)
    volume = max(0.1, 1 - distance / MAX_DISTANCE)

    # 左右声道平滑过渡
    pan = max(-1, min(1, x / MAX_DISTANCE))
    left = math.cos((pan + 1) * math.pi / 4) * volume
    right = math.sin((pan + 1) * math.pi / 4) * volume

    stereo = np.zeros((len(samples), 2), dtype=np.int16)
    stereo[:, 0] = (samples * left).astype(np.int16)
    stereo[:, 1] = (samples * right).astype(np.int16)
    return stereo.flatten()

# 初始化绕圈角度
angle = 0.0

# 读取数据并循环播放
data = wf.readframes(CHUNK)
while data:
    samples = np.frombuffer(data, dtype=np.int16)

    # 计算当前位置
    x = RADIUS * math.cos(angle)
    y = RADIUS * math.sin(angle)
    angle += SPEED * CHUNK / wf.getframerate()

    # 转换为左右声道
    stereo_chunk = get_stereo_chunk(samples, x, y)
    stream.write(stereo_chunk.tobytes())

    data = wf.readframes(CHUNK)
    if len(data) < CHUNK * wf.getsampwidth():
        # 循环播放
        wf.rewind()
        data = wf.readframes(CHUNK)

# 关闭流
stream.stop_stream()
stream.close()
p.terminate()

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐