Chapter 113 · 音频系统

音频分组

AudioGroup 将多个音频归类管理,可统一调节音量、批量淡入淡出,实现"音效/音乐/语音"独立控制。

113

概念

音频分组(AudioGroup)是音量管理的核心机制:
• 将 Sound/Music/Stream 加载时指定分组
• 对分组统一设置音量或执行 Fade
• 最终音量 = 主音量 × 分组音量 × 个体音量
典型分组:SFX(音效)、BGM(音乐)、Voice(语音)、Ambient(环境)。

API 速查

函数说明
int xgeAudioGroupInit(xge_audio_group pGroup)创建分组
void xgeAudioGroupFree(xge_audio_group pGroup)销毁分组
void xgeAudioGroupSetVolume(xge_audio_group pGroup, float fVolume)设置分组音量
float xgeAudioGroupGetVolume(xge_audio_group pGroup)获取分组音量
void xgeAudioGroupFade(xge_audio_group pGroup, float fFrom, float fTo, int iMilliseconds)分组音量渐变

编译本章示例

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

示例:三类音频独立控制

// 创建三个分组
xge_audio_group_t grpSFX, grpBGM, grpVoice;
xgeAudioGroupInit(&grpSFX);
xgeAudioGroupInit(&grpBGM);
xgeAudioGroupInit(&grpVoice);

// 加载时指定分组
xge_sound_t sndClick;
xgeSoundLoadGroup(&sndClick,
    "sfx/click.wav", &grpSFX);

xge_music_t bgmMain;
xgeMusicLoadGroup(&bgmMain,
    "music/main.ogg", &grpBGM);

xge_stream_t stmNarr;
xgeStreamOpenGroup(&stmNarr,
    "voice/narrator.ogg", &grpVoice);

// 设置界面:独立调节各类音量
xgeAudioGroupSetVolume(&grpSFX, 1.0f);
xgeAudioGroupSetVolume(&grpBGM, 0.6f);
xgeAudioGroupSetVolume(&grpVoice, 0.9f);

// 对话时:压低音乐,突出语音
xgeAudioGroupFade(&grpBGM,
    0.6f, 0.2f, 500);

// 对话结束:恢复音乐
xgeAudioGroupFade(&grpBGM,
    0.2f, 0.6f, 800);

// 清理
xgeAudioGroupFree(&grpSFX);
xgeAudioGroupFree(&grpBGM);
xgeAudioGroupFree(&grpVoice);

完整代码

将以下代码保存为 ch113_main.c,编译后运行即可看到音频分组独立控制效果:

#include "xge.h"

static xge_audio_group_t g_grpSFX;
static xge_audio_group_t g_grpBGM;
static xge_sound_t g_sndClick;
static xge_music_t g_bgm;
static xge_font g_font = NULL;
static float g_sfxVol = 1.0f;
static float g_bgmVol = 0.6f;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x081114FF);

    // 按键调节音量
    if (xgeKeyPressed(XGE_KEY_UP)) {
        g_sfxVol += 0.1f;
        if (g_sfxVol > 1.0f) g_sfxVol = 1.0f;
        xgeAudioGroupSetVolume(&g_grpSFX, g_sfxVol);
    }
    if (xgeKeyPressed(XGE_KEY_DOWN)) {
        g_sfxVol -= 0.1f;
        if (g_sfxVol < 0.0f) g_sfxVol = 0.0f;
        xgeAudioGroupSetVolume(&g_grpSFX, g_sfxVol);
    }
    if (xgeKeyPressed(XGE_KEY_SPACE)) {
        xgeSoundPlay(&g_sndClick);
    }
    // B: 压低音乐(模拟对话)
    if (xgeKeyPressed('B')) {
        xgeAudioGroupFade(&g_grpBGM,
            g_bgmVol, 0.15f, 500);
    }
    // N: 恢复音乐
    if (xgeKeyPressed('N')) {
        xgeAudioGroupFade(&g_grpBGM,
            0.15f, g_bgmVol, 800);
    }

    // HUD
    char buf[128];
    sprintf(buf, "SFX Vol: %.1f | BGM Vol: %.1f",
        g_sfxVol, g_bgmVol);
    xgeTextDraw(g_font, buf, 50, 50, 0xFFFFFFFF);
    xgeTextDraw(g_font,
        "Up/Down:SFX vol | Space:click | B:duck | N:restore",
        50, 90, 0xFFFFFFFF);

    // 音量条
    xgeShapeRectFill((xge_rect_t){50,140,200,16}, 0x333333FF);
    xgeShapeRectFill((xge_rect_t){50,140,200*g_sfxVol,16}, 0x4FD8C2FF);
    xgeShapeRectFill((xge_rect_t){50,170,200,16}, 0x333333FF);
    xgeShapeRectFill((xge_rect_t){50,170,200*g_bgmVol,16}, 0xFFB454FF);

    return XGE_OK;
}

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

    // 创建分组
    xgeAudioGroupInit(&g_grpSFX);
    xgeAudioGroupInit(&g_grpBGM);
    xgeAudioGroupSetVolume(&g_grpSFX, g_sfxVol);
    xgeAudioGroupSetVolume(&g_grpBGM, g_bgmVol);

    // 加载到分组
    xgeSoundLoadGroup(&g_sndClick, "sfx/click.wav", &g_grpSFX);
    xgeMusicLoadGroup(&g_bgm, "music/main.ogg", &g_grpBGM);
    xgeMusicSetLoop(&g_bgm, 1);
    xgeMusicPlay(&g_bgm);

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

    xgeSoundFree(&g_sndClick);
    xgeMusicFree(&g_bgm);
    xgeAudioGroupFree(&g_grpSFX);
    xgeAudioGroupFree(&g_grpBGM);
    xgeFontFree(g_font);
    xgeAudioUnit();
    xgeUnit();
    return 0;
}

代码讲解

  • xgeAudioGroupInit:创建分组,用于归类音频。
  • LoadGroup:加载时指定分组,最终音量 = 主音量 × 分组音量 × 个体音量。
  • xgeAudioGroupSetVolume:统一调节分组内所有音频音量。
  • xgeAudioGroupFade:分组淡入淡出,对话时压低音乐。
  • Free 顺序:先释放组内音频,再释放分组。

要点与陷阱

  • 音量叠加:最终音量 = 主音量 × 分组音量 × 个体音量。
  • LoadGroup:必须在加载时指定分组,加载后不可更改。
  • GroupFade:对整个分组渐变,影响组内所有音频。
  • Free 顺序:先释放组内音频,再释放分组。

进阶练习

  1. 实现音频设置面板:三个滑块分别控制 SFX/BGM/Voice。
  2. 暂停菜单:进入时 GroupFade 全部降低,退出时恢复。

已验证实现

权威源码为 examples/tutorial_capture/ch113_main1.c。它创建 xge_audio_group_t,将组音量设为 0.45,把测试音加载到该组并播放,然后读回组音量并验证停止状态。组路由可用于音乐、效果和语音分别混音。

XGE audio group routing and volume validation
图:第 113 章真实运行的分组混音测试。流程表示声音通过音频组进入引擎,状态条表示组音量被读回验证。