#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 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; }