diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 99466ddab..9fdd8d0b3 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -26,10 +26,14 @@ jobs:
brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest glm
- name: Configure
- run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_TESTS=ON -DVOXELENGINE_BUILD_APPDIR=1 -DVC_BUILD_NAME="${{ inputs.build_name }}"
+ run: |
+ cmake -DOPENAL_INCLUDE_DIR=$(brew --prefix openal-soft)/include \
+ -DOPENAL_LIBRARY=$(brew --prefix openal-soft)/lib/libopenal.dylib \
+ -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_TESTS=ON -DVOXELENGINE_BUILD_APPDIR=1 -DVC_BUILD_NAME="${{ inputs.build_name }}"
- name: Build
- run: cmake --build build -t install
+ run: |
+ cmake --build build -t install
- name: Make fix_dylibs.sh executable
run: chmod +x dev/fix_dylibs.sh
diff --git a/res/content/base/block_materials/carpet.json b/res/content/base/block_materials/carpet.json
index 4d4d11dca..00c3688ec 100644
--- a/res/content/base/block_materials/carpet.json
+++ b/res/content/base/block_materials/carpet.json
@@ -1,5 +1,6 @@
{
"steps-sound": "steps/carpet",
"place-sound": "blocks/carpet_place",
- "break-sound": "blocks/carpet_break"
+ "break-sound": "blocks/carpet_break",
+ "sound-absorption": 0.96
}
diff --git a/res/content/base/block_materials/glass.json b/res/content/base/block_materials/glass.json
index fd2e23238..8ad365675 100644
--- a/res/content/base/block_materials/glass.json
+++ b/res/content/base/block_materials/glass.json
@@ -1,5 +1,6 @@
{
"steps-sound": "steps/glass",
"place-sound": "blocks/glass_place",
- "break-sound": "blocks/glass_break"
+ "break-sound": "blocks/glass_break",
+ "sound-absorption": 0.3
}
diff --git a/res/content/base/block_materials/grass.json b/res/content/base/block_materials/grass.json
index 148c80c09..ff091aa95 100644
--- a/res/content/base/block_materials/grass.json
+++ b/res/content/base/block_materials/grass.json
@@ -1,5 +1,6 @@
{
"steps-sound": "steps/grass",
"place-sound": "steps/grass",
- "break-sound": "steps/grass"
+ "break-sound": "steps/grass",
+ "sound-absorption": 0.85
}
diff --git a/res/content/base/block_materials/grass_block.json b/res/content/base/block_materials/grass_block.json
index d45672a28..98ed4618a 100644
--- a/res/content/base/block_materials/grass_block.json
+++ b/res/content/base/block_materials/grass_block.json
@@ -2,5 +2,6 @@
"steps-sound": "steps/grass",
"place-sound": "blocks/ground_place",
"break-sound": "blocks/ground_break",
- "hit-sound": "blocks/ground_hit"
+ "hit-sound": "blocks/ground_hit",
+ "sound-absorption": 0.8
}
diff --git a/res/content/base/block_materials/ground.json b/res/content/base/block_materials/ground.json
index 951cb5dfe..8e78a8563 100644
--- a/res/content/base/block_materials/ground.json
+++ b/res/content/base/block_materials/ground.json
@@ -2,5 +2,6 @@
"steps-sound": "steps/ground",
"place-sound": "blocks/ground_place",
"break-sound": "blocks/ground_break",
- "hit-sound": "blocks/ground_hit"
+ "hit-sound": "blocks/ground_hit",
+ "sound-absorption": 0.7
}
diff --git a/res/content/base/block_materials/metal.json b/res/content/base/block_materials/metal.json
index 6f45903e1..0ab1b2b28 100644
--- a/res/content/base/block_materials/metal.json
+++ b/res/content/base/block_materials/metal.json
@@ -1,5 +1,6 @@
{
"steps-sound": "steps/metal",
"place-sound": "blocks/metal_place",
- "break-sound": "blocks/metal_break"
+ "break-sound": "blocks/metal_break",
+ "sound-absorption": 0.1
}
diff --git a/res/content/base/block_materials/sand.json b/res/content/base/block_materials/sand.json
index 67316ba36..c047aab9c 100644
--- a/res/content/base/block_materials/sand.json
+++ b/res/content/base/block_materials/sand.json
@@ -1,5 +1,6 @@
{
"steps-sound": "steps/sand",
"place-sound": "blocks/ground_place",
- "break-sound": "blocks/ground_break"
+ "break-sound": "blocks/ground_break",
+ "sound-absorption": 0.8
}
diff --git a/res/content/base/block_materials/stone.json b/res/content/base/block_materials/stone.json
index 43f579e7a..75052af52 100644
--- a/res/content/base/block_materials/stone.json
+++ b/res/content/base/block_materials/stone.json
@@ -2,5 +2,6 @@
"steps-sound": "steps/stone",
"place-sound": "blocks/stone_place",
"break-sound": "blocks/stone_break",
- "hit-sound": "blocks/stone_hit"
+ "hit-sound": "blocks/stone_hit",
+ "sound-absorption": 0.3
}
diff --git a/res/content/base/block_materials/wood.json b/res/content/base/block_materials/wood.json
index 0c8e9a469..edf1801c2 100644
--- a/res/content/base/block_materials/wood.json
+++ b/res/content/base/block_materials/wood.json
@@ -2,5 +2,6 @@
"steps-sound": "steps/wood",
"place-sound": "blocks/wood_place",
"break-sound": "blocks/wood_break",
- "hit-sound": "blocks/wood_hit"
+ "hit-sound": "blocks/wood_hit",
+ "sound-absorption": 0.5
}
diff --git a/res/layouts/pages/settings_audio.xml.lua b/res/layouts/pages/settings_audio.xml.lua
index c9b4be4b6..37609a900 100644
--- a/res/layouts/pages/settings_audio.xml.lua
+++ b/res/layouts/pages/settings_audio.xml.lua
@@ -24,6 +24,14 @@ function update_setting(x, id, name, postfix)
)
end
+function create_checkbox(id, name, tooltip)
+ tooltip = tooltip or ''
+ document.root:add(string.format(
+ "%s",
+ id, app.str_setting(id), gui.str(tooltip, "settings"), gui.str(name, "settings")
+ ))
+end
+
local initialized = false
function on_open()
@@ -71,4 +79,6 @@ function on_open()
if info then
selectbox.value = info.device_specifier
end
+
+ create_checkbox("audio.acoustic-effects", "Acoustic effects", "audio.acoustic-effects.tooltip")
end
diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua
index 900371e49..779ad50bf 100644
--- a/res/scripts/stdcmd.lua
+++ b/res/scripts/stdcmd.lua
@@ -17,6 +17,7 @@ console.add_command(
"clear",
"Clears the console",
function()
+ if not hud then error("command is unavailable in headless mode") return end
local document = Document.new("core:console")
document.log.text = ""
end
diff --git a/res/texts/en_US.txt b/res/texts/en_US.txt
index ae19130e3..a6c9c5391 100644
--- a/res/texts/en_US.txt
+++ b/res/texts/en_US.txt
@@ -17,10 +17,12 @@ devtools.traceback=Traceback (most recent call first)
devtools.output=Output
# Tooltips
+audio.acoustic-effects.tooltip=Acoustic effects may not be supported by your system
graphics.gamma.tooltip=Lighting brightness curve
graphics.backlight.tooltip=Backlight to prevent total darkness
graphics.dense-render.tooltip=Enables transparency in blocks like leaves
graphics.soft-lighting.tooltip=Enables blocks soft lighting
+graphics.advanced-render.tooltip=Use graphics pipeline supporting advanced effects like shadows, SSAO
# settings
settings.Controls Search Mode=Search by attached button name
diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt
index fd35f10ef..66dc40a42 100644
--- a/res/texts/ru_RU.txt
+++ b/res/texts/ru_RU.txt
@@ -38,10 +38,12 @@ error.dependency-version-not-met=Версия зависимости не соо
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
# Подсказки
+audio.acoustic-effects.tooltip=Акустические эффекты могут не поддерживаться системой
graphics.gamma.tooltip=Кривая яркости освещения
graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту
graphics.dense-render.tooltip=Включает прозрачность блоков, таких как листья
graphics.soft-lighting.tooltip=Включает мягкое освещение у блоков
+graphics.advanced-render.tooltip=Использовать графический конвейер, поддерживающий продвинутые эффекты, такие как тени и SSAO
# Меню
menu.Apply=Применить
@@ -111,6 +113,7 @@ settings.Borderless=Безрамочный
settings.Microphone=Микрофон
settings.microphone.None=Нет
settings.Clouds quality=Качество облаков
+settings.Acoustic effects=Акустические эффекты
# Управление
chunks.reload=Перезагрузить Чанки
diff --git a/src/audio/AL/ALAudio.cpp b/src/audio/AL/ALAudio.cpp
index 41ce83c08..e7494d306 100644
--- a/src/audio/AL/ALAudio.cpp
+++ b/src/audio/AL/ALAudio.cpp
@@ -40,6 +40,22 @@ static bool check_alc_errors(ALCdevice* device, const char* context) {
return true;
}
+static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti = nullptr;
+static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots = nullptr;
+static LPALGENEFFECTS alGenEffects = nullptr;
+static LPALDELETEEFFECTS alDeleteEffects = nullptr;
+static LPALISEFFECT alIsEffect = nullptr;
+static LPALEFFECTI alEffecti = nullptr;
+static LPALEFFECTF alEffectf = nullptr;
+static LPALGENFILTERS alGenFilters = nullptr;
+static LPALDELETEFILTERS alDeleteFilters = nullptr;
+static LPALISFILTER alIsFilter = nullptr;
+static LPALFILTERI alFilteri = nullptr;
+static LPALFILTERF alFilterf = nullptr;
+
+static inline constexpr int REVERB_EFFECT = 0;
+static inline constexpr int LOWPASS_FILTER = 0;
+
ALSound::ALSound(
ALAudio* al, uint buffer, const std::shared_ptr& pcm, bool keepPCM
)
@@ -376,12 +392,19 @@ void ALSpeaker::setLoop(bool loop) {
void ALSpeaker::play() {
paused = false;
manuallyStopped = false;
- auto p_channel = get_channel(this->channel);
+ auto channel = get_channel(this->channel);
AL_CHECK(alSourcef(
source,
AL_GAIN,
- volume * p_channel->getVolume()
+ volume * channel->getVolume()
));
+ if (al->useEffects && channel->isEffectsApplied()) {
+ AL_CHECK(alSource3i(source, AL_AUXILIARY_SEND_FILTER, al->effectSlots[0], 0, al->filters[0]));
+ //AL_CHECK(alSourcei(source, AL_DIRECT_FILTER, al->filters[LOWPASS_FILTER])); // TODO: use lowpass filter
+ } else {
+ AL_CHECK(alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, 0));
+ AL_CHECK(alSourcei(source, AL_DIRECT_FILTER, 0));
+ }
AL_CHECK(alSourcePlay(source));
}
@@ -455,15 +478,22 @@ int ALSpeaker::getPriority() const {
return priority;
}
-
bool ALSpeaker::isManuallyStopped() const {
return manuallyStopped;
}
static bool alc_enumeration_ext = false;
-ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
- : device(device), context(context) {
+ALAudio::ALAudio(
+ ALCdevice* device,
+ ALCcontext* context,
+ bool useEffects,
+ const AudioSettings& settings
+)
+ : device(device),
+ context(context),
+ settings(settings),
+ useEffects(useEffects) {
ALCint size;
alcGetIntegerv(device, ALC_ATTRIBUTES_SIZE, 1, &size);
std::vector attrs(size);
@@ -487,6 +517,11 @@ ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
}
alDopplerFactor(1.0 / 3.0);
+ alGetError();
+
+ if (useEffects) {
+ this->useEffects = initEffects();
+ }
}
ALAudio::~ALAudio() {
@@ -513,6 +548,80 @@ ALAudio::~ALAudio() {
context = nullptr;
}
+template
+static bool get_proc_address(const char* name, T& ptr) {
+ ptr = (T) alGetProcAddress(name);
+ return ptr != nullptr;
+}
+
+static bool request_efx_ext_functions() {
+ return get_proc_address("alAuxiliaryEffectSloti", alAuxiliaryEffectSloti)
+ && get_proc_address("alGenAuxiliaryEffectSlots", alGenAuxiliaryEffectSlots)
+ && get_proc_address("alGenEffects", alGenEffects)
+ && get_proc_address("alDeleteEffects", alDeleteEffects)
+ && get_proc_address("alIsEffect", alIsEffect)
+ && get_proc_address("alEffecti", alEffecti)
+ && get_proc_address("alEffectf", alEffectf)
+ && get_proc_address("alGenFilters", alGenFilters)
+ && get_proc_address("alDeleteFilters", alDeleteFilters)
+ && get_proc_address("alIsFilter", alIsFilter)
+ && get_proc_address("alFilteri", alFilteri)
+ && get_proc_address("alFilterf", alFilterf);
+}
+
+bool ALAudio::initEffects() {
+ if (!request_efx_ext_functions()) {
+ logger.error() << "could not get effects extension function pointers";
+ return false;
+ }
+ for (uint i = 0; i < maxEffectSlots; i++) {
+ effectSlots.emplace_back();
+ alGenAuxiliaryEffectSlots(1, &effectSlots[i]);
+ if (alGetError() != AL_NO_ERROR) {
+ break;
+ }
+ }
+ logger.info() << "created " << effectSlots.size() << " effect slots";
+
+ for (uint i = 0; i < 4; i++) {
+ effects.emplace_back();
+ alGenEffects(1, &effects[i]);
+ if (alGetError() != AL_NO_ERROR || !alIsEffect(effects[i])) {
+ logger.error() << "could not to create effect #" << i;
+ return false;
+ }
+ }
+ logger.info() << "created " << effects.size() << " effects";
+
+ for (uint i = 0; i < filters.size(); i++) {
+ alGenFilters(1, &filters[i]);
+ if (alGetError() != AL_NO_ERROR || !alIsFilter(filters[i])) {
+ logger.error() << "could not to create filter #" << i;
+ return false;
+ }
+ }
+
+ alEffecti(effects[REVERB_EFFECT], AL_EFFECT_TYPE, AL_EFFECT_REVERB);
+ if (alGetError() != AL_NO_ERROR) {
+ logger.error() << "reverb effect is not supported";
+ return false;
+ }
+
+ alFilteri(filters[LOWPASS_FILTER], AL_FILTER_TYPE, AL_FILTER_LOWPASS);
+ if (alGetError() != AL_NO_ERROR) {
+ logger.error() << "lowpass filter is not supported";
+ return false;
+ }
+ alFilterf(filters[LOWPASS_FILTER], AL_LOWPASS_GAIN, 1.0f);
+ alFilterf(filters[LOWPASS_FILTER], AL_LOWPASS_GAINHF, 0.01f);
+
+ alAuxiliaryEffectSloti(effectSlots[0], AL_EFFECTSLOT_EFFECT, effects[0]);
+ if (alGetError() == AL_NO_ERROR) {
+ logger.info() << "successfully loaded effect into effect slot";
+ }
+ return true;
+}
+
std::unique_ptr ALAudio::createSound(
std::shared_ptr pcm, bool keepPCM
) {
@@ -592,19 +701,36 @@ std::unique_ptr ALAudio::openInputDevice(
);
}
-std::unique_ptr ALAudio::create() {
+std::unique_ptr ALAudio::create(const AudioSettings& settings) {
alc_enumeration_ext = alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT");
ALCdevice* device = alcOpenDevice(nullptr);
- if (device == nullptr) return nullptr;
+ if (device == nullptr) {
+ return nullptr;
+ }
+ bool effects = true;
+ ALint attribs[4] {};
+ if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE) {
+ logger.info() << "AL effects extension present";
+ attribs[0] = ALC_MAX_AUXILIARY_SENDS;
+ attribs[1] = 4;
+ } else {
+ logger.warning() << "AL effects extension is not supported";
+ effects = false;
+ }
ALCcontext* context = alcCreateContext(device, nullptr);
if (!alcMakeContextCurrent(context)) {
alcCloseDevice(device);
return nullptr;
}
AL_CHECK();
+
+ ALCint sends = 0;
+ alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &sends);
+ logger.info() << "device supports " << sends << " aux sends per source";
+
logger.info() << "initialized";
- return std::make_unique(device, context);
+ return std::make_unique(device, context, effects, settings);
}
uint ALAudio::getFreeSource() {
@@ -664,3 +790,84 @@ void ALAudio::setListener(
void ALAudio::update(double) {
}
+
+void ALAudio::setAcoustics(Acoustics acoustics) {
+ if (!useEffects) {
+ return;
+ }
+ if (!settings.acousticEffects.get()) {
+ alAuxiliaryEffectSloti(effectSlots[0], AL_EFFECTSLOT_EFFECT, 0);
+ return;
+ }
+ int reverbEffect = effects[REVERB_EFFECT];
+ AL_CHECK(alEffectf(
+ reverbEffect,
+ AL_REVERB_GAIN,
+ glm::max(0.1f, glm::min(acoustics.reverbGain, 0.8f) *
+ (1.0f - glm::min(1.0f, acoustics.reverbAbsorption)))
+ ));
+ AL_CHECK(alEffectf(
+ reverbEffect,
+ AL_REVERB_REFLECTIONS_DELAY,
+ glm::clamp(
+ acoustics.reverbReflectionsDelay,
+ AL_REVERB_MIN_REFLECTIONS_DELAY,
+ AL_REVERB_MAX_REFLECTIONS_DELAY
+ )
+ ));
+ AL_CHECK(alEffectf(
+ reverbEffect,
+ AL_REVERB_LATE_REVERB_DELAY,
+ glm::clamp(
+ acoustics.reverbLateReflectionsDelay,
+ AL_REVERB_MIN_LATE_REVERB_DELAY,
+ AL_REVERB_MAX_LATE_REVERB_DELAY
+ )
+ ));
+ AL_CHECK(alEffectf(
+ reverbEffect,
+ AL_REVERB_DECAY_TIME,
+ glm::clamp(
+ acoustics.reverbDecayTime,
+ AL_REVERB_MIN_DECAY_TIME,
+ AL_REVERB_MAX_DECAY_TIME
+ )
+ ));
+ AL_CHECK(alEffectf(
+ reverbEffect,
+ AL_REVERB_ROOM_ROLLOFF_FACTOR,
+ glm::clamp(
+ acoustics.reverbRoomRolloff,
+ AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR,
+ AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR
+ )
+ ));
+ AL_CHECK(alEffectf(
+ reverbEffect,
+ AL_REVERB_REFLECTIONS_GAIN,
+ glm::clamp(
+ glm::pow(acoustics.reverbGain, 2.0f),
+ AL_REVERB_MIN_REFLECTIONS_GAIN,
+ AL_REVERB_MAX_REFLECTIONS_GAIN
+ )
+ ));
+ AL_CHECK(alEffectf(
+ reverbEffect,
+ AL_REVERB_LATE_REVERB_GAIN,
+ glm::clamp(
+ glm::pow(acoustics.reverbGain, 2.0f),
+ AL_REVERB_MIN_LATE_REVERB_GAIN,
+ AL_REVERB_MAX_LATE_REVERB_GAIN
+ )
+ ));
+ AL_CHECK(alEffectf(
+ reverbEffect,
+ AL_REVERB_DECAY_HFRATIO,
+ glm::clamp(
+ 1.0f - acoustics.reverbAbsorption,
+ AL_REVERB_MIN_DECAY_HFRATIO,
+ AL_REVERB_MAX_DECAY_HFRATIO
+ )
+ ));
+ alAuxiliaryEffectSloti(effectSlots[0], AL_EFFECTSLOT_EFFECT, reverbEffect);
+}
diff --git a/src/audio/AL/ALAudio.hpp b/src/audio/AL/ALAudio.hpp
index 5642f222b..b405d1473 100644
--- a/src/audio/AL/ALAudio.hpp
+++ b/src/audio/AL/ALAudio.hpp
@@ -3,19 +3,19 @@
#include
#include
#include
+#include
#include
#include
#include "typedefs.hpp"
#include "audio/audio.hpp"
+#include "audio/effects.hpp"
-#ifdef __APPLE__
-#include
-#include
-#else
#include
#include
-#endif
+#include
+
+struct AudioSettings;
namespace audio {
struct ALBuffer;
@@ -176,10 +176,25 @@ namespace audio {
std::vector allbuffers;
std::vector freebuffers;
-
uint maxSources = 256;
+ uint maxEffectSlots = 64;
+
+ const AudioSettings& settings;
+
+ bool initEffects();
public:
- ALAudio(ALCdevice* device, ALCcontext* context);
+ std::vector effectSlots;
+ std::vector effects;
+ std::array filters;
+
+ bool useEffects;
+
+ ALAudio(
+ ALCdevice* device,
+ ALCcontext* context,
+ bool effects,
+ const AudioSettings& settings
+ );
~ALAudio();
uint getFreeSource();
@@ -214,10 +229,12 @@ namespace audio {
void update(double delta) override;
+ void setAcoustics(Acoustics acoustics) override;
+
bool isDummy() const override {
return false;
}
- static std::unique_ptr create();
+ static std::unique_ptr create(const AudioSettings& settings);
};
}
diff --git a/src/audio/AL/alutil.hpp b/src/audio/AL/alutil.hpp
index bfa701184..56e5b0f7e 100644
--- a/src/audio/AL/alutil.hpp
+++ b/src/audio/AL/alutil.hpp
@@ -1,17 +1,12 @@
#pragma once
+#include "typedefs.hpp"
+
#include
#include
#include
-#include "typedefs.hpp"
-
-#ifdef __APPLE__
-#include
-#else
#include
-#endif
-
#include
#define AL_CHECK(STATEMENT) \
diff --git a/src/audio/NoAudio.hpp b/src/audio/NoAudio.hpp
index 0da1b4d63..c97794f36 100644
--- a/src/audio/NoAudio.hpp
+++ b/src/audio/NoAudio.hpp
@@ -104,6 +104,9 @@ namespace audio {
void update(double delta) override {
}
+ void setAcoustics(Acoustics) override {
+ }
+
bool isDummy() const override {
return true;
}
diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp
index dc0e57463..e8fd96e14 100644
--- a/src/audio/audio.cpp
+++ b/src/audio/audio.cpp
@@ -27,7 +27,8 @@ namespace {
static bool input_enabled = false;
}
-Channel::Channel(std::string name) : name(std::move(name)) {
+Channel::Channel(std::string name, bool effects)
+ : name(std::move(name)), effects(effects) {
}
float Channel::getVolume() const {
@@ -56,6 +57,10 @@ bool Channel::isPaused() const {
return paused;
}
+bool Channel::isEffectsApplied() const {
+ return effects;
+}
+
size_t PCMStream::readFully(char* buffer, size_t bufferSize, bool loop) {
if (!isOpen()) {
return 0;
@@ -159,7 +164,7 @@ void audio::initialize(
enabled = enabled && settings.enabled.get();
if (enabled) {
logger.info() << "initializing ALAudio backend";
- backend = ALAudio::create().release();
+ backend = ALAudio::create(settings).release();
}
if (backend == nullptr) {
if (enabled) {
@@ -171,15 +176,16 @@ void audio::initialize(
struct {
std::string name;
NumberSetting* setting;
+ bool effects;
} builtin_channels[] {
- {"master", &settings.volumeMaster},
- {"regular", &settings.volumeRegular},
- {"music", &settings.volumeMusic},
- {"ambient", &settings.volumeAmbient},
- {"ui", &settings.volumeUI}
+ {"master", &settings.volumeMaster, false},
+ {"regular", &settings.volumeRegular, true},
+ {"music", &settings.volumeMusic, false},
+ {"ambient", &settings.volumeAmbient, true},
+ {"ui", &settings.volumeUI, false}
};
for (auto& channel : builtin_channels) {
- create_channel(channel.name);
+ create_channel(channel.name, channel.effects);
objects_keeper.keepAlive(channel.setting->observe([=](auto value) {
audio::get_channel(channel.name)->setVolume(value * value);
}, true));
@@ -419,12 +425,12 @@ Speaker* audio::get_speaker(speakerid_t id) {
return found->second.get();
}
-int audio::create_channel(const std::string& name) {
+int audio::create_channel(const std::string& name, bool effects) {
int index = get_channel_index(name);
if (index != -1) {
return index;
}
- channels.emplace_back(std::make_unique(name));
+ channels.emplace_back(std::make_unique(name, effects));
return channels.size() - 1;
}
@@ -496,6 +502,10 @@ void audio::update(double delta) {
}
}
+void audio::set_acoustics(Acoustics acoustics) {
+ backend->setAcoustics(std::move(acoustics));
+}
+
void audio::reset_channel(int index) {
auto channel = get_channel(index);
if (channel == nullptr) {
diff --git a/src/audio/audio.hpp b/src/audio/audio.hpp
index 642b9235d..39304fd41 100644
--- a/src/audio/audio.hpp
+++ b/src/audio/audio.hpp
@@ -7,6 +7,7 @@
#include "typedefs.hpp"
#include "settings.hpp"
#include "io/fwd.hpp"
+#include "effects.hpp"
namespace audio {
/// @brief playing speaker uid
@@ -42,8 +43,9 @@ namespace audio {
/// @brief Channel volume setting
float volume = 1.0f;
bool paused = false;
+ bool effects;
public:
- Channel(std::string name);
+ Channel(std::string name, bool effects);
/// @brief Get channel volume
float getVolume() const;
@@ -72,6 +74,9 @@ namespace audio {
/// @brief Check if the channel is paused
bool isPaused() const;
+
+ /// @brief Check if the channel uses common acoustic effects / filters
+ bool isEffectsApplied() const;
};
/// @brief Pulse-code modulation data
@@ -394,6 +399,7 @@ namespace audio {
virtual std::vector getInputDeviceNames() = 0;
virtual std::vector getOutputDeviceNames() = 0;
virtual void update(double delta) = 0;
+ virtual void setAcoustics(Acoustics acoustics) = 0;
/// @brief Check if backend is an abstraction that does not internally
/// work with actual audio data or play anything
@@ -547,8 +553,9 @@ namespace audio {
/// @brief Create new channel.
/// All non-builtin channels will be destroyed on audio::reset() call
/// @param name channel name
+ /// @param effects use common acoustic effects / filters
/// @return new channel index
- int create_channel(const std::string& name);
+ int create_channel(const std::string& name, bool effects);
/// @brief Get channel index by name
/// @param name channel name
@@ -580,6 +587,8 @@ namespace audio {
/// @param delta time elapsed since the last update (seconds)
void update(double delta);
+ void set_acoustics(Acoustics acoustics);
+
/// @brief Stop all playing audio in channel, reset channel state
void reset_channel(int channel);
diff --git a/src/audio/effects.hpp b/src/audio/effects.hpp
new file mode 100644
index 000000000..f8f10e4af
--- /dev/null
+++ b/src/audio/effects.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include