sfizz icon indicating copy to clipboard operation
sfizz copied to clipboard

Cross-platform support for settings.xml

Open kmturley opened this issue 2 years ago • 10 comments

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.

kmturley avatar Jan 01 '22 03:01 kmturley

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:

  1. Linux - Load ~/.config/SFZTools/sfizz/settings.xml and parse the xml using https://www.npmjs.com/package/fast-xml-parser (90.7 kB)
  2. Mac - Access NSUserDefaults using a library https://www.npmjs.com/package/node-mac-userdefaults (32.4 kB)
  3. 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!

kmturley avatar Jan 02 '22 03:01 kmturley

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 :)

paulfd avatar Jan 05 '22 10:01 paulfd

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.

jpcima avatar Jan 05 '22 18:01 jpcima

@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?

kmturley avatar Jan 05 '22 19:01 kmturley

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?

jpcima avatar Jan 05 '22 19:01 jpcima

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.

atsushieno avatar Jan 06 '22 03:01 atsushieno

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..

paulfd avatar Jan 06 '22 08:01 paulfd

@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 avatar Jan 07 '22 03:01 kmturley

@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.

jpcima avatar Jan 07 '22 03:01 jpcima

Ideal world is to be able to get/set the folder. As I was hoping to keep the paths consistent with the other plugins: Screen Shot 2022-01-06 at 8 43 21 PM Screen Shot 2022-01-06 at 8 43 30 PM

However I can workaround that for now by using the default path

kmturley avatar Jan 07 '22 04:01 kmturley