Carla
Carla copied to clipboard
JSFX plugin format
This is a source-based plugin format, which is made by Cockos and part of Reaper. (Note that JSFX does not stand for JavaScript.)
There are dozens of these plugins shipped in Reaper (see InstallData/Effects
folder).
Most are rather short source code files, tagged with a free software license on them.
The implementation of JSFX is partially free software. EEL2 is the underlying programming language, which is published as part of WDL. There exists a free software implementation in jsusfx.
Some notes
- the language would be useful for prototyping all sorts of dsp
- the files are arranged in a multi-level folder hierarchy. one might need to compile them in order to scan them
- the source is expected to compile at runtime to native assembly ; the build system is a little convoluted, which has architecture-dependent code and involves assembly in the nasm syntax.
- the jsusfx implementation is last updated 3 years ago, and the EEL2 is a bit behind; in particular, the later one has support of the arm 64bit processor.
lets keep the discussion here then. my main doubt against all this is a sorta "vendor lockin" if we place this in carla. It kinda makes sense for carla to load them since it is really a plugin format in the end, plus the drag&drop would make this handy... but it would be something limited to carla instead of being usable everywhere.
the dynamic nature of these files likely makes it very tricky to have as general plugins though, that part I do not know how to solve.
If carla really ends up supporting jsfx, then I would see it as a motivation to do a carla mini-host, a 1-to-1 wrapper for the things carla is able to load.
how fast is the scanning for these files?
how fast is the scanning for these files?
This is not yet at a stage where I'm able to tell about the scanning speed.
limited to carla instead of being usable everywhere.
The idea is to go with the jsusfx library after patching it with some desirable updates. It doesn't look much maintained nowadays, so a fork seems in order.
These might be problems
- plugin unique identification
- requirement of
nasm
as build tool, or alternatively have some.o
prebuilts in the tree
With that being said, in Reaper the use of these as plugins is pretty transparent, same as other plugin types.
There is the current status at this branch. Currently it builds the support libraries. https://github.com/jpcima/Carla/tree/jsfx
Looks good so far. How are we going to deal with plugin folder, is there a spec to where these would be stored? If not, we probably need to store filenames as the carla project state.
Also, I asked about the speed of discovery of such plugins because we can either discovery them through python alike ladspa, dssi, vst2 and vst3 or through libcarla_utils.so that handles this directly (for those formats where scanning is known to be fast) Would be nice if the scanning was as fast as LV2 discovery, but not sure if really possible.
How are we going to deal with plugin folder, is there a spec to where these would be stored?
It's from some knowledge gathered so far, correct if I'm wrong.
There is not a path defined by spec, neither can Reaper set custom folders; instead, all goes in a fixed "Effects" folder.
If there exists something like a unique ID, it might be such as analysis/gain_reduction_scope
, a path relative to the effects folder, where the file can optionally be suffixed .jsfx
. Files .jsfx-inc
should be ignored.
Another thing is the import
statements: "this filename will be searched within the JS effect directory"
So the plugin should be made aware of what root folder it's loaded from, to set the imports right I guess.
Would be nice if the scanning was as fast as LV2 discovery, but not sure if really possible.
A quick look at jsusfx API hinted this to be possible, but will have to be verified.
The current progress of JSFX
- converted the ASM code to GAS syntax, and checked the objdump outputs to be 1:1, removing nasm as dependency
- implemented the discovery; currently it can't tell if the plugin requires MIDI in/out, or has custom graphics.
- implemented the frontend mostly, some uncertainty about what plugin label should be
- set the default paths for each OS to these of Reaper; not added the installation directory, because these JSFX get automatically copied by Reaper into the Home folder, creating duplicates in the scan.
notably missing
- [x] MIDI in/out: jsusfx API uses byte-based buffers instead of clearly separated events: it was intended for puredata. Also these events don't carry their offset, so they can't be frame-accurate. It needs modification of jsusfx to address these problems.
- [x] save/load: it should handle the optional
serialize
jsfx section. According to jsusfx's documentation, this is a to-do item.
There are TODO(jsfx)
mentions at relevant places.
wondering if this is better in a branch until 100% finalized, or already good enough to be placed in the main branch... what are your thoughts on it?
This depends of your own criteria of what makes it acceptable to merge or not. I'd call it beta for the time being, because of a few remaining items.
-
state: i wouldn't make a compatibility guarantee on the state format yet, until this gets tested better. That is, I should extract what state REAPER saves vs ours in a variety of scenarios. For instance, I distrust how jsusfx did
file_mem
serialization, which is intended for binary blobs I believe, but it wants to store the bytes identical to vars (that is, as floats) -
midi in/out: needs testing also. The
midi_arp
, while it outputs, doesn't seem that it works correctly. Also the short events output by jsfx are always 3 bytes, but MIDI allows some to be size 2 or even 1; so there's likely a correction of the event size to do after the output of jsfx. -
some interaction with frontend and carla in general, please review these relevant
TODO(jsfx)
comments another thing, it's that perhaps the identification is best handled by Unique ID where that ID would be the path's basename. Again, needs to test again Reaper for what happens there.
Misc:
most JSFX sources shipped in reaper have more metadata as comments (plugin category and author)
example: //tags: lo-fi distortion filter mangler
Perhaps we should parse the info comments. It might be that tags:
and author:
are planned as keywords for a future JSFX, who knows.
Verified the state of Jsusfx against Reaper, that's how it is:
- [x] the state is in 2 parts, parameters ("sliders") and serialization
- [x] maximum of 64 sliders which are saved
- [x] Reaper saves the parameters which are equal to default, not Jsusfx
- [x] serialized values get saved as float sequence, both
file_var
andfile_mem
- [x] Reaper encodes the float sequence in little-endian followed by base64. Jsusfx lets the client chose the storage format. I went with a form of XML, but it outputs one element per var, I should modify it for base64.
It can be confirmed from Reaper saves that the relative path serves as unique ID.
As an example, the unique ID would be analysis/loudness_meter
for the file /opt/REAPER/InstallData/Effects/analysis/loudness_meter
.
Does Carla provide a way to handle this?
The File class provides some ways to make relative paths.
During plugin load the carla settings struct provides access to the paths to look for in plugins. So this seems doable to adapt to in carla.
During plugin load the carla settings struct provides access to the paths to look for in plugins.
And can the discovery have access to the search paths too? It starts only with the absolute pathname.
For carla utils library yes, for discovery via CLI, no.
Which makes sense, if we run carla-discovery-native jsfx /path/to/something.jsfx
how could that possibly know what would JSFX_PATH
be?
If we keep carla-discovery are purely informational (like done with lv2) and the real searching&parsing through the utils/CachedPlugins.cpp code, then this will be doable.
The first call to carla_get_cached_plugin_count
usually includes the paths to look for, so we can cache and use that.
Understood. Then should carla-discovery of jsfx get rid of doInit
perhaps?
I make this run the compilation, but success can't be guaranteed without the import path deduced properly.
I would say we should avoid the compilation if possible, or is that required to fetch plugin info?
I would say we should avoid the compilation if possible, or is that required to fetch plugin info?
It's not required at all, just a note for later work: it needs to check the presence of a gfx section, that tells if plugin would have UI or not. There's a deficiency of the jsusfx library, which can only identify this after compiling (it's fixable).
Currently, discovery follows a logic such as: if doInit
, compile, otherwise just read header
Personally I would be ok with not knowing if jsfx have custom UIs or not. At least initially.
The "doInit" makes sense if process checks are enabled during discovery, but that only applies for carla-discovery not the utils lib
- [x] Note: there appears to be a mistake in file
jsusfx.cpp
The following should be modified fromifdef
toifndef
, to let scripts print their messages.#ifdef EEL_STRING_STDOUT_WRITE
In addition, it should preferably use the carla-specific IO functions.
- [x] Other: values don't parse correctly when
LC_NUMERIC != C
Test case is Reaperguitar/phaser
, which fails to modulate anything, because of multiplier0.5
parsed as0
.
There is a class in carla utils for the locate issues, CarlaScopeLocale or something like that. either put it around the entire file loading or specific sections of it.
Yes, this source code is C though. I'll fix it. Culprit is here https://github.com/falkTX/Carla/blob/555caea8a0e3aa28c1c1d9df081403b3efeadbe9/source/modules/eel2/source/WDL/eel2/nseel-compiler.c#L5869
Re: carla-specific IO functions
The Jsfx output connects to a function which has the fwrite
signature (it writes in chunks).
Hence it's not possible to connect with carla_stdout, as is?
if it expects no newline, then no. all the carla_std* functions append a newline at the end
it's okay to do a light refactor of carla's io functions, to get access to that FILE handle for direct writing?
also carla_stdout
and its friends have FILE* as a static-local into a static-inline function, it could be that these static handles end up multiple. (if multiple source units duplicate the function)
- [x] the files should be compiled as part of
init
, rather thanreload
a recompilation may modify the number of i/o ports, which is not acceptable.
Regarding author and category metadata
Many files contain comments similar to these ones.
//tags: guitar modulation filter gain
//author: Cockos
These look similar to the desc:
tag except for being commented.
Most of the stock jsfx have these, yet it's not a standardized syntax afaik.
(it might be for a future extension of the language?)
For time being, there is absolutely a possibility of parsing these pseudo-tags.
Makes sense to try to carefully parse them I think, they provide useful info.
btw, how do we differentiate between FX and synths?
btw, how do we differentiate between FX and synths?
- by category pseudo tags ("synthesis")
- absence of input pins
- possibly some light code analysis, for presence of midi calls
- [ ] jsusfx does not support convolution plugins
It does not support sliders written in that form, and the compilation/readHeader fails:
slider1:/amp_models:none:Model
The idea is that this slider (menu rather) controls an index of a file into a directory of wav IR files.
These wav files reside in the Data/amp_models
folder, whereas the effect is located inside Effects/guitar
.
The specification has something to say about this, more precisely: it scans wav/txt/ogg/raw files It does not mention that files should be listed in lexicographical order, but in Reaper they are.
There is library support to implement, but also the pesky path search.
(since we have to go one above from Effects
and into Data
, such that Data
is a hardcoded element of the path)
- [ ] hidden sliders
The sliders whose desc begins with character '-' should be hidden parameters. Specification says
You can also hide sliders by prefixing their names with "-". Such parameters will not be visible in the plug-in UI but still be active, automatable, etc.
Edit: perhaps leave that one for now until UI support
- [ ] output-only sliders
There is this unspecified syntax, which creates a slider with empty range (min=max=0), and its purpose is to serve as output-only parameter. (but we don't normally know that unless examining the code)
slider10:0,Reduction (dB)
- [ ] slider automation from DSP
The observation related to the item above is that DSP can assign to a slider variable and have the slider move as a result.
Carla should ~~detect the slider change~~ respond to jsfx sliderchange()
API, and automate the parameter.
Edit: check also the slider_automate
API which jsusfx has marked as to-do
Just wanted to come and drop a huge note of appreciation and support for this!
Many JSFX are as good or better than paid plugins, and there's such a large community collection of them.
Would be incredible to have a cross-platform implementation of JSFX host.
Thank you for working on this =)
Would be incredible to have a cross-platform implementation of JSFX host.
There exists one as part the host support library. If you can build this, you get a vst plugin which can host jsfx. https://github.com/jpcima/ysfx
I'll write a document later on how to build this.
Oh this is awesome, thanks for the tip @jpcima !