Js programming: Difference between revisions

From Reaper Accessibility Wiki
Jump to navigation Jump to search
Chessel (talk | contribs)
m →‎js Programming: Changed title and looked at formatting on first example.
Chessel (talk | contribs)
m →‎JSFX Programming: General read through and tidy up.
 
(One intermediate revision by the same user not shown)
Line 4: Line 4:
js plugins are Reaper's own plugin format similar in many ways to VST plugins.  A js plugin can process and generate audio and midi data and expose parameters to the user which can be automated.
js plugins are Reaper's own plugin format similar in many ways to VST plugins.  A js plugin can process and generate audio and midi data and expose parameters to the user which can be automated.


js plugins have a number of advantages such as easy to start writing, all parameters are exposed and these parameters are easy to interact with via a keyboard either directly in the user interface or by the OSARA parameter dialogue.  They also support graphics making them good for sighted users and it is also possible to add keyboard support though this is almost vanishingly rare.  Disadvantages are around limited to non-existent file handling.
js plugins have a number of advantages such as easy to start writing, all parameters are exposed and these parameters are easy to interact with via a keyboard either directly in the user interface or by the OSARA parameter dialogue.  They also support graphics making them good for sighted users and it is also possible to add keyboard support though this is almost vanishingly rare.   


== View and existing js plugin ==
== View Existing JSFX Plugins ==
js plugins are written in plain text files and there are plenty to take a look at.  From the Reaper Help Options menu select Resource path.  In here is a folder called "Effects" in which are all the js plugins that come with Reaper.
JSFX plugins are written in plain text files and there are plenty to take a look at.  From the Reaper Help Options menu select Resource path.  In here is a folder called "Effects" in which are all the js plugins that come with Reaper.


Typically a js plugin either has no file extension or an extension of jsfx.  Use your favourite plain text editor to open up any of the files to see what they look like.
Typically a JSFX plugin either has no file extension or an extension of jsfx.  Use your favourite plain text editor to open up any of the files to see what they look like.


It is useful to turn the vebosity of your screen reader so it speaks all characters.  It is important to capture all the code when programming.  For example, a semicolon is required at the end of each command and most people dont set their screen reader to announce these.
It is useful to turn the vebosity of your screen reader so it speaks all characters.  It is important to capture all the code when programming.  For example, a semicolon is required at the end of each command and most people dont set their screen reader to announce these.


In NVDA, the verbosity can be cycled with NVDA+P.
In NVDA, the verbosity can be cycled with NVDA+P.(What is VoiceOver equivalent?)


== Structure of a plugin ==
== Structure of a plugin ==
A js plugin is made up of a header and then a series of sections which have a predefined named and purpose.
A JSFX plugin is made up of a header and then a series of sections which have a predefined named and purpose.




Line 30: Line 30:
The @slider block is run whenever a parameter is changed either through automation or by the user.  For example, a parameter might be presented to the user as a percentage from 0 to 100 but the code needs this rescaling from -1 to 1.
The @slider block is run whenever a parameter is changed either through automation or by the user.  For example, a parameter might be presented to the user as a percentage from 0 to 100 but the code needs this rescaling from -1 to 1.


One thing I've noticed is that the @slider section runs even when a slider gains focus - i.e. when you tab to it. Thus, it is often helpful to copy slider contents into variables and then in the @slider section, compare the variable with the slider's current value to see if it has changed. If it has then update the variable and run any additional code necessary.  For instance:
Note the @slider section runs even when a slider gains focus - i.e. when you tab to it. Thus, it is often helpful to copy slider contents into variables and then in the @slider section, compare the variable with the slider's current value to see if it has changed. If it has then update the variable and run any additional code necessary.  For instance:


//code start
//code start
Line 52: Line 52:
This will set the filter cutoff only if the user actually changed the slider.
This will set the filter cutoff only if the user actually changed the slider.


Note that variable names can be mixed case, but they are case folded by the compiler. This means that MyVariable is the same as myvariable however screen reader pronounciation is better with mixed case, so that's what I tend to use. Underscores can also be used in variable names, i.e. my_variable is valid.
Importantly the case of variable names are ignored.  So the variable myfrequency (all lower case) and the variable MyFrequency (first letter capitalised) are processed as the same variable.  Using mixed case to improve pronunciation is a good idea.






The @block code is run as a new block of samples arrives.  The block size as defined in Reaper preferences in the devices section dictates how many sampless.  This is an area of code commonly used to process MIDI as all the MIDI notes and events coming up in the next block of samples can be processed and action taken accordingly.  Also, MIDI notes and events can be inserted so they are played once the block enters Reaper's play buffer.
 
The @block code is run as a new block of samples arrives.  The block size as defined in Reaper preferences in the devices section dictates how many samples This is. It is an area of code commonly used to process MIDI as all the MIDI notes and events coming up in the next block of samples can be processed here. Also, MIDI notes and events can be inserted so they are played once the block enters Reaper's play buffer.
 




Line 99: Line 101:




Desc: My first MIDI js plugin


desc:My first Midi


slider:0<-24,24,1>Semitone shift
slider1:0<-24,24,1>Offset
 


@slider
@slider


shift = slider1;
shift = slider1;


@block
@block
Line 116: Line 116:
(
(


  msg1 & 0xF0 = 0x90 ?
  msg1 & 0xF0 == 0x90 ?


  (
  (
Line 124: Line 124:
  )
  )


  :
  : msg1 & 0xF0 == 0x80 ?


  (
  (


   midisend( offset, msg1, msg2, msg3 );
   midisend( offset, msg1, msg2 + shift, msg3 );


  );
  )
 
  : midisend( offset, msg1, msg2, msg3 );


);
);
Line 136: Line 138:


In the same way as with the audio plugin, copy this code into a plain text file and save it in the Effects subfolder in the Reaper resource path ideally in your own subfolder holding all your plugins. Start up Reaper so it does a rescan and then insert reaSynth onto a track as as a VSTi.  Test you can play notes.  Now add your first MIDI plugin onto this track as well.  Before it has any effect thugh yu need to move reaSynth so it is below your first MIDI plugin since the plugin needs to update the MIDI data between it being played and it arriving at reaSynth.  Do this by either cutting and pasting reaSynth so it is second in the chain, or using the action bound to control-shift alt page down (Windows).
In the same way as with the audio plugin, copy this code into a plain text file and save it in the Effects subfolder in the Reaper resource path ideally in your own subfolder holding all your plugins. Start up Reaper so it does a rescan and then insert reaSynth onto a track as as a VSTi.  Test you can play notes.  Now add your first MIDI plugin onto this track as well.  Before it has any effect thugh yu need to move reaSynth so it is below your first MIDI plugin since the plugin needs to update the MIDI data between it being played and it arriving at reaSynth.  Do this by either cutting and pasting reaSynth so it is second in the chain, or using the action bound to control-shift alt page down (Windows).




Line 141: Line 144:


== Tools to help write plugins ==
== Tools to help write plugins ==
The most basic tool is your plain text editor of choice.  Reaper does have an inbult editor but it is not easily accessible.  So writing code in a plain text editor is the next best thing.  If you make a change to your code and save it though, you will need to return to Reaper and the FX chain , press control R on your plugin and replace the plugin with a new instance of itself to for the changes take effect.
The most basic tool is your plain text editor of choice.  Reaper does have an inbuilt editor but it is not easily accessible.  So writing code in a plain text editor is the next best thing.  If you make a change to your code and save it though, you will need to return to Reaper and the FX chain , press control R on your plugin and replace the plugin with a new instance of itself to for the changes take effect.  Or have a project saved and reload it using alt F, R for recent projects.




It is possible to view all the variables used in a plugin along with their values.  This can be done with NVDA object navigation by tabbing to the edit button in the FX dialogue, hitting enter , and uing object navigation to move right until a list control is reached.  Gone down and you can then review each variable.
 
It is possible to view all the variables used in a plugin along with their values.  This can be done with NVDA object navigation by tabbing to the edit button in the FX dialogue, hitting enter , and uing object navigation to move right until a list control is reached.  Go down one and you can then review each variable. Using NVDA num pad enter to activate this window also gives you first letter navigation.  On each line the name of the variable is given, then its value and then the number of times the variable is used in the code.
 
Reviewing the list of variables is particularly useful to check if there is a typo in the code.  A screen reader will not pronounce the two variables ChekX and CheckX differently but if you notice two variables in the list where you would expect only one then there is probably a typo in one of the variable names.  Or sometimes you might call a variable NoteOn and somewhere else call it OnNote.
 




Debugging is difficult though in js plugins.  There is no option to display a message box or output diagnostic info to a file. And some variables will change their value with every sample or every block making them impossible to track.  You cannot step through the code one line at a time reviewing variable values like you can in more complex IDE's.
Debugging is difficult though in js plugins.  There is no option to display a message box or output diagnostic info to a file. And some variables will change their value with every sample or every block making them impossible to track.  You cannot step through the code one line at a time reviewing variable values like you can in more complex IDE's.


It is possible to write debug messages to the screen as the program runs. THis might help in determining if a certain bit of code was executed for instance. Unfortunately, because you only see the last mesage written, it isn't as useful as it could be.
It is possible to write debug messages to the screen as the program runs. This might help in determining if a certain bit of code was executed for instance. Unfortunately, because you only see the last mesage written, it isn't as useful as it could be.


Use a statement like the following:
Use a statement like the following:
Line 155: Line 162:
sprintf(#dbg_desc, "a message, which may include values of integers %d, or floats %f", integerVar, floatVar);
sprintf(#dbg_desc, "a message, which may include values of integers %d, or floats %f", integerVar, floatVar);


However, monitoring the list of variables as described above does reflect the value real-time so it can be useful particularly on slowly changing variables or those driven by user activity.
Another trick is to have a slider which is written to when a particular bit of code is executed and perhaps only once:
condition == 1 && !done ?
(
done = 1;
slider64 = TestVariable;


); 


==Resources ==
==Resources ==


There are not a whole bunch of great resources for writing js plugins.  Probably the most comprehensive source of information are all the plugins that come with Reaper and found in the Effects folder.  But these can be complex and difficult to work out how they work.
Probably the most comprehensive source of information are all the plugins that come with Reaper and found in the Effects folder.  But these can be complex and difficult to work out how they work.


There is a good tutorial made by [https://www.admiralbumblebee.com/music/2018/02/08/Write-a-Reaper-MIDI-JSFX-from-scratch.html Admiral Bumblebeee] which goes through a modestly complex project to write a MIDI plugin.  There is also other useful content on this web site.
There is a good tutorial made by [https://www.admiralbumblebee.com/music/2018/02/08/Write-a-Reaper-MIDI-JSFX-from-scratch.html Admiral Bumblebeee] which goes through a modestly complex project to write a MIDI plugin.  There is also other useful content on this web site.

Latest revision as of 21:18, 20 November 2023

JSFX Programming

Introduction

js plugins are Reaper's own plugin format similar in many ways to VST plugins.  A js plugin can process and generate audio and midi data and expose parameters to the user which can be automated.

js plugins have a number of advantages such as easy to start writing, all parameters are exposed and these parameters are easy to interact with via a keyboard either directly in the user interface or by the OSARA parameter dialogue.  They also support graphics making them good for sighted users and it is also possible to add keyboard support though this is almost vanishingly rare. 

View Existing JSFX Plugins

JSFX plugins are written in plain text files and there are plenty to take a look at.  From the Reaper Help Options menu select Resource path.  In here is a folder called "Effects" in which are all the js plugins that come with Reaper.

Typically a JSFX plugin either has no file extension or an extension of jsfx.  Use your favourite plain text editor to open up any of the files to see what they look like.

It is useful to turn the vebosity of your screen reader so it speaks all characters.  It is important to capture all the code when programming.  For example, a semicolon is required at the end of each command and most people dont set their screen reader to announce these.

In NVDA, the verbosity can be cycled with NVDA+P.(What is VoiceOver equivalent?)

Structure of a plugin

A JSFX plugin is made up of a header and then a series of sections which have a predefined named and purpose.


The header can be as short as a declaration of the name of the plugin but can also hold much more information such as the author, date, version, release notes, instructions on usage and other information that helps with reaPack integration.


The header is followed by definition of the plugin parameters which is done by defining a slider for each parameter.  A slider has a min value, a max value, an increment value and a default value.  These are seen by the user in the plugin user interface as a slider and edit box for each parameter.


The @init block is run when the plugin first starts which is typically when it is first loaded onto an FX chain and when play starts. It is used to initialise variables to default values.  Note that the js plugin language does not require variables to be declared before they are used so this section does not need to include initialisation of all variables if this is done elsewhere.  Also, the default initialisation of a variable is to set it to zero.


The @slider block is run whenever a parameter is changed either through automation or by the user.  For example, a parameter might be presented to the user as a percentage from 0 to 100 but the code needs this rescaling from -1 to 1.

Note the @slider section runs even when a slider gains focus - i.e. when you tab to it. Thus, it is often helpful to copy slider contents into variables and then in the @slider section, compare the variable with the slider's current value to see if it has changed. If it has then update the variable and run any additional code necessary. For instance:

//code start // we define a slider called s_lowpassCutoff, with initial value of 50, min and max of 20 and 100, and label "lowpass cutoff" slider1:s_lowpassCutoff=50 <20, 100, 1>lowpass cutoff


@init lowpassCutoff = s_lowpassCutoff; // store the initial slider value to compare with later // do other initialization like initialize your filter, etc

@slider

// if slider has changed lowpassCutoff != s_lowpassCutoff ? ( lowpassCutoff = s_lowpassCutoff; // update the variable // set filter cutoff etc //... );

This will set the filter cutoff only if the user actually changed the slider.

Importantly the case of variable names are ignored. So the variable myfrequency (all lower case) and the variable MyFrequency (first letter capitalised) are processed as the same variable. Using mixed case to improve pronunciation is a good idea.



The @block code is run as a new block of samples arrives.  The block size as defined in Reaper preferences in the devices section dictates how many samples This is. It is an area of code commonly used to process MIDI as all the MIDI notes and events coming up in the next block of samples can be processed here. Also, MIDI notes and events can be inserted so they are played once the block enters Reaper's play buffer.


The@sample block is run every sample.  So yes, many thousands of times a second.  Commonly used to process audio as there is easy access to the value of the audio in each channel.


The @gfx block is used to draw graphics and process keyboard input.  I'm not sure when it is run.

First Audio Plugin

Let's dive in with a very simple js plugin that includes a volume control for audio.  Open up a plain text editor and copy and paste the following code into it.


Desc: My first audio plugin

slider1:100<0,100,1>Volume

@slider

scaler = slider1/100;

@sample

spl0 *= scaler;

spl1 *= scaler;


Save this in the Reaper resources Effects folder, ideally in a new subfolder to contain all your plugins.  Start up Reaper and it should do a rescan and make your new js plugin available to use.

Create a track and add some audio to it for example recording yourself speaking, by inserting an audio file or selecting something from media explorer.  Hit F on the track to bring up the FX chain dialogue and use the Add button to add your plugin.  Type in "first" as this is text included in the first line of the code copied into the plugin and is the name given to the plugin.  Arrow down and you should find the plugin.  Hit enter and it will get loaded onto the FX chain for the track.  Press tab to work your way through the plugin and you will come to the slider and edit controls for the volume parameter.

Press space to play the audio and then adjust the volume with either the slider or entering a new value in the edit field.  The volume changes.  Your first plugin.

A slider is defined at the top of the file which ranges from 0 to 100 with step sizes of 1 and an initial value of 100. Whenever the slider is moved the variable scaler is calculated based on the slider value. The slider ranges from 0 to 100 but scaler ranges from 0 to 1. Every time a sample is about to be played, the left and right channels get multiplied by the scaler variable. You can try playing around with this. What happens if you comment out the calculation for spl0? Add a double forward slash to that line. Save the file and reload the plugin on your track. You don't need to rescan, just press control R when the plugin is selected in the FX chain. Could you add another slider called Pan that ranges from -100 to +100 and changes the balance of the audio?

First MIDI Plugin

Here is a simple plugin that has a slider to change the pitch of any incoming MIDI notes.  The minimum code for MIDI is a little more than for audio.


desc:My first Midi

slider1:0<-24,24,1>Offset

@slider

shift = slider1;

@block

while( midirecv( offset, msg1, msg2, msg3 ) )

(

  msg1 & 0xF0 == 0x90 ?

  (

   midisend( offset, msg1, msg2 + shift, msg3 );

  )

  : msg1 & 0xF0 == 0x80 ?

  (

   midisend( offset, msg1, msg2 + shift, msg3 );

  )

  : midisend( offset, msg1, msg2, msg3 );

);


In the same way as with the audio plugin, copy this code into a plain text file and save it in the Effects subfolder in the Reaper resource path ideally in your own subfolder holding all your plugins. Start up Reaper so it does a rescan and then insert reaSynth onto a track as as a VSTi.  Test you can play notes.  Now add your first MIDI plugin onto this track as well.  Before it has any effect thugh yu need to move reaSynth so it is below your first MIDI plugin since the plugin needs to update the MIDI data between it being played and it arriving at reaSynth.  Do this by either cutting and pasting reaSynth so it is second in the chain, or using the action bound to control-shift alt page down (Windows).


Tab to the Semitone shift slider in your new plugin. Play a note and then change the slider.  The pitch of notes is shifted.

Tools to help write plugins

The most basic tool is your plain text editor of choice.  Reaper does have an inbuilt editor but it is not easily accessible.  So writing code in a plain text editor is the next best thing.  If you make a change to your code and save it though, you will need to return to Reaper and the FX chain , press control R on your plugin and replace the plugin with a new instance of itself to for the changes take effect. Or have a project saved and reload it using alt F, R for recent projects.


It is possible to view all the variables used in a plugin along with their values.  This can be done with NVDA object navigation by tabbing to the edit button in the FX dialogue, hitting enter , and uing object navigation to move right until a list control is reached.  Go down one and you can then review each variable. Using NVDA num pad enter to activate this window also gives you first letter navigation. On each line the name of the variable is given, then its value and then the number of times the variable is used in the code.

Reviewing the list of variables is particularly useful to check if there is a typo in the code. A screen reader will not pronounce the two variables ChekX and CheckX differently but if you notice two variables in the list where you would expect only one then there is probably a typo in one of the variable names. Or sometimes you might call a variable NoteOn and somewhere else call it OnNote.


Debugging is difficult though in js plugins.  There is no option to display a message box or output diagnostic info to a file. And some variables will change their value with every sample or every block making them impossible to track.  You cannot step through the code one line at a time reviewing variable values like you can in more complex IDE's.

It is possible to write debug messages to the screen as the program runs. This might help in determining if a certain bit of code was executed for instance. Unfortunately, because you only see the last mesage written, it isn't as useful as it could be.

Use a statement like the following:

sprintf(#dbg_desc, "a message, which may include values of integers %d, or floats %f", integerVar, floatVar);

However, monitoring the list of variables as described above does reflect the value real-time so it can be useful particularly on slowly changing variables or those driven by user activity.

Another trick is to have a slider which is written to when a particular bit of code is executed and perhaps only once:

condition == 1 && !done ?

(

done = 1;

slider64 = TestVariable;

);

Resources

Probably the most comprehensive source of information are all the plugins that come with Reaper and found in the Effects folder.  But these can be complex and difficult to work out how they work.

There is a good tutorial made by Admiral Bumblebeee which goes through a modestly complex project to write a MIDI plugin.  There is also other useful content on this web site.

The JSFX Programming Reference - Language Essentials is on Reaper's web site.  It is a reference manual though and not a tutorial but if you get into JSFX programming you will come to appreciate this resource.

The Reaper community are a helpful bunch.  The JSFX forum. Use the search feature or search with something like Google putting reaper jsfx forum in as part of your search. Sign up to post a question.