This commit is contained in:
MihailRis 2024-02-27 16:23:16 +03:00
parent 97539aa9f4
commit 58661a94a6
7 changed files with 349 additions and 138 deletions

View File

@ -5,6 +5,105 @@
using namespace audio; using namespace audio;
ALSound::ALSound(ALAudio* al, uint buffer, std::shared_ptr<PCM> pcm, bool keepPCM)
: al(al), buffer(buffer)
{
duration = pcm->getDuration();
if (keepPCM) {
this->pcm = pcm;
}
}
ALSound::~ALSound() {
al->freeBuffer(buffer);
buffer = 0;
}
Speaker* ALSound::newInstance(int priority) const {
uint source = al->getFreeSource();
if (source == 0) {
return nullptr;
}
return new ALSpeaker(al, source, priority);
}
ALSpeaker::ALSpeaker(ALAudio* al, uint source, int priority) : al(al), source(source), priority(priority) {
}
ALSpeaker::~ALSpeaker() {
if (source) {
stop();
}
}
State ALSpeaker::getState() const {
int state = AL::getSourcei(source, AL_SOURCE_STATE, AL_STOPPED);
switch (state) {
case AL_PLAYING: return State::playing;
case AL_PAUSED: return State::paused;
default: return State::stopped;
}
}
float ALSpeaker::getVolume() const {
return AL::getSourcef(source, AL_GAIN);
}
void ALSpeaker::setVolume(float volume) {
AL_CHECK(alSourcef(source, AL_GAIN, volume));
}
float ALSpeaker::getPitch() const {
return AL::getSourcef(source, AL_PITCH);
}
void ALSpeaker::setPitch(float pitch) {
AL_CHECK(alSourcef(source, AL_PITCH, pitch));
}
void ALSpeaker::play() {
AL_CHECK(alSourcePlay(source));
}
void ALSpeaker::pause() {
AL_CHECK(alSourcePause(source));
}
void ALSpeaker::stop() {
AL_CHECK(alSourceStop(source));
al->freeSource(source);
source = 0;
}
duration_t ALSpeaker::getTime() const {
return static_cast<duration_t>(AL::getSourcef(source, AL_SEC_OFFSET));
}
void ALSpeaker::setTime(duration_t time) {
AL_CHECK(alSourcef(source, AL_SEC_OFFSET, static_cast<float>(time)));
}
void ALSpeaker::setPosition(glm::vec3 pos) {
AL_CHECK(alSource3f(source, AL_POSITION, pos.x, pos.y, pos.z));
}
glm::vec3 ALSpeaker::getPosition() const {
return AL::getSource3f(source, AL_POSITION);
}
void ALSpeaker::setVelocity(glm::vec3 vel) {
AL_CHECK(alSource3f(source, AL_VELOCITY, vel.x, vel.y, vel.z));
}
glm::vec3 ALSpeaker::getVelocity() const {
return AL::getSource3f(source, AL_VELOCITY);
}
int ALSpeaker::getPriority() const {
return priority;
}
ALAudio::ALAudio(ALCdevice* device, ALCcontext* context) ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
: device(device), context(context) : device(device), context(context)
{ {
@ -26,22 +125,20 @@ ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
} }
ALAudio::~ALAudio() { ALAudio::~ALAudio() {
for (ALSource* source : allsources) { for (uint source : allsources) {
if (source->isPlaying()){ int state = AL::getSourcei(source, AL_SOURCE_STATE);
alSourceStop(source->id); if (state == AL_PLAYING || state == AL_PAUSED) {
alCheckErrorsMacro(); AL_CHECK(alSourceStop(source));
} }
alDeleteSources(1, &source->id); AL_CHECK(alDeleteSources(1, &source));
alCheckErrorsMacro();
} }
for (ALBuffer* buffer : allbuffers){ for (uint buffer : allbuffers){
alDeleteBuffers(1, &buffer->id); AL_CHECK(alDeleteBuffers(1, &buffer));
alCheckErrorsMacro();
} }
alcMakeContextCurrent(context); AL_CHECK(alcMakeContextCurrent(context));
alcDestroyContext(context); AL_CHECK(alcDestroyContext(context));
if (!alcCloseDevice(device)) { if (!alcCloseDevice(device)) {
std::cerr << "AL: device not closed!" << std::endl; std::cerr << "AL: device not closed!" << std::endl;
} }
@ -49,53 +146,6 @@ ALAudio::~ALAudio() {
context = nullptr; context = nullptr;
} }
bool ALSource::setBuffer(ALBuffer* buffer) {
alSourcei(id, AL_BUFFER, buffer->id);
return alCheckErrorsMacro();
}
bool ALSource::play(){
alSourcePlay(id);
return alCheckErrorsMacro();
}
bool ALSource::isPlaying() {
int state;
alGetSourcei(id, AL_SOURCE_STATE, &state);
return state == AL_PLAYING;
}
bool ALSource::setPosition(glm::vec3 position) {
alSource3f(id, AL_POSITION, position.x, position.y, position.z);
return alCheckErrorsMacro();
}
bool ALSource::setVelocity(glm::vec3 velocity) {
alSource3f(id, AL_VELOCITY, velocity.x, velocity.y, velocity.z);
return alCheckErrorsMacro();
}
bool ALSource::setLoop(bool loop) {
alSourcei(id, AL_LOOPING, AL_TRUE ? loop : AL_FALSE);
return alCheckErrorsMacro();
}
bool ALSource::setGain(float gain) {
alSourcef(id, AL_GAIN, gain);
return alCheckErrorsMacro();
}
bool ALSource::setPitch(float pitch) {
alSourcef(id, AL_PITCH, pitch);
return alCheckErrorsMacro();
}
bool ALBuffer::load(int format, const char* data, int size, int freq) {
alBufferData(id, format, data, size, freq);
return alCheckErrorsMacro();
}
Sound* ALAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) { Sound* ALAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
// TODO: implement // TODO: implement
return nullptr; return nullptr;
@ -110,59 +160,55 @@ ALAudio* ALAudio::create() {
alcCloseDevice(device); alcCloseDevice(device);
return nullptr; return nullptr;
} }
if (!alCheckErrorsMacro()) { AL_CHECK();
return nullptr;
}
std::cout << "AL: initialized" << std::endl; std::cout << "AL: initialized" << std::endl;
return new ALAudio(device, context); return new ALAudio(device, context);
} }
ALSource* ALAudio::getFreeSource(){ uint ALAudio::getFreeSource(){
if (!freesources.empty()){ if (!freesources.empty()){
ALSource* source = freesources.back(); uint source = freesources.back();
freesources.pop_back(); freesources.pop_back();
return source; return source;
} }
if (allsources.size() == maxSources){ if (allsources.size() == maxSources){
std::cerr << "attempted to create new source, but limit is " << maxSources << std::endl; std::cerr << "attempted to create new source, but limit is " << maxSources << std::endl;
return nullptr; return 0;
} }
ALuint id; ALuint id;
alGenSources(1, &id); alGenSources(1, &id);
if (!alCheckErrorsMacro()) if (!AL_GET_ERORR())
return nullptr; return 0;
ALSource* source = new ALSource(id); allsources.push_back(id);
allsources.push_back(source); return id;
return source;
} }
ALBuffer* ALAudio::getFreeBuffer(){ uint ALAudio::getFreeBuffer(){
if (!freebuffers.empty()){ if (!freebuffers.empty()){
ALBuffer* buffer = freebuffers.back(); uint buffer = freebuffers.back();
freebuffers.pop_back(); freebuffers.pop_back();
return buffer; return buffer;
} }
if (allbuffers.size() == maxBuffers){ if (allbuffers.size() == maxBuffers){
std::cerr << "attempted to create new ALbuffer, but limit is " << maxBuffers << std::endl; std::cerr << "attempted to create new ALbuffer, but limit is " << maxBuffers << std::endl;
return nullptr; return 0;
} }
ALuint id; ALuint id;
alGenBuffers(1, &id); alGenBuffers(1, &id);
if (!alCheckErrorsMacro()) { if (!AL_GET_ERORR()) {
return nullptr; return 0;
} }
ALBuffer* buffer = new ALBuffer(id); allbuffers.push_back(id);
allbuffers.push_back(buffer); return id;
return buffer;
} }
void ALAudio::freeSource(ALSource* source){ void ALAudio::freeSource(uint source){
freesources.push_back(source); freesources.push_back(source);
} }
void ALAudio::freeBuffer(ALBuffer* buffer){ void ALAudio::freeBuffer(uint buffer){
freebuffers.push_back(buffer); freebuffers.push_back(buffer);
} }
@ -171,7 +217,7 @@ std::vector<std::string> ALAudio::getAvailableDevices() const {
const ALCchar* devices; const ALCchar* devices;
devices = alcGetString(device, ALC_DEVICE_SPECIFIER); devices = alcGetString(device, ALC_DEVICE_SPECIFIER);
if (!alCheckErrorsMacro()) { if (!AL_GET_ERORR()) {
return devicesVec; return devicesVec;
} }
@ -188,10 +234,11 @@ std::vector<std::string> ALAudio::getAvailableDevices() const {
void ALAudio::setListener(glm::vec3 position, glm::vec3 velocity, glm::vec3 at, glm::vec3 up){ void ALAudio::setListener(glm::vec3 position, glm::vec3 velocity, glm::vec3 at, glm::vec3 up){
ALfloat listenerOri[] = { at.x, at.y, at.z, up.x, up.y, up.z }; ALfloat listenerOri[] = { at.x, at.y, at.z, up.x, up.y, up.z };
alListener3f(AL_POSITION, position.x, position.y, position.z); AL_CHECK(alListener3f(AL_POSITION, position.x, position.y, position.z));
alCheckErrorsMacro(); AL_CHECK(alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z));
alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z); AL_CHECK(alListenerfv(AL_ORIENTATION, listenerOri));
alCheckErrorsMacro(); }
alListenerfv(AL_ORIENTATION, listenerOri);
alCheckErrorsMacro(); void ALAudio::update(double delta) {
} }

View File

@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <unordered_map>
#ifdef __APPLE__ #ifdef __APPLE__
#include <OpenAL/al.h> #include <OpenAL/al.h>
@ -18,36 +19,70 @@
namespace audio { namespace audio {
struct ALBuffer; struct ALBuffer;
class ALAudio;
struct ALSource { class ALSound : public Sound {
uint id; ALAudio* al;
ALSource(uint id) : id(id) {} uint buffer;
std::shared_ptr<PCM> pcm;
duration_t duration;
public:
ALSound(ALAudio* al, uint buffer, std::shared_ptr<PCM> pcm, bool keepPCM);
~ALSound();
bool isPlaying(); duration_t getDuration() const override {
bool setPosition(glm::vec3 position); return duration;
bool setVelocity(glm::vec3 velocity); }
bool setBuffer(ALBuffer* buffer);
bool setLoop(bool loop); std::shared_ptr<PCM> getPCM() const override {
bool setGain(float gain); return pcm;
bool setPitch(float pitch); }
bool play();
Speaker* newInstance(int priority) const override;
}; };
struct ALBuffer { /// @brief AL source adapter
uint id; class ALSpeaker : public Speaker {
ALBuffer(uint id) : id(id) {} ALAudio* al;
bool load(int format, const char* data, int size, int freq); uint source;
int priority;
public:
ALSpeaker(ALAudio* al, uint source, int priority);
~ALSpeaker();
State getState() const override;
float getVolume() const override;
void setVolume(float volume) override;
float getPitch() const override;
void setPitch(float pitch) override;
void play() override;
void pause() override;
void stop() override;
duration_t getTime() const override;
void setTime(duration_t time) override;
void setPosition(glm::vec3 pos) override;
glm::vec3 getPosition() const override;
void setVelocity(glm::vec3 vel) override;
glm::vec3 getVelocity() const override;
int getPriority() const override;
}; };
class ALAudio : public Backend { class ALAudio : public Backend {
ALCdevice* device; ALCdevice* device;
ALCcontext* context; ALCcontext* context;
std::vector<ALSource*> allsources; std::vector<uint> allsources;
std::vector<ALSource*> freesources; std::vector<uint> freesources;
std::vector<ALBuffer*> allbuffers; std::vector<uint> allbuffers;
std::vector<ALBuffer*> freebuffers; std::vector<uint> freebuffers;
uint maxSources; uint maxSources;
uint maxBuffers; uint maxBuffers;
@ -56,10 +91,10 @@ namespace audio {
public: public:
~ALAudio(); ~ALAudio();
ALSource* getFreeSource(); uint getFreeSource();
ALBuffer* getFreeBuffer(); uint getFreeBuffer();
void freeSource(ALSource* source); void freeSource(uint source);
void freeBuffer(ALBuffer* buffer); void freeBuffer(uint buffer);
std::vector<std::string> getAvailableDevices() const; std::vector<std::string> getAvailableDevices() const;
@ -72,6 +107,8 @@ namespace audio {
glm::vec3 up glm::vec3 up
) override; ) override;
void update(double delta) override;
static ALAudio* create(); static ALAudio* create();
}; };
} }

View File

@ -19,8 +19,8 @@ namespace audio {
return pcm; return pcm;
} }
speakerid_t newInstance(int priority) const override { Speaker* newInstance(int priority) const override {
return 0; return nullptr;
} }
}; };
@ -37,6 +37,8 @@ namespace audio {
glm::vec3 up glm::vec3 up
) override {} ) override {}
void update(double delta) override {}
static NoAudio* create(); static NoAudio* create();
}; };
} }

View File

@ -35,7 +35,7 @@ std::int32_t convert_to_int(char* buffer, std::size_t len){
return a; return a;
} }
bool check_al_errors(const std::string& filename, const std::uint_fast32_t line){ bool AL::check_errors(const std::string& filename, const std::uint_fast32_t line){
ALenum error = alGetError(); ALenum error = alGetError();
if(error != AL_NO_ERROR){ if(error != AL_NO_ERROR){
std::cerr << "OpenAL ERROR (" << filename << ": " << line << ")\n" ; std::cerr << "OpenAL ERROR (" << filename << ": " << line << ")\n" ;

View File

@ -11,9 +11,10 @@
#include <AL/al.h> #include <AL/al.h>
#endif #endif
#define alCheckErrorsMacro() check_al_errors(__FILE__, __LINE__) #include <glm/glm.hpp>
bool check_al_errors(const std::string& filename, const std::uint_fast32_t line); #define AL_CHECK(STATEMENT) STATEMENT; AL::check_errors(__FILE__, __LINE__)
#define AL_GET_ERORR() AL::check_errors(__FILE__, __LINE__)
bool load_wav_file_header( bool load_wav_file_header(
std::ifstream& file, std::ifstream& file,
@ -31,22 +32,60 @@ char* load_wav(
ALsizei& size ALsizei& size
); );
static inline ALenum to_al_format(short channels, short samples){
bool stereo = (channels > 1);
switch (samples) { namespace AL {
case 16: bool check_errors(const std::string& filename, const std::uint_fast32_t line);
if (stereo)
return AL_FORMAT_STEREO16; /// @brief alGetSourcef wrapper
else /// @param source target source
return AL_FORMAT_MONO16; /// @param field enum value
case 8: /// @param def default value will be returned in case of error
if (stereo) /// @return field value or default
return AL_FORMAT_STEREO8; inline float getSourcef(uint source, ALenum field, float def=0.0f) {
else float value = def;
return AL_FORMAT_MONO8; AL_CHECK(alGetSourcef(source, field, &value));
default: return value;
return -1; }
/// @brief alGetSource3f wrapper
/// @param source target source
/// @param field enum value
/// @param def default value will be returned in case of error
/// @return field value or default
inline glm::vec3 getSource3f(uint source, ALenum field, glm::vec3 def={}) {
glm::vec3 value = def;
AL_CHECK(alGetSource3f(source, field, &value.x, &value.y, &value.z));
return value;
}
/// @brief alGetSourcei wrapper
/// @param source target source
/// @param field enum value
/// @param def default value will be returned in case of error
/// @return field value or default
inline float getSourcei(uint source, ALenum field, int def=0) {
int value = def;
AL_CHECK(alGetSourcei(source, field, &value));
return value;
}
static inline ALenum to_al_format(short channels, short samples){
bool stereo = (channels > 1);
switch (samples) {
case 16:
if (stereo)
return AL_FORMAT_STEREO16;
else
return AL_FORMAT_MONO16;
case 8:
if (stereo)
return AL_FORMAT_STEREO8;
else
return AL_FORMAT_MONO8;
default:
return -1;
}
} }
} }

View File

@ -5,6 +5,10 @@
#include "ALAudio.h" #include "ALAudio.h"
#include "NoAudio.h" #include "NoAudio.h"
namespace audio {
extern Backend* backend;
}
audio::Backend* audio::backend = nullptr; audio::Backend* audio::backend = nullptr;
void audio::initialize(bool enabled) { void audio::initialize(bool enabled) {
@ -26,6 +30,10 @@ void audio::setListener(
audio::backend->setListener(position, velocity, lookAt, up); audio::backend->setListener(position, velocity, lookAt, up);
} }
void audio::update(double delta) {
audio::backend->update(delta);
}
void audio::close() { void audio::close() {
delete audio::backend; delete audio::backend;
audio::backend = nullptr; audio::backend = nullptr;

View File

@ -11,6 +11,14 @@ namespace audio {
/// @brief duration unit is second /// @brief duration unit is second
using duration_t = float; using duration_t = float;
class Speaker;
enum class State {
playing,
paused,
stopped
};
/// @brief Pulse-code modulation data /// @brief Pulse-code modulation data
struct PCM { struct PCM {
/// @brief May contain 8 bit and 16 bit PCM data /// @brief May contain 8 bit and 16 bit PCM data
@ -46,20 +54,78 @@ namespace audio {
/// @brief Create new sound instance /// @brief Create new sound instance
/// @param priority instance priority. High priority instance can /// @param priority instance priority. High priority instance can
/// take out speaker from low priority instance /// take out speaker from low priority instance
/// @return new speaker id with sound bound or 0 /// @return new speaker with sound bound or nullptr
/// if all speakers are in use /// if all speakers are in use
virtual speakerid_t newInstance(int priority) const = 0; virtual Speaker* newInstance(int priority) const = 0;
};
/// @brief Audio source controller interface
class Speaker {
public:
virtual ~Speaker() {}
/// @brief Get current speaker state
/// @return speaker state
virtual State getState() const = 0;
/// @brief Get speaker audio gain
/// @return speaker audio gain value
virtual float getVolume() const = 0;
/// @brief Set speaker audio gain (must be positive)
/// @param volume new gain value
virtual void setVolume(float volume) = 0;
/// @brief Get speaker pitch multiplier
/// @return pitch multiplier
virtual float getPitch() const = 0;
/// @brief Set speaker pitch multiplier
/// @param pitch new pitch multiplier (must be positive)
virtual void setPitch(float pitch) = 0;
/// @brief Play, replay or resume audio
virtual void play() = 0;
/// @brief Pause playing audio and keep speaker alive
virtual void pause() = 0;
/// @brief Stop and destroy speaker
virtual void stop() = 0;
/// @brief Get current time position of playing audio
/// @return time position in seconds
virtual duration_t getTime() const = 0;
/// @brief Set playing audio time position
/// @param time time position in seconds
virtual void setTime(duration_t time) = 0;
/// @brief Set speaker 3D position in the world
/// @param pos new position
virtual void setPosition(glm::vec3 pos) = 0;
/// @brief Get speaker 3D position in the world
/// @return position
virtual glm::vec3 getPosition() const = 0;
/// @brief Set speaker movement velocity used for Doppler effect
/// @param vel velocity vector
virtual void setVelocity(glm::vec3 vel) = 0;
/// @brief Get speaker movement velocity used for Doppler effect
/// @return velocity vector
virtual glm::vec3 getVelocity() const = 0;
/// @brief Get speaker priority
/// @return speaker priority value
virtual int getPriority() const = 0;
}; };
class Backend { class Backend {
public: public:
virtual ~Backend() {}; virtual ~Backend() {};
/// @brief Create new sound from PCM data
/// @param pcm PCM data
/// @param keepPCM store PCM data in sound to make it accessible with
/// Sound::getPCM
/// @return new Sound instance
virtual Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0; virtual Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0;
virtual void setListener( virtual void setListener(
@ -68,14 +134,21 @@ namespace audio {
glm::vec3 lookAt, glm::vec3 lookAt,
glm::vec3 up glm::vec3 up
) = 0; ) = 0;
};
extern Backend* backend; virtual void update(double delta) = 0;
};
/// @brief Initialize audio system or use no audio mode /// @brief Initialize audio system or use no audio mode
/// @param enabled try to initialize actual audio /// @param enabled try to initialize actual audio
extern void initialize(bool enabled); extern void initialize(bool enabled);
/// @brief Create new sound from PCM data
/// @param pcm PCM data
/// @param keepPCM store PCM data in sound to make it accessible with
/// Sound::getPCM
/// @return new Sound instance
extern Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM);
/// @brief Configure 3D listener /// @brief Configure 3D listener
/// @param position listener position /// @param position listener position
/// @param velocity listener velocity (used for Doppler effect) /// @param velocity listener velocity (used for Doppler effect)
@ -87,6 +160,11 @@ namespace audio {
glm::vec3 lookAt, glm::vec3 lookAt,
glm::vec3 up glm::vec3 up
); );
/// @brief Update audio streams and sound instanced
/// @param delta time since the last update (seconds)
extern void update(double delta);
/// @brief Finalize audio system /// @brief Finalize audio system
extern void close(); extern void close();
}; };