Files
Frostbite2D/Frostbite2D/include/frostbite2D/resource/audio_database.h
Lenheart 35c80247b3 feat: 添加游戏数学工具类并重构相关代码
refactor: 将数学工具函数移至GameMath类
feat(音频): 实现地图音频控制器
feat(调试): 添加游戏调试UI组件
feat(地图): 增加移动区域边界获取方法
fix(角色): 修复角色移动区域抑制逻辑
refactor(世界): 重构游戏世界场景初始化
docs(音频): 完善音频数据库注释
2026-04-06 22:22:40 +08:00

354 lines
9.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <frostbite2D/types/type_alias.h>
#include <optional>
#include <random>
#include <string>
#include <unordered_map>
#include <vector>
namespace frostbite2D {
/**
* @brief 音频项类型枚举
*/
enum class AudioEntryType {
None, ///< invalid
Effect, ///< one-shot sound effect
Ambient, ///< looping ambient track
Music, ///< background music
Random ///< random sound group
};
/**
* @brief 音效配置
*/
struct AudioEffect {
std::string id; ///< 音效 ID
std::string file; ///< 文件路径
std::string subgroup; ///< 子组(可选)
int32 loopDelay = -1; ///< 循环延迟(-1 表示未设置)
int32 loopDelayRange = -1;///< 循环延迟范围(-1 表示未设置)
};
/**
* @brief 音乐配置
*/
struct AudioMusic {
std::string id; ///< 音乐 ID
std::string file; ///< 文件路径
int32 loopDelay = -1; ///< 循环延迟(-1 表示未设置)
};
/**
* @brief ?????????
*/
struct AudioAmbient {
std::string id; ///< ?????ID
std::string file; ///< ??????
int32 loopDelay = -1; ///< ????????1 ?????????
int32 loopDelayRange = -1;///< ???????????1 ?????????
};
/**
* @brief 随机音效项
*/
struct RandomItem {
std::string tag; ///< 音效标签ID
int32 probability = 0; ///< 概率权重
};
/**
* @brief 随机音效组
*/
struct AudioRandom {
std::string id; ///< 随机组 ID
std::vector<RandomItem> items; ///< 随机项列表
};
/**
* @brief 统一的音频条目(包含类型和数据)
*/
struct AudioEntry {
AudioEntryType type = AudioEntryType::None; ///< 条目类型
const AudioEffect* effect = nullptr; ///< 音效指针type == Effect 时有效)
const AudioAmbient* ambient = nullptr;///< ???????????ype == Ambient ??????
const AudioMusic* music = nullptr; ///< 音乐指针type == Music 时有效)
const AudioRandom* random = nullptr; ///< 随机组指针type == Random 时有效)
/// @brief 判断是否有效
bool isValid() const { return type != AudioEntryType::None; }
/// @brief 判断是否为音效
bool isEffect() const { return type == AudioEntryType::Effect; }
/// @brief ????????????
bool isAmbient() const { return type == AudioEntryType::Ambient; }
/// @brief 判断是否为音乐
bool isMusic() const { return type == AudioEntryType::Music; }
/// @brief 判断是否为随机组
bool isRandom() const { return type == AudioEntryType::Random; }
/// @brief 安全获取文件路径
std::string getFilePath() const {
if (type == AudioEntryType::Effect && effect)
return effect->file;
if (type == AudioEntryType::Ambient && ambient)
return ambient->file;
if (type == AudioEntryType::Music && music)
return music->file;
return "";
}
/// @brief 安全获取子组
std::string getSubgroup() const {
if (type == AudioEntryType::Effect && effect)
return effect->subgroup;
return "";
}
};
/**
* @brief 音频数据库(单例)
*
* 用于解析和访问 audio.xml 文件中的音频配置。
* 提供统一的查询接口,自动判断音频类型。
*
* @example
* auto& audioDB = AudioDatabase::get();
* if (audioDB.loadFromFile("audio.xml")) {
* // 统一查询
* if (auto entry = audioDB.get("GN_FINAL_SHOT_01")) {
* // 使用 entry
* }
*
* // 获取文件路径
* if (auto filePath = audioDB.getFilePath("GN_FINAL_SHOT_01")) {
* // 使用 *filePath
* }
*
* // 获取音效(自动处理随机组)
* if (auto soundId = audioDB.getSound("R_GN_FINAL_SHOT")) {
* // 使用 *soundId
* }
* }
*/
class AudioDatabase {
public:
/**
* @brief 获取单例实例
* @return 音频数据库实例引用
*/
static AudioDatabase& get();
AudioDatabase(const AudioDatabase&) = delete;
AudioDatabase& operator=(const AudioDatabase&) = delete;
// ---------------------------------------------------------------------------
// 加载和解析
// ---------------------------------------------------------------------------
/**
* @brief 从文件加载音频数据库
* @param path XML 文件路径
* @return 加载成功返回 true
*/
bool loadFromFile(const std::string& path);
/**
* @brief 从字符串加载音频数据库
* @param xmlContent XML 内容字符串
* @return 加载成功返回 true
*/
bool loadFromString(const std::string& xmlContent);
/**
* @brief 清空数据库
*/
void clear();
/**
* @brief 检查数据库是否已加载
* @return 已加载返回 true
*/
bool isLoaded() const;
// ---------------------------------------------------------------------------
// 统一查询接口
// ---------------------------------------------------------------------------
/**
* @brief 统一查询:输入任意 ID自动判断类型
* @param id 音频 ID可以是 EFFECT、MUSIC 或 RANDOM 的 ID
* @return 音频条目,未找到返回 std::nullopt
*/
std::optional<AudioEntry> get(const std::string& id);
/**
* @brief 获取音效 ID自动处理 RANDOM 类型)
*
* 如果 id 是 RANDOM 类型,会按概率随机选择一个音效;
* 如果 id 是 EFFECT 类型,直接返回该 ID。
*
* @param id 音效 ID 或随机组 ID
* @return 音效 ID未找到返回 std::nullopt
*/
std::optional<std::string> getSound(const std::string& id);
/**
* @brief 获取文件路径
*
* 对于 EFFECT 和 MUSIC 类型,返回对应的文件路径;
* 对于 RANDOM 类型,先随机选择一个音效再返回其文件路径。
*
* @param id 音频 ID
* @return 文件路径,未找到返回 std::nullopt
*/
std::optional<std::string> getFilePath(const std::string& id);
// ---------------------------------------------------------------------------
// 类型特定查询(高级用法)
// ---------------------------------------------------------------------------
/**
* @brief 获取音效配置
* @param id 音效 ID
* @return 音效配置指针,未找到返回 std::nullopt
*/
std::optional<const AudioEffect*> getEffect(const std::string& id);
/**
* @brief ????????????
* @param id ?????ID
* @return ????????????????????? std::nullopt
*/
std::optional<const AudioAmbient*> getAmbient(const std::string& id);
/**
* @brief 获取音乐配置
* @param id 音乐 ID
* @return 音乐配置指针,未找到返回 std::nullopt
*/
std::optional<const AudioMusic*> getMusic(const std::string& id);
/**
* @brief 获取随机音效组配置
* @param id 随机组 ID
* @return 随机组配置指针,未找到返回 std::nullopt
*/
std::optional<const AudioRandom*> getRandom(const std::string& id);
// ---------------------------------------------------------------------------
// 统计信息
// ---------------------------------------------------------------------------
/**
* @brief 获取音效数量
*/
size_t effectCount() const;
/**
* @brief ???????????
*/
size_t ambientCount() const;
/**
* @brief 获取音乐数量
*/
size_t musicCount() const;
/**
* @brief 获取随机组数量
*/
size_t randomCount() const;
// ---------------------------------------------------------------------------
// 便捷访问方法(推荐使用)
// ---------------------------------------------------------------------------
/**
* @brief 检查 ID 是否存在
* @param id 音频 ID
* @return 存在返回 true
*/
bool has(const std::string& id);
/**
* @brief 获取文件路径(不存在返回空字符串)
* @param id 音频 ID
* @return 文件路径,未找到返回空字符串
*/
std::string filePath(const std::string& id);
/**
* @brief 获取音效 ID自动处理随机组不存在返回空字符串
* @param idOrRandomId 音效 ID 或随机组 ID
* @return 音效 ID未找到返回空字符串
*/
std::string soundId(const std::string& idOrRandomId);
/**
* @brief 获取子组EFFECT 的 SUBGROUP 属性)
* @param id 音频 ID
* @return 子组名,未找到或不存在返回空字符串
*/
std::string subgroup(const std::string& id);
/**
* @brief 获取条目类型
* @param id 音频 ID
* @return 条目类型,未找到返回 AudioEntryType::None
*/
AudioEntryType typeOf(const std::string& id);
// ---------------------------------------------------------------------------
// 带检查的访问方法
// ---------------------------------------------------------------------------
/**
* @brief 尝试获取文件路径,带成功标志
* @param id 音频 ID
* @param outPath 输出文件路径
* @return 成功返回 true
*/
bool tryGetFilePath(const std::string& id, std::string& outPath);
/**
* @brief 尝试获取音效 ID带成功标志
* @param id 音效 ID 或随机组 ID
* @param outSoundId 输出音效 ID
* @return 成功返回 true
*/
bool tryGetSoundId(const std::string& id, std::string& outSoundId);
private:
AudioDatabase() = default;
~AudioDatabase() = default;
/**
* @brief 初始化随机数生成器
*/
void initializeRNG();
/**
* @brief 从随机组中按概率选择一个音效
* @param randomGroup 随机组配置
* @return 选中的音效 ID
*/
std::string selectFromRandom(const AudioRandom& randomGroup);
std::unordered_map<std::string, AudioEffect> effectMap_; ///< 音效映射
std::unordered_map<std::string, AudioAmbient> ambientMap_; ///< ????????
std::unordered_map<std::string, AudioMusic> musicMap_; ///< 音乐映射
std::unordered_map<std::string, AudioRandom> randomMap_; ///< 随机组映射
std::mt19937 rng_; ///< 随机数生成器
bool rngInitialized_ = false; ///< 随机数生成器是否已初始化
bool loaded_ = false;
};
} // namespace frostbite2D