Chapter 109 · 音频系统

音频初始化与全局控制

AudioInit 启动音频子系统,SetVolume 控制主音量,ListenerSet 设置 3D 听觉位置。

109

概念

XGE 音频系统基于 OpenAL 封装,支持音效(Sound)、音乐(Music)、流式音频(Stream)三种播放模式。 音频系统独立于渲染,需要在 xgeInit 之后显式调用 AudioInit 启动。 支持 3D 空间音频(Listener + Position)。

API 速查

函数说明
int xgeAudioInit(void)初始化音频系统
void xgeAudioUnit(void)关闭音频系统
int xgeAudioIsReady(void)查询是否就绪
void xgeAudioSetVolume(float fVolume)设置主音量 (0~1)
float xgeAudioGetVolume(void)获取主音量
void xgeAudioListenerSet(const xge_audio_listener_t* pListener)设置 3D 听者
xge_audio_listener_t xgeAudioListenerGet(void)获取听者状态

编译本章示例

gcc -O2 -I. -o ch109.exe ch109_main.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch109.exe

示例:初始化 + 音量控制

// 初始化音频(在 xgeInit 之后)
if (xgeAudioInit() != 0) {
    xgeLogWrite(XGE_LOG_WARN,
        "音频初始化失败,静音模式");
}

// 设置主音量 80%
xgeAudioSetVolume(0.8f);

// 设置 3D 听者位置(跟随玩家)
xge_audio_listener_t listener = { 0 };
listener.fX = player.x;
listener.fY = player.y;
listener.fZ = 0;
xgeAudioListenerSet(&listener);

// 每帧更新听者位置
void onFrame(void) {
    listener.fX = player.x;
    listener.fY = player.y;
    xgeAudioListenerSet(&listener);
}

// 退出时关闭
xgeAudioUnit();

完整代码

将以下代码保存为 ch109_main.c,编译后运行即可看到音频初始化与音量控制效果:

#include "xge.h"
#include <math.h>

static xge_font g_font = NULL;
static float g_vol = 0.8f;
static float g_time = 0.0f;

static int onFrame(void* pUser) {
    (void)pUser;
    g_time += 0.016f;

    // 用正弦波模拟音量变化
    g_vol = 0.5f + 0.5f * sinf(g_time * 2.0f);
    xgeAudioSetVolume(g_vol);

    // 更新 3D 听者位置(模拟玩家移动)
    xge_audio_listener_t listener = { 0 };
    listener.fX = 400.0f + 200.0f * cosf(g_time);
    listener.fY = 300.0f + 150.0f * sinf(g_time);
    xgeAudioListenerSet(&listener);

    // 绘制 HUD
    xgeClear(0x081114FF);
    char buf[128];
    sprintf(buf, "Audio Ready: %s",
        xgeAudioIsReady() ? "YES" : "NO");
    xgeTextDraw(g_font, buf, 50, 50, 0xFFFFFFFF);
    sprintf(buf, "Volume: %.2f", xgeAudioGetVolume());
    xgeTextDraw(g_font, buf, 50, 90, 0xFFFFFFFF);
    sprintf(buf, "Listener: (%.0f, %.0f)",
        listener.fX, listener.fY);
    xgeTextDraw(g_font, buf, 50, 130, 0xFFFFFFFF);

    // 绘制音量条
    xgeShapeRectFill((xge_rect_t){50,180,300,20}, 0x333333FF);
    xgeShapeRectFill((xge_rect_t){50,180,300*g_vol,20}, 0x4fd8c2FF);

    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch109 - Audio Init";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);

    // 初始化音频系统
    if (xgeAudioInit() != 0) {
        xgeLogWrite(XGE_LOG_WARN, "Audio init failed");
    }

    g_font = xgeFontLoad("res/font/default.ttf", 24);
    xgeRun(onFrame, NULL);

    xgeFontFree(g_font);
    xgeAudioUnit();
    xgeUnit();
    return 0;
}

代码讲解

  • xgeAudioInit:在 xgeInit 之后调用,启动 OpenAL 音频设备。
  • xgeAudioIsReady:检查音频设备是否可用(无声卡时返回 0)。
  • xgeAudioSetVolume / GetVolume:全局主音量控制,范围 0~1。
  • xgeAudioListenerSet:设置 3D 听者位置,影响所有带 Position 的音源。
  • xgeAudioUnit:退出前关闭音频系统,释放设备资源。

要点与陷阱

  • 初始化顺序:必须在 xgeInit 之后调用 AudioInit。
  • IsReady:音频设备可能不可用(无声卡),需检查。
  • 音量范围:0.0(静音)~ 1.0(最大)。
  • 3D 音频:Listener 位置影响所有带 Position 的 Sound/Stream。

进阶练习

  1. 实现音量设置 UI:滑块控制 SetVolume。
  2. 3D 音效演示:Sound positioned 在不同位置,Listener 移动。

已验证实现

权威源码为 examples/tutorial_capture/ch109_main1.c。它运行时生成 100 ms PCM WAV 测试音,调用 xgeAudioInit、设置监听器和全局音量、加载并启动声音,然后验证 xgeAudioIsReady、xgeSoundIsPlaying 和 xgeAudioGetVolume。临时文件在退出时删除,不依赖第三方音频资源。

XGE audio initialization and test tone playback validation
图:第 109 章真实运行结果。绿色流程表示音频引擎、测试音加载和播放状态均已验证;截图证明 API 状态,实际听感仍应在目标设备上做人工冒烟测试。