From 90facf9e90ffb22cff7c81703eb1a06fd8256dcc Mon Sep 17 00:00:00 2001 From: Ghabry Date: Tue, 26 Mar 2024 03:00:56 +0100 Subject: [PATCH] Audio: Add Api to get the shared Midi Out Instance Solves errors and lag in the audio settings when the device is opened multiple times --- src/audio.h | 10 ++++++++++ src/audio_generic.cpp | 23 +++++++++++++--------- src/audio_generic.h | 2 ++ src/platform/linux/midiout_device_alsa.cpp | 3 ++- src/window_settings.cpp | 3 ++- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/audio.h b/src/audio.h index 7449034abc..7a90137997 100644 --- a/src/audio.h +++ b/src/audio.h @@ -20,6 +20,7 @@ // Headers #include +#include "audio_generic_midiout.h" #include "filesystem_stream.h" #include "audio_secache.h" #include "game_config.h" @@ -39,6 +40,15 @@ struct AudioInterface { virtual void vGetConfig(Game_ConfigAudio& cfg) const = 0; + /** + * Instantiates and returns the Native Midi Out device. + * The Midi Out device is usually an exclusive resource. + * Implementing classes should only create one Midi Out and return the + * shared instance. + * @return Native Midi Out Device or nullptr on error + */ + virtual GenericAudioMidiOut* CreateAndGetMidiOut() { return nullptr; } + /** * Update audio. Must be called each frame. */ diff --git a/src/audio_generic.cpp b/src/audio_generic.cpp index cd45e13dbe..0b46fc7c41 100644 --- a/src/audio_generic.cpp +++ b/src/audio_generic.cpp @@ -198,6 +198,19 @@ void GenericAudio::Update() { // no-op, handled by the Decode function called through a thread } +GenericAudioMidiOut* GenericAudio::CreateAndGetMidiOut() { + if (!midi_thread) { + midi_thread = std::make_unique(); + std::string status_message; + if (midi_thread->IsInitialized(status_message)) { + midi_thread->StartThread(); + } else { + midi_thread.reset(); + } + } + return midi_thread.get(); +} + void GenericAudio::SetFormat(int frequency, AudioDecoder::Format format, int channels) { output_format.frequency = frequency; output_format.format = format; @@ -222,15 +235,7 @@ bool GenericAudio::PlayOnChannel(BgmChannel& chan, Filesystem_Stream::InputStrea bool wildmidi = Audio().GetWildMidiEnabled() && MidiDecoder::CreateWildMidi(true); if (!fluidsynth && !wildmidi && Audio().GetNativeMidiEnabled()) { - if (!midi_thread) { - midi_thread = std::make_unique(); - std::string status_message; - if (midi_thread->IsInitialized(status_message)) { - midi_thread->StartThread(); - } else { - midi_thread.reset(); - } - } + CreateAndGetMidiOut(); if (midi_thread) { midi_thread->LockMutex(); diff --git a/src/audio_generic.h b/src/audio_generic.h index 04755a2c7d..ab1ab3cf5e 100644 --- a/src/audio_generic.h +++ b/src/audio_generic.h @@ -61,6 +61,8 @@ class GenericAudio : public AudioInterface { void vGetConfig(Game_ConfigAudio&) const override {} + GenericAudioMidiOut* CreateAndGetMidiOut() override; + void SetFormat(int frequency, AudioDecoder::Format format, int channels); virtual void LockMutex() const = 0; diff --git a/src/platform/linux/midiout_device_alsa.cpp b/src/platform/linux/midiout_device_alsa.cpp index 9ed75b590c..d17bae9da1 100644 --- a/src/platform/linux/midiout_device_alsa.cpp +++ b/src/platform/linux/midiout_device_alsa.cpp @@ -79,7 +79,8 @@ AlsaMidiOutDevice::AlsaMidiOutDevice(std::string& status_message) { return; } - Output::Debug("ALSA MIDI: Using client {}:{}:{}", dst_client, dst_port_name, dst_port); + status_message = fmt::format("ALSA MIDI: Using client {}:{}:{}", dst_client, dst_port_name, dst_port); + Output::DebugStr(status_message); status = snd_seq_create_simple_port(midi_out, "Harmony", SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION); diff --git a/src/window_settings.cpp b/src/window_settings.cpp index decf01f81c..e603f83b4a 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -327,7 +327,8 @@ void Window_Settings::RefreshAudioMidi() { if (cfg.native_midi.IsOptionVisible()) { AddOption(cfg.native_midi, []() { Audio().SetNativeMidiEnabled(Audio().GetConfig().native_midi.Toggle()); }); - if (!GenericAudioMidiOut().IsInitialized(GetFrame().options.back().help2)) { + auto midi_out = Audio().CreateAndGetMidiOut(); + if (!midi_out || !midi_out->IsInitialized(GetFrame().options.back().help2)) { GetFrame().options.back().text += " [Not working]"; GetFrame().options.back().color = Font::ColorKnockout; } else if (cfg.native_midi.Get() && !used) {