mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-01-03 01:19:38 +01:00
Ported from godot-plus: Add ability to mute AudioServer.
Adds the option to change the audio driver to the Dummy driver and back at runtime, with a set of MuteState flags - Disabled (user control), Silence (period of silence), Focus Loss (when app is not in focus), and Paused (when app is paused).
Control for the flags is added for the editor in EditorSettings, and for the project in ProjectSettings.
Editor defaults to muted (Dummy driver) when there is no audio output, and automatically switches to active on output. This significantly reduces CPU usage.
- lawnjelly
cedb01fb84
This commit is contained in:
parent
8ab1998fcb
commit
40a9b72afb
@ -181,6 +181,12 @@
|
||||
If [code]true[/code], the bus at index [code]bus_idx[/code] is in solo mode.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_enabled" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
If [code]false[/code], the audio server is disabled / muted.
|
||||
</description>
|
||||
</method>
|
||||
<method name="lock">
|
||||
<return type="void" />
|
||||
<description>
|
||||
@ -274,6 +280,13 @@
|
||||
Sets the volume of the bus at index [code]bus_idx[/code] to [code]volume_db[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_enabled">
|
||||
<return type="void" />
|
||||
<argument index="0" name="enabled" type="bool" />
|
||||
<description>
|
||||
Allows disabling / muting the audio server. As well as muting, this will minimize audio CPU usage.
|
||||
</description>
|
||||
</method>
|
||||
<method name="swap_bus_effects">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bus_idx" type="int" />
|
||||
|
@ -294,6 +294,19 @@
|
||||
<member name="audio/mix_rate.web" type="int" setter="" getter="" default="0">
|
||||
Safer override for [member audio/mix_rate] in the Web platform. Here [code]0[/code] means "let the browser choose" (since some browsers do not like forcing the mix rate).
|
||||
</member>
|
||||
<member name="audio/muting/mute_driver" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], the current audio driver will be disabled, minimizing CPU usage. This will affect both audio output and input.
|
||||
[b]Note:[/b] The driver can be enabled and disabled at runtime using the [method AudioServer.set_enabled] function.
|
||||
</member>
|
||||
<member name="audio/muting/mute_on_focus_loss" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], the current audio driver will be automatically disabled when the application loses focus, and re-enabled when the application regains focus.
|
||||
</member>
|
||||
<member name="audio/muting/mute_on_pause" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], the current audio driver will be automatically disabled when the application is paused by the OS, and re-enabled when the application is resumed.
|
||||
</member>
|
||||
<member name="audio/muting/mute_on_silence" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], the current audio driver will be automatically disabled after a period of silence is detected, and re-enabled when audio attempts to play.
|
||||
</member>
|
||||
<member name="audio/output_latency" type="int" setter="" getter="" default="15">
|
||||
Specifies the preferred output latency in milliseconds for audio. Lower values will result in lower audio latency at the cost of increased CPU usage. Low values may result in audible cracking on slower hardware.
|
||||
Audio output latency may be constrained by the host operating system and audio hardware drivers. If the host can not provide the specified audio output latency then Godot will attempt to use the nearest latency allowed by the host. As such you should always use [method AudioServer.get_output_latency] to determine the actual audio output latency.
|
||||
|
@ -6035,6 +6035,11 @@ EditorNode::EditorNode() {
|
||||
EditorSettings::get_singleton()->set("", file_changed_action);
|
||||
}
|
||||
|
||||
AudioServer::get_singleton()->set_enabled(!EDITOR_DEF_RST("interface/audio/muting/mute_driver", false));
|
||||
AudioDriverManager::set_mute_sensitivity(AudioDriverManager::MUTE_FLAG_SILENCE, EDITOR_DEF_RST("interface/audio/muting/mute_on_silence", true));
|
||||
AudioDriverManager::set_mute_sensitivity(AudioDriverManager::MUTE_FLAG_PAUSED, EDITOR_DEF_RST("interface/audio/muting/mute_on_pause", true));
|
||||
AudioDriverManager::set_mute_sensitivity(AudioDriverManager::MUTE_FLAG_FOCUS_LOSS, EDITOR_DEF_RST("interface/audio/muting/mute_on_focus_loss", true));
|
||||
|
||||
EDITOR_DEF_RST("interface/scene_tabs/restore_scenes_on_load", false);
|
||||
EDITOR_DEF_RST("interface/scene_tabs/show_thumbnail_on_hover", true);
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "scene_tree.h"
|
||||
|
||||
#include "core/input/shortcut.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/object/message_queue.h"
|
||||
@ -44,12 +45,12 @@
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/animation/scene_tree_tween.h"
|
||||
#include "scene/debugger/script_debugger_remote.h"
|
||||
#include "core/input/shortcut.h"
|
||||
#include "scene/resources/dynamic_font.h"
|
||||
#include "scene/resources/material.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
#include "servers/audio_server.h"
|
||||
#include "servers/navigation_server.h"
|
||||
#include "servers/physics_2d_server.h"
|
||||
#include "servers/physics_server.h"
|
||||
@ -57,8 +58,8 @@
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For freetype.
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/world_3d.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
#include "scene/resources/world_3d.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -638,7 +639,7 @@ bool SceneTree::idle(float p_time) {
|
||||
if (multiplayer_poll) {
|
||||
multiplayer->poll();
|
||||
}
|
||||
|
||||
|
||||
ThreadPool::get_singleton()->update();
|
||||
|
||||
emit_signal("idle_frame");
|
||||
@ -837,6 +838,8 @@ void SceneTree::_notification(int p_notification) {
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_WM_FOCUS_IN: {
|
||||
AudioDriverManager::set_mute_flag(AudioDriverManager::MUTE_FLAG_FOCUS_LOSS, false);
|
||||
|
||||
InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
|
||||
if (id) {
|
||||
id->ensure_touch_mouse_raised();
|
||||
@ -845,6 +848,21 @@ void SceneTree::_notification(int p_notification) {
|
||||
get_root()->propagate_notification(p_notification);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_WM_FOCUS_OUT: {
|
||||
AudioDriverManager::set_mute_flag(AudioDriverManager::MUTE_FLAG_FOCUS_LOSS, true);
|
||||
get_root()->propagate_notification(p_notification);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_APP_PAUSED: {
|
||||
AudioDriverManager::set_mute_flag(AudioDriverManager::MUTE_FLAG_PAUSED, true);
|
||||
get_root()->propagate_notification(p_notification);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_APP_RESUMED: {
|
||||
AudioDriverManager::set_mute_flag(AudioDriverManager::MUTE_FLAG_PAUSED, false);
|
||||
get_root()->propagate_notification(p_notification);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
if (!Engine::get_singleton()->is_editor_hint()) {
|
||||
get_root()->propagate_notification(p_notification);
|
||||
@ -862,11 +880,8 @@ void SceneTree::_notification(int p_notification) {
|
||||
case NOTIFICATION_OS_IME_UPDATE:
|
||||
case NOTIFICATION_WM_MOUSE_ENTER:
|
||||
case NOTIFICATION_WM_MOUSE_EXIT:
|
||||
case NOTIFICATION_WM_FOCUS_OUT:
|
||||
case NOTIFICATION_WM_ABOUT:
|
||||
case NOTIFICATION_CRASH:
|
||||
case NOTIFICATION_APP_RESUMED:
|
||||
case NOTIFICATION_APP_PAUSED: {
|
||||
case NOTIFICATION_CRASH: {
|
||||
get_root()->propagate_notification(p_notification);
|
||||
} break;
|
||||
|
||||
|
@ -30,8 +30,8 @@
|
||||
|
||||
#include "audio_driver_dummy.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
Error AudioDriverDummy::init() {
|
||||
active.clear();
|
||||
@ -98,6 +98,7 @@ void AudioDriverDummy::finish() {
|
||||
|
||||
if (samples_in) {
|
||||
memdelete_arr(samples_in);
|
||||
samples_in = nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -30,20 +30,26 @@
|
||||
|
||||
#include "audio_server.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "scene/resources/audio_stream_sample.h"
|
||||
#include "servers/audio/audio_driver_dummy.h"
|
||||
#include "servers/audio/effects/audio_effect_compressor.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "main/main.h"
|
||||
#define MARK_EDITED set_edited(true);
|
||||
#else
|
||||
#define MARK_EDITED
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ENABLED
|
||||
// Compile with this define to get logging output (via print_verbose).
|
||||
// #define AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
#endif
|
||||
|
||||
AudioDriver *AudioDriver::singleton = nullptr;
|
||||
AudioDriver *AudioDriver::get_singleton() {
|
||||
return singleton;
|
||||
@ -173,6 +179,13 @@ AudioDriver *AudioDriverManager::drivers[MAX_DRIVERS] = {
|
||||
&AudioDriverManager::dummy_driver,
|
||||
};
|
||||
int AudioDriverManager::driver_count = 1;
|
||||
int AudioDriverManager::desired_driver_id = -2;
|
||||
int AudioDriverManager::actual_driver_id = -2;
|
||||
|
||||
// Defaults are for the editor, outside the editor will be overridden.
|
||||
uint32_t AudioDriverManager::_mute_state = AudioDriverManager::MuteFlags::MUTE_FLAG_DISABLED;
|
||||
uint32_t AudioDriverManager::_mute_state_final = AudioDriverManager::MuteFlags::MUTE_FLAG_DISABLED;
|
||||
uint32_t AudioDriverManager::_mute_state_mask = UINT32_MAX;
|
||||
|
||||
void AudioDriverManager::add_driver(AudioDriver *p_driver) {
|
||||
ERR_FAIL_COND(driver_count >= MAX_DRIVERS);
|
||||
@ -186,12 +199,39 @@ int AudioDriverManager::get_driver_count() {
|
||||
return driver_count;
|
||||
}
|
||||
|
||||
void AudioDriverManager::initialize(int p_driver) {
|
||||
GLOBAL_DEF_RST("audio/enable_audio_input", false);
|
||||
GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
|
||||
GLOBAL_DEF_RST("audio/mix_rate.web", 0); // Safer default output_latency for web (use browser default).
|
||||
GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
|
||||
GLOBAL_DEF_RST("audio/output_latency.web", 50); // Safer default output_latency for web.
|
||||
void AudioDriverManager::_log(String p_sz, int p_driver_id) {
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
if ((p_driver_id >= 0) && (p_driver_id < driver_count)) {
|
||||
p_sz += get_driver(p_driver_id)->get_name();
|
||||
}
|
||||
print_verbose(p_sz);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioDriverManager::_set_driver(int p_driver) {
|
||||
desired_driver_id = p_driver;
|
||||
|
||||
if (!is_active()) {
|
||||
// last driver is always the dummy
|
||||
p_driver = driver_count - 1;
|
||||
}
|
||||
|
||||
// noop
|
||||
if (actual_driver_id == p_driver) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool needs_start = false;
|
||||
|
||||
// give the previous driver a chance to finish
|
||||
if ((actual_driver_id != -2) && (actual_driver_id < driver_count)) {
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("finishing audio driver ", actual_driver_id);
|
||||
#endif
|
||||
AudioDriverManager::get_driver(actual_driver_id)->finish();
|
||||
actual_driver_id = -2;
|
||||
needs_start = true;
|
||||
}
|
||||
|
||||
int failed_driver = -1;
|
||||
|
||||
@ -199,9 +239,22 @@ void AudioDriverManager::initialize(int p_driver) {
|
||||
if (p_driver >= 0 && p_driver < driver_count) {
|
||||
if (drivers[p_driver]->init() == OK) {
|
||||
drivers[p_driver]->set_singleton();
|
||||
actual_driver_id = p_driver;
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("init success audio driver ", actual_driver_id);
|
||||
#endif
|
||||
if (needs_start) {
|
||||
drivers[p_driver]->start();
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("\tstarting driver ", p_driver);
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
failed_driver = p_driver;
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("init failure audio driver ", p_driver);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,20 +267,111 @@ void AudioDriverManager::initialize(int p_driver) {
|
||||
|
||||
if (drivers[i]->init() == OK) {
|
||||
drivers[i]->set_singleton();
|
||||
actual_driver_id = i;
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("init success (any) audio driver ", actual_driver_id);
|
||||
#endif
|
||||
if (needs_start) {
|
||||
drivers[p_driver]->start();
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("\tstarting driver ", p_driver);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (driver_count > 1 && String(AudioDriver::get_singleton()->get_name()) == "Dummy") {
|
||||
if (is_active() && (driver_count > 1) && (String(AudioDriver::get_singleton()->get_name()) == "Dummy")) {
|
||||
WARN_PRINT("All audio drivers failed, falling back to the dummy driver.");
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverManager::initialize(int p_driver) {
|
||||
bool mute_driver = GLOBAL_DEF("audio/muting/mute_driver", false);
|
||||
|
||||
GLOBAL_DEF_RST("audio/enable_audio_input", false);
|
||||
GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
|
||||
GLOBAL_DEF_RST("audio/mix_rate.web", 0); // Safer default output_latency for web (use browser default).
|
||||
GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
|
||||
GLOBAL_DEF_RST("audio/output_latency.web", 50); // Safer default output_latency for web.
|
||||
|
||||
// These Project settings are only actually set and used outside the editor.
|
||||
// The Editor uses EditorSettings defined in editor_node.cpp.
|
||||
bool mute_on_pause = GLOBAL_DEF("audio/muting/mute_on_pause", true);
|
||||
bool mute_on_silence = GLOBAL_DEF("audio/muting/mute_on_silence", false);
|
||||
bool mute_on_focus_loss = GLOBAL_DEF("audio/muting/mute_on_focus_loss", false);
|
||||
|
||||
// Defaults for outside the editor
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!(Engine::get_singleton()->is_editor_hint() || Main::is_project_manager())) {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
// Note that these can be set differently on different platforms if desired.
|
||||
// e.g. Android may want to ensure mute when app paused etc.
|
||||
_mute_state = mute_driver ? MuteFlags::MUTE_FLAG_DISABLED : 0;
|
||||
|
||||
// Sensitive to all but focus and quiet mode
|
||||
_mute_state_mask = MuteFlags::MUTE_FLAG_DISABLED;
|
||||
if (mute_on_pause) {
|
||||
_mute_state_mask |= MuteFlags::MUTE_FLAG_PAUSED;
|
||||
}
|
||||
if (mute_on_silence) {
|
||||
_mute_state_mask |= MuteFlags::MUTE_FLAG_SILENCE;
|
||||
}
|
||||
if (mute_on_focus_loss) {
|
||||
_mute_state_mask |= MuteFlags::MUTE_FLAG_FOCUS_LOSS;
|
||||
}
|
||||
|
||||
_update_mute_state();
|
||||
}
|
||||
|
||||
_set_driver(p_driver);
|
||||
}
|
||||
|
||||
AudioDriver *AudioDriverManager::get_driver(int p_driver) {
|
||||
ERR_FAIL_INDEX_V(p_driver, driver_count, nullptr);
|
||||
return drivers[p_driver];
|
||||
}
|
||||
|
||||
void AudioDriverManager::_update_mute_state() {
|
||||
_mute_state_final = _mute_state & _mute_state_mask;
|
||||
}
|
||||
|
||||
void AudioDriverManager::set_mute_sensitivity(MuteFlags p_flag, bool p_enabled) {
|
||||
if (p_enabled) {
|
||||
_mute_state_mask |= p_flag;
|
||||
} else {
|
||||
_mute_state_mask &= ~p_flag;
|
||||
}
|
||||
_update_mute_state();
|
||||
_set_driver(desired_driver_id);
|
||||
}
|
||||
|
||||
void AudioDriverManager::set_mute_flag(MuteFlags p_flag, bool p_enabled) {
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("set_mute_flag " + itos(p_flag) + " " + String(Variant(p_enabled)) + ", flags was " + itos(_mute_state) + " (final flags " + itos(_mute_state_final) + ")");
|
||||
#endif
|
||||
bool enabled = _mute_state & p_flag;
|
||||
|
||||
if (enabled == p_enabled) {
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("\tno change");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (p_enabled) {
|
||||
_mute_state |= p_flag;
|
||||
} else {
|
||||
_mute_state &= ~p_flag;
|
||||
}
|
||||
_update_mute_state();
|
||||
#ifdef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
||||
_log("\tflags now " + itos(_mute_state) + " (final flags " + itos(_mute_state_final) + ")");
|
||||
#endif
|
||||
_set_driver(desired_driver_id);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//////////////////////////////////////////////
|
||||
//////////////////////////////////////////////
|
||||
@ -247,6 +391,9 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(buses.empty() && todo, "AudioServer bus count is less than 1.");
|
||||
|
||||
bool processing_allowed = AudioDriverManager::is_audio_processing_allowed();
|
||||
|
||||
while (todo) {
|
||||
if (to_mix == 0) {
|
||||
_mix_step();
|
||||
@ -254,36 +401,53 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
|
||||
|
||||
int to_copy = MIN(to_mix, todo);
|
||||
|
||||
Bus *master = buses[0];
|
||||
if (processing_allowed) {
|
||||
Bus *master = buses[0];
|
||||
|
||||
int from = buffer_size - to_mix;
|
||||
int from_buf = p_frames - todo;
|
||||
int from = buffer_size - to_mix;
|
||||
int from_buf = p_frames - todo;
|
||||
|
||||
//master master, send to output
|
||||
int cs = master->channels.size();
|
||||
for (int k = 0; k < cs; k++) {
|
||||
if (master->channels[k].active) {
|
||||
const AudioFrame *buf = master->channels[k].buffer.ptr();
|
||||
//master master, send to output
|
||||
int cs = master->channels.size();
|
||||
|
||||
for (int j = 0; j < to_copy; j++) {
|
||||
float l = CLAMP(buf[from + j].l, -1.0, 1.0);
|
||||
int32_t vl = l * ((1 << 20) - 1);
|
||||
int32_t vl2 = (vl < 0 ? -1 : 1) * (ABS(vl) << 11);
|
||||
p_buffer[(from_buf + j) * (cs * 2) + k * 2 + 0] = vl2;
|
||||
// take away 1 from the stride as we are manually incrementing one for stereo
|
||||
uintptr_t stride_minus_one = (cs * 2) - 1;
|
||||
|
||||
float r = CLAMP(buf[from + j].r, -1.0, 1.0);
|
||||
int32_t vr = r * ((1 << 20) - 1);
|
||||
int32_t vr2 = (vr < 0 ? -1 : 1) * (ABS(vr) << 11);
|
||||
p_buffer[(from_buf + j) * (cs * 2) + k * 2 + 1] = vr2;
|
||||
}
|
||||
for (int k = 0; k < cs; k++) {
|
||||
// destination start for data will be the same in all cases
|
||||
int32_t *dest = &p_buffer[(from_buf * (cs * 2)) + (k * 2)];
|
||||
|
||||
} else {
|
||||
for (int j = 0; j < to_copy; j++) {
|
||||
p_buffer[(from_buf + j) * (cs * 2) + k * 2 + 0] = 0;
|
||||
p_buffer[(from_buf + j) * (cs * 2) + k * 2 + 1] = 0;
|
||||
if (master->channels[k].active) {
|
||||
const AudioFrame *buf = master->channels[k].buffer.ptr();
|
||||
|
||||
for (int j = 0; j < to_copy; j++) {
|
||||
float l = CLAMP(buf[from + j].l, -1.0, 1.0);
|
||||
int32_t vl = l * ((1 << 20) - 1);
|
||||
int32_t vl2 = (vl < 0 ? -1 : 1) * (ABS(vl) << 11);
|
||||
*dest = vl2;
|
||||
dest++;
|
||||
|
||||
float r = CLAMP(buf[from + j].r, -1.0, 1.0);
|
||||
int32_t vr = r * ((1 << 20) - 1);
|
||||
int32_t vr2 = (vr < 0 ? -1 : 1) * (ABS(vr) << 11);
|
||||
*dest = vr2;
|
||||
dest += stride_minus_one;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Bizarrely, profiling indicates that detecting the common case of cs == 1
|
||||
// and k == 0, and using memset is SLOWER than setting individually.
|
||||
// (Perhaps it gets optimized to a faster instruction than memset).
|
||||
for (int j = 0; j < to_copy; j++) {
|
||||
*dest = 0;
|
||||
dest++;
|
||||
*dest = 0;
|
||||
dest += stride_minus_one;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // if audio processing allowed
|
||||
|
||||
todo -= to_copy;
|
||||
to_mix -= to_copy;
|
||||
@ -296,6 +460,7 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
|
||||
|
||||
void AudioServer::_mix_step() {
|
||||
bool solo_mode = false;
|
||||
bool processing_allowed = AudioDriverManager::is_audio_processing_allowed();
|
||||
|
||||
for (int i = 0; i < buses.size(); i++) {
|
||||
Bus *bus = buses[i];
|
||||
@ -341,48 +506,50 @@ void AudioServer::_mix_step() {
|
||||
//go bus by bus
|
||||
Bus *bus = buses[i];
|
||||
|
||||
for (int k = 0; k < bus->channels.size(); k++) {
|
||||
if (bus->channels[k].active && !bus->channels[k].used) {
|
||||
//buffer was not used, but it's still active, so it must be cleaned
|
||||
AudioFrame *buf = bus->channels.write[k].buffer.ptrw();
|
||||
if (processing_allowed) {
|
||||
for (int k = 0; k < bus->channels.size(); k++) {
|
||||
if (bus->channels[k].active && !bus->channels[k].used) {
|
||||
//buffer was not used, but it's still active, so it must be cleaned
|
||||
AudioFrame *buf = bus->channels.write[k].buffer.ptrw();
|
||||
|
||||
for (uint32_t j = 0; j < buffer_size; j++) {
|
||||
buf[j] = AudioFrame(0, 0);
|
||||
for (uint32_t j = 0; j < buffer_size; j++) {
|
||||
buf[j] = AudioFrame(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//process effects
|
||||
if (!bus->bypass) {
|
||||
for (int j = 0; j < bus->effects.size(); j++) {
|
||||
if (!bus->effects[j].enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
|
||||
#endif
|
||||
|
||||
for (int k = 0; k < bus->channels.size(); k++) {
|
||||
if (!(bus->channels[k].active || bus->channels[k].effect_instances[j]->process_silence())) {
|
||||
//process effects
|
||||
if (!bus->bypass) {
|
||||
for (int j = 0; j < bus->effects.size(); j++) {
|
||||
if (!bus->effects[j].enabled) {
|
||||
continue;
|
||||
}
|
||||
bus->channels.write[k].effect_instances.write[j]->process(bus->channels[k].buffer.ptr(), temp_buffer.write[k].ptrw(), buffer_size);
|
||||
}
|
||||
|
||||
//swap buffers, so internal buffer always has the right data
|
||||
for (int k = 0; k < bus->channels.size(); k++) {
|
||||
if (!(buses[i]->channels[k].active || bus->channels[k].effect_instances[j]->process_silence())) {
|
||||
continue;
|
||||
}
|
||||
SWAP(bus->channels.write[k].buffer, temp_buffer.write[k]);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bus->effects.write[j].prof_time += OS::get_singleton()->get_ticks_usec() - ticks;
|
||||
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
|
||||
#endif
|
||||
|
||||
for (int k = 0; k < bus->channels.size(); k++) {
|
||||
if (!(bus->channels[k].active || bus->channels[k].effect_instances[j]->process_silence())) {
|
||||
continue;
|
||||
}
|
||||
bus->channels.write[k].effect_instances.write[j]->process(bus->channels[k].buffer.ptr(), temp_buffer.write[k].ptrw(), buffer_size);
|
||||
}
|
||||
|
||||
//swap buffers, so internal buffer always has the right data
|
||||
for (int k = 0; k < bus->channels.size(); k++) {
|
||||
if (!(buses[i]->channels[k].active || bus->channels[k].effect_instances[j]->process_silence())) {
|
||||
continue;
|
||||
}
|
||||
SWAP(bus->channels.write[k].buffer, temp_buffer.write[k]);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bus->effects.write[j].prof_time += OS::get_singleton()->get_ticks_usec() - ticks;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} // if processing allowed
|
||||
|
||||
//process send
|
||||
|
||||
@ -422,17 +589,19 @@ void AudioServer::_mix_step() {
|
||||
}
|
||||
}
|
||||
|
||||
//apply volume and compute peak
|
||||
for (uint32_t j = 0; j < buffer_size; j++) {
|
||||
buf[j] *= volume;
|
||||
if (processing_allowed) {
|
||||
//apply volume and compute peak
|
||||
for (uint32_t j = 0; j < buffer_size; j++) {
|
||||
buf[j] *= volume;
|
||||
|
||||
float l = ABS(buf[j].l);
|
||||
if (l > peak.l) {
|
||||
peak.l = l;
|
||||
}
|
||||
float r = ABS(buf[j].r);
|
||||
if (r > peak.r) {
|
||||
peak.r = r;
|
||||
float l = ABS(buf[j].l);
|
||||
if (l > peak.l) {
|
||||
peak.l = l;
|
||||
}
|
||||
float r = ABS(buf[j].r);
|
||||
if (r > peak.r) {
|
||||
peak.r = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -449,7 +618,7 @@ void AudioServer::_mix_step() {
|
||||
}
|
||||
}
|
||||
|
||||
if (send) {
|
||||
if (send && processing_allowed) {
|
||||
//if not master bus, send
|
||||
AudioFrame *target_buf = thread_get_channel_mix_buffer(send->index_cache, k);
|
||||
|
||||
@ -475,6 +644,8 @@ bool AudioServer::thread_has_channel_mix_buffer(int p_bus, int p_buffer) const {
|
||||
}
|
||||
|
||||
AudioFrame *AudioServer::thread_get_channel_mix_buffer(int p_bus, int p_buffer) {
|
||||
last_sound_played_ms = OS::get_singleton()->get_ticks_msec();
|
||||
|
||||
ERR_FAIL_INDEX_V(p_bus, buses.size(), nullptr);
|
||||
ERR_FAIL_INDEX_V(p_buffer, buses[p_bus]->channels.size(), nullptr);
|
||||
|
||||
@ -1023,6 +1194,23 @@ void AudioServer::update() {
|
||||
prof_time = 0;
|
||||
#endif
|
||||
|
||||
// give audio driver option to turn off to throttle CPU
|
||||
if (AudioDriverManager::get_mute_sensitivity(AudioDriverManager::MUTE_FLAG_SILENCE)) {
|
||||
uint32_t time_ms = OS::get_singleton()->get_ticks_msec();
|
||||
if (time_ms >= last_sound_played_ms) {
|
||||
uint32_t diff_ms = time_ms - last_sound_played_ms;
|
||||
if (diff_ms <= 10000) {
|
||||
if (AudioDriverManager::get_mute_flag(AudioDriverManager::MUTE_FLAG_SILENCE)) {
|
||||
AudioDriverManager::set_mute_flag(AudioDriverManager::MUTE_FLAG_SILENCE, false);
|
||||
}
|
||||
} else {
|
||||
if (!AudioDriverManager::get_mute_flag(AudioDriverManager::MUTE_FLAG_SILENCE)) {
|
||||
AudioDriverManager::set_mute_flag(AudioDriverManager::MUTE_FLAG_SILENCE, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (RBSet<CallbackItem>::Element *E = update_callbacks.front(); E; E = E->next()) {
|
||||
E->get().callback(E->get().userdata);
|
||||
}
|
||||
@ -1249,6 +1437,14 @@ void AudioServer::set_device(String device) {
|
||||
AudioDriver::get_singleton()->set_device(device);
|
||||
}
|
||||
|
||||
void AudioServer::set_enabled(bool p_enabled) {
|
||||
AudioDriverManager::set_mute_flag(AudioDriverManager::MUTE_FLAG_DISABLED, !p_enabled);
|
||||
}
|
||||
|
||||
bool AudioServer::is_enabled() const {
|
||||
return !AudioDriverManager::get_mute_flag(AudioDriverManager::MUTE_FLAG_DISABLED);
|
||||
}
|
||||
|
||||
Array AudioServer::capture_get_device_list() {
|
||||
return AudioDriver::get_singleton()->capture_get_device_list();
|
||||
}
|
||||
@ -1327,6 +1523,9 @@ void AudioServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout);
|
||||
ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &AudioServer::set_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_enabled"), &AudioServer::is_enabled);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bus_count"), "set_bus_count", "get_bus_count");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "device"), "set_device", "get_device");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "capture_device"), "capture_set_device", "capture_get_device");
|
||||
@ -1356,6 +1555,7 @@ AudioServer::AudioServer() {
|
||||
mix_time = 0;
|
||||
mix_size = 0;
|
||||
global_rate_scale = 1;
|
||||
last_sound_played_ms = 0;
|
||||
}
|
||||
|
||||
AudioServer::~AudioServer() {
|
||||
@ -1487,3 +1687,5 @@ AudioBusLayout::AudioBusLayout() {
|
||||
buses.resize(1);
|
||||
buses.write[0].name = "Master";
|
||||
}
|
||||
|
||||
#undef AUDIO_DRIVER_MANAGER_LOGGING_ENABLED
|
@ -125,19 +125,58 @@ class AudioDriverManager {
|
||||
MAX_DRIVERS = 10
|
||||
};
|
||||
|
||||
public:
|
||||
enum MuteFlags {
|
||||
// User enables or disables audio, e.g. via button in editor
|
||||
MUTE_FLAG_DISABLED = 1 << 0,
|
||||
// Whether app is in focus
|
||||
MUTE_FLAG_FOCUS_LOSS = 1 << 1,
|
||||
// Whether app is paused / resumed
|
||||
MUTE_FLAG_PAUSED = 1 << 2,
|
||||
// When a section of silence is detected, the audio can be muted
|
||||
MUTE_FLAG_SILENCE = 1 << 3,
|
||||
};
|
||||
|
||||
private:
|
||||
static const int DEFAULT_MIX_RATE = 44100;
|
||||
static const int DEFAULT_OUTPUT_LATENCY = 15;
|
||||
|
||||
static AudioDriver *drivers[MAX_DRIVERS];
|
||||
static int driver_count;
|
||||
static int desired_driver_id;
|
||||
static int actual_driver_id;
|
||||
|
||||
static uint32_t _mute_state; // raw flags
|
||||
static uint32_t _mute_state_final; // flags after applying mask
|
||||
static uint32_t _mute_state_mask;
|
||||
|
||||
static AudioDriverDummy dummy_driver;
|
||||
|
||||
static void _set_driver(int p_driver);
|
||||
static void _update_mute_state();
|
||||
|
||||
static void _log(String p_sz, int p_driver_id = -1);
|
||||
|
||||
public:
|
||||
static void add_driver(AudioDriver *p_driver);
|
||||
static void initialize(int p_driver);
|
||||
static int get_driver_count();
|
||||
static AudioDriver *get_driver(int p_driver);
|
||||
|
||||
// Various modes flags can be used to mute the audio, depending on the sensitivity in the _mute_state_mask.
|
||||
static void set_mute_flag(MuteFlags p_flag, bool p_enabled);
|
||||
static bool get_mute_flag(MuteFlags p_flag) { return _mute_state & p_flag; }
|
||||
|
||||
// Whether audio should play, or whether the audio system should be considered muted.
|
||||
static bool is_active() { return _mute_state_final == 0; }
|
||||
|
||||
// Audio processing can be throttled down EXCEPT for the case of silence mode, where any sound should
|
||||
// wake up the driver.
|
||||
static bool is_audio_processing_allowed() { return (_mute_state_final & (~MUTE_FLAG_SILENCE)) == 0; }
|
||||
|
||||
// Sets the sensitivity mask for different mute flags.
|
||||
static void set_mute_sensitivity(MuteFlags p_flag, bool p_enabled);
|
||||
static bool get_mute_sensitivity(MuteFlags p_flag) { return _mute_state_mask & p_flag; }
|
||||
};
|
||||
|
||||
class AudioBusLayout;
|
||||
@ -235,6 +274,10 @@ private:
|
||||
|
||||
Mutex audio_data_lock;
|
||||
|
||||
// keep a rough record of when the last sound was output
|
||||
// so we can throttle audio during silence
|
||||
uint32_t last_sound_played_ms;
|
||||
|
||||
void init_channels_and_buffers();
|
||||
|
||||
void _mix_step();
|
||||
@ -367,6 +410,9 @@ public:
|
||||
String get_device();
|
||||
void set_device(String device);
|
||||
|
||||
void set_enabled(bool p_enabled);
|
||||
bool is_enabled() const;
|
||||
|
||||
Array capture_get_device_list();
|
||||
String capture_get_device();
|
||||
void capture_set_device(const String &p_name);
|
||||
|
Loading…
Reference in New Issue
Block a user