Files
2026-07-01 00:02:34 +08:00

358 lines
6.8 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 "dl_audio.h"
#ifndef DL_DISABLE_AUDIO
//#define AL_LIBTYPE_STATIC
#include <AL/al.h>
#include <AL/alc.h>
#include "dl_range.h"
#include "base/dl_string.h"
#include "io/dl_file.h"
#include "dl_audio_loader.h"
#include "render/dl_factory.h"
namespace dl
{
class SoundImp
{
public:
ALuint _source;
};
class AudioImp
{
public:
std::array<ALuint, NUM_MAX_FILE> _allBuffer;
std::array<bool, NUM_MAX_FILE> _allFlag;//使用标记
AudioImp()
{
assert((void*)&_allFlag == (void*)&_allFlag[0]);
_allFlag.fill(false);
}
};
Sound::Sound() :
_deleteWaitStop{ true }
{
}
void Sound::Play()
{
if (!g_audio->IsOpen())
return;
alSourcePlay(_source);
if (alGetError() != AL_NO_ERROR)
{
log_err0("alSourcePlay失败!");
}
}
void Sound::Stop()
{
if (!g_audio->IsOpen())
return;
alSourceStop(_source);
if (alGetError() != AL_NO_ERROR)
{
log_err0("alSourceStop失败!");
}
}
void Sound::SetLoop(bool loop)
{
if (!g_audio->IsOpen())
return;
alSourcei(_source, AL_LOOPING, loop);
}
void Sound::SetVolume(float volume)
{
if (!g_audio->IsOpen())
return;
alSourcef(_source, AL_GAIN, volume);
}
bool Sound::IsPlaying()
{
if (!g_audio->IsOpen())
return false;
ALint isplaying;
alGetSourcei(_source, AL_SOURCE_STATE, &isplaying);
if (isplaying == AL_PLAYING)
{
return true;
}
return false;
}
Sound::~Sound()
{
g_audio->_delete_sound(this);
}
Audio::Audio(bool open) :
_bSetPath{ false },
_bOpen{ open }
{
if (!_bOpen)
return;
//初始化
ALCdevice* device = alcOpenDevice(nullptr);//选择首选设备
if (!device)
{
log_err("音频设备获取失败!");
_bOpen = false;
return;
}
_imp = new AudioImp;
if (device)
{
ALCcontext* context = alcCreateContext(device, nullptr);
alcMakeContextCurrent(context);
}
//检查EAX 2.0支持
bool b_EAX = alIsExtensionPresent("EAX2.0");
//生成缓冲区
alGetError(); //清除错误码
//生成缓冲区
alGenBuffers(NUM_MAX_FILE, _imp->_allBuffer.data());
if (alGetError() != AL_NO_ERROR)
{
log_err0("生成缓冲区失败!");
return;
}
//音高倍数
//alSourcef(source, AL_PITCH, 1.0f);
////声音的增益
//alSourcef(source, AL_GAIN, 1.0f);
////设置位置
//ALfloat pos[] = { 0.0f,0.0f,0.0f };
//alSourcefv(source, AL_POSITION, pos);
////设置声音移动速度
//ALfloat velocity[] = {0.0f,0.0f,0.0f};
//alSourcefv(source, AL_VELOCITY, velocity);
////设置是否循环播放
//alSourcei(source, AL_LOOPING, true);
}
void Audio::Init(std::string_view path_name)
{
json j = Json::ParseFile(path_name);
if (j.is_null())
return;
std::string path;
if (Json::Get(j, "path", path))
SetPath(path);
Json::Get(j, "whitelist", _vecWhitelist);
}
constexpr std::string_view SUPPORT_FORMAT[] = { "wav", "ogg", "flac", "mp3" };
void Audio::Load(std::string_view name, std::string_view path_name)
{
if (!_bOpen)
return;
auto iter = _mapName2Buffer.find(std::string{ name });
if (iter != _mapName2Buffer.end())
{
log_err("资源名字已经存在:{},id:{}", name, iter->second);
return;
}
//格式检查
std::string str_extension = File::GetExtension(path_name);
if (!Any::Equal(SUPPORT_FORMAT, str_extension))
{
log_err("不支持的音频文件格式:{}", path_name);
for (auto& iter : SUPPORT_FORMAT)
{
log_err("支持的音频文件格式:{}", iter);
}
return;
}
AudioLoader::PCM pcm;
if (str_extension == "wav")
{
if (!AudioLoader::ReadWav(path_name, pcm))
{
log_err("解析wav文件失败:{}", path_name);
return;
}
}
else if (str_extension == "ogg")
{
if (!AudioLoader::ReadOgg(path_name, pcm))
{
log_err("解析ogg文件失败:{}", path_name);
return;
}
}
else if (str_extension == "flac")
{
if (!AudioLoader::ReadFlac(path_name, pcm))
{
log_err("解析flac文件失败:{}", path_name);
return;
}
}
else if (str_extension == "mp3")
{
if (!AudioLoader::ReadMp3(path_name, pcm))
{
log_err("解析mp3文件失败:{}", path_name);
return;
}
}
else
{
log_err("解析格式暂未实现:{}", path_name);
return;
}
//寻找空位
size_t index = Find::FirstIndex(_imp->_allFlag, false);
if (index == -1)
{
log_err0("无可用缓冲区,尝试修改 NUM_MAX_FILE!");
return;
}
//计算format
ALenum format = 0;
if (pcm._numChannel == 1)
{
if (pcm._bitPerSample == 8)
format = AL_FORMAT_MONO8;
else if (pcm._bitPerSample == 16)
format = AL_FORMAT_MONO16;
else
format = AL_FORMAT_MONO16;
}
else if (pcm._numChannel == 2)
{
if (pcm._bitPerSample == 8)
format = AL_FORMAT_STEREO8;
else if (pcm._bitPerSample == 16)
format = AL_FORMAT_STEREO16;
else
format = AL_FORMAT_STEREO16;
}
assert(format);
//填充buff
alBufferData(_imp->_allBuffer[index], format,
pcm._buf.data(), (ALsizei)pcm._buf.size(), (ALsizei)pcm._freq);
if (alGetError() != AL_NO_ERROR)
{
log_err0("填充缓冲区失败!");
return;
}
_imp->_allFlag[index] = true;
_mapName2Buffer[std::string{ name }] = index;
log_msg("加载音频文件成功:{},{},{}", path_name, name, index);
}
std::vector<std::string> Audio::GetAllSound()
{
std::vector<std::string> ret;
for (auto& iter : _mapName2Buffer)
{
ret.push_back(iter.first);
}
return ret;
}
gcSound Audio::Create(std::string_view name)
{
if (!_bOpen)
return CreateDefault();
auto iter = _mapName2Buffer.find(std::string{ name });
if (iter == _mapName2Buffer.end())
{
log_err("资源不存在:{}", name);
return CreateDefault();
}
//生成source
ALuint source;
alGenSources(1, &source);
if (alGetError() != AL_NO_ERROR)
{
log_err("生成源失败:{}", name);
return CreateDefault();
}
//附加buffer到source
alSourcei(source, AL_BUFFER, _imp->_allBuffer[iter->second]);
if (alGetError() != AL_NO_ERROR)
{
log_err("alSourcei失败:{}", name);
return CreateDefault();
}
gcSound ret = get_gc<Sound>();
ret->_source = source;
return ret;
}
void Audio::_load()
{
if (!_bSetPath)
return;
// 加载音效文件
std::vector<std::string> mul_file;
File::GetPathFileAll(_strPath, mul_file, ".(wav|ogg|flac|mp3)$");
bool load_all = _vecWhitelist.empty();
for (std::string& path_name : mul_file)
{
std::string name = String::GetResName(_strPath, path_name);
if (load_all || Any::Equal(_vecWhitelist, name))
g_audio->Load(name, path_name);
else
log_msg("跳过加载一个音效(未在白名单内):{}", name);
}
}
void Audio::_delete_sound(Sound* snd)
{
if (!g_audio->IsOpen())
return;
if (!snd->_deleteWaitStop)
{
alDeleteSources(1, &snd->_source);
}
else
_listDelete.push_back(snd->_source);
}
Audio::~Audio()
{
if (!_bOpen)
return;
for (auto& iter : _listDelete)
{
alDeleteSources(1, &iter);
}
alDeleteBuffers(NUM_MAX_FILE, _imp->_allBuffer.data());
ALCcontext* context = alcGetCurrentContext();
ALCdevice* device = alcGetContextsDevice(context);
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
alcCloseDevice(device);
delete _imp;
}
}
#endif