更新SDL3_mixer 3.2正式版的音频接口
This commit is contained in:
@@ -2,6 +2,11 @@
|
||||
.DS_Store
|
||||
**/.DS_Store
|
||||
|
||||
# Agent
|
||||
.agent/
|
||||
.agents/
|
||||
.claude/
|
||||
|
||||
# IDE
|
||||
.config/
|
||||
.idea/
|
||||
|
||||
@@ -180,8 +180,8 @@ function(setup_project_dependencies)
|
||||
SDL3
|
||||
SDL3
|
||||
"https://github.com/libsdl-org/SDL.git"
|
||||
"release-3.2.24"
|
||||
"external/SDL-release-3.2.24"
|
||||
"release-3.4.2"
|
||||
"external/SDL-release-3.4.2"
|
||||
AUTO # 使用全局BUILD_SHARED_LIBS设置
|
||||
)
|
||||
|
||||
@@ -195,8 +195,8 @@ function(setup_project_dependencies)
|
||||
SDL3_image
|
||||
SDL3_image
|
||||
"https://github.com/libsdl-org/SDL_image.git"
|
||||
"release-3.2.4"
|
||||
"external/SDL_image-release-3.2.4"
|
||||
"release-3.4.0"
|
||||
"external/SDL_image-release-3.4.0"
|
||||
AUTO # 使用全局BUILD_SHARED_LIBS设置
|
||||
)
|
||||
|
||||
@@ -205,8 +205,8 @@ function(setup_project_dependencies)
|
||||
SDL3_mixer
|
||||
SDL3_mixer
|
||||
"https://github.com/libsdl-org/SDL_mixer.git"
|
||||
"30c1301055a35ee87b8679279b6fc88e10d28fa3"
|
||||
"external/SDL_mixer-30c1301"
|
||||
"release-3.2.0"
|
||||
"external/SDL_mixer-release-3.2.0"
|
||||
AUTO # 使用全局BUILD_SHARED_LIBS设置
|
||||
)
|
||||
|
||||
|
||||
@@ -1,53 +1,64 @@
|
||||
#include "audio_player.h"
|
||||
#include "../resource/resource_manager.h"
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <glm/common.hpp>
|
||||
|
||||
namespace engine::audio {
|
||||
AudioPlayer::~AudioPlayer() = default;
|
||||
AudioPlayer::~AudioPlayer() {
|
||||
if (music_track_) {
|
||||
MIX_DestroyTrack(music_track_);
|
||||
music_track_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AudioPlayer::AudioPlayer(engine::resource::ResourceManager* resource_manager)
|
||||
: resource_manager_(resource_manager) {
|
||||
if (!resource_manager_) {
|
||||
throw std::runtime_error("AudioPlayer 构造失败: 提供的 ResourceManager 指针为空。");
|
||||
}
|
||||
mixer_ = resource_manager_->getMixer();
|
||||
music_track_ = MIX_CreateTrack(mixer_);
|
||||
if (!music_track_) {
|
||||
throw std::runtime_error("AudioPlayer 构造失败: 无法创建音乐轨道: " + std::string(SDL_GetError()));
|
||||
}
|
||||
}
|
||||
|
||||
int AudioPlayer::playSound(std::string_view sound_path, int channel) {
|
||||
int AudioPlayer::playSound(std::string_view sound_path) {
|
||||
|
||||
Mix_Chunk* chunk = resource_manager_->getSound(sound_path); // 通过 ResourceManager 获取资源
|
||||
if (!chunk) {
|
||||
MIX_Audio* audio = resource_manager_->getSound(sound_path); // 通过 ResourceManager 获取资源
|
||||
if (!audio) {
|
||||
spdlog::error("AudioPlayer: 无法获取音效 '{}' 播放。", sound_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int played_channel = Mix_PlayChannel(channel, chunk, 0); // 播放音效
|
||||
if (played_channel == -1) {
|
||||
if (!MIX_PlayAudio(mixer_, audio)) { // 即发即忘方式播放音效
|
||||
spdlog::error("AudioPlayer: 无法播放音效 '{}': {}", sound_path, SDL_GetError());
|
||||
} else {
|
||||
spdlog::trace("AudioPlayer: 播放音效 '{}' 在通道 {}。", sound_path, played_channel);
|
||||
return -1;
|
||||
}
|
||||
return played_channel;
|
||||
spdlog::trace("AudioPlayer: 播放音效 '{}'。", sound_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AudioPlayer::playMusic(std::string_view music_path, int loops, int fade_in_ms) {
|
||||
if (music_path == current_music_) return true; // 如果当前音乐已经在播放,则不重复播放
|
||||
current_music_ = music_path;
|
||||
Mix_Music* music = resource_manager_->getMusic(music_path); // 通过 ResourceManager 获取资源
|
||||
MIX_Audio* music = resource_manager_->getMusic(music_path); // 通过 ResourceManager 获取资源
|
||||
if (!music) {
|
||||
spdlog::error("AudioPlayer: 无法获取音乐 '{}' 播放。", music_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
Mix_HaltMusic(); // 停止之前的音乐
|
||||
MIX_StopTrack(music_track_, 0); // 立即停止之前的音乐
|
||||
MIX_SetTrackAudio(music_track_, music); // 设置音乐轨道的音频源
|
||||
|
||||
bool result = false;
|
||||
// 配置播放参数(循环次数、淡入时长)
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetNumberProperty(props, MIX_PROP_PLAY_LOOPS_NUMBER, loops);
|
||||
if (fade_in_ms > 0) {
|
||||
result = Mix_FadeInMusic(music, loops, fade_in_ms); // 淡入播放音乐
|
||||
} else {
|
||||
result = Mix_PlayMusic(music, loops);
|
||||
SDL_SetNumberProperty(props, MIX_PROP_PLAY_FADE_IN_MILLISECONDS_NUMBER, fade_in_ms);
|
||||
}
|
||||
bool result = MIX_PlayTrack(music_track_, props);
|
||||
SDL_DestroyProperties(props);
|
||||
|
||||
if (!result) {
|
||||
spdlog::error("AudioPlayer: 无法播放音乐 '{}': {}", music_path, SDL_GetError());
|
||||
@@ -58,45 +69,39 @@ bool AudioPlayer::playMusic(std::string_view music_path, int loops, int fade_in_
|
||||
}
|
||||
|
||||
void AudioPlayer::stopMusic(int fade_out_ms) {
|
||||
if (fade_out_ms > 0) {
|
||||
Mix_FadeOutMusic(fade_out_ms); // 淡出音乐
|
||||
} else {
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
spdlog::trace("AudioPlayer: 停止音乐。");
|
||||
Sint64 fade_frames = (fade_out_ms > 0) ? MIX_TrackMSToFrames(music_track_, fade_out_ms) : 0;
|
||||
MIX_StopTrack(music_track_, fade_frames);
|
||||
current_music_.clear();
|
||||
spdlog::trace("AudioPlayer: 停止音乐。");
|
||||
}
|
||||
|
||||
void AudioPlayer::pauseMusic() {
|
||||
Mix_PauseMusic();
|
||||
MIX_PauseTrack(music_track_);
|
||||
spdlog::trace("AudioPlayer: 暂停音乐。");
|
||||
}
|
||||
|
||||
void AudioPlayer::resumeMusic() {
|
||||
Mix_ResumeMusic();
|
||||
MIX_ResumeTrack(music_track_);
|
||||
spdlog::trace("AudioPlayer: 恢复音乐。");
|
||||
}
|
||||
|
||||
void AudioPlayer::setSoundVolume(float volume, int channel) {
|
||||
// 将浮点音量(0-1)转换为SDL_mixer的音量(0-128)
|
||||
int sdl_volume = static_cast<int>(glm::max(0.0f, glm::min(1.0f, volume)) * MIX_MAX_VOLUME);
|
||||
Mix_Volume(channel, sdl_volume);
|
||||
spdlog::trace("AudioPlayer: 设置通道 {} 的音量为 {:.2f}。", channel, volume);
|
||||
void AudioPlayer::setSoundVolume(float volume) {
|
||||
// 通过混音器整体增益控制音效音量(0.0-1.0)
|
||||
MIX_SetMixerGain(mixer_, volume);
|
||||
spdlog::trace("AudioPlayer: 设置音效音量为 {:.2f}。", volume);
|
||||
}
|
||||
|
||||
void AudioPlayer::setMusicVolume(float volume) {
|
||||
int sdl_volume = static_cast<int>(glm::max(0.0f, glm::min(1.0f, volume)) * MIX_MAX_VOLUME);
|
||||
Mix_VolumeMusic(sdl_volume);
|
||||
MIX_SetTrackGain(music_track_, volume);
|
||||
spdlog::trace("AudioPlayer: 设置音乐音量为 {:.2f}。", volume);
|
||||
}
|
||||
|
||||
float AudioPlayer::getMusicVolume() {
|
||||
// SDL_mixer的音量(0-128)转为 0~1.0 的浮点数
|
||||
return static_cast<float>(Mix_VolumeMusic(-1)) / static_cast<float>(MIX_MAX_VOLUME);
|
||||
/* 参数 -1 表示查询当前音量 */
|
||||
return MIX_GetTrackGain(music_track_);
|
||||
}
|
||||
|
||||
float AudioPlayer::getSoundVolume(int channel) {
|
||||
return static_cast<float>(Mix_Volume(channel, -1)) / static_cast<float>(MIX_MAX_VOLUME);
|
||||
float AudioPlayer::getSoundVolume() {
|
||||
return MIX_GetMixerGain(mixer_);
|
||||
}
|
||||
|
||||
} // namespace engine::audio
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace engine::resource {
|
||||
class ResourceManager;
|
||||
}
|
||||
|
||||
struct Mix_Chunk;
|
||||
struct Mix_Music;
|
||||
struct MIX_Mixer;
|
||||
struct MIX_Track;
|
||||
|
||||
namespace engine::audio {
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace engine::audio {
|
||||
class AudioPlayer final{
|
||||
private:
|
||||
engine::resource::ResourceManager* resource_manager_; ///< @brief 指向 ResourceManager 的非拥有指针,用于加载和管理音频资源。
|
||||
MIX_Mixer* mixer_{nullptr}; ///< @brief SDL_mixer 混音器指针(非拥有)。
|
||||
MIX_Track* music_track_{nullptr}; ///< @brief 专用于背景音乐播放的轨道(拥有)。
|
||||
std::string current_music_; ///< @brief 当前正在播放的音乐路径,用于避免重复播放同一音乐。
|
||||
|
||||
public:
|
||||
@@ -37,13 +39,12 @@ public:
|
||||
|
||||
// --- 播放控制方法 ---
|
||||
/**
|
||||
* @brief 播放音效(chunk)。
|
||||
* @brief 播放音效。
|
||||
* 如果尚未缓存,则通过 ResourceManager 加载音效。
|
||||
* @param sound_path 音效文件的路径。
|
||||
* @param channel 要播放的特定通道,或 -1 表示第一个可用通道。默认为 -1。
|
||||
* @return 音效正在播放的通道,出错时返回 -1。
|
||||
* @return 成功返回 0,出错返回 -1。
|
||||
*/
|
||||
int playSound(std::string_view sound_path, int channel = -1);
|
||||
int playSound(std::string_view sound_path);
|
||||
|
||||
/**
|
||||
* @brief 播放背景音乐。如果正在播放,则淡出之前的音乐。
|
||||
@@ -72,11 +73,10 @@ public:
|
||||
void resumeMusic();
|
||||
|
||||
/**
|
||||
* @brief 设置音效通道的音量。
|
||||
* @brief 设置音效音量。
|
||||
* @param volume 音量级别(0.0-1.0)。
|
||||
* @param channel 通道号(-1 表示所有通道)。默认为 -1。
|
||||
*/
|
||||
void setSoundVolume(float volume, int channel = -1);
|
||||
void setSoundVolume(float volume);
|
||||
|
||||
/**
|
||||
* @brief 设置音乐通道的音量。
|
||||
@@ -92,10 +92,9 @@ public:
|
||||
|
||||
/**
|
||||
* @brief 获取当前音效音量。
|
||||
* @param channel 通道号(-1 表示所有通道)。默认为 -1。
|
||||
* @return 音量级别(0.0-1.0)。
|
||||
*/
|
||||
float getSoundVolume(int channel = -1);
|
||||
float getSoundVolume();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -27,14 +27,14 @@ void AudioComponent::init()
|
||||
}
|
||||
}
|
||||
|
||||
void AudioComponent::playSound(std::string_view sound_id, int channel, bool use_spatial)
|
||||
void AudioComponent::playSound(std::string_view sound_id, bool use_spatial)
|
||||
{
|
||||
// 如果 sound_id 是音效 ID,则在查找在map中查找对应的路径; 没找到的话则把 sound_id 当作路径直接使用
|
||||
auto sound_path = sound_id_to_path_.find(std::string(sound_id)) != sound_id_to_path_.end() ? sound_id_to_path_[std::string(sound_id)] : sound_id;
|
||||
|
||||
if (use_spatial && transform_) { // 使用空间定位
|
||||
// TODO: (SDL_Mixer 不支持空间定位,未来更换音频库时可以方便地实现)
|
||||
// 这里给一个简单的功能:150像素范围内播放,否则不播放
|
||||
// TODO: 正式版 SDL3_Mixer 已经支持空间定位,但本期课程不展开,你可以尝试自己完成。
|
||||
// 这里给一个简单的功能:150像素范围内播放,否则不播放
|
||||
auto camera_center = camera_->getPosition() + camera_->getViewportSize() / 2.0f; // 相机中心
|
||||
auto object_pos = transform_->getPosition();
|
||||
float distance = glm::length(camera_center - object_pos);
|
||||
@@ -42,10 +42,8 @@ void AudioComponent::playSound(std::string_view sound_id, int channel, bool use_
|
||||
spdlog::debug("AudioComponent::playSound: 音效 '{}' 超出范围,不播放。", sound_id);
|
||||
return; // 超出范围,不播放
|
||||
}
|
||||
audio_player_->playSound(sound_path, channel);
|
||||
} else { // 不使用空间定位
|
||||
audio_player_->playSound(sound_path, channel);
|
||||
}
|
||||
audio_player_->playSound(sound_path);
|
||||
}
|
||||
|
||||
void AudioComponent::addSound(std::string_view sound_id, std::string_view sound_path)
|
||||
|
||||
@@ -39,11 +39,10 @@ public:
|
||||
|
||||
/**
|
||||
* @brief 播放音效。
|
||||
* @param sound_path 音效文件的id (或路径)。
|
||||
* @param channel 要播放的特定通道,或 -1 表示第一个可用通道。
|
||||
* @param sound_id 音效文件的id (或路径)。
|
||||
* @param use_spatial 是否使用空间定位。
|
||||
*/
|
||||
void playSound(std::string_view sound_id, int channel = -1, bool use_spatial = false);
|
||||
void playSound(std::string_view sound_id, bool use_spatial = false);
|
||||
|
||||
/**
|
||||
* @brief 添加音效到映射表。
|
||||
|
||||
@@ -6,16 +6,16 @@ namespace engine::resource {
|
||||
|
||||
// 构造函数:初始化SDL_mixer
|
||||
AudioManager::AudioManager() {
|
||||
// 使用所需的格式初始化SDL_mixer(推荐OGG、MP3)
|
||||
MIX_InitFlags flags = MIX_INIT_OGG | MIX_INIT_MP3;
|
||||
if ((Mix_Init(flags) & flags) != flags) {
|
||||
throw std::runtime_error("AudioManager 错误: Mix_Init 失败: " + std::string(SDL_GetError()));
|
||||
// SDL_mixer 3.2.0 不再需要传入格式标志
|
||||
if (!MIX_Init()) {
|
||||
throw std::runtime_error("AudioManager 错误: MIX_Init 失败: " + std::string(SDL_GetError()));
|
||||
}
|
||||
|
||||
// SDL3打开音频设备的方法。默认值:44100 Hz,默认格式,2声道(立体声),2048采样块大小
|
||||
if (!Mix_OpenAudio(0, nullptr)) {
|
||||
Mix_Quit(); // 如果OpenAudio失败,先清理Mix_Init,再抛出异常
|
||||
throw std::runtime_error("AudioManager 错误: Mix_OpenAudio 失败: " + std::string(SDL_GetError()));
|
||||
// 创建连接到默认音频输出设备的混音器
|
||||
mixer_ = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr);
|
||||
if (!mixer_) {
|
||||
MIX_Quit();
|
||||
throw std::runtime_error("AudioManager 错误: MIX_CreateMixerDevice 失败: " + std::string(SDL_GetError()));
|
||||
}
|
||||
spdlog::trace("AudioManager 构造成功。");
|
||||
}
|
||||
@@ -23,44 +23,43 @@ AudioManager::AudioManager() {
|
||||
AudioManager::~AudioManager()
|
||||
{
|
||||
// 立即停止所有音频播放
|
||||
Mix_HaltChannel(-1); // 停止所有音效
|
||||
Mix_HaltMusic(); // 停止音乐
|
||||
MIX_StopAllTracks(mixer_, 0);
|
||||
|
||||
// 清理资源映射(unique_ptrs会调用删除器)
|
||||
clearSounds();
|
||||
clearMusic();
|
||||
|
||||
// 关闭音频设备
|
||||
Mix_CloseAudio();
|
||||
// 销毁混音器(同时关闭音频设备)
|
||||
MIX_DestroyMixer(mixer_);
|
||||
|
||||
// 退出SDL_mixer子系统
|
||||
Mix_Quit();
|
||||
MIX_Quit();
|
||||
spdlog::trace("AudioManager 析构成功。");
|
||||
}
|
||||
|
||||
// --- 音效管理 ---
|
||||
Mix_Chunk* AudioManager::loadSound(std::string_view file_path) {
|
||||
MIX_Audio* AudioManager::loadSound(std::string_view file_path) {
|
||||
// 首先检查缓存
|
||||
auto it = sounds_.find(std::string(file_path));
|
||||
if (it != sounds_.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// 加载音效块
|
||||
// 加载音效块(predecode=true,预先解码为PCM,适合短音效)
|
||||
spdlog::debug("加载音效: {}", file_path);
|
||||
Mix_Chunk* raw_chunk = Mix_LoadWAV(file_path.data());
|
||||
if (!raw_chunk) {
|
||||
MIX_Audio* audio = MIX_LoadAudio(mixer_, file_path.data(), true);
|
||||
if (!audio) {
|
||||
spdlog::error("加载音效失败: '{}': {}", file_path, SDL_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 使用unique_ptr存储在缓存中
|
||||
sounds_.emplace(file_path, std::unique_ptr<Mix_Chunk, SDLMixChunkDeleter>(raw_chunk));
|
||||
sounds_.emplace(file_path, std::unique_ptr<MIX_Audio, MIXAudioDeleter>(audio));
|
||||
spdlog::debug("成功加载并缓存音效: {}", file_path);
|
||||
return raw_chunk;
|
||||
return audio;
|
||||
}
|
||||
|
||||
Mix_Chunk* AudioManager::getSound(std::string_view file_path) {
|
||||
MIX_Audio* AudioManager::getSound(std::string_view file_path) {
|
||||
auto it = sounds_.find(std::string(file_path));
|
||||
if (it != sounds_.end()) {
|
||||
return it->second.get();
|
||||
@@ -73,7 +72,7 @@ void AudioManager::unloadSound(std::string_view file_path) {
|
||||
auto it = sounds_.find(std::string(file_path));
|
||||
if (it != sounds_.end()) {
|
||||
spdlog::debug("卸载音效: {}", file_path);
|
||||
sounds_.erase(it); // unique_ptr处理Mix_FreeChunk
|
||||
sounds_.erase(it); // unique_ptr处理MIX_DestroyAudio
|
||||
} else {
|
||||
spdlog::warn("尝试卸载不存在的音效: {}", file_path);
|
||||
}
|
||||
@@ -87,28 +86,28 @@ void AudioManager::clearSounds() {
|
||||
}
|
||||
|
||||
// --- 音乐管理 ---
|
||||
Mix_Music* AudioManager::loadMusic(std::string_view file_path) {
|
||||
MIX_Audio* AudioManager::loadMusic(std::string_view file_path) {
|
||||
// 首先检查缓存
|
||||
auto it = music_.find(std::string(file_path));
|
||||
if (it != music_.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// 加载音乐
|
||||
// 加载音乐(predecode=false,流式解码,适合长音乐)
|
||||
spdlog::debug("加载音乐: {}", file_path);
|
||||
Mix_Music* raw_music = Mix_LoadMUS(file_path.data());
|
||||
if (!raw_music) {
|
||||
MIX_Audio* audio = MIX_LoadAudio(mixer_, file_path.data(), false);
|
||||
if (!audio) {
|
||||
spdlog::error("加载音乐失败: '{}': {}", file_path, SDL_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 使用unique_ptr存储在缓存中
|
||||
music_.emplace(file_path, std::unique_ptr<Mix_Music, SDLMixMusicDeleter>(raw_music));
|
||||
music_.emplace(file_path, std::unique_ptr<MIX_Audio, MIXAudioDeleter>(audio));
|
||||
spdlog::debug("成功加载并缓存音乐: {}", file_path);
|
||||
return raw_music;
|
||||
return audio;
|
||||
}
|
||||
|
||||
Mix_Music* AudioManager::getMusic(std::string_view file_path) {
|
||||
MIX_Audio* AudioManager::getMusic(std::string_view file_path) {
|
||||
auto it = music_.find(std::string(file_path));
|
||||
if (it != music_.end()) {
|
||||
return it->second.get();
|
||||
@@ -121,7 +120,7 @@ void AudioManager::unloadMusic(std::string_view file_path) {
|
||||
auto it = music_.find(std::string(file_path));
|
||||
if (it != music_.end()) {
|
||||
spdlog::debug("卸载音乐: {}", file_path);
|
||||
music_.erase(it); // unique_ptr处理Mix_FreeMusic
|
||||
music_.erase(it); // unique_ptr处理MIX_DestroyAudio
|
||||
} else {
|
||||
spdlog::warn("尝试卸载不存在的音乐: {}", file_path);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace engine::resource {
|
||||
|
||||
/**
|
||||
* @brief 管理 SDL_mixer 音效 (Mix_Chunk) 和音乐 (Mix_Music)。
|
||||
* @brief 管理 SDL_mixer 音效和音乐 (统一为 MIX_Audio 类型)。
|
||||
*
|
||||
* 提供音频资源的加载和缓存功能。构造失败时会抛出异常。
|
||||
* 仅供 ResourceManager 内部使用。
|
||||
@@ -19,33 +19,26 @@ class AudioManager final{
|
||||
friend class ResourceManager;
|
||||
|
||||
private:
|
||||
// Mix_Chunk 的自定义删除器
|
||||
struct SDLMixChunkDeleter {
|
||||
void operator()(Mix_Chunk* chunk) const {
|
||||
if (chunk) {
|
||||
Mix_FreeChunk(chunk);
|
||||
// MIX_Audio 的自定义删除器(音效和音乐统一类型)
|
||||
struct MIXAudioDeleter {
|
||||
void operator()(MIX_Audio* audio) const {
|
||||
if (audio) {
|
||||
MIX_DestroyAudio(audio);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Mix_Music 的自定义删除器
|
||||
struct SDLMixMusicDeleter {
|
||||
void operator()(Mix_Music* music) const {
|
||||
if (music) {
|
||||
Mix_FreeMusic(music);
|
||||
}
|
||||
}
|
||||
};
|
||||
MIX_Mixer* mixer_{nullptr}; ///< @brief SDL_mixer 混音器实例
|
||||
|
||||
// 音效存储 (文件路径 -> Mix_Chunk)
|
||||
std::unordered_map<std::string, std::unique_ptr<Mix_Chunk, SDLMixChunkDeleter>> sounds_;
|
||||
// 音乐存储 (文件路径 -> Mix_Music)
|
||||
std::unordered_map<std::string, std::unique_ptr<Mix_Music, SDLMixMusicDeleter>> music_;
|
||||
// 音效存储 (文件路径 -> MIX_Audio,预解码)
|
||||
std::unordered_map<std::string, std::unique_ptr<MIX_Audio, MIXAudioDeleter>> sounds_;
|
||||
// 音乐存储 (文件路径 -> MIX_Audio,流式解码)
|
||||
std::unordered_map<std::string, std::unique_ptr<MIX_Audio, MIXAudioDeleter>> music_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数。初始化 SDL_mixer 并打开音频设备。
|
||||
* @throws std::runtime_error 如果 SDL_mixer 初始化或打开音频设备失败。
|
||||
* @brief 构造函数。初始化 SDL_mixer 并创建音频设备混音器。
|
||||
* @throws std::runtime_error 如果 SDL_mixer 初始化或创建混音器失败。
|
||||
*/
|
||||
AudioManager();
|
||||
|
||||
@@ -57,15 +50,17 @@ public:
|
||||
AudioManager(AudioManager&&) = delete;
|
||||
AudioManager& operator=(AudioManager&&) = delete;
|
||||
|
||||
MIX_Mixer* getMixer() const { return mixer_; } ///< @brief 获取混音器指针
|
||||
|
||||
private: // 仅供 ResourceManager 访问的方法
|
||||
|
||||
Mix_Chunk* loadSound(std::string_view file_path); ///< @brief 从文件路径加载音效
|
||||
Mix_Chunk* getSound(std::string_view file_path); ///< @brief 尝试获取已加载音效的指针,如果未加载则尝试加载
|
||||
MIX_Audio* loadSound(std::string_view file_path); ///< @brief 从文件路径加载音效(预解码)
|
||||
MIX_Audio* getSound(std::string_view file_path); ///< @brief 尝试获取已加载音效的指针,如果未加载则尝试加载
|
||||
void unloadSound(std::string_view file_path); ///< @brief 卸载指定的音效资源
|
||||
void clearSounds(); ///< @brief 清空所有音效资源
|
||||
|
||||
Mix_Music* loadMusic(std::string_view file_path); ///< @brief 从文件路径加载音乐
|
||||
Mix_Music* getMusic(std::string_view file_path); ///< @brief 尝试获取已加载音乐的指针,如果未加载则尝试加载
|
||||
MIX_Audio* loadMusic(std::string_view file_path); ///< @brief 从文件路径加载音乐(流式解码)
|
||||
MIX_Audio* getMusic(std::string_view file_path); ///< @brief 尝试获取已加载音乐的指针,如果未加载则尝试加载
|
||||
void unloadMusic(std::string_view file_path); ///< @brief 卸载指定的音乐资源
|
||||
void clearMusic(); ///< @brief 清空所有音乐资源
|
||||
|
||||
|
||||
@@ -52,11 +52,11 @@ void ResourceManager::clearTextures() {
|
||||
}
|
||||
|
||||
// --- 音频接口实现 ---
|
||||
Mix_Chunk* ResourceManager::loadSound(std::string_view file_path) {
|
||||
MIX_Audio* ResourceManager::loadSound(std::string_view file_path) {
|
||||
return audio_manager_->loadSound(file_path);
|
||||
}
|
||||
|
||||
Mix_Chunk* ResourceManager::getSound(std::string_view file_path) {
|
||||
MIX_Audio* ResourceManager::getSound(std::string_view file_path) {
|
||||
return audio_manager_->getSound(file_path);
|
||||
}
|
||||
|
||||
@@ -68,11 +68,11 @@ void ResourceManager::clearSounds() {
|
||||
audio_manager_->clearSounds();
|
||||
}
|
||||
|
||||
Mix_Music* ResourceManager::loadMusic(std::string_view file_path) {
|
||||
MIX_Audio* ResourceManager::loadMusic(std::string_view file_path) {
|
||||
return audio_manager_->loadMusic(file_path);
|
||||
}
|
||||
|
||||
Mix_Music* ResourceManager::getMusic(std::string_view file_path) {
|
||||
MIX_Audio* ResourceManager::getMusic(std::string_view file_path) {
|
||||
return audio_manager_->getMusic(file_path);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,10 @@ void ResourceManager::clearMusic() {
|
||||
audio_manager_->clearMusic();
|
||||
}
|
||||
|
||||
MIX_Mixer* ResourceManager::getMixer() {
|
||||
return audio_manager_->getMixer();
|
||||
}
|
||||
|
||||
// --- 字体接口实现 ---
|
||||
TTF_Font* ResourceManager::loadFont(std::string_view file_path, int point_size) {
|
||||
return font_manager_->loadFont(file_path, point_size);
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
// 前向声明 SDL 类型
|
||||
struct SDL_Renderer;
|
||||
struct SDL_Texture;
|
||||
struct Mix_Chunk;
|
||||
struct Mix_Music;
|
||||
struct MIX_Audio;
|
||||
struct MIX_Mixer;
|
||||
struct TTF_Font;
|
||||
|
||||
namespace engine::resource {
|
||||
@@ -54,18 +54,21 @@ public:
|
||||
glm::vec2 getTextureSize(std::string_view file_path); ///< @brief 获取指定纹理的尺寸
|
||||
void clearTextures(); ///< @brief 清空所有纹理资源
|
||||
|
||||
// -- Sound Effects (Chunks) --
|
||||
Mix_Chunk* loadSound(std::string_view file_path); ///< @brief 载入音效资源
|
||||
Mix_Chunk* getSound(std::string_view file_path); ///< @brief 尝试获取已加载音效的指针,如果未加载则尝试加载
|
||||
// -- Sound Effects --
|
||||
MIX_Audio* loadSound(std::string_view file_path); ///< @brief 载入音效资源
|
||||
MIX_Audio* getSound(std::string_view file_path); ///< @brief 尝试获取已加载音效的指针,如果未加载则尝试加载
|
||||
void unloadSound(std::string_view file_path); ///< @brief 卸载指定的音效资源
|
||||
void clearSounds(); ///< @brief 清空所有音效资源
|
||||
|
||||
// -- Music --
|
||||
Mix_Music* loadMusic(std::string_view file_path); ///< @brief 载入音乐资源
|
||||
Mix_Music* getMusic(std::string_view file_path); ///< @brief 尝试获取已加载音乐的指针,如果未加载则尝试加载
|
||||
MIX_Audio* loadMusic(std::string_view file_path); ///< @brief 载入音乐资源
|
||||
MIX_Audio* getMusic(std::string_view file_path); ///< @brief 尝试获取已加载音乐的指针,如果未加载则尝试加载
|
||||
void unloadMusic(std::string_view file_path); ///< @brief 卸载指定的音乐资源
|
||||
void clearMusic(); ///< @brief 清空所有音乐资源
|
||||
|
||||
// -- Mixer --
|
||||
MIX_Mixer* getMixer(); ///< @brief 获取 SDL_mixer 混音器指针
|
||||
|
||||
// -- Fonts --
|
||||
TTF_Font* loadFont(std::string_view file_path, int point_size); ///< @brief 载入字体资源
|
||||
TTF_Font* getFont(std::string_view file_path, int point_size); ///< @brief 尝试获取已加载字体的指针,如果未加载则尝试加载
|
||||
|
||||
@@ -45,7 +45,7 @@ void JumpBehavior::update(float delta_time, AIComponent& ai_component) {
|
||||
auto is_on_ground = physics_component->hasCollidedBelow(); // 着地标志
|
||||
if (is_on_ground) { // 如果在地面上
|
||||
if (audio_component && jump_timer_ < 0.001f) { // 刚刚落地时(进入idle状态),如果有音频组件,播放音效
|
||||
audio_component->playSound("cry", -1, true); // 使用空间音频
|
||||
audio_component->playSound("cry", true); // 使用空间音频
|
||||
}
|
||||
jump_timer_ += delta_time; // 增加跳跃计时器
|
||||
physics_component->velocity_.x = 0.0f; // 停止水平移动(否则会有惯性)
|
||||
|
||||
Reference in New Issue
Block a user