Files
DNF_DEV/source/Asset/Asset_Audio.hpp
2026-02-08 16:20:50 +08:00

306 lines
9.4 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 <string>
#include <unordered_map>
#include <vector>
#include <fstream>
#include <stdexcept>
#include "rapidxml/rapidxml.hpp"
class AudioConfigReader
{
public:
// --------------------------
// 1. 结构体定义放在类内public区外部可通过 AudioConfigReader::SoundEffect 访问
// --------------------------
struct SoundEffect
{
std::string file_path; // 文件路径FILE属性
int volume_adjust = 100; // 音量调整默认100
int priority = 0; // 优先级默认0
};
struct RandomItem
{
std::string tag; // 关联的音效IDTAG属性
int prob = 0; // 概率PROB属性
};
struct RandomSoundGroup
{
std::vector<RandomItem> items; // 组内所有项
int delay = 0; // 延迟DELAY属性
int loop_delay = 0; // 循环延迟
int loop_delay_range = 0; // 循环延迟范围
};
struct Music
{
std::string file_path; // 文件路径FILE属性
int loop_delay = 0; // 循环延迟LOOP_DELAY属性
};
public:
// 构造函数:初始化解析器
AudioConfigReader() = default;
// 析构函数:清理资源
~AudioConfigReader() = default;
// 核心函数解析XML文件返回是否成功
bool LoadFromFile(const std::string &xml_path)
{
// 1. 读取XML文件内容
std::ifstream file(xml_path, std::ios::binary);
if (!file.is_open())
{
throw std::runtime_error("无法打开XML文件: " + xml_path);
}
std::string xml_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
// 2. 解析XML
rapidxml::xml_document<> doc;
try
{
doc.parse<rapidxml::parse_default>(&xml_content[0]);
}
catch (const rapidxml::parse_error &e)
{
throw std::runtime_error("XML解析错误: " + std::string(e.what()) +
"(位置: " + std::string(e.where<char>()) + "");
}
// 3. 获取根节点
rapidxml::xml_node<> *root = doc.first_node("AudioTagDatabase");
if (!root)
{
throw std::runtime_error("XML根节点错误: 找不到AudioTagDatabase");
}
// 4. 解析所有节点
ParseEffects(root); // 解析<EFFECT>
ParseRandomGroups(root); // 解析<RANDOM>
ParseMusics(root); // 解析<MUSIC>
return true;
}
// ------------------------------
// 接口1获取音效配置<EFFECT>
// ------------------------------
bool HasEffect(const std::string &effect_id) const
{
return m_effects.find(effect_id) != m_effects.end();
}
const SoundEffect &GetEffect(const std::string &effect_id) const
{
auto it = m_effects.find(effect_id);
if (it == m_effects.end())
{
throw std::out_of_range("音效ID不存在: " + effect_id);
}
return it->second;
}
// 获取所有音效ID列表
std::vector<std::string> GetAllEffectIds() const
{
return GetAllKeys(m_effects);
}
// ------------------------------
// 接口2获取随机音效组配置<RANDOM>
// ------------------------------
bool HasRandomGroup(const std::string &group_id) const
{
return m_random_groups.find(group_id) != m_random_groups.end();
}
const RandomSoundGroup &GetRandomGroup(const std::string &group_id) const
{
auto it = m_random_groups.find(group_id);
if (it == m_random_groups.end())
{
throw std::out_of_range("随机组ID不存在: " + group_id);
}
return it->second;
}
// 获取所有随机组ID列表
std::vector<std::string> GetAllRandomGroupIds() const
{
return GetAllKeys(m_random_groups);
}
// ------------------------------
// 接口3获取背景音乐配置<MUSIC>
// ------------------------------
bool HasMusic(const std::string &music_id) const
{
return m_musics.find(music_id) != m_musics.end();
}
const Music &GetMusic(const std::string &music_id) const
{
auto it = m_musics.find(music_id);
if (it == m_musics.end())
{
throw std::out_of_range("背景音乐ID不存在: " + music_id);
}
return it->second;
}
// 获取所有背景音乐ID列表
std::vector<std::string> GetAllMusicIds() const
{
return GetAllKeys(m_musics);
}
private:
// 辅助函数:解析<EFFECT>节点
void ParseEffects(rapidxml::xml_node<> *root)
{
for (rapidxml::xml_node<> *node = root->first_node("EFFECT");
node; node = node->next_sibling("EFFECT"))
{
// 必选属性ID和FILE
rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID");
rapidxml::xml_attribute<> *file_attr = node->first_attribute("FILE");
if (!id_attr || !file_attr)
{
continue; // 跳过无效节点
}
SoundEffect effect;
effect.file_path = NormalizePath(file_attr->value()); // 统一路径分隔符
// 可选属性VOLUME_ADJUST
if (auto attr = node->first_attribute("VOLUME_ADJUST"))
{
effect.volume_adjust = std::stoi(attr->value());
}
// 可选属性PRIORITY
if (auto attr = node->first_attribute("PRIORITY"))
{
effect.priority = std::stoi(attr->value());
}
m_effects[id_attr->value()] = effect;
}
}
// 辅助函数:解析<RANDOM>节点
void ParseRandomGroups(rapidxml::xml_node<> *root)
{
for (rapidxml::xml_node<> *node = root->first_node("RANDOM");
node; node = node->next_sibling("RANDOM"))
{
// 必选属性ID
rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID");
if (!id_attr)
{
continue; // 跳过无效节点
}
RandomSoundGroup group;
// 可选属性:延迟相关
if (auto attr = node->first_attribute("DELAY"))
{
group.delay = std::stoi(attr->value());
}
if (auto attr = node->first_attribute("LOOP_DELAY"))
{
group.loop_delay = std::stoi(attr->value());
}
if (auto attr = node->first_attribute("LOOP_DELAY_RANGE"))
{
group.loop_delay_range = std::stoi(attr->value());
}
// 解析子节点<ITEM>
for (rapidxml::xml_node<> *item_node = node->first_node("ITEM");
item_node; item_node = item_node->next_sibling("ITEM"))
{
rapidxml::xml_attribute<> *tag_attr = item_node->first_attribute("TAG");
rapidxml::xml_attribute<> *prob_attr = item_node->first_attribute("PROB");
if (!tag_attr || !prob_attr)
{
continue; // 跳过无效项
}
group.items.push_back({tag_attr->value(),
std::stoi(prob_attr->value())});
}
m_random_groups[id_attr->value()] = group;
}
}
// 辅助函数:解析<MUSIC>节点
void ParseMusics(rapidxml::xml_node<> *root)
{
for (rapidxml::xml_node<> *node = root->first_node("MUSIC");
node; node = node->next_sibling("MUSIC"))
{
// 必选属性ID和FILE
rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID");
rapidxml::xml_attribute<> *file_attr = node->first_attribute("FILE");
if (!id_attr || !file_attr)
{
continue; // 跳过无效节点
}
Music music;
music.file_path = NormalizePath(file_attr->value()); // 统一路径分隔符
// 可选属性LOOP_DELAY
if (auto attr = node->first_attribute("LOOP_DELAY"))
{
music.loop_delay = std::stoi(attr->value());
}
m_musics[id_attr->value()] = music;
}
}
// 辅助函数:统一路径分隔符(将\转为/,兼容跨平台)
std::string NormalizePath(const std::string &path)
{
std::string normalized = path;
for (char &c : normalized)
{
if (c == '\\')
{
c = '/';
}
}
return normalized;
}
// 辅助函数获取哈希表中所有key
template <typename Map>
std::vector<std::string> GetAllKeys(const Map &map) const
{
std::vector<std::string> keys;
keys.reserve(map.size());
for (const auto &pair : map)
{
keys.push_back(pair.first);
}
return keys;
}
private:
// 存储解析后的数据
std::unordered_map<std::string, SoundEffect> m_effects; // 音效映射ID -> 音效)
std::unordered_map<std::string, RandomSoundGroup> m_random_groups; // 随机组映射ID -> 随机组)
std::unordered_map<std::string, Music> m_musics; // 背景音乐映射ID -> 音乐)
};