Skip to content

Commit

Permalink
Implement audio-driver sleep for WASAPI
Browse files Browse the repository at this point in the history
To conserve battery and improve support for device sleep on portable
Windows devices, implemented temporary release of audio resources
by editor when not playing audio.
  • Loading branch information
wlsnmrk committed Oct 25, 2024
1 parent 61accf0 commit 8a5ed88
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 4 deletions.
2 changes: 1 addition & 1 deletion drivers/coreaudio/audio_driver_coreaudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class AudioDriverCoreAudio : public AudioDriver {
virtual Error input_stop() override;

bool try_lock();
void stop();
virtual void stop() override;

AudioDriverCoreAudio();
~AudioDriverCoreAudio() {}
Expand Down
19 changes: 17 additions & 2 deletions drivers/wasapi/audio_driver_wasapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {

if (!ad->audio_output.audio_client) {
Error err = ad->init_output_device(true);
if (err == OK) {
if (err == OK && ad->audio_output.active.is_set()) {
ad->start();
}

Expand Down Expand Up @@ -918,7 +918,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
}

void AudioDriverWASAPI::start() {
if (audio_output.audio_client) {
if (audio_output.audio_client && !audio_output.active.is_set()) {
HRESULT hr = audio_output.audio_client->Start();
if (hr != S_OK) {
ERR_PRINT("WASAPI: Start failed");
Expand All @@ -928,6 +928,21 @@ void AudioDriverWASAPI::start() {
}
}

void AudioDriverWASAPI::stop() {
if (audio_output.audio_client && audio_output.active.is_set()) {
HRESULT hr = audio_output.audio_client->Stop();
if (hr != S_OK) {
ERR_PRINT("WASAPI: Stop failed");
} else {
hr = audio_output.audio_client->Reset();
if (hr != S_OK) {
ERR_PRINT("WASAPI: Reset failed");
}
audio_output.active.clear();
}
}
}

void AudioDriverWASAPI::lock() {
mutex.lock();
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/wasapi/audio_driver_wasapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ class AudioDriverWASAPI : public AudioDriver {

virtual Error init() override;
virtual void start() override;
virtual bool supports_sleep() const override { return true; }
virtual void stop() override;
virtual int get_mix_rate() const override;
virtual SpeakerMode get_speaker_mode() const override;
virtual float get_latency() override;
Expand Down
59 changes: 58 additions & 1 deletion servers/audio_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ void AudioDriver::audio_server_process(int p_frames, int32_t *p_buffer, bool p_u
}
}

void AudioDriver::stop()
{
WARN_PRINT(vformat(R"(Trying to stop audio driver, but the driver (%s) doesn't support sleeping.)", get_name()));
}

void AudioDriver::update_mix_time(int p_frames) {
_last_mix_frames = p_frames;
if (OS::get_singleton()) {
Expand Down Expand Up @@ -329,6 +334,10 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
to_mix -= to_copy;
}

if (falling_asleep && OS::get_singleton()->get_ticks_msec() - sleep_countdown_start_msec > sleep_countdown_msec) {
_go_to_sleep();
}

#ifdef DEBUG_ENABLED
prof_time.add(OS::get_singleton()->get_ticks_usec() - prof_ticks);
#endif
Expand Down Expand Up @@ -641,6 +650,22 @@ void AudioServer::_mix_step() {
}
}

bool should_sleep = playback_list.begin() == playback_list.end() && sample_playback_list.begin() == sample_playback_list.end();
for (int i = 0; should_sleep && i < buses.size(); ++i) {
Bus *bus = buses[i];
for (int k = 0; should_sleep && k < bus->channels.size(); ++k) {
if (bus->channels[k].active) {
should_sleep = false;
}
}
}

if (should_sleep) {
_start_sleep_countdown();
} else {
_stop_sleep_countdown();
}

mix_frames += buffer_size;
to_mix = buffer_size;
}
Expand Down Expand Up @@ -692,6 +717,32 @@ AudioServer::AudioStreamPlaybackListNode *AudioServer::_find_playback_list_node(
return nullptr;
}

void AudioServer::_start_sleep_countdown() {
if (!falling_asleep && !sleeping && Engine::get_singleton()->is_editor_hint() && AudioDriver::get_singleton()->supports_sleep()) {
falling_asleep = true;
sleep_countdown_start_msec = OS::get_singleton()->get_ticks_msec();
}
}

void AudioServer::_stop_sleep_countdown() {
falling_asleep = false;
}

void AudioServer::_go_to_sleep()
{
AudioDriver::get_singleton()->stop();
falling_asleep = false;
sleeping = true;
}

void AudioServer::_wake_from_sleep() {
_stop_sleep_countdown();
if (sleeping) {
AudioDriver::get_singleton()->start();
sleeping = false;
}
}

void AudioServer::_delete_stream_playback(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND(p_playback.is_null());
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
Expand Down Expand Up @@ -1233,6 +1284,7 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, con
playback_node->state.store(AudioStreamPlaybackListNode::PLAYING);

playback_list.insert(playback_node);
_wake_from_sleep();
}

void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) {
Expand Down Expand Up @@ -1480,8 +1532,12 @@ void AudioServer::init() {
set_bus_name(0, "Master");

if (AudioDriver::get_singleton()) {
AudioDriver::get_singleton()->start();
AudioDriver::get_singleton()->set_sample_bus_count(1);
if (Engine::get_singleton()->is_editor_hint() && AudioDriver::get_singleton()->supports_sleep()) {
sleeping = true;
} else {
AudioDriver::get_singleton()->start();
}
}

#ifdef TOOLS_ENABLED
Expand Down Expand Up @@ -1867,6 +1923,7 @@ void AudioServer::start_sample_playback(const Ref<AudioSamplePlayback> &p_playba
ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null.");
AudioDriver::get_singleton()->start_sample_playback(p_playback);
sample_playback_list.ordered_insert(p_playback);
_wake_from_sleep();
}

void AudioServer::stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
Expand Down
11 changes: 11 additions & 0 deletions servers/audio_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class AudioDriver {

virtual Error init() = 0;
virtual void start() = 0;
virtual bool supports_sleep() const { return false; }
virtual void stop();
virtual int get_mix_rate() const = 0;
virtual SpeakerMode get_speaker_mode() const = 0;
virtual float get_latency() { return 0; }
Expand Down Expand Up @@ -295,6 +297,15 @@ class AudioServer : public Object {
AudioFrame lookahead[LOOKAHEAD_BUFFER_SIZE];
};

bool falling_asleep = false;
bool sleeping = false;
uint64_t sleep_countdown_start_msec;
uint64_t sleep_countdown_msec = 5000;
void _start_sleep_countdown();
void _stop_sleep_countdown();
void _go_to_sleep();
void _wake_from_sleep();

SafeList<AudioStreamPlaybackListNode *> playback_list;
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard;
void _delete_stream_playback(Ref<AudioStreamPlayback> p_playback);
Expand Down

0 comments on commit 8a5ed88

Please sign in to comment.