1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-06 12:20:30 +00:00

Do all audio mixing in the AudioServer

This commit is contained in:
Ellen Poe
2021-08-27 10:28:23 -07:00
parent 53843ba872
commit 3598d300cb
10 changed files with 1051 additions and 929 deletions

View File

@@ -32,13 +32,19 @@
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/error/error_macros.h"
#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/math/audio_frame.h"
#include "core/os/os.h"
#include "core/string/string_name.h"
#include "core/templates/pair.h"
#include "scene/resources/audio_stream_sample.h"
#include "servers/audio/audio_driver_dummy.h"
#include "servers/audio/effects/audio_effect_compressor.h"
#include <cstring>
#ifdef TOOLS_ENABLED
#define MARK_EDITED set_edited(true);
#else
@@ -234,6 +240,7 @@ AudioDriver *AudioDriverManager::get_driver(int p_driver) {
//////////////////////////////////////////////
void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
mix_count++;
int todo = p_frames;
#ifdef DEBUG_ENABLED
@@ -331,10 +338,156 @@ void AudioServer::_mix_step() {
bus->soloed = false;
}
}
for (CallbackItem *ci : mix_callback_list) {
ci->callback(ci->userdata);
}
//make callbacks for mixing the audio
for (Set<CallbackItem>::Element *E = callbacks.front(); E; E = E->next()) {
E->get().callback(E->get().userdata);
for (AudioStreamPlaybackListNode *playback : playback_list) {
// Paused streams are no-ops. Don't even mix audio from the stream playback.
if (playback->state.load() == AudioStreamPlaybackListNode::PAUSED) {
continue;
}
bool fading_out = playback->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION || playback->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE;
AudioFrame *buf = mix_buffer.ptrw();
// Copy the lookeahead buffer into the mix buffer.
for (int i = 0; i < LOOKAHEAD_BUFFER_SIZE; i++) {
buf[i] = playback->lookahead[i];
}
// Mix the audio stream
unsigned int mixed_frames = playback->stream_playback->mix(&buf[LOOKAHEAD_BUFFER_SIZE], playback->pitch_scale.get(), buffer_size);
if (mixed_frames != buffer_size) {
// We know we have at least the size of our lookahead buffer for fade-out purposes.
float fadeout_base = 0.87;
float fadeout_coefficient = 1;
static_assert(LOOKAHEAD_BUFFER_SIZE == 32, "Update fadeout_base and comment here if you change LOOKAHEAD_BUFFER_SIZE.");
// 0.87 ^ 32 = 0.0116. There might still be a pop but it'll be way better than if we didn't do this.
for (unsigned int idx = mixed_frames; idx < buffer_size; idx++) {
fadeout_coefficient *= fadeout_base;
buf[idx] *= fadeout_coefficient;
}
AudioStreamPlaybackListNode::PlaybackState new_state;
new_state = AudioStreamPlaybackListNode::AWAITING_DELETION;
playback->state.store(new_state);
} else {
// Move the last little bit of what we just mixed into our lookahead buffer.
for (int i = 0; i < LOOKAHEAD_BUFFER_SIZE; i++) {
playback->lookahead[i] = buf[buffer_size + i];
}
}
ERR_FAIL_COND(playback->bus_details.load() == nullptr);
// By putting null into the bus details pointers, we're taking ownership of their memory for the duration of this mix.
AudioStreamPlaybackBusDetails *bus_details = nullptr;
{
std::atomic<AudioStreamPlaybackBusDetails *> bus_details_atomic = nullptr;
bus_details = playback->bus_details.exchange(bus_details_atomic);
}
ERR_FAIL_COND(bus_details == nullptr);
AudioStreamPlaybackBusDetails *prev_bus_details = playback->prev_bus_details;
// Mix to any active buses.
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
if (!bus_details->bus_active[idx]) {
continue;
}
int bus_idx = thread_find_bus_index(bus_details->bus[idx]);
int prev_bus_idx = -1;
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
if (!prev_bus_details->bus_active[search_idx]) {
continue;
}
if (prev_bus_details->bus[search_idx].hash() == bus_details->bus[idx].hash()) {
prev_bus_idx = search_idx;
}
}
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
if (fading_out) {
bus_details->volume[idx][channel_idx] = AudioFrame(0, 0);
}
AudioFrame channel_vol = bus_details->volume[idx][channel_idx];
AudioFrame prev_channel_vol = AudioFrame(0, 0);
if (prev_bus_idx != -1) {
prev_channel_vol = prev_bus_details->volume[prev_bus_idx][channel_idx];
}
_mix_step_for_channel(channel_buf, buf, prev_channel_vol, channel_vol, playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
}
}
// Now go through and fade-out any buses that were being played to previously that we missed by going through current data.
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
if (!prev_bus_details->bus_active[idx]) {
continue;
}
int bus_idx = thread_find_bus_index(prev_bus_details->bus[idx]);
int current_bus_idx = -1;
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
if (bus_details->bus[search_idx] == prev_bus_details->bus[idx]) {
current_bus_idx = search_idx;
}
}
if (current_bus_idx != -1) {
// If we found a corresponding bus in the current bus assignments, we've already mixed to this bus.
continue;
}
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
AudioFrame prev_channel_vol = prev_bus_details->volume[idx][channel_idx];
// Fade out to silence
_mix_step_for_channel(channel_buf, buf, prev_channel_vol, AudioFrame(0, 0), playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
}
}
// Copy the bus details we mixed with to the previous bus details to maintain volume ramps.
std::copy(std::begin(bus_details->bus_active), std::end(bus_details->bus_active), std::begin(prev_bus_details->bus_active));
std::copy(std::begin(bus_details->bus), std::end(bus_details->bus), std::begin(prev_bus_details->bus));
for (int bus_idx = 0; bus_idx < MAX_BUSES_PER_PLAYBACK; bus_idx++) {
std::copy(std::begin(bus_details->volume[bus_idx]), std::end(bus_details->volume[bus_idx]), std::begin(prev_bus_details->volume[bus_idx]));
}
AudioStreamPlaybackBusDetails *bus_details_expected = nullptr;
// Only put the bus details pointer back if it hasn't been updated already.
if (!playback->bus_details.compare_exchange_strong(/* expected= */ bus_details_expected, /* new= */ bus_details)) {
// If it *has* been updated already, queue the old one for deletion.
bus_details_graveyard.insert(bus_details);
}
switch (playback->state.load()) {
case AudioStreamPlaybackListNode::AWAITING_DELETION:
case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION:
playback_list.erase(playback, [](AudioStreamPlaybackListNode *p) {
if (p->prev_bus_details)
delete p->prev_bus_details;
if (p->bus_details)
delete p->bus_details;
p->stream_playback.unref();
delete p;
});
break;
case AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE: {
// Pause the stream.
AudioStreamPlaybackListNode::PlaybackState old_state, new_state;
do {
old_state = playback->state.load();
new_state = AudioStreamPlaybackListNode::PAUSED;
} while (!playback->state.compare_exchange_strong(/* expected= */ old_state, new_state));
} break;
case AudioStreamPlaybackListNode::PLAYING:
case AudioStreamPlaybackListNode::PAUSED:
// No-op!
break;
}
}
for (int i = buses.size() - 1; i >= 0; i--) {
@@ -464,6 +617,53 @@ void AudioServer::_mix_step() {
to_mix = buffer_size;
}
void AudioServer::_mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_source_buf, AudioFrame p_vol_start, AudioFrame p_vol_final, float p_attenuation_filter_cutoff_hz, float p_highshelf_gain, AudioFilterSW::Processor *p_processor_l, AudioFilterSW::Processor *p_processor_r) {
if (p_highshelf_gain != 0) {
AudioFilterSW filter;
filter.set_mode(AudioFilterSW::HIGHSHELF);
filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate());
filter.set_cutoff(p_attenuation_filter_cutoff_hz);
filter.set_resonance(1);
filter.set_stages(1);
filter.set_gain(p_highshelf_gain);
ERR_FAIL_COND(p_processor_l == nullptr);
ERR_FAIL_COND(p_processor_r == nullptr);
bool is_just_started = p_vol_start.l == 0 && p_vol_start.r == 0;
p_processor_l->set_filter(&filter, /* clear_history= */ is_just_started);
p_processor_l->update_coeffs(buffer_size);
p_processor_r->set_filter(&filter, /* clear_history= */ is_just_started);
p_processor_r->update_coeffs(buffer_size);
for (unsigned int frame_idx = 0; frame_idx < buffer_size; frame_idx++) {
// Make this buffer size invariant if buffer_size ever becomes a project setting.
float lerp_param = (float)frame_idx / buffer_size;
AudioFrame vol = p_vol_final * lerp_param + (1 - lerp_param) * p_vol_start;
AudioFrame mixed = vol * p_source_buf[frame_idx];
p_processor_l->process_one_interp(mixed.l);
p_processor_r->process_one_interp(mixed.r);
p_out_buf[frame_idx] += mixed;
}
} else {
for (unsigned int frame_idx = 0; frame_idx < buffer_size; frame_idx++) {
// Make this buffer size invariant if buffer_size ever becomes a project setting.
float lerp_param = (float)frame_idx / buffer_size;
p_out_buf[frame_idx] += (p_vol_final * lerp_param + (1 - lerp_param) * p_vol_start) * p_source_buf[frame_idx];
}
}
}
AudioServer::AudioStreamPlaybackListNode *AudioServer::_find_playback_list_node(Ref<AudioStreamPlayback> p_playback) {
for (AudioStreamPlaybackListNode *playback_list_node : playback_list) {
if (playback_list_node->stream_playback == p_playback) {
return playback_list_node;
}
}
return nullptr;
}
bool AudioServer::thread_has_channel_mix_buffer(int p_bus, int p_buffer) const {
if (p_bus < 0 || p_bus >= buses.size()) {
return false;
@@ -923,9 +1123,216 @@ float AudioServer::get_playback_speed_scale() const {
return playback_speed_scale;
}
void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time) {
ERR_FAIL_COND(p_playback.is_null());
Map<StringName, Vector<AudioFrame>> map;
map[p_bus] = p_volume_db_vector;
start_playback_stream(p_playback, map, p_start_time);
}
void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time) {
ERR_FAIL_COND(p_playback.is_null());
AudioStreamPlaybackListNode *playback_node = new AudioStreamPlaybackListNode();
playback_node->stream_playback = p_playback;
playback_node->stream_playback->start(p_start_time);
AudioStreamPlaybackBusDetails *new_bus_details = new AudioStreamPlaybackBusDetails();
int idx = 0;
for (KeyValue<StringName, Vector<AudioFrame>> pair : p_bus_volumes) {
ERR_FAIL_COND(pair.value.size() < channel_count);
ERR_FAIL_COND(pair.value.size() != MAX_CHANNELS_PER_BUS);
new_bus_details->bus_active[idx] = true;
new_bus_details->bus[idx] = pair.key;
for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) {
new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx];
}
}
playback_node->bus_details = new_bus_details;
playback_node->prev_bus_details = new AudioStreamPlaybackBusDetails();
playback_node->setseek.set(-1);
playback_node->pitch_scale.set(1);
playback_node->highshelf_gain.set(0);
playback_node->attenuation_filter_cutoff_hz.set(0);
memset(playback_node->prev_bus_details->volume, 0, sizeof(playback_node->prev_bus_details->volume));
for (AudioFrame &frame : playback_node->lookahead) {
frame = AudioFrame(0, 0);
}
playback_node->state.store(AudioStreamPlaybackListNode::PLAYING);
playback_list.insert(playback_node);
}
void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND(p_playback.is_null());
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return;
}
AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
do {
old_state = playback_node->state.load();
new_state = AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION;
} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
}
void AudioServer::set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes) {
ERR_FAIL_COND(p_volumes.size() != MAX_CHANNELS_PER_BUS);
Map<StringName, Vector<AudioFrame>> map;
map[p_bus] = p_volumes;
set_playback_bus_volumes_linear(p_playback, map);
}
void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes) {
ERR_FAIL_COND(p_bus_volumes.size() > MAX_BUSES_PER_PLAYBACK);
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return;
}
AudioStreamPlaybackBusDetails *old_bus_details, *new_bus_details = new AudioStreamPlaybackBusDetails();
int idx = 0;
for (KeyValue<StringName, Vector<AudioFrame>> pair : p_bus_volumes) {
ERR_FAIL_COND(pair.value.size() < channel_count);
ERR_FAIL_COND(pair.value.size() != MAX_CHANNELS_PER_BUS);
new_bus_details->bus_active[idx] = true;
new_bus_details->bus[idx] = pair.key;
for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) {
new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx];
}
}
do {
old_bus_details = playback_node->bus_details.load();
} while (!playback_node->bus_details.compare_exchange_strong(old_bus_details, new_bus_details));
bus_details_graveyard.insert(old_bus_details);
}
void AudioServer::set_playback_all_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, Vector<AudioFrame> p_volumes) {
ERR_FAIL_COND(p_playback.is_null());
ERR_FAIL_COND(p_volumes.size() != MAX_CHANNELS_PER_BUS);
Map<StringName, Vector<AudioFrame>> map;
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return;
}
for (int bus_idx = 0; bus_idx < MAX_BUSES_PER_PLAYBACK; bus_idx++) {
if (playback_node->bus_details.load()->bus_active[bus_idx]) {
map[playback_node->bus_details.load()->bus[bus_idx]] = p_volumes;
}
}
set_playback_bus_volumes_linear(p_playback, map);
}
void AudioServer::set_playback_pitch_scale(Ref<AudioStreamPlayback> p_playback, float p_pitch_scale) {
ERR_FAIL_COND(p_playback.is_null());
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return;
}
playback_node->pitch_scale.set(p_pitch_scale);
}
void AudioServer::set_playback_paused(Ref<AudioStreamPlayback> p_playback, bool p_paused) {
ERR_FAIL_COND(p_playback.is_null());
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return;
}
if (!p_paused && playback_node->state == AudioStreamPlaybackListNode::PLAYING) {
return; // No-op.
}
if (p_paused && (playback_node->state == AudioStreamPlaybackListNode::PAUSED || playback_node->state == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE)) {
return; // No-op.
}
AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
do {
old_state = playback_node->state.load();
new_state = p_paused ? AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE : AudioStreamPlaybackListNode::PLAYING;
} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
}
void AudioServer::set_playback_highshelf_params(Ref<AudioStreamPlayback> p_playback, float p_gain, float p_attenuation_cutoff_hz) {
ERR_FAIL_COND(p_playback.is_null());
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return;
}
playback_node->attenuation_filter_cutoff_hz.set(p_attenuation_cutoff_hz);
playback_node->highshelf_gain.set(p_gain);
}
bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND_V(p_playback.is_null(), false);
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return false;
}
return playback_node->state.load() == AudioStreamPlaybackListNode::PLAYING;
}
float AudioServer::get_playback_position(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND_V(p_playback.is_null(), 0);
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return 0;
}
return playback_node->stream_playback->get_playback_position();
}
bool AudioServer::is_playback_paused(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND_V(p_playback.is_null(), false);
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return false;
}
return playback_node->state.load() == AudioStreamPlaybackListNode::PAUSED || playback_node->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE;
}
uint64_t AudioServer::get_mix_count() const {
return mix_count;
}
void AudioServer::notify_listener_changed() {
for (CallbackItem *ci : listener_changed_callback_list) {
ci->callback(ci->userdata);
}
}
void AudioServer::init_channels_and_buffers() {
channel_count = get_channel_count();
temp_buffer.resize(channel_count);
mix_buffer.resize(buffer_size + LOOKAHEAD_BUFFER_SIZE);
for (int i = 0; i < temp_buffer.size(); i++) {
temp_buffer.write[i].resize(buffer_size);
@@ -943,7 +1350,7 @@ void AudioServer::init() {
channel_disable_threshold_db = GLOBAL_DEF_RST("audio/buses/channel_disable_threshold_db", -60.0);
channel_disable_frames = float(GLOBAL_DEF_RST("audio/buses/channel_disable_time", 2.0)) * get_mix_rate();
ProjectSettings::get_singleton()->set_custom_property_info("audio/buses/channel_disable_time", PropertyInfo(Variant::FLOAT, "audio/buses/channel_disable_time", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"));
buffer_size = 1024; //hardcoded for now
buffer_size = 512; //hardcoded for now
init_channels_and_buffers();
@@ -1030,9 +1437,17 @@ void AudioServer::update() {
prof_time = 0;
#endif
for (Set<CallbackItem>::Element *E = update_callbacks.front(); E; E = E->next()) {
E->get().callback(E->get().userdata);
for (CallbackItem *ci : update_callback_list) {
ci->callback(ci->userdata);
}
mix_callback_list.maybe_cleanup();
update_callback_list.maybe_cleanup();
listener_changed_callback_list.maybe_cleanup();
playback_list.maybe_cleanup();
for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard) {
bus_details_graveyard.erase(bus_details, [](AudioStreamPlaybackBusDetails *d) { delete d; });
}
bus_details_graveyard.maybe_cleanup();
}
void AudioServer::load_default_bus_layout() {
@@ -1098,40 +1513,49 @@ double AudioServer::get_time_since_last_mix() const {
AudioServer *AudioServer::singleton = nullptr;
void AudioServer::add_callback(AudioCallback p_callback, void *p_userdata) {
lock();
CallbackItem ci;
ci.callback = p_callback;
ci.userdata = p_userdata;
callbacks.insert(ci);
unlock();
}
void AudioServer::remove_callback(AudioCallback p_callback, void *p_userdata) {
lock();
CallbackItem ci;
ci.callback = p_callback;
ci.userdata = p_userdata;
callbacks.erase(ci);
unlock();
}
void AudioServer::add_update_callback(AudioCallback p_callback, void *p_userdata) {
lock();
CallbackItem ci;
ci.callback = p_callback;
ci.userdata = p_userdata;
update_callbacks.insert(ci);
unlock();
CallbackItem *ci = new CallbackItem();
ci->callback = p_callback;
ci->userdata = p_userdata;
update_callback_list.insert(ci);
}
void AudioServer::remove_update_callback(AudioCallback p_callback, void *p_userdata) {
lock();
CallbackItem ci;
ci.callback = p_callback;
ci.userdata = p_userdata;
update_callbacks.erase(ci);
unlock();
for (CallbackItem *ci : update_callback_list) {
if (ci->callback == p_callback && ci->userdata == p_userdata) {
update_callback_list.erase(ci, [](CallbackItem *c) { delete c; });
}
}
}
void AudioServer::add_mix_callback(AudioCallback p_callback, void *p_userdata) {
CallbackItem *ci = new CallbackItem();
ci->callback = p_callback;
ci->userdata = p_userdata;
mix_callback_list.insert(ci);
}
void AudioServer::remove_mix_callback(AudioCallback p_callback, void *p_userdata) {
for (CallbackItem *ci : mix_callback_list) {
if (ci->callback == p_callback && ci->userdata == p_userdata) {
mix_callback_list.erase(ci, [](CallbackItem *c) { delete c; });
}
}
}
void AudioServer::add_listener_changed_callback(AudioCallback p_callback, void *p_userdata) {
CallbackItem *ci = new CallbackItem();
ci->callback = p_callback;
ci->userdata = p_userdata;
listener_changed_callback_list.insert(ci);
}
void AudioServer::remove_listener_changed_callback(AudioCallback p_callback, void *p_userdata) {
for (CallbackItem *ci : listener_changed_callback_list) {
if (ci->callback == p_callback && ci->userdata == p_userdata) {
listener_changed_callback_list.erase(ci, [](CallbackItem *c) { delete c; });
}
}
}
void AudioServer::set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout) {