Merge pull request #52237 from ellenhp/polyphony

Add optional polyphonic playback to built-in audio player nodes
This commit is contained in:
Juan Linietsky 2021-09-07 17:38:51 -03:00 committed by GitHub
commit ca11f8ad30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 428 additions and 242 deletions

View file

@ -189,11 +189,21 @@ float AudioStream::get_length() const {
return 0;
}
bool AudioStream::is_monophonic() const {
bool ret;
if (GDVIRTUAL_CALL(_is_monophonic, ret)) {
return ret;
}
return true;
}
void AudioStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic);
GDVIRTUAL_BIND(_instance_playback);
GDVIRTUAL_BIND(_get_stream_name);
GDVIRTUAL_BIND(_get_length);
GDVIRTUAL_BIND(_is_monophonic);
}
////////////////////////////////
@ -221,6 +231,10 @@ float AudioStreamMicrophone::get_length() const {
return 0;
}
bool AudioStreamMicrophone::is_monophonic() const {
return true;
}
void AudioStreamMicrophone::_bind_methods() {
}
@ -388,6 +402,14 @@ float AudioStreamRandomPitch::get_length() const {
return 0;
}
bool AudioStreamRandomPitch::is_monophonic() const {
if (audio_stream.is_valid()) {
return audio_stream->is_monophonic();
}
return true; // It doesn't really matter what we return here, but no sense instancing a many playbacks of a null stream.
}
void AudioStreamRandomPitch::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream);
ClassDB::bind_method(D_METHOD("get_audio_stream"), &AudioStreamRandomPitch::get_audio_stream);

View file

@ -102,12 +102,14 @@ protected:
GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instance_playback)
GDVIRTUAL0RC(String, _get_stream_name)
GDVIRTUAL0RC(float, _get_length)
GDVIRTUAL0RC(bool, _is_monophonic)
public:
virtual Ref<AudioStreamPlayback> instance_playback();
virtual String get_stream_name() const;
virtual float get_length() const;
virtual bool is_monophonic() const;
};
// Microphone
@ -129,6 +131,8 @@ public:
virtual float get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
AudioStreamMicrophone();
};
@ -187,6 +191,7 @@ public:
virtual String get_stream_name() const override;
virtual float get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
AudioStreamRandomPitch();
};

View file

@ -64,6 +64,10 @@ float AudioStreamGenerator::get_length() const {
return 0;
}
bool AudioStreamGenerator::is_monophonic() const {
return true;
}
void AudioStreamGenerator::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mix_rate", "hz"), &AudioStreamGenerator::set_mix_rate);
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamGenerator::get_mix_rate);

View file

@ -54,6 +54,7 @@ public:
virtual String get_stream_name() const override;
virtual float get_length() const override;
virtual bool is_monophonic() const override;
AudioStreamGenerator();
};

View file

@ -363,10 +363,10 @@ void AudioServer::_mix_step() {
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_base = 0.94;
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.
static_assert(LOOKAHEAD_BUFFER_SIZE == 64, "Update fadeout_base and comment here if you change LOOKAHEAD_BUFFER_SIZE.");
// 0.94 ^ 64 = 0.01906. 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;
@ -381,29 +381,24 @@ void AudioServer::_mix_step() {
}
}
ERR_FAIL_COND(playback->bus_details.load() == nullptr);
AudioStreamPlaybackBusDetails *ptr = playback->bus_details.load();
ERR_FAIL_COND(ptr == 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;
AudioStreamPlaybackBusDetails bus_details = *ptr;
// Mix to any active buses.
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
if (!bus_details->bus_active[idx]) {
if (!bus_details.bus_active[idx]) {
continue;
}
int bus_idx = thread_find_bus_index(bus_details->bus[idx]);
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]) {
if (!playback->prev_bus_details->bus_active[search_idx]) {
continue;
}
if (prev_bus_details->bus[search_idx].hash() == bus_details->bus[idx].hash()) {
if (playback->prev_bus_details->bus[search_idx].hash() == bus_details.bus[idx].hash()) {
prev_bus_idx = search_idx;
}
}
@ -411,13 +406,13 @@ void AudioServer::_mix_step() {
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);
bus_details.volume[idx][channel_idx] = AudioFrame(0, 0);
}
AudioFrame channel_vol = bus_details->volume[idx][channel_idx];
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];
prev_channel_vol = playback->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]);
}
@ -425,14 +420,14 @@ void AudioServer::_mix_step() {
// 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]) {
if (!playback->prev_bus_details->bus_active[idx]) {
continue;
}
int bus_idx = thread_find_bus_index(prev_bus_details->bus[idx]);
int bus_idx = thread_find_bus_index(playback->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]) {
if (bus_details.bus[search_idx] == playback->prev_bus_details->bus[idx]) {
current_bus_idx = search_idx;
}
}
@ -443,24 +438,17 @@ void AudioServer::_mix_step() {
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];
AudioFrame prev_channel_vol = playback->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));
std::copy(std::begin(bus_details.bus_active), std::end(bus_details.bus_active), std::begin(playback->prev_bus_details->bus_active));
std::copy(std::begin(bus_details.bus), std::end(bus_details.bus), std::begin(playback->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);
std::copy(std::begin(bus_details.volume[bus_idx]), std::end(bus_details.volume[bus_idx]), std::begin(playback->prev_bus_details->volume[bus_idx]));
}
switch (playback->state.load()) {
@ -1132,7 +1120,7 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Str
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) {
void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time, float p_highshelf_gain, float p_attenuation_cutoff_hz, float p_pitch_scale) {
ERR_FAIL_COND(p_playback.is_null());
AudioStreamPlaybackListNode *playback_node = new AudioStreamPlaybackListNode();
@ -1154,10 +1142,9 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map
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);
playback_node->pitch_scale.set(p_pitch_scale);
playback_node->highshelf_gain.set(p_highshelf_gain);
playback_node->attenuation_filter_cutoff_hz.set(p_attenuation_cutoff_hz);
memset(playback_node->prev_bus_details->volume, 0, sizeof(playback_node->prev_bus_details->volume));
@ -1181,6 +1168,9 @@ void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) {
AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
do {
old_state = playback_node->state.load();
if (old_state == AudioStreamPlaybackListNode::AWAITING_DELETION) {
break; // Don't fade out again.
}
new_state = AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION;
} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
@ -1206,6 +1196,9 @@ void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_pla
int idx = 0;
for (KeyValue<StringName, Vector<AudioFrame>> pair : p_bus_volumes) {
if (idx >= MAX_BUSES_PER_PLAYBACK) {
break;
}
ERR_FAIL_COND(pair.value.size() < channel_count);
ERR_FAIL_COND(pair.value.size() != MAX_CHANNELS_PER_BUS);
@ -1214,6 +1207,7 @@ void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_pla
for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) {
new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx];
}
idx++;
}
do {
@ -1260,17 +1254,18 @@ void AudioServer::set_playback_paused(Ref<AudioStreamPlayback> p_playback, bool
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;
if (!p_paused && old_state == AudioStreamPlaybackListNode::PLAYING) {
return; // No-op.
}
if (p_paused && (old_state == AudioStreamPlaybackListNode::PAUSED || old_state == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE)) {
return; // No-op.
}
} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
}
@ -1444,10 +1439,15 @@ void AudioServer::update() {
update_callback_list.maybe_cleanup();
listener_changed_callback_list.maybe_cleanup();
playback_list.maybe_cleanup();
for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard_frame_old) {
bus_details_graveyard_frame_old.erase(bus_details, [](AudioStreamPlaybackBusDetails *d) { delete d; });
}
for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard) {
bus_details_graveyard.erase(bus_details, [](AudioStreamPlaybackBusDetails *d) { delete d; });
bus_details_graveyard_frame_old.insert(bus_details);
bus_details_graveyard.erase(bus_details);
}
bus_details_graveyard.maybe_cleanup();
bus_details_graveyard_frame_old.maybe_cleanup();
}
void AudioServer::load_default_bus_layout() {

View file

@ -163,7 +163,7 @@ public:
AUDIO_DATA_INVALID_ID = -1,
MAX_CHANNELS_PER_BUS = 4,
MAX_BUSES_PER_PLAYBACK = 6,
LOOKAHEAD_BUFFER_SIZE = 32,
LOOKAHEAD_BUFFER_SIZE = 64,
};
typedef void (*AudioCallback)(void *p_userdata);
@ -262,6 +262,9 @@ private:
SafeList<AudioStreamPlaybackListNode *> playback_list;
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard;
// TODO document if this is necessary.
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard_frame_old;
Vector<Vector<AudioFrame>> temp_buffer; //temp_buffer for each level
Vector<AudioFrame> mix_buffer;
Vector<Bus *> buses;
@ -364,8 +367,10 @@ public:
void set_playback_speed_scale(float p_scale);
float get_playback_speed_scale() const;
// Convenience method.
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time = 0);
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time = 0);
// Expose all parameters.
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time = 0, float p_highshelf_gain = 0, float p_attenuation_cutoff_hz = 0, float p_pitch_scale = 1);
void stop_playback_stream(Ref<AudioStreamPlayback> p_playback);
void set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes);