Skip to content

Commit

Permalink
backend.midi: Delete instances not in use by any running programs
Browse files Browse the repository at this point in the history
  • Loading branch information
EmoonX committed Jan 19, 2025
1 parent c3d90a3 commit 9cb7d06
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 1 deletion.
29 changes: 28 additions & 1 deletion bottles/backend/utils/midi.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

from typing import Self

from fluidsynth import Synth # type: ignore [import-untyped]
from ctypes import c_void_p
from fluidsynth import cfunc, Synth # type: ignore [import-untyped]

from bottles.backend.logger import Logger
from bottles.backend.models.config import BottleConfig
Expand All @@ -39,6 +40,7 @@ def find_or_create(cls, soundfont_path: str) -> Self:

for fs in cls.__active_instances.values():
if fs.soundfont_path == soundfont_path:
fs.program_count += 1
return fs

fs = cls(soundfont_path)
Expand All @@ -50,6 +52,7 @@ def __init__(self, soundfont_path: str):
self.soundfont_path = soundfont_path
self.id = self.__get_vacant_id()
self.__start()
self.program_count = 1

@classmethod
def __get_vacant_id(cls) -> int:
Expand Down Expand Up @@ -81,3 +84,27 @@ def register_as_current(self, config: BottleConfig):
data=f"#{self.id}",
value_type="REG_SZ",
)

def decrement_program_counter(self):
"""Decrement program counter; if it reaches zero, delete this instance."""
self.program_count -= 1
if self.program_count == 0:
self.__delete()

def __delete(self):
"""Kill underlying synthetizer and remove FluidSynth instance from dict."""

def __delete_synth(synth: Synth):
"""Bind missing function and run deletion routines."""
delete_fluid_midi_driver = cfunc(
"delete_fluid_midi_driver", c_void_p, ("driver", c_void_p, 1)
)
delete_fluid_midi_driver(synth.midi_driver)
synth.delete()

logging.info(
"Killing FluidSynth server with SoundFont"
f" #{self.id} ('{self.soundfont_path}')…"
)
__delete_synth(self.synth)
self.__active_instances.pop(self.id)
7 changes: 7 additions & 0 deletions bottles/backend/wine/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def __init__(

self.fluidsynth = None
if (soundfont_path := midi_soundfont) not in (None, ""):
# FluidSynth instance is bound to WineExecutor as a member to control
# the former's lifetime (deleted when no more references from executors)
self.fluidsynth = FluidSynth.find_or_create(soundfont_path)
self.fluidsynth.register_as_current(config)

Expand Down Expand Up @@ -361,3 +363,8 @@ def __set_monitors(self):
winedbg = WineDbg(self.config, silent=True)
for m in self.monitoring:
winedbg.wait_for_process(name=m)

def __del__(self):
"""On exit, kill FluidSynth instance if this was the last executor using it."""
if self.fluidsynth:
self.fluidsynth.decrement_program_counter()

0 comments on commit 9cb7d06

Please sign in to comment.