推
This commit is contained in:
306
source/Asset/Asset_Audio.hpp
Normal file
306
source/Asset/Asset_Audio.hpp
Normal file
@@ -0,0 +1,306 @@
|
||||
#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; // 关联的音效ID(TAG属性)
|
||||
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 -> 音乐)
|
||||
};
|
||||
Reference in New Issue
Block a user