diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 40c66b0bc52..216100bac6c 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -31,6 +31,7 @@ #ifdef ALSA_ENABLED +#include "os/os.h" #include "project_settings.h" #include @@ -44,7 +45,7 @@ Error AudioDriverALSA::init() { samples_in = NULL; samples_out = NULL; - mix_rate = GLOBAL_DEF("audio/mix_rate", 44100); + mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); speaker_mode = SPEAKER_MODE_STEREO; channels = 2; @@ -86,19 +87,25 @@ Error AudioDriverALSA::init() { status = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &mix_rate, NULL); CHECK_FAIL(status < 0); - int latency = GLOBAL_DEF("audio/output_latency", 25); - buffer_size = closest_power_of_2(latency * mix_rate / 1000); + // In ALSA the period size seems to be the one that will determine the actual latency + // Ref: https://www.alsa-project.org/main/index.php/FramesPeriods + unsigned int periods = 2; + int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + buffer_frames = closest_power_of_2(latency * mix_rate / 1000); + buffer_size = buffer_frames * periods; + period_size = buffer_frames; // set buffer size from project settings status = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size); CHECK_FAIL(status < 0); - // make period size 1/8 - period_size = buffer_size >> 3; status = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &period_size, NULL); CHECK_FAIL(status < 0); - unsigned int periods = 2; + if (OS::get_singleton()->is_stdout_verbose()) { + print_line("audio buffer frames: " + itos(period_size) + " calculated latency: " + itos(period_size * 1000 / mix_rate) + "ms"); + } + status = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, NULL); CHECK_FAIL(status < 0); diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h index 83601be41bd..c76ec0da61b 100644 --- a/drivers/alsa/audio_driver_alsa.h +++ b/drivers/alsa/audio_driver_alsa.h @@ -51,6 +51,7 @@ class AudioDriverALSA : public AudioDriver { unsigned int mix_rate; SpeakerMode speaker_mode; + snd_pcm_uframes_t buffer_frames; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; int channels; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 356b1ad958c..1798c84d854 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -33,6 +33,7 @@ #include +#include "os/os.h" #include "project_settings.h" Error AudioDriverPulseAudio::init() { @@ -44,7 +45,7 @@ Error AudioDriverPulseAudio::init() { samples_in = NULL; samples_out = NULL; - mix_rate = GLOBAL_DEF("audio/mix_rate", 44100); + mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); speaker_mode = SPEAKER_MODE_STEREO; channels = 2; @@ -53,12 +54,17 @@ Error AudioDriverPulseAudio::init() { spec.channels = channels; spec.rate = mix_rate; - int latency = GLOBAL_DEF("audio/output_latency", 25); - buffer_size = closest_power_of_2(latency * mix_rate / 1000); + int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + buffer_frames = closest_power_of_2(latency * mix_rate / 1000); + buffer_size = buffer_frames * channels; + + if (OS::get_singleton()->is_stdout_verbose()) { + print_line("audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); + } pa_buffer_attr attr; - // set to appropriate buffer size from global settings - attr.tlength = buffer_size; + // set to appropriate buffer length (in bytes) from global settings + attr.tlength = buffer_size * sizeof(int16_t); // set them to be automatically chosen attr.prebuf = (uint32_t)-1; attr.maxlength = (uint32_t)-1; @@ -80,8 +86,8 @@ Error AudioDriverPulseAudio::init() { ERR_FAIL_COND_V(pulse == NULL, ERR_CANT_OPEN); } - samples_in = memnew_arr(int32_t, buffer_size * channels); - samples_out = memnew_arr(int16_t, buffer_size * channels); + samples_in = memnew_arr(int32_t, buffer_size); + samples_out = memnew_arr(int16_t, buffer_size); mutex = Mutex::create(); thread = Thread::create(AudioDriverPulseAudio::thread_func, this); @@ -106,18 +112,18 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { while (!ad->exit_thread) { if (!ad->active) { - for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) { + for (unsigned int i = 0; i < ad->buffer_size; i++) { ad->samples_out[i] = 0; } } else { ad->lock(); - ad->audio_server_process(ad->buffer_size, ad->samples_in); + ad->audio_server_process(ad->buffer_frames, ad->samples_in); ad->unlock(); - for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) { + for (unsigned int i = 0; i < ad->buffer_size; i++) { ad->samples_out[i] = ad->samples_in[i] >> 16; } } @@ -125,7 +131,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { // pa_simple_write always consumes the entire buffer int error_code; - int byte_size = ad->buffer_size * sizeof(int16_t) * ad->channels; + int byte_size = ad->buffer_size * sizeof(int16_t); if (pa_simple_write(ad->pulse, ad->samples_out, byte_size, &error_code) < 0) { // can't recover here fprintf(stderr, "PulseAudio failed and can't recover: %s\n", pa_strerror(error_code)); @@ -175,13 +181,20 @@ void AudioDriverPulseAudio::finish() { exit_thread = true; Thread::wait_to_finish(thread); - if (pulse) + if (pulse) { pa_simple_free(pulse); + pulse = NULL; + } if (samples_in) { memdelete_arr(samples_in); + samples_in = NULL; + } + + if (samples_out) { memdelete_arr(samples_out); - }; + samples_out = NULL; + } memdelete(thread); if (mutex) { @@ -194,10 +207,15 @@ void AudioDriverPulseAudio::finish() { AudioDriverPulseAudio::AudioDriverPulseAudio() { + samples_in = NULL; + samples_out = NULL; mutex = NULL; thread = NULL; pulse = NULL; latency = 0; + buffer_frames = 0; + buffer_size = 0; + channels = 0; } AudioDriverPulseAudio::~AudioDriverPulseAudio() { diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index 2f567266175..9ae0b7e50c6 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -51,6 +51,7 @@ class AudioDriverPulseAudio : public AudioDriver { unsigned int mix_rate; SpeakerMode speaker_mode; + unsigned int buffer_frames; unsigned int buffer_size; int channels; diff --git a/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp index 7de3ff192e3..ae5fdd28b64 100644 --- a/drivers/rtaudio/audio_driver_rtaudio.cpp +++ b/drivers/rtaudio/audio_driver_rtaudio.cpp @@ -107,14 +107,13 @@ Error AudioDriverRtAudio::init() { options.numberOfBuffers = 4; parameters.firstChannel = 0; - mix_rate = GLOBAL_DEF("audio/mix_rate", 44100); + mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); - int latency = GLOBAL_DEF("audio/output_latency", 25); - // calculate desired buffer_size - unsigned int buffer_size = closest_power_of_2(latency * mix_rate / 1000); + int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + unsigned int buffer_frames = closest_power_of_2(latency * mix_rate / 1000); if (OS::get_singleton()->is_stdout_verbose()) { - print_line("audio buffer size: " + itos(buffer_size)); + print_line("audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); } short int tries = 2; @@ -127,7 +126,7 @@ Error AudioDriverRtAudio::init() { }; try { - dac->openStream(¶meters, NULL, RTAUDIO_SINT32, mix_rate, &buffer_size, &callback, this, &options); + dac->openStream(¶meters, NULL, RTAUDIO_SINT32, mix_rate, &buffer_frames, &callback, this, &options); active = true; break; @@ -199,7 +198,7 @@ AudioDriverRtAudio::AudioDriverRtAudio() { active = false; mutex = NULL; dac = NULL; - mix_rate = 44100; + mix_rate = DEFAULT_MIX_RATE; speaker_mode = SPEAKER_MODE_STEREO; } diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 6e01b5f524d..4c1f90ec242 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -65,6 +65,18 @@ Error AudioDriverWASAPI::init_device() { format_tag = pwfex->wFormatTag; bits_per_sample = pwfex->wBitsPerSample; + switch (channels) { + case 2: // Stereo + case 6: // Surround 5.1 + case 8: // Surround 7.1 + break; + + default: + ERR_PRINT("WASAPI: Unsupported number of channels"); + ERR_FAIL_V(ERR_CANT_OPEN); + break; + } + if (format_tag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfex = (WAVEFORMATEXTENSIBLE *)pwfex; @@ -83,13 +95,6 @@ Error AudioDriverWASAPI::init_device() { } } - int latency = GLOBAL_DEF("audio/output_latency", 25); - buffer_size = closest_power_of_2(latency * mix_rate / 1000); - - if (OS::get_singleton()->is_stdout_verbose()) { - print_line("audio buffer size: " + itos(buffer_size)); - } - hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfex, NULL); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); @@ -102,11 +107,20 @@ Error AudioDriverWASAPI::init_device() { hr = audio_client->GetService(IID_IAudioRenderClient, (void **)&render_client); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + UINT32 max_frames; hr = audio_client->GetBufferSize(&max_frames); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + // Due to WASAPI Shared Mode we have no control of the buffer size + buffer_frames = max_frames; + + // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) + buffer_size = buffer_frames * channels; samples_in.resize(buffer_size); - buffer_frames = buffer_size / channels; + + if (OS::get_singleton()->is_stdout_verbose()) { + print_line("audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); + } return OK; } @@ -200,7 +214,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { HRESULT hr = ad->audio_client->GetCurrentPadding(&cur_frames); if (hr == S_OK) { // Check how much frames are available on the WASAPI buffer - UINT32 avail_frames = ad->max_frames - cur_frames; + UINT32 avail_frames = ad->buffer_frames - cur_frames; UINT32 write_frames = avail_frames > left_frames ? left_frames : avail_frames; BYTE *buffer = NULL; @@ -332,7 +346,6 @@ AudioDriverWASAPI::AudioDriverWASAPI() { mutex = NULL; thread = NULL; - max_frames = 0; format_tag = 0; bits_per_sample = 0; diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index b91751f87e9..d1d3663fe57 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -48,7 +48,6 @@ class AudioDriverWASAPI : public AudioDriver { Mutex *mutex; Thread *thread; - UINT32 max_frames; WORD format_tag; WORD bits_per_sample; diff --git a/platform/osx/audio_driver_osx.cpp b/platform/osx/audio_driver_osx.cpp index 8c5a734f765..78c52af2018 100644 --- a/platform/osx/audio_driver_osx.cpp +++ b/platform/osx/audio_driver_osx.cpp @@ -77,7 +77,7 @@ Error AudioDriverOSX::initDevice() { break; }*/ - mix_rate = GLOBAL_DEF("audio/mix_rate", 44100); + mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); zeromem(&strdesc, sizeof(strdesc)); strdesc.mFormatID = kAudioFormatLinearPCM; @@ -92,16 +92,20 @@ Error AudioDriverOSX::initDevice() { result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc)); ERR_FAIL_COND_V(result != noErr, FAILED); - int latency = GLOBAL_DEF("audio/output_latency", 25); - unsigned int buffer_size = closest_power_of_2(latency * mix_rate / 1000); + int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) + buffer_frames = closest_power_of_2(latency * mix_rate / 1000); + + result = AudioUnitSetProperty(audio_unit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, kOutputBus, &buffer_frames, sizeof(UInt32)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + buffer_size = buffer_frames * channels; + samples_in.resize(buffer_size); if (OS::get_singleton()->is_stdout_verbose()) { - print_line("audio buffer size: " + itos(buffer_size) + " calculated latency: " + itos(buffer_size * 1000 / mix_rate)); + print_line("audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); } - samples_in.resize(buffer_size); - buffer_frames = buffer_size / channels; - AURenderCallbackStruct callback; zeromem(&callback, sizeof(AURenderCallbackStruct)); callback.inputProc = &AudioDriverOSX::output_callback; @@ -234,7 +238,7 @@ void AudioDriverOSX::start() { }; int AudioDriverOSX::get_mix_rate() const { - return 44100; + return mix_rate; }; AudioDriver::SpeakerMode AudioDriverOSX::get_speaker_mode() const { @@ -282,8 +286,12 @@ AudioDriverOSX::AudioDriverOSX() { active = false; mutex = NULL; - mix_rate = 44100; + mix_rate = 0; channels = 2; + + buffer_size = 0; + buffer_frames = 0; + samples_in.clear(); }; diff --git a/platform/osx/audio_driver_osx.h b/platform/osx/audio_driver_osx.h index ac178b89f36..a7e68c81416 100644 --- a/platform/osx/audio_driver_osx.h +++ b/platform/osx/audio_driver_osx.h @@ -45,8 +45,9 @@ class AudioDriverOSX : public AudioDriver { Mutex *mutex; int mix_rate; - int channels; - int buffer_frames; + unsigned int channels; + unsigned int buffer_frames; + unsigned int buffer_size; Vector samples_in; diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp index 992fece85f5..1ae0e7b96ba 100644 --- a/servers/audio/audio_driver_dummy.cpp +++ b/servers/audio/audio_driver_dummy.cpp @@ -37,17 +37,16 @@ Error AudioDriverDummy::init() { active = false; thread_exited = false; exit_thread = false; - pcm_open = false; samples_in = NULL; - mix_rate = 44100; + mix_rate = DEFAULT_MIX_RATE; speaker_mode = SPEAKER_MODE_STEREO; channels = 2; - int latency = GLOBAL_DEF("audio/output_latency", 25); - buffer_size = next_power_of_2(latency * mix_rate / 1000); + int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + buffer_frames = closest_power_of_2(latency * mix_rate / 1000); - samples_in = memnew_arr(int32_t, buffer_size * channels); + samples_in = memnew_arr(int32_t, buffer_frames * channels); mutex = Mutex::create(); thread = Thread::create(AudioDriverDummy::thread_func, this); @@ -59,17 +58,15 @@ void AudioDriverDummy::thread_func(void *p_udata) { AudioDriverDummy *ad = (AudioDriverDummy *)p_udata; - uint64_t usdelay = (ad->buffer_size / float(ad->mix_rate)) * 1000000; + uint64_t usdelay = (ad->buffer_frames / float(ad->mix_rate)) * 1000000; while (!ad->exit_thread) { - if (!ad->active) { - - } else { + if (ad->active) { ad->lock(); - ad->audio_server_process(ad->buffer_size, ad->samples_in); + ad->audio_server_process(ad->buffer_frames, ad->samples_in); ad->unlock(); }; diff --git a/servers/audio/audio_driver_dummy.h b/servers/audio/audio_driver_dummy.h index b3fea593892..90af1961b74 100644 --- a/servers/audio/audio_driver_dummy.h +++ b/servers/audio/audio_driver_dummy.h @@ -43,8 +43,8 @@ class AudioDriverDummy : public AudioDriver { int32_t *samples_in; static void thread_func(void *p_udata); - int buffer_size; + unsigned int buffer_frames; unsigned int mix_rate; SpeakerMode speaker_mode; @@ -53,7 +53,6 @@ class AudioDriverDummy : public AudioDriver { bool active; bool thread_exited; mutable bool exit_thread; - bool pcm_open; public: const char *get_name() const { diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index c0343399c3f..3139c6bb7a5 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -155,6 +155,29 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) { todo -= to_copy; to_mix -= to_copy; } + +#ifdef DEBUG_ENABLED + if (OS::get_singleton() && OS::get_singleton()->is_stdout_verbose()) { + static uint64_t first_ticks = 0; + static uint64_t last_ticks = 0; + static uint64_t ticks = 0; + static int count = 0; + static int total = 0; + + ticks = OS::get_singleton()->get_ticks_msec(); + if ((ticks - first_ticks) > 10 * 1000) { + print_line("Audio Driver " + String(AudioDriver::get_singleton()->get_name()) + " average latency: " + itos(total / count) + "ms (frame=" + itos(p_frames) + ")"); + first_ticks = ticks; + total = 0; + count = 0; + } + + total += ticks - last_ticks; + count++; + + last_ticks = ticks; + } +#endif } void AudioServer::_mix_step() { diff --git a/servers/audio_server.h b/servers/audio_server.h index 05e92ceaf08..13a74856c81 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -54,6 +54,9 @@ public: SPEAKER_SURROUND_71, }; + static const int DEFAULT_MIX_RATE = 44100; + static const int DEFAULT_OUTPUT_LATENCY = 15; + static AudioDriver *get_singleton(); void set_singleton();