From 58661a94a6012b645787e047cb9eb544f06529ed Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 27 Feb 2024 16:23:16 +0300 Subject: [PATCH] update --- src/audio/ALAudio.cpp | 219 +++++++++++++++++++++++++----------------- src/audio/ALAudio.h | 83 +++++++++++----- src/audio/NoAudio.h | 6 +- src/audio/alutil.cpp | 2 +- src/audio/alutil.h | 73 ++++++++++---- src/audio/audio.cpp | 8 ++ src/audio/audio.h | 96 ++++++++++++++++-- 7 files changed, 349 insertions(+), 138 deletions(-) diff --git a/src/audio/ALAudio.cpp b/src/audio/ALAudio.cpp index 68853a76..fe955202 100644 --- a/src/audio/ALAudio.cpp +++ b/src/audio/ALAudio.cpp @@ -5,6 +5,105 @@ using namespace audio; +ALSound::ALSound(ALAudio* al, uint buffer, std::shared_ptr 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(AL::getSourcef(source, AL_SEC_OFFSET)); +} + +void ALSpeaker::setTime(duration_t time) { + AL_CHECK(alSourcef(source, AL_SEC_OFFSET, static_cast(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) : device(device), context(context) { @@ -26,22 +125,20 @@ ALAudio::ALAudio(ALCdevice* device, ALCcontext* context) } ALAudio::~ALAudio() { - for (ALSource* source : allsources) { - if (source->isPlaying()){ - alSourceStop(source->id); - alCheckErrorsMacro(); + for (uint source : allsources) { + int state = AL::getSourcei(source, AL_SOURCE_STATE); + if (state == AL_PLAYING || state == AL_PAUSED) { + AL_CHECK(alSourceStop(source)); } - alDeleteSources(1, &source->id); - alCheckErrorsMacro(); + AL_CHECK(alDeleteSources(1, &source)); } - for (ALBuffer* buffer : allbuffers){ - alDeleteBuffers(1, &buffer->id); - alCheckErrorsMacro(); + for (uint buffer : allbuffers){ + AL_CHECK(alDeleteBuffers(1, &buffer)); } - alcMakeContextCurrent(context); - alcDestroyContext(context); + AL_CHECK(alcMakeContextCurrent(context)); + AL_CHECK(alcDestroyContext(context)); if (!alcCloseDevice(device)) { std::cerr << "AL: device not closed!" << std::endl; } @@ -49,53 +146,6 @@ ALAudio::~ALAudio() { 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, bool keepPCM) { // TODO: implement return nullptr; @@ -110,59 +160,55 @@ ALAudio* ALAudio::create() { alcCloseDevice(device); return nullptr; } - if (!alCheckErrorsMacro()) { - return nullptr; - } + AL_CHECK(); std::cout << "AL: initialized" << std::endl; return new ALAudio(device, context); } -ALSource* ALAudio::getFreeSource(){ +uint ALAudio::getFreeSource(){ if (!freesources.empty()){ - ALSource* source = freesources.back(); + uint source = freesources.back(); freesources.pop_back(); return source; } if (allsources.size() == maxSources){ std::cerr << "attempted to create new source, but limit is " << maxSources << std::endl; - return nullptr; + return 0; } ALuint id; alGenSources(1, &id); - if (!alCheckErrorsMacro()) - return nullptr; + if (!AL_GET_ERORR()) + return 0; - ALSource* source = new ALSource(id); - allsources.push_back(source); - return source; + allsources.push_back(id); + return id; } -ALBuffer* ALAudio::getFreeBuffer(){ +uint ALAudio::getFreeBuffer(){ if (!freebuffers.empty()){ - ALBuffer* buffer = freebuffers.back(); + uint buffer = freebuffers.back(); freebuffers.pop_back(); return buffer; } if (allbuffers.size() == maxBuffers){ std::cerr << "attempted to create new ALbuffer, but limit is " << maxBuffers << std::endl; - return nullptr; + return 0; } ALuint id; alGenBuffers(1, &id); - if (!alCheckErrorsMacro()) { - return nullptr; + if (!AL_GET_ERORR()) { + return 0; } - ALBuffer* buffer = new ALBuffer(id); - allbuffers.push_back(buffer); - return buffer; + allbuffers.push_back(id); + return id; } -void ALAudio::freeSource(ALSource* source){ +void ALAudio::freeSource(uint source){ freesources.push_back(source); } -void ALAudio::freeBuffer(ALBuffer* buffer){ +void ALAudio::freeBuffer(uint buffer){ freebuffers.push_back(buffer); } @@ -171,7 +217,7 @@ std::vector ALAudio::getAvailableDevices() const { const ALCchar* devices; devices = alcGetString(device, ALC_DEVICE_SPECIFIER); - if (!alCheckErrorsMacro()) { + if (!AL_GET_ERORR()) { return devicesVec; } @@ -188,10 +234,11 @@ std::vector ALAudio::getAvailableDevices() const { 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 }; - alListener3f(AL_POSITION, position.x, position.y, position.z); - alCheckErrorsMacro(); - alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z); - alCheckErrorsMacro(); - alListenerfv(AL_ORIENTATION, listenerOri); - alCheckErrorsMacro(); + AL_CHECK(alListener3f(AL_POSITION, position.x, position.y, position.z)); + AL_CHECK(alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z)); + AL_CHECK(alListenerfv(AL_ORIENTATION, listenerOri)); +} + +void ALAudio::update(double delta) { + } diff --git a/src/audio/ALAudio.h b/src/audio/ALAudio.h index 89f6816e..164d6671 100644 --- a/src/audio/ALAudio.h +++ b/src/audio/ALAudio.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef __APPLE__ #include @@ -18,36 +19,70 @@ namespace audio { struct ALBuffer; + class ALAudio; - struct ALSource { - uint id; - ALSource(uint id) : id(id) {} + class ALSound : public Sound { + ALAudio* al; + uint buffer; + std::shared_ptr pcm; + duration_t duration; + public: + ALSound(ALAudio* al, uint buffer, std::shared_ptr pcm, bool keepPCM); + ~ALSound(); - bool isPlaying(); - bool setPosition(glm::vec3 position); - bool setVelocity(glm::vec3 velocity); - bool setBuffer(ALBuffer* buffer); - bool setLoop(bool loop); - bool setGain(float gain); - bool setPitch(float pitch); - bool play(); + duration_t getDuration() const override { + return duration; + } + + std::shared_ptr getPCM() const override { + return pcm; + } + + Speaker* newInstance(int priority) const override; }; - struct ALBuffer { - uint id; - ALBuffer(uint id) : id(id) {} - bool load(int format, const char* data, int size, int freq); + /// @brief AL source adapter + class ALSpeaker : public Speaker { + ALAudio* al; + 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 { ALCdevice* device; ALCcontext* context; - std::vector allsources; - std::vector freesources; + std::vector allsources; + std::vector freesources; - std::vector allbuffers; - std::vector freebuffers; + std::vector allbuffers; + std::vector freebuffers; uint maxSources; uint maxBuffers; @@ -56,10 +91,10 @@ namespace audio { public: ~ALAudio(); - ALSource* getFreeSource(); - ALBuffer* getFreeBuffer(); - void freeSource(ALSource* source); - void freeBuffer(ALBuffer* buffer); + uint getFreeSource(); + uint getFreeBuffer(); + void freeSource(uint source); + void freeBuffer(uint buffer); std::vector getAvailableDevices() const; @@ -72,6 +107,8 @@ namespace audio { glm::vec3 up ) override; + void update(double delta) override; + static ALAudio* create(); }; } diff --git a/src/audio/NoAudio.h b/src/audio/NoAudio.h index 41bf356e..1006bb83 100644 --- a/src/audio/NoAudio.h +++ b/src/audio/NoAudio.h @@ -19,8 +19,8 @@ namespace audio { return pcm; } - speakerid_t newInstance(int priority) const override { - return 0; + Speaker* newInstance(int priority) const override { + return nullptr; } }; @@ -37,6 +37,8 @@ namespace audio { glm::vec3 up ) override {} + void update(double delta) override {} + static NoAudio* create(); }; } diff --git a/src/audio/alutil.cpp b/src/audio/alutil.cpp index b4827ff3..42b2d38a 100644 --- a/src/audio/alutil.cpp +++ b/src/audio/alutil.cpp @@ -35,7 +35,7 @@ std::int32_t convert_to_int(char* buffer, std::size_t len){ 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(); if(error != AL_NO_ERROR){ std::cerr << "OpenAL ERROR (" << filename << ": " << line << ")\n" ; diff --git a/src/audio/alutil.h b/src/audio/alutil.h index 2462a073..ab60f081 100644 --- a/src/audio/alutil.h +++ b/src/audio/alutil.h @@ -11,9 +11,10 @@ #include #endif -#define alCheckErrorsMacro() check_al_errors(__FILE__, __LINE__) +#include -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( std::ifstream& file, @@ -31,22 +32,60 @@ char* load_wav( ALsizei& size ); -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; +namespace AL { + bool check_errors(const std::string& filename, const std::uint_fast32_t line); + + /// @brief alGetSourcef 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 getSourcef(uint source, ALenum field, float def=0.0f) { + float value = def; + AL_CHECK(alGetSourcef(source, field, &value)); + return value; + } + + /// @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; + } } } diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 16dfaaa6..aa8d1db6 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -5,6 +5,10 @@ #include "ALAudio.h" #include "NoAudio.h" +namespace audio { + extern Backend* backend; +} + audio::Backend* audio::backend = nullptr; void audio::initialize(bool enabled) { @@ -26,6 +30,10 @@ void audio::setListener( audio::backend->setListener(position, velocity, lookAt, up); } +void audio::update(double delta) { + audio::backend->update(delta); +} + void audio::close() { delete audio::backend; audio::backend = nullptr; diff --git a/src/audio/audio.h b/src/audio/audio.h index ed23a06c..7b16eec1 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -11,6 +11,14 @@ namespace audio { /// @brief duration unit is second using duration_t = float; + class Speaker; + + enum class State { + playing, + paused, + stopped + }; + /// @brief Pulse-code modulation data struct PCM { /// @brief May contain 8 bit and 16 bit PCM data @@ -46,20 +54,78 @@ namespace audio { /// @brief Create new sound instance /// @param priority instance priority. High priority instance can /// 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 - 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 { public: 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, bool keepPCM) = 0; virtual void setListener( @@ -68,14 +134,21 @@ namespace audio { glm::vec3 lookAt, glm::vec3 up ) = 0; - }; - extern Backend* backend; + virtual void update(double delta) = 0; + }; /// @brief Initialize audio system or use no audio mode /// @param enabled try to initialize actual audio 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, bool keepPCM); + /// @brief Configure 3D listener /// @param position listener position /// @param velocity listener velocity (used for Doppler effect) @@ -87,6 +160,11 @@ namespace audio { glm::vec3 lookAt, 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 extern void close(); };