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

234 lines
6.6 KiB
C++
Raw Permalink 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.
#include "Asset_SoundPack.h"
Asset_SoundPack::Asset_SoundPack()
{
int device = -1; // 使用默认设备
unsigned long freq = 44100; // 采样频率PCM常用的采样率之一可以按需调整
// 构造NPK信息
InitNpkList();
// 解析audio.xml
XmlConfig.LoadFromFile("audio.xml");
// 初始化SDL_mixer支持OGG格式
// 44100: 采样率, MIX_DEFAULT_FORMAT: 音频格式, 2: 声道数(立体声), 4096: 缓冲区大小
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 8192) < 0)
{
SDL_LogError(0, "SDL_mixer初始化失败! Mix_Error: %s\n", Mix_GetError());
}
if (Mix_Init(MIX_INIT_MP3 | MIX_INIT_OGG) < 0)
{
SDL_LogError(0, "Mix_Init failed: %s\n", Mix_GetError());
}
// 分配通道
ChannelCount = Mix_AllocateChannels(64);
SetAllChannelVolume(0);
}
Asset_SoundPack::~Asset_SoundPack()
{
}
void Asset_SoundPack::InitNpkList()
{
DIR *dir;
struct dirent *ent;
std::string path = "SoundPacks/";
dir = opendir(path.c_str());
while ((ent = readdir(dir)))
{
// 跳过.和..目录
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
continue;
std::string RealPath = path + ent->d_name;
Ifstream_NPK Fs;
Fs.open(RealPath.c_str(), std::ios::in | std::ios::binary);
if (Fs.is_open())
{
std::string Header = Fs.ReadString();
// 如果是NPK
if (Header == "NeoplePack_Bill")
{
// 读取img数量
int ImageCount = Fs.ReadInt();
for (int i = 0; i < ImageCount; ++i)
{
// 直接读取当前项的偏移、长度和路径
auto offset = Fs.ReadUnsignedInt();
auto length = Fs.ReadUnsignedInt();
auto path = Fs.ReadInfo();
// 构造 WAV 并插入映射表
lp_arr[path] = WAV{
offset, // offset
length, // size
path, // lpWavName直接复用读取的path
RealPath, // lpBelongsFile
i // img_index
};
}
}
}
}
closedir(dir);
}
void Asset_SoundPack::Reclaims()
{
}
Mix_Chunk *Asset_SoundPack::GetOgg(const std::string &lpWavName)
{
if (map_sound.find(lpWavName) != map_sound.end())
{
map_sound[lpWavName].LastUseTime = SDL_GetTicks64();
return map_sound[lpWavName].lpMusic;
}
// 在NPK映射表中找到
if (lp_arr.find(lpWavName) != lp_arr.end())
{
WAV &wav = lp_arr[lpWavName];
if (wav.lpBelongsFile.empty())
return nullptr;
Ifstream_NPK Fs;
Fs.open(wav.lpBelongsFile.c_str(), std::ios::in | std::ios::binary);
if (Fs.is_open())
{
Fs.seekg(wav.offset, std::ios::beg);
auto buffer = Fs.ReadCustomSize(wav.size);
SDL_RWops *rw = SDL_RWFromConstMem(buffer, wav.size);
if (!rw)
{
SDL_Log("创建内存流失败: %s\n", SDL_GetError());
Mix_CloseAudio();
SDL_Quit();
return nullptr;
}
Mix_Chunk *music = Mix_LoadWAV_RW(rw, 0);
if (!music)
{
SDL_Log("加载OGG失败: %s\n", Mix_GetError());
SDL_RWclose(rw); // 手动关闭流
Mix_CloseAudio();
SDL_Quit();
return nullptr;
}
map_sound[lpWavName].lpMusic = music;
map_sound[lpWavName].LastUseTime = SDL_GetTicks64();
return music;
}
}
// 去Music文件夹中寻找
else
{
Mix_Chunk *music = Mix_LoadWAV(lpWavName.c_str());
if (!music)
{
SDL_Log("加载OGG失败: %s\n", Mix_GetError());
Mix_CloseAudio();
SDL_Quit();
return nullptr;
}
map_sound[lpWavName].lpMusic = music;
map_sound[lpWavName].LastUseTime = SDL_GetTicks64();
return music;
}
}
void Asset_SoundPack::PlayAudio(const std::string Key)
{
std::string RealKey = Key;
// 先判断是不是随机组 如果是随机组去里面调出真正要播放的Key
if (XmlConfig.HasRandomGroup(Key))
{
auto Group = XmlConfig.GetRandomGroup(Key);
int RandNum = rand() % 100;
for (auto &item : Group.items)
{
if (RandNum <= item.prob)
{
RealKey = item.tag;
}
}
}
// 如果是特效音频
if (XmlConfig.HasEffect(Key))
{
std::string oggPath = XmlConfig.GetEffect(Key).file_path;
int volume = XmlConfig.GetEffect(Key).volume_adjust;
PlayEffect(oggPath, volume);
}
// 如果是背景音乐
else if (XmlConfig.HasMusic(Key))
{
std::string oggPath = XmlConfig.GetMusic(Key).file_path;
PlayMusic(Key, oggPath);
}
else
{
SDL_LogError(0, "找不到音频: %s\n", Key.c_str());
}
}
void Asset_SoundPack::StopAudio(const std::string Key)
{
StopChannel(backgroud_music_map[Key]);
backgroud_music_map.erase(Key);
}
void Asset_SoundPack::PlayMusic(const std::string Key, const std::string oggpath)
{
auto ogg = GetOgg(oggpath);
if (ogg)
{
int channel = FindFreeChannelInRange(0, 4);
backgroud_music_map[Key] = Mix_PlayChannel(channel, ogg, -1);
}
}
void Asset_SoundPack::PlayEffect(const std::string Key, int Volume)
{
auto ogg = GetOgg(Key);
if (ogg)
{
int channel = FindFreeChannelInRange(4, ChannelCount - 1);
Mix_VolumeChunk(ogg, Volume);
Mix_PlayChannel(channel, ogg, 0);
}
}
void Asset_SoundPack::StopChannel(int Channel)
{
Mix_HaltChannel(Channel); // 停止指定通道的音效
}
void Asset_SoundPack::SetAllChannelVolume(int Volume)
{
Mix_Volume(-1, Volume);
}
std::map<std::string, int> Asset_SoundPack::GetBackgroudMusicVector()
{
return backgroud_music_map;
}
int Asset_SoundPack::FindFreeChannelInRange(int startChannel, int endChannel)
{
// 遍历通道若总通道数小于32则只检查到最后一个通道
for (int channel = startChannel; channel <= endChannel; channel++)
{
// 检查通道是否空闲Mix_Playing返回0表示空闲
if (Mix_Playing(channel) == 0)
{
return channel; // 返回第一个空闲通道
}
}
// 若所有通道都在使用中
return -1;
}