推
This commit is contained in:
233
source/Asset/Asset_SoundPack.cpp
Normal file
233
source/Asset/Asset_SoundPack.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user