Note: the most recent updated JSFX documentation is available here: REAPER/JS Programming Reference.
The old, outdated version follows:
JSFX Coding Documentation HTML Dump
Version 1.0, Copyright (C) 2004-2007, Cockos Incorporated
editor keys, effect format, code format, reference
Editor Controls
JSFX Editor Controls
The text editor is similar to most standard text editors. Here is a list of some
of the less obvious controls:
ESC, Ctrl+Q: quit the editor (prompt for save)
Ctrl+S: save (and recompile) effect
Ctrl+N: save as new effect
Ctrl+R: toggle auto recompiling (recompile on edit)
Ctrl+Z: undo
Ctrl+Y: redo
Ctrl+F: find string
Ctrl+G: find next
Ctrl+K: look up symbol (can use to debug the value of a variable)
Insert: toggle overwrite mode
Text selection / copy / paste:
Ctrl+E: select text (once in select mode, use arrow keys)
Ctrl+C: copy text
Ctrl+X: cut text
Ctrl+V: paste text
Ctrl+A: select all
Effect Format
JSFX Effect Format
--------------------------------------------------------------------------------
Using the effect editor, you may modify existing effects as well as create new
effects. Effects are composed of eight parts:
1. User readable description of the effect
desc:effect description
This provides a user-readable description of the effect. For example, you
may wish to describe what it does, i.e. 'Reverb' or 'Delay', etc.
2. 'Slider' definition(s)
slider1:5<0,10,1>slider description
...
You may specify as few as none and as many as eight of these, ranging from
slider1 to slider8. The first value in the above definition (5) specifies
the default value of the slider. The second value (0) specifies the minimum
extent of the slider, and the third value (10) specifies the maximum
extent. Finally, the fourth value (1), which is optional, specifies the
increment of the slider when using the knobs/arrow keys. If not specified
this defaults to 1/100th of the slider range.
There are also some extended slider syntaxes. One is to have name aliases
for positive integer values of the slider. Here is an example:
slider1:0<0,2,1{off,on,really really on}>my option
This will allow the user to switch between off, on and really really on
which corresponds to 0, 1 and 2.
The other extended slider syntax allows the user to select files in a
particular directory, and then code in @slider can open and read the
selected file. For example:
slider1:/my_directory:default_value:slider name
This will look in data/my_directory for all files of .wav, .txt, or .raw.
Later, in the @slider section, you can have code:
lastslider1 != (slider1|0) ?
(
lastslider1=slider1|0;
myfile = file_open(slider1);
myfile > 0 ?
(
file_var(myfile,somevar);
file_close(myfile);
)
);
3. Data filename definition(s) (optional)
filename:0,filename.raw
filename:1,somefile.wav
Specify a list of filenames which can be opened with file_open() in user code.
This is list of files must be in order and not skip any values, starting with 0.
To open the files in your code, use file_open(index). See file_open() for more
information.
4. Initialization code
@init
myvar=0;
somevar=32;
...
Initialization code is specified by @init on a line by itself, followed by
any number of lines of code (see the Code Format page for code format).
Initialization code is executed on the first load of an effect. Use this to
initialize any variables that only need to be initialized once,
i.e. constants.
5. Slider change code
@slider
myvar=slider1;
...
Slider change code is specified by @slider on a line by itself, followed by
any number of lines of code (see the Code Format page for code format).
Slider change code is executed both on the first load of an effect, and at
any subsequent slider change (knob movement, keyboard change, etc). Use
this to modify any variables that need to be modified when the parameters
of your effect change, or to validate the changes to the effect (if you
modify the contents of slider1-slider8 in this code, the values shown by
the slider in the UI will be updated).
6. On-block code
@block
myvar+=samplesblock;
...
On-block code is specified by @block on a line by itself, followed by
any number of lines of code (see the Code Format page for code format).
On-block code is executed prior to processing every block of samples
(which is usually around 128-1024 samples), and allows code to execute
very often (many times per second), but not at every sample. This is often
good for checking or setting triggers (see the trigger section on the
reference page)
7. Per-sample code
@sample
spl0 *= 2;
spl1 *= 2;
...
Per-sample code is specified by @sample on a line by itself, followed by
any number of lines of code (see the Code Format page for code format).
Per-sample code is executed for every sample processed, and allows for
direct manipulation of each sample (the variables spl0 and spl1, for left
and right channels, respectively).
8. Serialization code (optional)
@serialize
file_var(0,mystatevar);
file_var(0,myotherstatevar);
file_mem(0,offset,length);
...
Serialization code is specified by @serialize on a line by itself,
followed by any number of lines of code (see the Code Format page
for code format). This code is executed when the user loads a preset,
or saves a preset. It should only really call the special
serialization functions, whose function depends on whether it is a
load or save operation (your calling code can also query the mode, if
desired, using file_avail(). if file_avail(0) returns >= 0, it is read,
otherwise it is a write)). For the file handle for all file functions,
just use 0, rather than calling file_open().
For more information on defining the code sections, hit the right arrow to go
to the 'Code Format' page.
<end>
Code format
Writing code with JSFX
--------------------------------------------------------------------------------
JSFX lets you create effects with a simple language that has many
similarities to C. For example, to assign a value to a variable, you can do:
x = 5;
Note that variables do not need to be declared--by simply using a variable,
it is declared. Variable names are not case sensitive, so using x is the same as
as using X. All variables are numbers.
Comments can be specified by using // to comment until end of line:
x = 5; // set x to equal 5
Comments can also be specified by beginning with /* and ending with */:
x = 5; /* this is a multi-line
comment */
or,
x = 5; /* bla bla */ y=5;
There are a plethora of built-in operators, most of which behave very much like
their C counterparts:
x = 5*y;
x = 5 * y + 4;
x = y + z;
For a full list of the available operators, see the Reference Page.
There are built-in functions, many of which may be familiar:
x = pow(2,y); // raise 2 to the Yth power
x = sin($pi * 1.5); // get SINE of 1.5 * PI
x = min(y,z); // get minimum of Y or Z
For a full list of the available functions, see the Reference Page.
Each effect has its own limited amount of memory that it can use for temporary
storage. The syntax to access the private memory is as follows:
buffer = 1024; // set buffer to reference the local memory starting at 1024
x=buffer[0]; // read first item in buffer
buffer[1]=x; // write to second item in buffer
buffer[n]=x; // write to n'th item in buffer
Each effect has 1,048,576 slots of storage, so you may partition your memory use
however you desire.
There are logic operators available, to allow for conditional code, for example:
x == 5 ? y = 3; // if x is equal to 5, then set y to 3.
x == 5 ? y = 3 : z += 1; /* if x is equal to 5, then set y to 3, otherwise add
one to Z */
x == 5 ? (
y = 3; // if x is equal to 5, do all of this.
x = cos(z);
z += 5;
);
x == 5 ? (
foo=1; // if x==5
) : (
foo=2; // if x!=5
);
The operators || (logical or) and && (logical and) can also be used for this
purpose:
x == 5 && z=y; // if x is equal to 5, set z to y
<end>
Reference
JSFX Code Reference
--------------------------------------------------------------------------------
Operators
The following operators can be used within your code segments to make expressions:
=
Example: y = z;
Assigns the value of 'z' to 'y'. 'z' can be a variable or an expression.
*
Example: y * z
Multiplies two values and returns the product.
/
Example: y / z
Divides two values and returns the quotient.
%
Example: y % z
Divides two values and returns the remainder.
^
Example: y ^ z
Returns the first parameter raised to the second parameter-th power.
+
Example: y + z
Adds two values and returns the sum.
-
Example: y - z
Subtracts two values and returns the difference.
|
Example: y | z
Converts both values to integer, and returns bitwise OR of values.
&
Example: y & z
Converts both values to integer, and returns bitwise AND of values.
*=
Example: y *= z
Multiplies two values and stores the product back into 'y'.
/=
Example: y /= z
Divides two values and stores the quotient back into 'y'.
%=
Example: y %= z
Divides two values and stores the remainder back into 'y'.
^=
Example: y ^= z
Raises first parameter to the second parameter-th power, saves back to 'y'.
+=
Example: y += z
Adds two values and stores the sum back into 'y'.
-=
Example: y -= z
Subtracts two values and stores the difference back into 'y'.
|=
Example: y |= z
Converts both values to integer, and stores the bitwise OR into 'y'
&=
Example: y &= z
Converts both values to integer, and stores the bitwise AND into 'y'
||
Example: y || z
Returns logical OR of values. If y is nonzero, 'z' is not evaluated.
&&
Example: y && z
Returns logical AND of values. If y is zero, 'z' is not evaluated.
==
Example: y == z
Compares two values, returns 1 if equal, 0 if not.
!=
Example: y != z
Compares two values, returns 0 if equal, 1 if not.
<
Example: y < z
Compares two values, returns 1 if first parameter is less than second.
>
Example: y > z
Compares two values, returns 1 if first parameter is greater than second.
<=
Example: y <= z
Compares two values, returns 1 if first is less than or equal to second.
>=
Example: y >= z
Compares two values, returns 1 if first is greater than or equal to second.
!
Example: !z
Returns the logical NOT of the parameter.
?
Example: y ? z;
Example: y ? z : x;
Evaluates the first parameter 'y', and if nonzero, evaluates and returns
the second parameter 'z'. If a third parameter 'x' is specified, and the
first parameter is zero, evaluates and returns 'x'. The latter two
expressions may often contain multiple statements seperated by semicolons,
i.e.
x % 5 ? (
f += 1;
x *= 1.5;
) : (
f=max(3,f);
x=0;
);
[ ]
Example: z=x[y]; or x[y]=z;
Example: z=gmem[y]; or gmem[y]=z;
In the first form, you may use brackets to index into memory that is
local to your effect. Your effect has 1 million items of memory
and you may access them either with fixed offsets (i.e. 16811[0]) or
with variables (myBuffer[5]). If a value in the brackets is omitted
then 0 is used instead.
If 'gmem' is specified (the second form), then instead of local effect
memory, the buffer is the global storage buffer, which is 1 million items
that are shared across all effects.
Simple Functions
The following functions can be used within your code segments:
sin(angle)
Example: s = sin(theta);
Returns the Sine of the angle specified (specified in radians).
(To convert from degrees to radians, multiply by $pi/180, or 0.017453)
cos(angle)
Example: s = cos(theta);
Returns the Cosine of the angle specified (specified in radians).
tan(angle)
Example: s = tan(theta);
Returns the Tangent of the angle specified (specified in radians).
asin(x)
Example: theta = asin(s);
Returns the Arc Sine of the value specified (return value is in radians).
acos(x)
Example: theta = acos(s);
Returns the Arc Cosine of the value specified (return value is in radians).
atan(x)
Example: theta = atan(s);
Returns the Arc Tangent of the value specified (return value is in radians).
atan2(x,y)
Example: theta = atan2(s,sd);
Returns the Arc Tangent of s divided by sd (return value is in radians).
sqr(x)
Example: ss = sqr(s);
Returns the square of the parameter (i.e. same as ss=s*s;).
sqrt(x)
Example: ss = sqrt(s);
Returns the square root of the parameter.
The parameter should be greater than or equal to zero.
pow(x,y)
Example: p = pow(s,y);
Returns the first parameter raised to the second parameter-th power.
This is exactly the same as the operator ^, and is provided for
compatibility.
exp(x)
Example: ss = exp(s);
Returns the number e (approx 2.718) raised to the parameter-th power.
log(x)
Example: ss = log(s);
Returns the natural logarithm (base e) of the parameter.
log10(x)
Example: ss = log10(s);
Returns the logarithm (base 10) of the parameter.
abs(x)
Example: ss = abs(s);
Returns the absolute value of the parameter.
min(x,y)
Example: s = min(x,y);
Returns the minimum value of the two parameters.
max(x,y)
Example: s = max(x,y);
Returns the maximum value of the two parameters.
sign(x)
Example: s = sign(x);
Returns the sign of the parameter (-1, 0, or 1).
rand(x)
Example: s = rand(x);
Returns a psuedorandom number between 0 and the parameter.
floor(x)
Example: s = floor(x);
Rounds the value to the lowest integer possible (i.e. 3.9 becomes 3).
ceil(x)
Example: s = ceil(x);
Rounds the value to the highest integer possible (i.e. 3.1 becomes 4).
invsqrt(x)
Example: s = invsqrt(x);
Returns an inverse square root (1/sqrt(x)) approximation of the parameter.
Special Functions
The following functions are more complex than the simple functions above:
loop(count,code)
Example: loop(32,
r += b;
b = buf * 1.5;
);
Evaluates the second parameter a finite number of times, specified by the
first parameter. If the first parameter is less than 1, the second parameter
is not evaluated. Be careful with specifying large values for the first
parameter -- it is possible to hang your effect for long periods of time
doing so. The maximum for the first parameter is approximately 1 million.
while(code)
Example: while(
a += b;
b *= 1.5;
a < 1000; // as long as a is below 1000, we go again.
);
Evaluates the first parameter a finite number of times, until the last
statement in the code block is zero. There is an artificial limit for
number of executions of about 1 million.
mdct(start_index, size), imdct(start_index, size)
Example: mdct(0, 512);
Performs a modified DCT (or inverse in the case of imdct()) on the data
in the local memory buffer at the offset specified by the first parameter.
The second parameter controls the size of the MDCT, and it MUST be one of
the following: 64, 128, 256, 512, or 1024. The MDCT takes the number of
inputs provided, and replaces the first half of them with the results. The
IMDCT takes size/2 inputs, and gives size results.
Note that the MDCT must NOT cross a 16,384 item boundary, so be sure to
specify the offset accordingly.
The MDCT/IMDCT provided also provide windowing, so your code is not required
to window the overlapped results, but simply add them. See the example
effects for more information.
fft(start_index, size), ifft(start_index, size)
fft_permute(index,size), fft_ipermute(index,size)
Example: fft(0, 512);
fft_permute(0, 512);
0[32]=0;
fft_ipermute(0, 512);
ifft(0, 512);
// scale output by 1/512.0, too.
Performs a FFT (or inverse in the case of ifft()) on the data in the local
memory buffer at the offset specified by the first parameter.
The size of the FFT is specified by the second parameter, which must be
32, 64, 128, 256, 512, 1024, 2048, or 4096. The outputs are permuted, so if
you plan to use them in-order, call fft_permute(idx, size) before and
fft_ipermute(idx,size) after your in-order use.
Your inputs or outputs will need to be scaled down by 1/size, if used.
Note that the FFT/IFFT require real/imaginary input pairs (so a
256 point FFT actually works with 512 items), while the real FFT/IFFT
(rfft() and irfft(), below) provide a real-FFT.
Note that the FFT/IFFT must NOT cross a 16,384 item boundary, so be sure to
specify the offset accordingly.
rfft(start_index, size), irfft(start_index, size)
rfft_permute(index,size), rfft_ipermute(index, size)
Example: rfft(0, 512);
rfft_permute(0, 512);
0[16]=0;
rfft_ipermute(0, 512);
rifft(0, 512);
// scale output by 1/512.0, too.
Performs a real FFT (or inverse in the case of irfft()) on the data
in the localmemory buffer at the offset specified by the first parameter.
The size of the FFT is specified by the second parameter, which must be
32, 64, 128, 256, 512, 1024, 2048, or 4096. The outputs are permuted, so if
you plan to use them in-order, call rfft_permute(idx, size) before and
rfft_ipermute(idx, size) after your in-order use.
Your inputs or outputs will need to be scaled down by 1/size, if used.
rfft() and irfft() provide a real-FFT, which uses only a single value
per point.
Note that the FFT/IFFT must NOT cross a 16,384 item boundary, so be sure to
specify the offset accordingly.
convolve_c(dest,src,size), convolve_r(dest,src,size)
Used to convolve two buffers, typically after FFTing them.
convolve_c works with complex numbers, convolve_r works with
real numbers. The sizes specify number of items (in the case of
convolve_c, this signifies the number of complex number pairs).
Note that the convolution must NOT cross a 16,384 item boundary,
so be sure to specify the offset accordingly.
freembuf(top)
Example: freembuf(top);
The freembuf() function provides a facility for you to notify the memory
manager that you are no longer using a portion of the local memory buffer.
For example, if the user changed a parameter on your effect halving your
memory requirements, you should use the lowest indices possible, and call
this function with the highest index you are using plus 1, i.e. if you are
using 128,000 items, you should call freembuf(128001); If you are no longer
using any memory, you should call freembuf(0);
Note that calling this does not guarantee that the memory is freed or
cleared, it just provides a hint that it is OK to free it.
memcpy(dest,source,length)
Example: memcpy(0,1024,1024);
The memcpy() function provides the ability to quickly copy regions of the
local memory buffer. The regions may overlap, but neither region may cross
a 16,384 item boundary (they may be on different pages, however).
memset(dest,value,length)
Example: memset(0,0,1024);
The memset() function provides the ability to quickly set a region of the
local memory buffer to a particular value. Unlike memcpy(), this region
may be of any length and cross any boundaries.
sliderchange(mask)
Example: sliderchange(slider4);
or
Example: sliderchange(2 ^ sliderindex);
The sliderchange() function provides a facility for you to notify JSFX
that you have changed a slider1-slider8 variable so that it can update the
display. This function is not necessary to call from the @slider code
segment, it is provided so that other code (i.e. @block or @sample) can
update the sliders. The parameter can be the variables slider1-slider8, in
which case that slider is refreshed. Otherwise, it can be a bitmask of which
sliders have changed, where 1 would be the first slider, 2 would be the
second, 4 would be the third, and so on (up to 128 being the 8th slider).
set_slider(effect.slider,value)
get_slider(effect.slider)
Example: set_slider(1.3,get_slider(1.3)+1);
Sets (or gets) the value of any effects' slider. The whole number of the first
parameter is the index of the effect, starting at 1. The fractional part of the
number specifies the slider index (1-8) of the slider.
set_bypass(effect,value)
get_bypass(effect)
Example: set_bypass(1,!get_bypass(1));
Sets (or gets) the bypass state of any effect. Set to any positive nonzero value
to bypass the effect, zero to have the effect be active.
File Functions
The following functions can be used in @serialize sections, @preload sections,
and extended file-slider code sections. On @serialize sections, they can be for
read or for write, but on all others they are for reading only.
file_open(index or slider)
Example:
filename:0,myfile.wav
handle = file_open(0);
Example:
slider1:/mydata:mydef.wav:WAV File
handle = file_open(slider1);
Opens a file from either the effect filename list or from a file slider.
Once open, you may use all of the file functions available. Be sure to
close the file handle when done with it, using file_close().
file_close(handle)
Example: file_close(handle);
Closes a file opened with file_open().
file_rewind(handle)
Example: file_rewind(handle);
You can use this to rewind the current file to the beginning, to
re-read the file etc.
file_var(handle,variable)
Example: file_var(handle,myVar);
This reads (or writes) the variable from(to) the current file.
file_mem(handle,offset, length)
Example: file_mem(handle,offset,len);
This reads (or writes) the block of local memory from(to) the current file.
Returns the actual number of items read (or written).
file_avail(handle)
Example: len=file_avail(handle);
Returns the number of items remaining in the file, if it is in read
mode. Returns < 0 if in write mode. If the file is in text mode
(file_text(handle) returns TRUE), then the return value is simply
0 if EOF, 1 if not EOF.
file_riff(handle,nch,samplrate)
Example:
file_riff(handle,nch,samplrate);
nch ? file_mem(handle,0,file_avail(0));
If the file was a RIFF WAV file (and ended in .wav),
this will set the first parameter to the number of
channels, and the second to the samplerate.
file_text(handle,istext)
Example:
istext=file_text(handle);
istext ? use_diff_avail syntax;
If the file was a text file (and ended in .txt), this will
return 1. If you need to use different file_avail() logic
for text files (you often will), you can query it this way.
Text file notes
Note that if in a extended file-slider code section or a @preload section, and
the extension of the file is .txt, it will read one line at a time, ignoring
non-number lines. Note that file_avail() should be called to check for EOF
after each read, and if it returns 0, the last file_var() should be ignored.
You can also use file_mem(offs,bignum) and it will read the maximum available.
The format of each line in the text file can be either a floating point number,
a binary number beginning with 'b', i.e. b0101010111, or a hexadecimal number
beginning with 'x', i.e. xDEADF000.
For more information on the format of the text file, view the help when editing a text
file.
MIDI Functions
The following functions can be used in @block or @sample sections
on supported platforms (currently only REAPER, but soon standalone).
offset is offset from current block, in samples. msg1 is status byte,
msg23 is second (and third if available) data bytes, second byte is
low 8 bits (msg23&0xff), third byte is next 8 bits (msg23/256)&0xff.
midisend(offset,msg1,msg23)
Example: midisend(0,9*16 + 0,69|(127*256)); // send note 69 to
channel 0 at velocity 127 (max)
midirecv(offset,msg1,msg23)
Example:
midirecv(offset,msg1,msg23);
(msg1&240)==9*16 ? ( /* note on! */ )
note: midirecv returns msg1 as a return value, as well.
so you can do things like:
while( midirecv(offs,msg1,msg23) ? ( midisend(offs,msg1,msg23); ); );
Special Variables
The following variables are used by JSFX for special purposes:
spl0, spl1
Context: @sample only
Usage: read/write
The variables spl0 and spl1 represent the current sample pair in the @sample
code. Their values are meaningful from -1 to 1, and anything outside of this
range will be clamped before playback. On a very basic level, their values
represent the speaker position at that point in time.
srate
Context: available everywhere
Usage: read-only
The srate variable is set by the system to whatever the current sampling
frequency is set to. In general this will never change, and is system
specific. Usually it will be 48000 or 96000, represting 48khz or 96khz.
samplesblock
Context: @block only
Usage: read-only
The samplesblock variable can be used within the @block code to see how
many samples will come before the next @block code. Useful for tracking
time, etc.
slider1-slider8
Context: @slider, @block, @sample
Usage: read/write
The variables slider1, slider2, ... slider8 allow interaction between the
user and the effect, allowing the effect to be adjusted by the user and
likewise allow the effect to show the user information (see the function
sliderchange()). The values of these sliders are purely user-defined, and
will be shown to the user, as well as tweaked by the user. If you modify
one of these values in the @block or @sample code, it is recommended that
you call the sliderchange() function.
trigger
Context: @block, @sample
Usage: read/write
The trigger variable provides a facility for triggering effects.
Effects can be triggered by the user, or by other effects. The trigger
variable is a bitmask, where each bit represents a unique trigger. For
example, to check for trigger 5 (triggered also by the key '5' on the
keyboard):
isourtrig = trigger & (2^5);
Conversely, to set trigger 5:
trigger |= 2^5;
Or, to clear trigger 5:
trigger & (2^5) ? trigger -= 2^5;
The trigger variable is preserved across effects, so that an effect
higher on the effect list can control an effect that is lower.
It is recommended that you use this variable in @block, but only
sparingly in @sample.
reg00-reg99
Context: available everywhere
Usage: read/write
The 100 variables reg00, reg01, reg02, .. reg99 are shared across all
effects and can be used for inter-effect communication. Their use should
be documented in the effect descriptions to avoid collisions with other
effects.
tempo
Context: @block, @sample
Usage: read-only
The current project tempo, if supported (REAPER), or 120 on standalone.
play_state
Context: @block, @sample
Usage: read-only
The current playback state in REAPER (0=stopped, <0=error, 1=playing,
2=paused, 5=recording, 6=record paused.
play_position
Context: @block, @sample
Usage: read-only
The current playback position in REAPER (at last @block), in seconds.
beat_position
Context: @block, @sample
Usage: read-only
The current playback position (at last @block) in REAPER, in beats
(beats usually = quarternotes).
pdc_delay
Context: @block, @slider
Usage: read-write
The current delay added by the plug-in, in samples. Note that you shouldnt
change this too often.
pdc_bot_ch, pdc_top_ch
Context: @block, @slider
Usage: read-write
The channels that are delayed by pdc_delay. For example:
pdc_bot_ch=0; pdc_top_ch=2; // delays the first two channels (spl0/spl1).
pdc_bot_ch=2; pdc_top_ch=5; // delays channels spl2,spl3, and spl4.
(this is provided so that channels you dont delay can be delayed properly
by the host.
<end>