JSFX Programming Guide for REAPER
JSFX Programming Guide for REAPER
top Introduction
JSFX are simple text files, which become full featured plug-ins when loaded into
REAPER. Because they are distributed in source form, you can edit existing JSFX to
suit your needs, or you can write new JSFX from scratch. (If editing an existing
JSFX, we recommend that you save it as something with a new name, so you do you
lose your changes when upgrading REAPER).
This guide will offer an outline of the structure of the JSFX text file, the syntax
for writing code, and a list of all functions and special variables available for
use.
JSFX are text files that are composed of some description lines followed by one or
more code sections.
The description lines that can be specified are:
desc:Effect Description
This line should be specified once and only once, and defines the name of the
effect which will be displayed to the user. Ideally this line should be the first
line of the file, so that it can be quickly identified as a JSFX file.
tags:space delimited list of tags
You can specify a list of tags that this plug-in should be (eventually) categorized
with. In REAPER v6.74+, including "instrument" will cause it to appear in the
"Instruments" list.
slider1:5<0,10,1>slider description
You can specify up to 64 of these lines to specify parameters that the user can
control using standard UI controls (typically a fader and text input, but this can
vary, see below). These parameters are also automatable from REAPER.
In the above example, the first 1 specifies the first parameter, 5 is the default
value of the parameter, 0 is the minimum value, 10 is the maximum value, and 1 is
the change increment. slider description is what is displayed to the user.
Appending :log or :sqr to the change increment causes the slider to use
log/exponential shaping or polynomial shaping.
If you use :log=X, X will be the midpoint of the slider scale. If you use :sqr=X, X
will be the exponent of the polynomial (2 is the default).
Note that changing the type of shaping (or the X of :log=X mode) of the slider may
affect existing projects that automate the parameter. If you use :log! or :sqr!
or :log!=X or :sqr!=X, then the parameter shaping will not affect automation (and
compatibility will be preserved).
in_pin:name_1
in_pin:name_2
out_pin:none
These optional lines export names for each of the JSFX pins (effect channels), for
display in REAPER's plug-in pin connector dialog.
If the only named in_pin or out_pin is labeled "none", REAPER will know that the
effect has no audio inputs and/or outputs, which enables some processing
optimizations. MIDI-only FX should specify in_pin:none and out_pin:none.
filename:0,[Link]
These lines can be used to specify filenames which can be used by code later. These
definitions include 0 (the index) and a filename. The indices must be listed in
order without gaps -- i.e. the first should always be 0, the second (if any) always
should be 1, and so on.
To use for generic data files, the files should be located in the REAPER\Data
directory, and these can be opened with file_open(), passing the filename index.
You may also specify a PNG file. If you specify a file ending in .png, it will be
opened from the same directory as the effect, and you can use the filename index as
a parameter to gfx_blit(). -- REAPER 2.018+
options:option_dependent_syntax
This line can be used to specify JSFX options (use spaces to separate multiple
options):
options:gmem=someUniquelyNamedSpace
This option allows plugins to allocate their own global shared buffer, see gmem[].
options:want_all_kb
Enables the "Send all keyboard input to plug-in" option for new instances of the
plug-in, see gfx_getchar().
options:maxmem=XYZ
Requests that the maximum memory available to the plug-in be limited to the slots
specified. By default this is about 8 million slots, and the maximum amount is
currently 32 million. The script can check the memory availble using __memtop().
options:no_meter
Requests that the plug-in has no meters.
options:gfx_idle -- REAPER 6.44+
If specified, @gfx will be called periodically (though possibly at a reduced rate)
even when the UI is closed. In this case gfx_ext_flags will have 2 set.
options:gfx_idle_only -- REAPER 6.44+
If specified, @gfx will ONLY be called periodically and a UI will not be disabled.
Useful for plug-ins that do not have a custom UI but want to do some idle
processing from the UI thread.
options:gfx_hz=60 -- REAPER 6.44+
If specified, the @gfx section may be run at a rate closer to the frequency
specified (note that the update frequencies should not be relied on, code should
use audio sample accounting or time_precise() to draw framerate independently.
Note that files that are designed for import only (such as function libraries)
should ideally be named [Link]-inc, as these will be ignored in the user FX list
in REAPER.
Following the description lines, there should be code sections. All of the code
sections are optional (though an effect without any would likely have limited use).
Code sections are declared by a single line, then followed by as much code as
needed until the end of the file, or until the next code section. Each code section
can only be defined once. The following code sections are currently used:
@init
The code in the @init section gets executed on effect load, on samplerate changes,
and on start of playback. If you wish this code to not execute on start of playback
or samplerate changes, you can set ext_noinit to 1.0.
All memory and variables are zero on load, and are re-zeroed before calling @init.
To avoid this behavior, a script can define a non-empty (it can be trivial code
that has no side effect) @serialize code section, which will prevent
memory/variables from being cleared on @init.
@slider
The code in the @slider section gets executed following an @init, or when a
parameter (slider) changes. Ideally code in here should detect when a slider has
changed, and adapt to the new parameters (ideally avoiding clicks or glitches). The
parameters defined with sliderX: can be read using the variables sliderX.
@block
The code in the @block section is executed before processing each sample block.
Typically a block is whatever length as defined by the audio hardware, or anywhere
from 128-2048 samples. In this code section the samplesblock variable will be valid
(and set to the size of the upcoming block).
@sample
The code in the @sample section is executed for every PCM audio sample. This code
can analyze, process, or synthesize, by reading, modifying, or writing to the
variables spl0, spl1, ... spl63.
@serialize
The code in the @serialize section is executed when the plug-in needs to load or
save some extended state. The sliderX parameters are saved automatically, but if
there are internal state variables or memory that should be saved, they should be
saved/restored here using file_var() or file_mem() (passing an argument of 0 for
the file handle). (If the code needs to detect whether it is saving or loading, it
can do so with file_avail() (file_avail(0) will return <0 if it is writing).
Note when saving the state of variables or memory, they are stored in a more
compact 32 bit representation, so a slight precision loss is possible. Note also
that you should not clear any variables saved/loaded by @serialize in @init, as
sometimes @init will be called following @serialize.
@gfx [width] [height]
The @gfx section gets executed around 30 times a second when the plug-ins GUI is
open. You can do whatever processing you like in this (Typically using gfx_*()).
Note that this code runs in a separate thread from the audio processing, so you may
have both running simultaneously which could leave certain variables/RAM in an
unpredictable state.
The @gfx section has two optional parameters, which can specify the desired
width/height of the graphics area. Set either of these to 0 (or omit them) to
specify that the code doesn't care what size it gets. Note that these are simply
hints to request this size -- you may not always get the specified size. Your code
in this section should use the gfx_w, gfx_h variables to actually determine drawing
dimensions.
Note also that if no drawing occurs in @gfx, then no update will occur (plug-ins
should ideally detect when no update is necessary and do nothing in @gfx if an
update would be superfluous).
Listed from highest precedence to lowest (but one should use parentheses whenever
there is doubt!):
[ ]
z=x[y];
x[y]=z;
You may use brackets to index into memory that is local to your effect. Your effect
has approximately 8 million (8,388,608) slots of memory and you may access them
either with fixed offsets (i.e. 16811[0]) or with variables (myBuffer[5]). The sum
of the value to the left of the brackets and the value within the brackets is used
to index memory. If a value in the brackets is omitted then only the value to the
left of the brackets is used.
z=gmem[y];
gmem[y]=z;
If 'gmem' is specified as the left parameter to the brackets, then the global
shared buffer is used, which is approximately 1 million (1,048,576) slots that are
shared across all instances of all JSFX effects.
If y is non-zero, executes and returns z, otherwise executes and returns x (or 0.0
if ': x' is not specified).
Note that the expressions used can contain multiple statements within parentheses,
such as:
x % 5 ? (
f += 1;
x *= 1.5;
) : (
f=max(3,f);
x=0;
);
y = z -- assigns the value of 'z' to 'y'. 'z' can be a variable or an expression.
y *= z -- multiplies two values and stores the product back into 'y'.
y /= divisor -- divides two values and stores the quotient back into 'y'.
y %= divisor -- converts the absolute values of y and divisor to integers (may be
32-bit or 64-bit integers depending on platform/OS/etc), returns and sets y to the
remainder of y divided by divisor.
base ^= exponent -- raises first parameter to the second parameter-th power, saves
back to 'base'
y += z -- adds two values and stores the sum back into 'y'.
y -= z -- subtracts 'z' from 'y' and stores the difference into 'y'.
y |= z -- converts both values to integer, and stores the bitwise OR into 'y'
y &= z -- converts both values to integer, and stores the bitwise AND into 'y'
y ~= z -- converts both values to integer, and stores the bitwise XOR into 'y' --
REAPER 4.25+
top Loops
Evaluates the first parameter once in order to determine a loop count. If the loop
count 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. In the interest of avoiding common
runtime hangs, the loop count will be limited to approximately 1,000,000: if you
need a loop with more iterations, you may wish to reconsider your design (or as a
last resort, nest loops).
The first parameter is only evaluated once (so modifying it within the code will
have no effect on the number of loops). For a loop of indeterminate length,
see while() below.
while(code)
while(
a += b;
b *= 1.5;
a < 1000; // as long as a is below 1000, we go again.
);
Evaluates the first parameter until the last statement in the code block evaluates
to zero.
In the interest of avoiding common runtime hangs, the loop count will be limited to
approximately 1,000,000: if you need a loop with more iterations, you may wish to
reconsider your design (or as a last resort, nest loops).
while(condition) ( code ) -- REAPER 4.59+
while ( a < 1000 ) (
a += b;
b *= 1.5;
);
Evaluates the parameter, and if nonzero, evaluates the following code block, and
repeats. This is similar to a C style while() construct.
In the interest of avoiding common runtime hangs, the repeat count will be limited
to approximately 1,000,000: if you need a loop with more iterations, you may wish
to reconsider your design (or as a last resort, nest loops).
Basic Functionality:
spl0, spl1 ... spl63
Context: @sample only
Usage: read/write
The variables spl0 and spl1 represent the current left and right samples in @sample
code.
The normal +0dB range is -1.0 .. 1.0, but overs are allowed (and will eventually be
clipped if not reduced by a later effect).
On a very basic level, these values represent the speaker position at the point in
time, but if you need more information you should do more research on PCM audio.
If the effect is operating on a track that has more than 2 channels, then
spl2..splN will be set with those channels values as well. If you do not modify a
splX variable, it will be passed through unmodified.
See also spl(x) below, though splX is generally slightly faster than spl(X)
spl(channelindex) -- REAPER 2.018+
Context: @sample only
If you wish to programmatically choose which sample to access, use this function
(rather than splX). This is slightly slower than splX, however has the advantage
that you can do spl(variable) (enabling easily configurable channel mappings).
Valid syntaxes include:
spl(channelindex)=somevalue;
spl(5)+=spl(3);
The values of these sliders are purely effect-defined, and will be shown to the
user, as well as tweaked by the user.
slider(sliderindex) -- REAPER 3.11+
Context: available everywhere
If you wish to programmatically choose which slider to access, use this function
(rather than sliderX). Valid syntaxes include:
val = slider(sliderindex);
slider(i) = 1;
trigger
Context: @block, @sample
Usage: read/write
The trigger variable provides a facility for triggering effects.
If this variable is used in an effect, the UI will show 10 trigger buttons, which
when checked will result in the appropriate bit being set in this variable.
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;
It is recommended that you use this variable in @block, but only sparingly
in @sample.
Audio and transport state:
srate
Context: available everywhere
Usage: read-only
The srate variable is set by the system to whatever the current sampling frequency
is set to (usually 44100 to 192000). Generally speaking your @init code
section will be called when this changes, though it's probably a good idea not to
depend too much on that.
num_ch
Context: most contexts (see comments)
Usage: read-only
Specifies the number of channels available (usually 2). Note however splXX are
still available even if this count is less, their inputs/outputs are just ignored.
You can change the channel count available via in_pin:/out_pin: lines.
samplesblock
Context: most contexts (see comments)
Usage: read-only
The samplesblock variable can be used within @block code to see how many samples
will come before the next @block call. It may also be valid in other contexts
(though your code should handle invalid values in other contexts with grace).
tempo
Context: @block, @sample
Usage: read-only
The current project tempo, in "bpm". An example value would be 120.0.
play_state
Context: @block, @sample
Usage: read-only
The current playback state of 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 (as of last @block), in seconds.
beat_position
Context: @block, @sample
Usage: read-only The current playback position (as of last @block) in REAPER, in
beats (beats = quarternotes in /4 time signatures).
ts_num
Context: @block, @sample
Usage: read-only The current time signature numerator, i.e. 3.0 if using 3/4 time.
ts_denom
Context: @block, @sample
Usage: read-only The current time signature denominator, i.e. 4.0 if using 3/4
time.
Extended Functionality:
ext_noinit
Context: @init only
Set this variable to 1.0 in your @init section if you do not wish for @init to be
called (and variables/RAM to be possibly cleared) on every transport start.
ext_nodenorm
Context: @init only
Set this variable to 1.0 in your @init section if you do not wish to have anti-
denormal noise added to input.
ext_tail_size -- REAPER 6.71+
Context: @init, @slider
Set to nonzero if the plug-in produces silence from silence. If positive, specifies
length in samples that the plug-in should keep processing after silence (either the
output tail length, or the number of samples needed for the plug-in state to
settle). If set to -1, REAPER will use automatic output silence detection and let
plug-in state settle. If set to -2, then REAPER will assume the plug-in has no tail
and no inter-sample state.
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. regXX aliases to
_global.regXX.
_global.* -- -- REAPER 4.5+
Context: available everywhere
Usage: read/write
Like regXX, _global.* are variables shared between all instances of all effects.
Delay Compensation (PDC):
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. This specifies the amount of the delay that should be compensated,
however you need to set the pdc_bot_ch and pdc_top_ch below to tell JS which
channels should be compensated.
pdc_bot_ch, pdc_top_ch
Context: @block, @slider
Usage: read-write
The channels that are delayed by pdc_delay. For example:
(this is provided so that channels you dont delay can be properly synchronized by
the host).
pdc_midi
Context: @block, @slider
Usage: read-write
If set to 1.0, this will delay compensate MIDI as well as any specified audio
channels.
Graphics and Mouse:
gfx_* and mouse_* are also defined for use in @gfx code.
midisend(0, $x90, 69, 127); // send note 69 to channel 0 at velocity 127 (new
syntax)
midisend(0, $x90, 69+(127*256)); // send note 69 to channel 0 at velocity 127
(old synatx)
midisend(10,$xD4,50); // set channel pressure on channel 4 to 50, at 10
samples into current block
Sends a 2 or 3 byte MIDI message. If only three parameters are specified, the
second lowest byte of the third parameter will be used as a third byte in the MIDI
message. Returns 0 on failure, otherwise msg1.
midisend_buf(offset,buf, len) -- REAPER 4.60+
buf = 100000;
buf[0] = $x90;
buf[1] = 69;
buf[2] = 127;
midisend_buf(10,buf,3); // send (at sample offset 10) note-on channel 0, note
69, velocity 127
buf[0] = $xf0;
buf[1] = $x01;
...
buf[n] = $xf7;
midisend_buf(0,buf,n+1); // send sysex f0 01 .. f7
Sends a variable length MIDI message. Can be used to send normal MIDI messages, or
SysEx messages. When sending SysEx, logic is used to automatically add leading 0xf0
and trailing 0xf7 bytes, if necessary, but if you are sending sysEx and in doubt
you should include those bytes (particularly if sending very short SysEx messages).
Returns the length sent, or 0 on error.
This function is very similar to midisyx, but preferable in that it can be used to
send non-SysEx messages and has no restrcitions relating to the alignment of the
buffer being sent.
midisend_str(offset,string) -- REAPER 4.60+
@block
while (midirecv(offset,msg1,msg2,msg3)) ( // REAPER 4.59+ syntax while()
msg1==$x90 && msg3!=0 ? (
noteon_cnt+=1; // count note-ons
) : (
midisend(offset,msg1,msg2,msg3); // passthrough other events
)
);
The above example will filter all noteons on channel 0, passing through other
events. The construct above is commonly used -- if any of the midirecv*() functions
are called, one must always get all events and send any events desired to be passed
through.
If only three parameters are passed to midirecv, the third parameter will receive
both the second and third bytes of a MIDI message (second byte + (third byte *
256)).
Receives a message to a buffer, including any SysEx messages whose length is not
more than maxlen.
@block
buf = 10000;
maxlen = 65536;
while ((recvlen = midirecv_buf(offset,buf,maxlen)) > 0) (
recvlen <= 3 && buf[0] == $x90 && buf[2] !=0 ? (
noteon_cnt+=1; // count note-ons
) : (
midisend_buf(offset,buf,recvlen); // passthrough other events
)
);
The above example will filter all noteons on channel 0, passing through other
events. The construct above is commonly used -- if any of the midirecv*()
functions, one must always get all events and send any events desired to be passed
through.
If maxlen is smaller than the length of the MIDI message, the MIDI message will
automatically be passed through.
For one and two byte MIDI messages (such as channel pressure), the length returned
may or may not be 2 or 3.
midirecv_str(offset, string) -- REAPER 4.60+
@block
while (midirecv_str(offset,#str)) (
strlen(#str) <= 3 && str_getchar(#str,0) == $x90 && str_getchar(#str,2) != 0
? (
noteon_cnt+=1; // count note-ons
) : (
midisend_str(offset,#str);
)
);
The above example will filter all noteons on channel 0, passing through other
events. The construct above is commonly used -- if any of the midirecv*()
functions, one must always get all events and send any events desired to be passed
through.
Sends a SysEx message -- if the message does not begin with F0 and end with F7,
these will be automatically added. If the message crosses any 64k boundaries, it
will be sent as multiple messages. This function is
deprecated, midisend_buf() should probably be used instead.
The following functions can be used in the @serialize section or in other sections.
filename:0,[Link]
handle = file_open(0);
Example:
slider1:/mydata:[Link]:WAV File
handle = file_open(slider1);
handle = file_open(string);
Opens a file from either the effect filename list or from a file slider, or from
a string (REAPER 4.59+). 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().
The search path for finding files depends on the method used, but generally
speaking in 4.59+ it will look in the same path as the current effect, then in the
JS Data/ directory.
file_close(handle);
file_rewind(handle);
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 if in a @serialize write) the variable from(to) the current
file.
file_mem(handle,offset, length)
Example:
amt=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 media file (.wav, .ogg, etc), this will set the first parameter
to the number of channels, and the second to the samplerate.
REAPER 6.29+: if the caller sets nch to 'rqsr' and samplerate to a valid
samplerate, the file will be resampled to the desired samplerate (this must ONLY be
called before any file_var() or file_mem() calls and will change the value returned
by file_avail())
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.
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.
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, 1024, 2048, or 4096. 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 65,536 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_real(start_index, size), ifft_real(start_index, size)
fft_permute(index,size), fft_ipermute(index,size)
Example:
buffer=0;
fft(buffer, 512);
fft_permute(buffer, 512);
buffer[32]=0;
fft_ipermute(buffer, 512);
ifft(buffer, 512);
// need to 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 16, 32, 64, 128, 256, 512, 1024,
2048, 4096, 8192, 16384, or 32768. The outputs are permuted, so if you plan to use
them in-order, call fft_permute(buffer, size) before
and fft_ipermute(buffer,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).
Note that the FFT/IFFT must NOT cross a 65,536 item boundary, so be sure to specify
the offset accordingly.
Note that the convolution must NOT cross a 65,536 item boundary, so be sure to
specify the offset accordingly.
Memory Utility
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)
The memcpy() function provides the ability to quickly copy regions of the local
memory buffer. If the buffers overlap and either buffer crosses a 65,536 item
boundary, the results may be undefined.
memset(dest,value,length)
The memset() function provides the ability to quickly set a region of the local
memory buffer to a particular value.
mem_multiply_sum(buf1,buf2,length) -- REAPER 6.74+
Sums the products of length items of buf1 and buf2. If buf2 is exactly -1, then
sums the squares of items in buf1. If buf2 is exactly -2 then sums the absolute
values of buf1. If buf2 is exactly -3 then sums the values of buf1. If buf2 is
another negative value, the result is undefined.
mem_insert_shuffle(buf,len,value) -- REAPER 6.74+
Shuffles buf to the right by one element, inserting value as buf[0], and returning
the previous buf[len-1].
__memtop()
Returns the total number of memory slots available to the plug-in.
Stack
A small (approximately 4096 item) user stack is available for use in code (REAPER
4.25+):
stack_push(value)
Pushes value onto the user stack, returns a reference to the value.
stack_pop(value)
Pops a value from the user stack into value, or into a temporary buffer if value is
not specified, and returns a reference to where the stack was popped. Note that no
checking is done to determine if the stack is empty, and as such stack_pop() will
never fail.
stack_peek(index)
Returns a reference to the item on the top of the stack (if index is 0), or to the
Nth item on the stack if index is greater than 0.
stack_exch(value)
Exchanges a value with the top of the stack, and returns a reference to the
parameter (with the new value).
Atomic Variable Access
Guaranteed-atomic updates/accesses of values across contexts (specifically @gfx and
other contexts). Normally these are unnecessary, but they are provided for the
discriminating JSFX user -- REAPER 4.5+:
atomic_setifequal(dest,value,newvalue)
Sets dest to newvalue if dest equals value. Returns the old value of dest. On
Windows this is known as InterlockedCompareExchange().
atomic_exch(val1,val2)
Exchanges val1 and val2, returns the new value of val1.
atomic_add(dest_val1,val2)
Adds val2 to dest_val1, returns the value of dest_val1.
atomic_set(dest_val1,val2)
Sets dest_val1 to val2, returns the value of dest_val1.
atomic_get(val)
Returns the value of val.
sliderchange(slider4);
or
sliderchange(2 ^ sliderindex);
The sliderchange() function provides a facility for you to notify REAPER/JS that
you have changed a sliderX variable from code so that it can update any embedded
displays.
This function does not send automation notifications to the host -- use
slider_automate() if that is desired.
If sliderchange() is called from @gfx with -1.0 as a parameter, REAPER will add a
new undo point. This is useful if internal state changes due to user interaction in
@gfx.
slider_automate(mask or sliderX[, end_touch]) -- end_touch requires REAPER 6.74+
Example:
slider_automate(slider4);
or
slider_automate(2 ^ sliderindex);
The slider_automate() function provides a facility for you to notify REAPER/JS that
you have changed a sliderX variable so that it can update the display, and record
the move as automation. This function is not necessary to call from
the @slider code section, it is provided so that other code sections can write
programmatic automation messages.
top Strings
Strings can be specified as literals using quotes, such as "This is a test string".
Much of the syntax mirrors that of C: you must escape quotes with backslashes to
put them in strings ("He said \"hello, world\" to me"), multiple literal strings
will be automatically concatenated by the compiler. Unlike C, quotes can span
multiple lines. There is a soft limit on the size of each string: attempts to grow
a string past about 16KB will result in the string not being modified.
Strings are always refered to by a number, so one can reference a string using a
normal JS variable:
x = "hello world";
gfx_drawstr(x);
Literal strings are immutable (meaning they cannot be modified). If you wish to
have mutable strings, you have three choices:
You can use the fixed values of 0-1023:
x = 50; // string slot 50
strcpy(x, "hello ");
strcat(x, "world");
gfx_drawstr(x);
This mode is useful if you need to build or load a table of strings.
You can use # to get an instance of a temporary string:
x = #;
strcpy(x, "hello ");
strcat(x, "world");
gfx_drawstr(x);
Note that the scope of these temporary instances is very limited and unpredictable,
and their initial values are undefined.
Finally, you can use named strings, which are the equivalent of normal variables:
x = #myString;
strcpy(x, "hello world");
The value of named strings is defined to be empty at script load, and to persist
throughout the life of your script. There is also a shortcut to assign/append to
named strings:
#myString = "hello "; // same as strcpy(#myString, "hello ");
#myString += "world"; // same as strcat(#myString, "world");
Examples:
match("*blah*", "this string has the word blah in it") == 1
match("*blah", "this string ends with the word blah") == 1
You can also use format specifiers to match certain types of data, and optionally
put that into a variable:
%s means 1 or more chars
%0s means 0 or more chars
%5s means exactly 5 chars
%5-s means 5 or more chars
%-10s means 1-10 chars
%3-5s means 3-5 chars.
%0-5s means 0-5 chars.
%x, %d, %u, and %f are available for use similarly
%c can be used, but can't take any length modifiers
Use uppercase (%S, %D, etc) for lazy matching
The variables can be specified as additional parameters to match(), or directly
within {} inside the format tag (in this case the variable will always be a global
variable):
match("*%4d*","some four digit value is 8000, I say",blah)==1 && blah == 8000
match("*%4{blah}d*","some four digit value is 8000, I say")==1 && blah == 8000
top Graphics
Effects can specify a @gfx code section, from which the effect can draw its own
custom UI and/or analysis display.
These functions and variables must only be used from the @gfx section.
gfx_set(r[g,b,a,mode,dest]) -- REAPER 4.76+
Sets gfx_r/gfx_g/gfx_b to r or r,g,b. gfx_a is set to 1 if not
specified. gfx_mode is set to 0 if not specified. gfx_dest is set only if dest is
specified.
gfx_lineto(x,y,aa) -- the aa parameter is optional in REAPER 4.59+
Draws a line from gfx_x,gfx_y to x,y. if aa is 0.5 or greater, then antialiasing is
used. Updates gfx_x and gfx_y to x,y.
gfx_line(x,y,x2,y2[,aa]) -- REAPER 4.59+
Draws a line from x,y to x2,y2, and if aa is not specified or 0.5 or greater, it
will be antialiased.
gfx_rectto(x,y)
Fills a rectangle from gfx_x,gfx_y to x,y. Updates gfx_x,gfx_y to x,y.
gfx_rect(x,y,w,h) -- REAPER 4.59+
Fills a rectngle at x,y, w,h pixels in dimension.
gfx_setpixel(r,g,b)
Writes a pixel of r,g,b to gfx_x,gfx_y.
gfx_getpixel(r,g,b)
Gets the value of the pixel at gfx_x,gfx_y into r,g,b.
gfx_drawnumber(n,ndigits)
Draws the number "n" with "ndigits" of precision to gfx_x, gfx_y, and
updates gfx_x to the right side of the drawing. The text height is gfx_texth
gfx_drawchar($'c')
Draws the character 'c' (can be a numeric ASCII code as well), to gfx_x, gfx_y, and
moves gfx_x over by the size of the character.
gfx_drawstr(str[,flags,right,bottom]) -- REAPER 4.59+
Draws a string at gfx_x, gfx_y, and updates gfx_x/gfx_y so that subsequent draws
will occur in a similar place:
gfx_drawstr("a"); gfx_drawstr("b");
will look about the same as:
gfx_drawstr("ab");
For the "source" parameter specify -1 to use the main framebuffer as source, or
0..127 to use the image specified (or PNG file in a filename: line).
gfx_blit(source, scale, rotation[, srcx, srcy, srcw, srch, destx, desty, destw,
desth, rotxoffs, rotyoffs]) -- REAPER 4.59+
Srcx/srcy/srcw/srch specify the source rectangle (if omitted srcw/srch default to
image size), destx/desty/destw/desth specify dest rectangle (if not specified,
these will default to reasonable defaults -- destw/desth default to srcw/srch *
scale).
gfx_blitext(source, coordinatelist, rotation) -- REAPER 2.018+
This is a version of gfx_blit which takes many of its parameters via a buffer
rather than direct parameters.
For the "source" parameter specify -1 to use the main framebuffer as source, or
0..127 to use the image specified (or PNG file in a filename: line).
coordinatelist should be an index to memory where a list of 10 parameters are
stored, such as:
If char is passed and nonzero, returns whether that key is currently down.
Common values are standard ASCII, such as 'a', 'A', '=' and '1', but for many keys
multi-byte values are used, including 'home', 'up', 'down', 'left', 'rght', 'f1'..
'f12', 'pgup', 'pgdn', 'ins', and 'del'.
If the user has the "send all keyboard input to plug-in" option set, then many
modified and special keys will be returned, including:
Ctrl/Cmd+A..Ctrl+Z as 1..26
Ctrl/Cmd+Alt+A..Z as 257..282,
Alt+A..Z as 'A'+256..'Z'+256
27 for ESC
13 for Enter
' ' for space
Example:
JS now supports user defined functions, as well as some basic object style data
access.
Functions can be defined anywhere in top level code (i.e. not within an existing ()
block, but before or after existing code), and in any section, although functions
defined in @init can be used from other sections (whereas functions defined in
other sections are local to those sections). Functions are not able to be called
recursively -- this is enforced by functions only being able to call functions that
are declared before the current function, and functions not being able to call
themselves. Functions may have 0 to 40 parameters. To define a function, use the
following syntax:
function getSampleRate()
(
srate; // return srate
);
function mySine(x)
(
// taylor approximation
x - (x^3)/(3*2) + (x^5)/(5*4*3*2) - (x^7)/(7*6*5*4*3*2) +
(x^9)/(9*8*7*6*5*4*3*2);
);
function calculateSomething(x y)
(
x += mySine(y);
x/y;
);
function test2()
(
this.set_foo(32);
);
whatever.test2(); // [Link] = 32
Additionally functions can use the "this.." prefix for navigating up the namespace
hierarchy, such as:
function set_par_foo(x)
(
this..foo = x;
);
a.set_par_foo(1); // sets foo (global) to 1
a.b.set_par_foo(1); // sets [Link] to 1