Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio: Write to device's buffer directly without temporary buffers. #90013

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions drivers/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ SConscript("unix/SCsub")
SConscript("windows/SCsub")

# Sounds drivers
SConscript("alsa/SCsub")
SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] in ["ios", "macos"]:
SConscript("coreaudio/SCsub")

if env["platform"] == "linuxbsd":
if env["alsa"]:
SConscript("alsa/SCsub")
if env["pulseaudio"]:
SConscript("pulseaudio/SCsub")

if env["platform"] == "windows":
if env["xaudio2"]:
SConscript("xaudio2/SCsub")
SConscript("wasapi/SCsub")
Comment on lines +12 to 24
Copy link
Contributor Author

@kus04e4ek kus04e4ek Apr 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better this way, as no empty files will be compiled. Maybe it would be better to add checks like this to other drivers? Also should I delete checks like #ifdef ALSA_ENABLED in files?

if not env.msvc:
SConscript("backtrace/SCsub")
if env["xaudio2"]:
SConscript("xaudio2/SCsub")

# Midi drivers
SConscript("alsamidi/SCsub")
Expand All @@ -36,6 +40,8 @@ if env["opengl3"]:

# Core dependencies
SConscript("png/SCsub")
if env["platform"] == "windows" and not env.msvc:
SConscript("backtrace/SCsub")
Comment on lines +43 to +44
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved out of Sounds drivers section


env.add_source_files(env.drivers_sources, "*.cpp")

Expand Down
5 changes: 2 additions & 3 deletions drivers/alsa/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

Import("env")

if "alsa" in env and env["alsa"]:
if env["use_sowrap"]:
env.add_source_files(env.drivers_sources, "asound-so_wrap.c")
if env["use_sowrap"]:
env.add_source_files(env.drivers_sources, "asound-so_wrap.c")

env.add_source_files(env.drivers_sources, "*.cpp")
79 changes: 30 additions & 49 deletions drivers/alsa/audio_driver_alsa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ extern int initialize_pulse(int verbose);
Error AudioDriverALSA::init_output_device() {
mix_rate = _get_configured_mix_rate();

speaker_mode = SPEAKER_MODE_STEREO;
// TODO: `channels` and `buffer_format` are hardcoded.
channels = 2;
buffer_format = BUFFER_FORMAT_INTEGER_16;

// If there is a specified output device check that it is really present
// If there is a specified output device check that it is really present.
if (output_device_name != "Default") {
PackedStringArray list = get_output_device_list();
if (list.find(output_device_name) == -1) {
Expand All @@ -62,20 +63,15 @@ Error AudioDriverALSA::init_output_device() {
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;

#define CHECK_FAIL(m_cond) \
if (m_cond) { \
fprintf(stderr, "ALSA ERR: %s\n", snd_strerror(status)); \
if (pcm_handle) { \
snd_pcm_close(pcm_handle); \
pcm_handle = nullptr; \
} \
ERR_FAIL_COND_V(m_cond, ERR_CANT_OPEN); \
#define CHECK_FAIL(m_cond) \
if (m_cond) { \
if (pcm_handle) { \
snd_pcm_close(pcm_handle); \
pcm_handle = nullptr; \
} \
ERR_FAIL_V_MSG(ERR_CANT_OPEN, vformat("ALSA: %s", snd_strerror(status))); \
}

//todo, add
//6 chans - "plug:surround51"
//4 chans - "plug:surround40";

if (output_device_name == "Default") {
status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
} else {
Expand All @@ -97,48 +93,43 @@ Error AudioDriverALSA::init_output_device() {
status = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
CHECK_FAIL(status < 0);

//not interested in anything else
status = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE);
CHECK_FAIL(status < 0);

//todo: support 4 and 6
status = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2);
status = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels);
CHECK_FAIL(status < 0);

status = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &mix_rate, nullptr);
CHECK_FAIL(status < 0);

// 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
// 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 = Engine::get_singleton()->get_audio_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
// Set buffer size from project settings.
snd_pcm_uframes_t buffer_size = buffer_frames * periods;
status = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size);
CHECK_FAIL(status < 0);

status = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &period_size, nullptr);
status = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &buffer_frames, nullptr);
CHECK_FAIL(status < 0);

print_verbose("Audio buffer frames: " + itos(period_size) + " calculated latency: " + itos(period_size * 1000 / mix_rate) + "ms");
print_verbose("Audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");

status = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, nullptr);
CHECK_FAIL(status < 0);

status = snd_pcm_hw_params(pcm_handle, hwparams);
CHECK_FAIL(status < 0);

//snd_pcm_hw_params_free(&hwparams);

snd_pcm_sw_params_alloca(&swparams);

status = snd_pcm_sw_params_current(pcm_handle, swparams);
CHECK_FAIL(status < 0);

status = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, period_size);
status = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, buffer_frames);
CHECK_FAIL(status < 0);

status = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
Expand All @@ -147,9 +138,7 @@ Error AudioDriverALSA::init_output_device() {
status = snd_pcm_sw_params(pcm_handle, swparams);
CHECK_FAIL(status < 0);

samples_in.resize(period_size * channels);
samples_out.resize(period_size * channels);

samples_out.resize(buffer_frames * channels * get_size_of_sample(buffer_format));
return OK;
}

Expand All @@ -162,7 +151,7 @@ Error AudioDriverALSA::init() {
#endif
#ifdef PULSEAUDIO_ENABLED
// On pulse enabled systems Alsa will silently use pulse.
// It doesn't matter if this fails as that likely means there is no pulse
// It doesn't matter if this fails as that likely means there is no pulse.
initialize_pulse(dylibloader_verbose);
#endif

Expand Down Expand Up @@ -200,28 +189,16 @@ void AudioDriverALSA::thread_func(void *p_udata) {
ad->lock();
ad->start_counting_ticks();

if (!ad->active.is_set()) {
for (uint64_t i = 0; i < ad->period_size * ad->channels; i++) {
ad->samples_out.write[i] = 0;
}

} else {
ad->audio_server_process(ad->period_size, ad->samples_in.ptrw());
ad->audio_server_process(ad->buffer_frames, ad->samples_out.ptr(), ad->active.is_set());

for (uint64_t i = 0; i < ad->period_size * ad->channels; i++) {
ad->samples_out.write[i] = ad->samples_in[i] >> 16;
}
}

int todo = ad->period_size;
int todo = ad->buffer_frames;
int total = 0;

while (todo && !ad->exit_thread.is_set()) {
int16_t *src = (int16_t *)ad->samples_out.ptr();
int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo);
int wrote = snd_pcm_writei(ad->pcm_handle, ad->samples_out.ptr() + total, todo);

if (wrote > 0) {
total += wrote;
total += wrote * ad->channels * AudioDriver::get_size_of_sample(ad->buffer_format);
todo -= wrote;
} else if (wrote == -EAGAIN) {
ad->stop_counting_ticks();
Expand Down Expand Up @@ -273,8 +250,12 @@ int AudioDriverALSA::get_mix_rate() const {
return mix_rate;
}

AudioDriver::SpeakerMode AudioDriverALSA::get_speaker_mode() const {
return speaker_mode;
int AudioDriverALSA::get_output_channels() const {
return channels;
}

AudioDriver::BufferFormat AudioDriverALSA::get_output_buffer_format() const {
return buffer_format;
}

PackedStringArray AudioDriverALSA::get_output_device_list() {
Expand Down
27 changes: 12 additions & 15 deletions drivers/alsa/audio_driver_alsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,23 @@ class AudioDriverALSA : public AudioDriver {
String output_device_name = "Default";
String new_output_device = "Default";

Vector<int32_t> samples_in;
Vector<int16_t> samples_out;

Error init_output_device();
void finish_output_device();

static void thread_func(void *p_udata);
LocalVector<int8_t> samples_out;

unsigned int mix_rate = 0;
SpeakerMode speaker_mode;
int channels = 0;

BufferFormat buffer_format = NO_BUFFER;

snd_pcm_uframes_t buffer_frames;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
int channels = 0;

SafeFlag active;
SafeFlag exit_thread;

Error init_output_device();
void finish_output_device();

static void thread_func(void *p_udata);

public:
virtual const char *get_name() const override {
return "ALSA";
Expand All @@ -80,18 +78,17 @@ class AudioDriverALSA : public AudioDriver {
virtual Error init() override;
virtual void start() override;
virtual int get_mix_rate() const override;
virtual SpeakerMode get_speaker_mode() const override;

virtual void lock() override;
virtual void unlock() override;
virtual void finish() override;

virtual int get_output_channels() const override;
virtual BufferFormat get_output_buffer_format() const override;

virtual PackedStringArray get_output_device_list() override;
virtual String get_output_device() override;
virtual void set_output_device(const String &p_name) override;

AudioDriverALSA() {}
~AudioDriverALSA() {}
};

#endif // ALSA_ENABLED
Expand Down
Loading
Loading