358 lines
6.8 KiB
C++
358 lines
6.8 KiB
C++
#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 |