From dd974ded406aa945d5fd998c968f00c6fa15fd51 Mon Sep 17 00:00:00 2001 From: Mathias R Date: Mon, 11 Aug 2025 09:50:23 +0200 Subject: [PATCH] DSP Presets (#2309) This PR adds support for DSP presets to be reused across players. --- music_assistant/constants.py | 1 + music_assistant/controllers/config.py | 46 ++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/music_assistant/constants.py b/music_assistant/constants.py index 9e6759bf..70116edb 100644 --- a/music_assistant/constants.py +++ b/music_assistant/constants.py @@ -56,6 +56,7 @@ CONF_DEPRECATED_EQ_BASS: Final[str] = "eq_bass" CONF_DEPRECATED_EQ_MID: Final[str] = "eq_mid" CONF_DEPRECATED_EQ_TREBLE: Final[str] = "eq_treble" CONF_PLAYER_DSP: Final[str] = "player_dsp" +CONF_PLAYER_DSP_PRESETS: Final[str] = "player_dsp_presets" CONF_OUTPUT_CHANNELS: Final[str] = "output_channels" CONF_FLOW_MODE: Final[str] = "flow_mode" CONF_LOG_LEVEL: Final[str] = "log_level" diff --git a/music_assistant/controllers/config.py b/music_assistant/controllers/config.py index e8d3a1da..cb540592 100644 --- a/music_assistant/controllers/config.py +++ b/music_assistant/controllers/config.py @@ -22,7 +22,7 @@ from music_assistant_models.config_entries import ( PlayerConfig, ProviderConfig, ) -from music_assistant_models.dsp import DSPConfig, ToneControlFilter +from music_assistant_models.dsp import DSPConfig, DSPConfigPreset, ToneControlFilter from music_assistant_models.enums import EventType, ProviderFeature, ProviderType from music_assistant_models.errors import ( ActionUnavailable, @@ -39,6 +39,7 @@ from music_assistant.constants import ( CONF_DEPRECATED_EQ_TREBLE, CONF_ONBOARD_DONE, CONF_PLAYER_DSP, + CONF_PLAYER_DSP_PRESETS, CONF_PLAYERS, CONF_PROVIDERS, CONF_SERVER_ID, @@ -536,6 +537,49 @@ class ConfigController: ) return config + @api_command("config/dsp_presets/get") + async def get_dsp_presets(self) -> list[DSPConfigPreset]: + """Return all user-defined DSP presets.""" + raw_presets = self.get(CONF_PLAYER_DSP_PRESETS, {}) + return [DSPConfigPreset.from_dict(preset) for preset in raw_presets.values()] + + @api_command("config/dsp_presets/save") + async def save_dsp_presets(self, preset: DSPConfigPreset) -> DSPConfigPreset: + """ + Save/update a user-defined DSP presets. + + This method will validate the config before saving it to the persistent storage. + """ + preset.validate() + + if preset.preset_id is None: + # Generate a new preset_id if it does not exist + preset.preset_id = shortuuid.random(8).lower() + + # Save the preset to the persistent storage + self.set(f"{CONF_PLAYER_DSP_PRESETS}/preset_{preset.preset_id}", preset.to_dict()) + + all_presets = await self.get_dsp_presets() + + self.mass.signal_event( + EventType.DSP_PRESETS_UPDATED, + data=all_presets, + ) + + return preset + + @api_command("config/dsp_presets/remove") + async def remove_dsp_preset(self, preset_id: str) -> None: + """Remove a user-defined DSP preset.""" + self.mass.config.remove(f"{CONF_PLAYER_DSP_PRESETS}/preset_{preset_id}") + + all_presets = await self.get_dsp_presets() + + self.mass.signal_event( + EventType.DSP_PRESETS_UPDATED, + data=all_presets, + ) + def create_default_player_config( self, player_id: str, -- 2.34.1