234 lines
6.6 KiB
C++
234 lines
6.6 KiB
C++
#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;
|
||
}
|