sfizz
sfizz copied to clipboard
Cross-platform support for settings.xml
Background @atsushieno is working on a pipeline to generate audio programmatically, and that requires the sfizz path to be programmatically set. I was browsing his project and came across this code:
sed -e "s/%%WORKDIR%%/$(WORKDIR_ESCAPED)/" sfizz-settings.xml > ~/.config/SFZTools/sfizz/settings.xml
https://github.com/atsushieno/augene-ng-production/blob/main/Makefile#L25
The settings file is here:
<?xml version="1.0"?>
<properties>
<entry key="user_files_dir">%%WORKDIR%%/sounds/sfz</entry>
<entry key="current_theme">Dark</entry>
</properties>
https://github.com/atsushieno/augene-ng-production/blob/main/sfizz-settings.xml
It seems that sfizz supports settings.xml
files. I see it in the source-code:
https://github.com/sfztools/sfizz/blob/develop/plugins/common/plugin/SfizzSettings.cpp#L92
However it is inside an if statement, for Linux only.
Problem When Sfizz is initially installed, there is not a way to set the default settings programmatically on Mac & Windows. The code exists and works for Linux only.
Solution
Update SfizzSettings code to support cross-platform use of an settings.xml
. Using the logic:
If a setting does not exist in storage, then fall back to settings.xml (if it exists)
This will allow developers to install sfizz + a settings file, to programmatically configure defaults for user_files_dir
and current_theme
and other settings which are added in the future.
Was discussing on Discord and @jpcima mentioned I can just load settings from the User registries (rather than from xml), which would not require any changes to sfizz.
I looked into reading/writing from the current User registries, in order to set the sfizz user_files_dir
programmatically.
As each platform has a different implementation, I would also need to support three different solutions on my side. Those are:
- Linux - Load
~/.config/SFZTools/sfizz/settings.xml
and parse the xml using https://www.npmjs.com/package/fast-xml-parser (90.7 kB) - Mac - Access
NSUserDefaults
using a library https://www.npmjs.com/package/node-mac-userdefaults (32.4 kB) - Windows - Us a library to read/write to regedit: https://www.npmjs.com/package/regedit (179 kB)
As you can see it adds additional dependencies and requires a custom implementation for each platform. This serves as further proof using settings.xml files cross-platform would be simpler and easier for everyone!
Hi @kmturley !
As you can see it adds additional dependencies and requires a custom implementation for each platform. This servers as further proof using settings.xml files cross-platform would be simpler and easier for everyone!
On Mac and Windows using NSUserDefaults and the registry respectively seems to be the platform-idiomatic way to handle user settings. If you do cross-platform things, it is usually better to adapt to the platform ways rather than bending them to your own way. The reason is that down the line, platforms will usually provide an upgrade path or backwards compatibility for their recommended practices, but they will not accomodate your hacks and tricks. I'll think more about it and also discuss with jpcima who wrote this original handling :)
atsushieno is working on a pipeline to generate audio programmatically
Judging by your use case, I've been wondering about the appropriateness of this solution. If I understand right, you would want to give sfizz a temporary sfz directory for the lifetime of the execution of your program.
This will modify the sfizz global setting to do so, but the problem is this will (temporarily) affect all sfizz instances which during the runtime of this program (including maybe concurrent executions of your program, if that's a possibility)
One potentially cleaner way would be to define the sfz path by environment variable, overriding use of settings altogether if such variable is defined.
@paulfd There are pros/cons as only 2 of 3 operating systems support that approach. But ultimately it's you decision on how you want to handle internal settings/configuration. However for external setting of configuration a simple clean approach is best for users. For example I designed StudioRack interface to make it easy cross-platform to set a configuration setting:
// studiorack config set <key> <val>
studiorack config set pluginFolder ~/Library/Audio/Plug-ins
As a user of the program, I don't need to know/care how/where it is stored :)
@jpcima Environment variables would be ideal for CI pipelines:
export SFIZZ_USER_FILES_DIR=~/Library/Audio/Plug-ins
Command line arguments would also work well for CI pipelines:
sfizz --user_files_dir=~/Library/Audio/Plug-ins
However when programmatically installing sfizz onto someone's machine (sfizz is then run inside the DAW or independently by user running the executable), I don't believe that would work?
However when programmatically installing sfizz onto someone's machine (sfizz is then run inside the DAW or independently by user running the executable), I don't believe that would work?
@kmturley It depends of the details of your use case exactly. If the DAW is given the environment variable, it will be inherited down to sfizz running inside it.
I totally forgot the most obvious before, but sfizz has a predefined user directory defined as <Documents>/SFZ instruments
. Did you consider installing there?
A tiny additional context: I blogged about my MP3 production on CI last year, and mentioned StudioRack there, and had some talk with @kmturley. It would be therefore not only for StudioRack developers' concern but also for my own concern (TBH I don't care about non-Linux environment at the moment).
Using config files outside Linux was @kmturley's idea, but I second the idea of supporting it too.
On Mac and Windows using NSUserDefaults and the registry respectively seems to be the platform-idiomatic way to handle user settings. If you do cross-platform things, it is usually better to adapt to the platform ways rather than bending them to your own way. The reason is that down the line, platforms will usually provide an upgrade path or backwards compatibility for their recommended practices, but they will not accomodate your hacks and tricks.
This may be still sometimes true, but modern cross-platform development frameworks do not necessarily follow the idea of storing settings on registry on Windows. For example, dotnet (already 20+ years old) System.Configuration classes store user configuration settings as a file, not a registry entry, and do not really accomodate any migration paths (the config files are generated on the appropriate directories under AppData
so users and/or app developers can just copy over the new environment). Considering what dotnet is for Microsoft, I believe it is the platform-idiomatic way if that exists.
After looking more into this it seems that macOS also supports having config files somewhere in ~/Library so I think we could do this, and the environment variable would also make sense..
@jpcima Regarding the default folder. I can definitely hardcode that path, but if someone changes it afterwards I would install the instruments into the wrong location.
I discovered on a Mac you can read/write plist settings using the defaults
program:
defaults read tools.sfz.sfizz user_files_dir
Which returns the custom path I set via Sfizz:
/Users/username/Library/Audio/Plug-Ins/SFZ
You can reset it using:
defaults write tools.sfz.sfizz user_files_dir "/Users/username/Documents/SFZ Instruments"
If we could compile bash commands to read user_files_dir cross platform. I could avoid installing libraries and just run a bash command for each platform.
@kmturley It seems a good idea. IIRC however, I believe we do scan the "SFZ instruments" as secondary folder at all times, regardless what the setting is defined to.
Ideal world is to be able to get/set the folder. As I was hoping to keep the paths consistent with the other plugins:
However I can workaround that for now by using the default path