PDA

View Full Version : Background and eel ReaScript thread (4.6 pre 10a)


Xenakios
01-31-2014, 12:21 PM
This thread is to discuss the new ReaScript features that allow running the scripts continuously in the background, as well as writing the scripts in the eel2-language.

mwe
01-31-2014, 01:46 PM
OK, I'll play. Do I need to do anything specific to enable EEL scripting on my PC? Something akin to installing Python? I don't see anything in preferences.

Xenakios
01-31-2014, 02:04 PM
OK, I'll play. Do I need to do anything specific to enable EEL scripting on my PC? Something akin to installing Python? I don't see anything in preferences.

eel based ReaScript is directly supported by Reaper as it is pretty much the same language as Jesusonic. Whether Python or eel is used for the scripts depends on the file extension (.py or .eel) used for the script files.

Argitoth
01-31-2014, 02:14 PM
Re: running scripts continuously in the background - what kind of things can be monitored and then acted upon? how about when "some_hotkey_is_pressed" or "some_script_is_run" or "some_action_is_executed"?

mwe
01-31-2014, 02:48 PM
eel based ReaScript is directly supported by Reaper as it is pretty much the same language as Jesusonic. Whether Python or eel is used for the scripts depends on the file extension (.py or .eel) used for the script files.
So I can mix .py and .eel in the same Scripts directory? Are eel scripts compiled at some point like JS? Does eel represent a performance boost over Python?

IXix
01-31-2014, 03:04 PM
So I can mix .py and .eel in the same Scripts directory? Are eel scripts compiled at some point like JS? Does eel represent a performance boost over Python?
They're both compiled at runtime. Whether one is faster than the other... dunno. My money's on EEL.

jnif
01-31-2014, 03:37 PM
It seems to be possible to run multiple scripts at the same time. Is there any limit on how many scripts can be running in the background?
Is it necessary to somehow manually protect multiple scripts accessing/modifying same "elements" at the same time?

There should be some easy way to see which scripts are running.
Also CPU load of each script could be shown in performance meter.

Is it possible to save project so that background scripts are running? Scripts would stop after project is closed. And they would start again automatically when project is opened again.

Is it possible to limit the target/scope of scripts? Like track FX is limited to track and take FX is limited to take. It would be useful to be able to run a script on single track or item only. And how could we then see where (on what track or item) those scripts are running?

Is it possible to temporarily disable script execution without killing/removing it? Similar to FX bypass.

In general, I think there should be a better way to manage background scripts. Starting, killing, disabling, opening/closing gfx window, "assigning" (for example using drag-n-dropping) to track or item, etc.

jnif

Xenakios
01-31-2014, 03:38 PM
They're both compiled at runtime. Whether one is faster than the other... dunno. My money's on EEL.

Eel is probably faster but is trickier to write as it's a more low level language than Python. Speed isn't likely to be a big issue, though. The scripts usually shouldn't attempt to do things that are very performance sensitive anyway. An exception to this could be scripts that want to analyze audio, for example. The overhead of certain operations in Python could be a significant bottleneck in something like that.

Justin
01-31-2014, 03:41 PM
Scripts are generally scoped globally like extensions, rather than per-project. You can see if any scripts are running in the Actions menu.

semiquaver
01-31-2014, 06:15 PM
Re: running scripts continuously in the background - what kind of things can be monitored and then acted upon? how about when "some_hotkey_is_pressed" or "some_script_is_run" or "some_action_is_executed"?

can ReaScript listen for keypresses? I haven't figured out how to do this yet if so...

I would like to write a front end with vim-like "gestures" to define actions' scope

Anton9
01-31-2014, 08:02 PM
My first .eel script.., some wacky random playrate fun.


function Random_Playrate()
(
rp = rand(4);
CSurf_OnPlayRateChange(rp);
defer("Random_Playrate();");
);

Random_Playrate();

Anton9
01-31-2014, 08:07 PM
Justin,

The defer() functions are awesome..., is there any way to not have the ReaScript Task Control dialog appear when stopping a persistent script?
If not could you maybe look into adding that option?

Thank you

Anton9
01-31-2014, 08:53 PM
I think the "State" column or perhaps a separate column in the Actions dialog would make for a nice place to show the status of a running script.

Anton9
01-31-2014, 10:21 PM
Justin,

Given this code as an example.., is there a way to control the rate of the OSC messages being sent?


function Random_LT()
(
val = rand();
OscLocalMessageToHost("/fxparam/last_touched/value", val);
defer("Random_LT();");
);

Random_LT();

Anton9
01-31-2014, 11:03 PM
No way to use the recently added MIDI API functions(MIDI_InsertNote, etc) via EEL :(
Hopefully this will be added soon?

Tale
02-01-2014, 03:12 AM
Given this code as an example.., is there a way to control the rate of the OSC messages being sent?
I guess you could use time_precise() for that:


function Random_LT()
(
t2 = time_precise();
t2 - t1 >= 0.100 ? ( // Once every 100 ms
t1 = t2;
val = rand();
OscLocalMessageToHost("/fxparam/last_touched/value", val);
);
defer("Random_LT();");
);

Random_LT();

jnif
02-01-2014, 03:22 AM
How can I easily save and load the internal state of a persistent script? And is it possible to make the internal state changes undoable?
It would be nice to have some built in methods to do those things. Something like @serialize in JS FX plugins.

jnif

spk77
02-01-2014, 04:37 AM
I have a very basic question.

How would I do this in eel - how to convert "int" to "string"? (Python code below)
selItemCount = str(RPR_CountSelectedMediaItems(0))
RPR_ShowConsoleMsg(selItemCount)


Edit:
Actually I want to convert this (part of a script) from Python to eel (but still want to know how to convert from "int" to "str" :)):
def msg(m):
RPR_ShowConsoleMsg(str(m))

def select():
selItemCount = RPR_CountSelectedMediaItems(0)
if selItemCount == 0:
msg("Select items")
return

Edit2:
This seems to work
function select()
(
selItemCount = CountSelectedMediaItems(0);
selItemCount == 0 ?
ShowConsoleMsg("Select items");
);

select();

jnif
02-01-2014, 05:04 AM
I have a very basic question.

How would I do this in eel - how to convert "int" to "string"? (Python code below)
selItemCount = str(RPR_CountSelectedMediaItems(0))
RPR_ShowConsoleMsg(selItemCount)



selItemCount = sprintf(#, "%d", CountSelectedMediaItems(0));
ShowConsoleMsg(selItemCount);

jnif

spk77
02-01-2014, 05:09 AM
Thanks jnif.

Anton9
02-01-2014, 08:02 AM
I guess you could use time_precise() for that

Sweeeeet.., Thank you Tale

spk77
02-01-2014, 09:31 AM
Here's a little speed test (and my first eel -script). (Licecap is slowing down my computer)

http://stash.reaper.fm/19657/speed%20eel%20vs%20python.gif

Select items with same name (by active take name) - eel2:

function selectByName()
(
selItemCount = CountSelectedMediaItems(0);
selItemCount != 0 ? : ShowConsoleMsg("Select items");
#sel_item_active_take_name = "";
sel_item = GetSelectedMediaItem(0, 0);
sel_item_active_take = GetActiveTake(sel_item);
GetSetMediaItemTakeInfo_String(sel_item_active_tak e, "P_NAME", #sel_item_active_take_name, 0);
// ShowConsoleMsg(#sel_item_active_take_name);

i = 0;
loop(CountMediaItems(0),
#active_take_name = "";
current_item = GetMediaItem(0, i);
active_take = GetActiveTake(current_item);
GetSetMediaItemTakeInfo_String(active_take, "P_NAME", #active_take_name, 0);
strcmp(#sel_item_active_take_name, #active_take_name) ?
SetMediaItemSelected(current_item, 0)
:
SetMediaItemSelected(current_item, 1);
i += 1;
);

UpdateArrange()
);

selectByName();

Select items with same name (by active take name) - Python 3.3.3 (RPR_Undo_BeginBlock2/Endblock was commented out when making the licecap gif):

# Select items (by selected item name)

from reaper_python import *
from contextlib import contextmanager

@contextmanager
def undoable(message):
RPR_Undo_BeginBlock2(0)
try:
yield
finally:
RPR_Undo_EndBlock2(0, message, -1)

def msg(m):
RPR_ShowConsoleMsg(str(m))

def selectByName():
selItemCount = RPR_CountSelectedMediaItems(0)
if selItemCount == 0:
msg("Select one item")
return

selItem = RPR_GetSelectedMediaItem(0, 0)
activeTake = RPR_GetActiveTake(selItem)
name = str(RPR_GetSetMediaItemTakeInfo_String(activeTake, "P_NAME", "", False)[3])

allItemsCount = RPR_CountMediaItems(0)
for i in range(allItemsCount):
item = RPR_GetMediaItem(0, i)
activeTake = RPR_GetActiveTake(item)
if str(RPR_GetSetMediaItemTakeInfo_String(activeTake, "P_NAME", "", False)[3]) == name:
RPR_SetMediaItemSelected(item, True)
else:
RPR_SetMediaItemSelected(item, False)

RPR_UpdateArrange()

with undoable("Select items (by selected item name)"):
selectByName()

Argitoth
02-01-2014, 09:44 AM
Sooo, should we bother writing Python anymore? Spk77, thanks for recreating that script in eel! Python is sooo slooooow. :( (when I am working with thousands of items)

Xenakios
02-01-2014, 09:49 AM
Sooo, should we bother writing Python anymore? Spk77, thanks for recreating that script in eel! Python is sooo slooooow. :( (when I am working with thousands of items)

Note that the perceived slowness in scripting may be because of Reaper's undo system. If you are modifying thousands of media items, Reaper can take a long time generating the undo history entry. You should do a timing benchmark inside the script code to see how long the script itself actually runs, if you want to be sure.

Working with Python is definitely still an option to consider as eel isn't the cleanest and easiest language around. (Consider for example it only has fixed size arrays compared to the dynamic memory allocation Python can do.)

Anton9
02-01-2014, 09:52 AM
Nice script spk77.., would be cool to see your Arpeggiator converted to EEL if possible.

Argitoth
02-01-2014, 10:18 AM
You should do a timing benchmark inside the script code to see how long the script itself actually runs, if you want to be sure.

x = 0

while x = 0
wait 1 microsecond
z = z + 1

script finished
x = 1

msg(z)

like that?

Justin
02-01-2014, 10:24 AM
How can I easily save and load the internal state of a persistent script? And is it possible to make the internal state changes undoable?
It would be nice to have some built in methods to do those things. Something like @serialize in JS FX plugins.

jnif

For EEL, there are two obvious routes -- you can use fopen("myconfigurationname.txt","r" or "w') to read/write a file. If given a relative pathname it will be mapped to your REAPER AppData path (this reminds me, we need to add mkdir()...).

The other option is to use SetExtState()/GetExtState()..

Also, you can use atexit("myexitfunc()"); to write any state to disk when your script is about to get unloaded...

Xenakios
02-01-2014, 10:27 AM
x = 0

while x = 0
wait 1 microsecond
z = z + 1

script finished
x = 1

msg(z)

like that?

...? No idea what you are thinking with that.

mwe
02-01-2014, 10:37 AM
I would think


time_precise(t0);

// Your script here

time_precise(t1);

elapsed_t = t1 - t0;

ShowConsoleMsg(strcat((sprintf(#, "%{elapsed_t}f")), " seconds\n"));


EDIT: Changed to mirror(more or less) Xenakios' Python version

Xenakios
02-01-2014, 10:56 AM
To time Python code (tested on Windows) :


import time

t0=time.clock()

# the script payload code here

t1=time.clock()

# result is in seconds

elapsed_t=t1-t0

RPR_ShowConsoleMsg(str(elapsed_t)+" seconds\n")

Anton9
02-01-2014, 11:29 AM
Justin,

It might be cool to have a field in the render dialog where we could specify a ReaScript that would run during render time.., any thoughts?

For examp; a script that automates FX parameters could be applied during a render.

xpander
02-01-2014, 12:05 PM
Apart from some desperate reverse engineering attempts, I've never done reascripts. So please forgive a total noobie question. Despite being a lower level language, is EEL still able to manage the same things python scripts can do at this moment? Iow, if I know an existing reascript, it is possible to port that to EEL as well?

I've had an ongoing problem of not been able to use some really useful scripts because they won't work portable, host has to have a working python install. Since EEL doesn't have this restriction, that would open some really nice opportunities for some of us left out so far.

Justin
02-01-2014, 02:37 PM
Apart from some desperate reverse engineering attempts, I've never done reascripts. So please forgive a total noobie question. Despite being a lower level language, is EEL still able to manage the same things python scripts can do at this moment? Iow, if I know an existing reascript, it is possible to port that to EEL as well?

I've had an ongoing problem of not been able to use some really useful scripts because they won't work portable, host has to have a working python install. Since EEL doesn't have this restriction, that would open some really nice opportunities for some of us left out so far.

It depends on how much Python you use -- simple scripts should port relatively easy, however Python provides a lot more capability, so other things might not port as easily.... For a very basic example, see awesomeifer.eel vs .py ...

xpander
02-01-2014, 04:29 PM
Thanks Justin. I have already checked the examples you and some of the members here have provided. Yes, at least those have given a glimpse of how the EEL scripts are done and how they compare to the python ones. With any luck...and most probably lots of help from our way more advanced forumites, I might finally get at least some of the scripts portable. It's a step forward already, so thanks again.

Breeder
02-03-2014, 12:03 AM
It seems .eel can't use functions exported by extensions. Will this get sorted at some point?

Argitoth
02-03-2014, 01:42 PM
can we do regex functions in EEL at all?

timlloyd
02-03-2014, 03:39 PM
can we do regex functions in EEL at all?

I haven't tried this in a .eel script yet, but from what I've seen so far it seems like essentially the same language as JS. From the JS reference:


match(needle, haystack, ...) -- search for needle in haystack
matchi(needle, haystack, ...) -- search for needle in haystack (case insensitive)
For these you can use simplified regex-style wildcards:

* = match 0 or more characters
*? = match 0 or more characters, lazy
+ = match 1 or more characters
+? = match 1 or more characters, lazy
? = match one character


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

Argitoth
02-03-2014, 04:31 PM
Edit:
uhhh... what about groups? Brackets? Do-not-match "^" What about spaces, new lines, etc.?

What is %x %f %u %s?

What is the regex based on? I've never seen this kind of implementation. Where can I get a complete explanation?

Anton9
02-03-2014, 07:13 PM
Is there a way of doing conditionals within EEL.., such as IF and ElseIF ?

Justin
02-03-2014, 07:14 PM
Edit:
uhhh... what about groups? Brackets? Do-not-match "^" What about spaces, new lines, etc.?

What is %x %f %u %s?

What is the regex based on? I've never seen this kind of implementation. Where can I get a complete explanation?

It's more like shell globbing than regex... There are plenty of these things out there in the world.

Justin
02-03-2014, 07:15 PM
Is there a way of doing conditionals within EEL.., such as IF and ElseIF ?

Yes, ? and :.

x==5 ? ( y = 10; ) : ( z = 10);

In C that would be:
if (x == 5) { y = 10; } else { z = 10; }

Anton9
02-03-2014, 07:18 PM
ah.., cool.., thanks!

harmonicaman
02-03-2014, 11:34 PM
"scripts continuously in the background"
super noob question :D :
will it be possible to build new iteractive tools ?
for example, with scripts we can already move envelope points to grid,give them a defined value and shape,
so now, in real time, with freehand and a script running in background, paint snapped points or even defined patterns ?

musicbynumbers
02-04-2014, 06:40 AM
"scripts continuously in the background"
super noob question :D :
will it be possible to build new iteractive tools ?
for example, with scripts we can already move envelope points to grid,give them a defined value and shape,
so now, in real time, with freehand and a script running in background, paint snapped points or even defined patterns ?

whoa! like your thinking there! :)

Could this give us those eccentric ways to edit item edges and ripple later items at the same time, freeing up the Justin and Schwa from us bothering them about it? ;)

Argitoth
02-04-2014, 08:33 AM
It's more like shell globbing than regex... There are plenty of these things out there in the world.

:(

People complain that you need to RTFM... so I am inventing a response to this.

WTFM? (where's the !@#$%^& manual)

Justin
02-04-2014, 09:32 AM
:(

People complain that you need to RTFM... so I am inventing a response to this.

WTFM? (where's the !@#$%^& manual)

The reascript autogenerated html has a summary...

Argitoth
02-04-2014, 10:34 AM
The reascript autogenerated html has a summary...
OHHHHHHoohohohooo.... it actually does have some decent explanations! Cool!

Stretto
02-04-2014, 12:58 PM
I know this won't go anywhere, but why eel? Why not lua/luajit? lua is extremely performant, easy to use as a scripting language, and pretty easy to learn.

Obviously with eel just coming out it won't even be considered but maybe in the future?

Anton9
02-04-2014, 06:11 PM
Is there a way to do something like my python script, but in EEL?
I don't see any way of doing a for loop to iterate through a range.

from sws_python import *

# Select every n'th note.py

# user input dialog
dialog_name = "Select every n'th note"
howmanyfields = 2
field_names = "Start Note,n'th Note"
default_values = "0,2" # If you want default values enter them between the "" examp: "0,2"
maxreturnlength = 10 # have this at least one more than you expect as return length. I just choose arbitrary.
User_Input = RPR_GetUserInputs(dialog_name,howmanyfields, field_names, default_values, maxreturnlength) # Dialog setup


takeInMe = RPR_MIDIEditor_GetTake(RPR_MIDIEditor_GetActive()) # the pointer of the currently active take in the midieditor

midiTake=FNG_AllocMidiTake(takeInMe) # allocates memory for a midi take

notesCount=FNG_CountMidiNotes(midiTake) # counts all notes of "midiTake"

if User_Input[0] == 1: # first item in the list it returns is 1 (true) for "user clicked ok", 0 (false) for "cancel"
UserValues = User_Input[4].split(',')
Start_Note = int(UserValues[0]) # Dialog input field 1
nth_Note = int(UserValues[1]) # Dialog input filed 2

for i in range(Start_Note,notesCount,nth_Note): # iterates through all notes
currNote=FNG_GetMidiNote(midiTake, i) # gets a pointer of the current note with index i
FNG_SetMidiNoteIntProperty(currNote, "SELECTED",1) # set note property to selected state


FNG_FreeMidiTake(midiTake) # "Commit changes to MIDI take and free allocated memory"

mwe
02-04-2014, 07:12 PM
I think you'd need to do something like


i = Start_Note;
while(
...do some stuff...
i += nth_Note;
i <= notesCount;
);

Justin
02-04-2014, 08:17 PM
We included EEL because we basically get it for free, as it's used elsewhere in REAPER as well.

Anton9
02-04-2014, 10:24 PM
I think you'd need to do something like


i = Start_Note;
while(
...do some stuff...
i += nth_Note;
i <= notesCount;
);


Perfect.., Thank you mwe!!! Here is what I came up with.., this script will select every n'th track.
//Select every n'th track

ct=CountTracks(); //count total # of tracks

i = 0; //first track to select[zero based]
while(x=GetTrack(0, i);
SetTrackSelected(x, 1);
i += 3; //select every n'th
i <= ct;
);

This is just a stepping stone.., what I would like to do is create a user dialog with options for n'th selection of various things such as (tracks, items, takes, MIDI_events, maybe FX too)

Anyway I don't get much time for coding so if anyone wants to run with this idea.., go for it.., if not I'll post what I come up with as I get around to it.

spk77
02-04-2014, 10:28 PM
I know this won't go anywhere, but why eel? Why not lua/luajit? lua is extremely performant, easy to use as a scripting language, and pretty easy to learn.

Obviously with eel just coming out it won't even be considered but maybe in the future?

EEL scripts seem to run/perform quite fast, even on my old XP from 2004:

http://stash.reaper.fm/19694/eel%20item%20inspector.gif

Anton9
02-04-2014, 10:48 PM
Hey spk77,

That's a pretty sweet little Item Inspector!
You should post it. :D

Argitoth
02-04-2014, 10:53 PM
You should post it. :DI second and third this notion. I am worth 2.

Anton9
02-04-2014, 11:41 PM
How would you do a UI_refresh in EEL?
I guess it would involve PreventUIRefresh()
However I'm not sure how to set it up.

timlloyd
02-05-2014, 12:44 AM
this script will select every n'th track.
//Select every n'th track

ct=CountTracks(); //count total # of tracks

i = 0; //first track to select[zero based]
while(x=GetTrack(0, i);
SetTrackSelected(x, 1);
i += 3; //select every n'th
i <= ct;
);


Or this


i = 0;
loop(CountTracks(),
tr = GetTrack(0, i);
SetTrackSelected(tr, 1);
i += 3;
);


It's like using a for loop -- use it if you know the loop count before the loop starts.

timlloyd
02-05-2014, 12:51 AM
How would you do a UI_refresh in EEL?
I guess it would involve PreventUIRefresh()
However I'm not sure how to set it up.

Same as in a Python reascript but without "RPR_" prepended:

EEL: PreventUIRefresh(int prevent_count)

PreventUIRefresh(1)

... some code

PreventUIRefresh(-1)

"prevent_count" should be 0 when the script terminates, or weird things will happen until reaper is relaunched, or you run another script that sets it to 0.

Edit, just realised I misread you :)

Open the API from Reaper and have a look at UpdateArrange() and UpdateTimeline()

Argitoth
02-05-2014, 09:17 AM
I heard with EEL you can create GUI...

how do you get user inputs?

timlloyd
02-05-2014, 09:40 AM
I heard with EEL you can create GUI...

how do you get user inputs?

Yes, with the gfx functions.

Getting user input via dialog box works in almost the same way as in a Python reascript. Have a look at the API.

Anton9
02-05-2014, 10:19 AM
It's like using a for loop -- use it if you know the loop count before the loop starts.

Thank you timlloyd.., it's always good to have more than one way of tackling a problem. :)

Oh.., and UpdateArrange() was just what I was looking for. I had totally forgot about that one.

Thanks

heda
02-05-2014, 11:49 AM
I hope "scripts running continuously in the background" doesn't mean that Reaper can become more unstable and if a script crashes it doesn't crash Reaper.
I want to learn py and eel !!! argh... where to start? :D

Anton9
02-05-2014, 12:22 PM
Devs,

Regarding the AudioAccessor functions.., it would be awesome if we could get a function that could apply a fadeIn/fadeOut over 'x' amount of samples to the audio in the buffer.

I invision a script that gets the length of MIDI notes from a MIDI item then extracts aduio from one or more audio items/files based on MIDI note lengths. It applies crossfades so as to avoid artifacts then the audio from the accessor buffers is dumped into a single or multiple items.., and BAM! instant beat creation/mangling heaven.

Argitoth
02-06-2014, 12:06 AM
How do you get the time selection start/end with EEL?

GetSet_LoopTimeRange(bool isSet, bool isLoop, &startOut, &endOut, bool allowautoseek)

how do you put that into a variable?

timlloyd
02-06-2014, 01:05 AM
function str(s)
(
sprintf(#, "%f", s);
);

function msg(s)
(
ShowConsoleMsg(s);
);

function bla()
(
GetSet_LoopTimeRange(0, 0, start, end, 0);

msg(str(start));
msg("\n");
msg(str(end));
msg("\n");
);

bla();

Argitoth
02-06-2014, 01:18 AM
sweet, thank you!

a few posts up I asked how to end a script. in python it's "Return". How do you end a script in EEL?

spk77
02-06-2014, 09:52 AM
Hey spk77,

That's a pretty sweet little Item Inspector!
You should post it. :D

I second and third this notion. I am worth 2.

Thanks, I'll post it after I have added some functionality to it. It took me many hours to figure out how to make a "delayed toggle button" and still don't know if this is a best way to do it:

"Delayed toggle button"
function is_value_between(value start end)
(
(value >= start && value <= end) ? 1 : 0;
);

function is_mouse_inside_rectangle(rect_x rect_y rect_w rect_h)
(
(is_value_between(mouse_x, rect_x, rect_w) && is_value_between(mouse_y, rect_y, rect_h)) ? 1 : 0;
);

mute_btn_click_delay = 0.3; //if left mb is held down -> toggle mute after 0.3 seconds
function draw_button(btn_x btn_y btn_w btn_h r g b a)
(
gfx_x = btn_x;
gfx_y = btn_y;

gfx_r = r;
gfx_g = g;
gfx_b = b;
gfx_a = a;

is_mouse_inside_rectangle(btn_x, btn_y, btn_x + btn_w, btn_y + btn_h) ? (
(mouse_cap == 1 && time_precise() - mute_btn_timer >= mute_btn_click_delay) ? (
//mute_btn_timer = 0;
(item = GetSelectedMediaItem(0,0)) ? (
GetMediaItemInfo_Value(item, "B_MUTE") ? (
SetMediaItemInfo_Value(item, "B_MUTE", 0)
):(
SetMediaItemInfo_Value(item, "B_MUTE", 1)
);
UpdateArrange();
time_precise(mute_btn_timer);
);
mute_btn_state = 1;
gfx_r = r + 0.2;
gfx_g = g + 0.2;
gfx_b = b + 0.2;
):(
mute_btn_state = 0;
gfx_r = r + 0.05;
gfx_g = g + 0.05;
gfx_b = b + 0.05;
);
);
gfx_rectto(gfx_x + btn_w, gfx_y + btn_h);
//mute_btn_state;
);

function run()
(
//draw_button(btn_x btn_y btn_w btn_h r g b a)
draw_button(10, 10, 20, 20, 0.5, 0.8, 0.5, 1);

gfx_x = mouse_x + 20;
gfx_y = mouse_y + 20;
gfx_printf("btn state: %d", mute_btn_state);
gfx_update();
gfx_getchar() >= 0 ? defer("run();");
);

gfx_init("Delayed click test",300,200);
run();

(edit. hmmm...too low framerate in LICEcap)
I'm holding the left mbutton down on the "button":
http://stash.reaper.fm/19704/eel%20delayed%20toggle.gif

Argitoth
02-06-2014, 10:07 AM
to switch between 0 and 1:

abs(0 - 1) = 1
abs(1 - 1) = 0

does that help, spk77?

or what about this: !value -- returns the logical NOT of the parameter (if the parameter is 0.0, returns 1.0, otherwise returns 0.0).

spk77
02-06-2014, 11:54 AM
to switch between 0 and 1:

abs(0 - 1) = 1
abs(1 - 1) = 0

does that help, spk77?

or what about this: !value -- returns the logical NOT of the parameter (if the parameter is 0.0, returns 1.0, otherwise returns 0.0).

This part changes the "mute" state of an item:
(item = GetSelectedMediaItem(0,0)) ? ( // if there is "selected item" in "selected items" at index 0
GetMediaItemInfo_Value(item, "B_MUTE") ? ( // if mute property == 1
SetMediaItemInfo_Value(item, "B_MUTE", 0) // set item "not muted"
):( // else
SetMediaItemInfo_Value(item, "B_MUTE", 1) // set item "muted"
);
);

I wanted to make a "button" which changes something (mute state in this case) at a certain interval while mouse cursor is on the button and the left mouse button is held down. Try to change "mute_btn_click_delay = 0.3" to 0 -> press the button and hold left mbutton down to see what happens. :)

Argitoth
02-06-2014, 12:24 PM
really long code you won't like just for fun


SetMediaItemInfo_Value(item, "B_MUTE", abs(GetMediaItemInfo_Value(item, "B_MUTE")-1));


or this


x -= GetMediaItemInfo_Value(item, "B_MUTE");
SetMediaItemInfo_Value(item, "B_MUTE", abs(x));


to remove the nested if statements

spk77
02-06-2014, 12:29 PM
(item = GetSelectedMediaItem(0,0)) ? (
x = GetMediaItemInfo_Value(item, "B_MUTE");
SetMediaItemInfo_Value(item, "B_MUTE", abs(x-1));
);



just meant you could shorten your code. does that work?

Yes, it seems to work too.

Q: How to make a (toggle) button which changes f.ex "mute state" only one time per "left mouse click". (It should be easy, but it wasn't :))

Argitoth
02-06-2014, 12:37 PM
sorry to triple post about the same thing, but I'm just learning and loving it, this works too!

SetMediaItemInfo_Value(item, "B_MUTE", !GetMediaItemInfo_Value(item, "B_MUTE"));
add the !, that's it.

Argitoth
02-06-2014, 01:14 PM
what's the point of this?

+value -- returns value unmodified.

+x = x
x = +x
+x = +x

from: http://www.reaper.fm/sdk/js/basiccode.php#js_ops

Anton9
02-06-2014, 02:45 PM
spk77,

You might be able to use the XOR command '~'.
In this other scripting language I use you can do toggles like this.

key.z = key.z ~ clicked(mouse.LeftButton)

Edit: It looks like EELs XOR is bitwise so it might not work.., oh-well.

Anton9
02-06-2014, 04:52 PM
I just discovered that if you assign a variable using "_global." and the script you use it in is persistent.., as long as it's running other scripts can access that variable. Pretty cool..., might be useful.

Argitoth
02-06-2014, 06:14 PM
Is it possible to use EEL as a frontend for python? So EEL runs python scripts, and stores variables for use in in other python scripts?

Justin
02-06-2014, 09:37 PM
Updated http://www.1014.org/shiz/shup/trackfft.eel for 4.60pre13, the audioaccessor API updated to be more useful (AudioAccessorValidateState())...

Justin
02-06-2014, 09:38 PM
what's the point of this?

+value -- returns value unmodified.

+x = x
x = +x
+x = +x

from: http://www.reaper.fm/sdk/js/basiccode.php#js_ops

It's just one of those things, making things consistent, since you can do (-x), allow (+x) as well...

James HE
02-06-2014, 09:54 PM
Yes, it seems to work too.

Q: How to make a (toggle) button which changes f.ex "mute state" only one time per "left mouse click". (It should be easy, but it wasn't :))

This is what I do in JS, it should roughly translate to reascript. Probably faster than calling time_precise.




mouse_cap == x ? m_tog+=1 : m_tog=0;

m_tog == 1 ? variable = -variable;



I just set up toggles to be -1 or 1 for JS. And declare the initial state to be -1.

If you want it to be Boolean in your code that's relatively easy to do as well using the XOR.




mouse_cap == x ? m_tog+=1 : m_tog=0;

m_tog == 1 ? variable~=1;



this will switch variable between 1 and 0.

Argitoth
02-06-2014, 10:52 PM
With audio accessor, can do you something like:

"if sample at cursor of any item is below 0 (below the zero crossing line), set item inverted"

Argitoth
02-07-2014, 12:23 AM
Updated http://www.1014.org/shiz/shup/trackfft.eel for 4.60pre13, the audioaccessor API updated to be more useful (AudioAccessorValidateState())...

AWESOOOM! Um, is it supposed to be inverted like this?
http://www.elanhickler.com/transfer/eel_gfx_behavior.gif

spk77
02-07-2014, 12:53 AM
Thanks everyone.

We can set (initialize) the width/height of the graphics window with:
EEL: gfx_init("name"[,width,height])

Initializes the graphics window with title name. Suggested width and height can be specified.

Once the graphics window is open, gfx_update() should be called periodically.

It would be nice if we could "get/set x,y,w,h of a window". (I guess there should be "Get window handle" or "Get window handle by window name" etc. also)

Anton9
02-07-2014, 02:27 AM
Can the transparency of graphics windows be adjusted?
If not.., it sure would make for a nice addition.

Justin
02-07-2014, 09:10 AM
AWESOOOM! Um, is it supposed to be inverted like this?
http://www.elanhickler.com/transfer/eel_gfx_behavior.gif

Change:
ny = gfx_h*0.2 + gfx_h*0.1*oscbuffer[i*2];

to

ny = gfx_h*0.2 - gfx_h*0.1*oscbuffer[i*2];

harmonicaman
02-07-2014, 01:53 PM
whoa! like your thinking there! :)

Could this give us those eccentric ways to edit item edges and ripple later items at the same time, freeing up the Justin and Schwa from us bothering them about it? ;)

Thanks, hope this kind of ideas will pick the interest of talented coders here, because for me, the last time I wrote lines of code was in the dark ages, on a Thomson MO5 at school, and it was something like "Hello World"

nofish
02-07-2014, 02:36 PM
Updated http://www.1014.org/shiz/shup/trackfft.eel for 4.60pre13, the audioaccessor API updated to be more useful (AudioAccessorValidateState())...

Assuming more of this awesome stuff comes up in the future, would it be possible to add a "always in front" pin to those windows for us dual-monitor users please ? (like plugins for not hiding behind undocked maxed mixer)

spk77
02-07-2014, 03:11 PM
Not much functionality yet (just to select the active take in item), but I decided to post it anyway. Maybe it will help the other beginners too.

Item Inspector:

http://stash.reaper.fm/19713/eel%20item%20inspector2.gif

// Item Inspector
// SPK 77 8.2.2014 (yes, 8.2.)

// gfx window size
window_w = 400;
window_h = 150;

// "left mouse btn" state (0 or 1 - checked at the end)
lmb_already_down = 0;

function select_font(index)
(
index == 1 ? gfx_setfont(1,"Verdana", 16);
index == 2 ? gfx_setfont(2,"Verdana", 16, 'bu');
);

function is_value_between(value start end)
(
(value >= start && value <= end) ? 1 : 0;
);

function is_mouse_inside_rectangle(rect_x rect_y rect_w rect_h)
(
(is_value_between(mouse_x, rect_x, rect_w) && is_value_between(mouse_y, rect_y, rect_h)) ? 1 : 0;
);

function draw_clickable_string(str_x str_y r g b a, clickable_str, upd_x, upd_y)
(
gfx_x = str_x;
//gfx_y = str_y;

gfx_r = r;
gfx_g = g;
gfx_b = b;
gfx_a = a;

clickable_string = clickable_str;
gfx_measurestr(clickable_string, str_w, str_h);

// is mouse on string?
is_mouse_inside_rectangle(str_x, str_y, str_x + str_w, str_y + str_h) ? (
// is mouse on string and lmb down?
mouse_cap == 1 ? (
// is mouse on string and lmb down and already clicked on the string?
(lmb_already_down == 0 && (item = GetSelectedMediaItem(0,0))) ? (
SetActiveTake(take);
UpdateArrange();
);
// Highlight string (2 states):
// mouse on string and lmb down
gfx_r = r + 0.2;
gfx_g = g + 0.2;
gfx_b = b + 0.2;
):(
// mouse on string and lmb up
gfx_r = r + 0.1;
gfx_g = g + 0.1;
gfx_b = b + 0.1;
);
);
// draw string:
gfx_printf("%s", clickable_string);
upd_x ? gfx_x += str_w + 1;
upd_y ? gfx_y += str_h + 1;
);

function draw_label(lbl_x lbl_y r g b a, name, upd_x, upd_y)
(
gfx_x = lbl_x;
//gfx_y = lbl_y;

gfx_r = r;
gfx_g = g;
gfx_b = b;
gfx_a = a;

label_text = name;
gfx_measurestr(label_text, lbl_w, lbl_h);

// draw string:
gfx_printf("%s", label_text);
upd_x ? gfx_x += lbl_w + 1;
upd_y ? gfx_y += lbl_h + 1;
);

function get_item_volume(item)
(
item_volume = GetMediaItemInfo_Value(item, "D_VOL")
);

function get_item_volume_dB(item_volume)
(
item_volumedB = 20 * log10(item_volume);
);

function get_item_volume_dB_str(item_volume)
(
item_volumedB = 20 * log10(item_volume);
item_volumedB_str = sprintf(#, "%f", item_volumedB);
);

function run()
(
gfx_x = 10;
gfx_y = 10;

select_font(1);
// draw_label(lbl_x lbl_y r g b a, name, upd_x, upd_y)
// draw_label(10, 10, 0.5, 0.5, 0.8, 1, "Item Vol", 1, 0);

(sel_item = GetSelectedMediaItem(0, 0)) ? (
// draw_clickable_string(str_x str_y r g b a, clickable_str, upd_x, upd_y)
// draw_clickable_string(gfx_x, gfx_y, 0.5, 0.8, 0.5, 1, get_item_volume_dB_str(get_item_volume(sel_item)), 0, 0);
(take_count = CountTakes(sel_item)) ? (
take_counter = 0;
loop(take_count,
(take = GetTake(sel_item, take_counter)) ? (
(active_take = GetActiveTake(sel_item)) ?
take == active_take ? select_font(2) : select_font(1);
GetTakeName(#take_name, take);
// draw_clickable_string(str_x str_y r g b a, clickable_str, upd_x, upd_y)
draw_clickable_string(10, gfx_y, 0.5, 0.8, 0.5, 1, #take_name, 0, 1);
take_counter += 1;
);
);
);
);

mouse_cap == 1 ? lmb_already_down = 1 : lmb_already_down = 0;
gfx_update();
gfx_getchar() >= 0 ? defer("run();");
);

gfx_init("Item/take Inspector", window_w, window_h);
run();

James HE
02-08-2014, 12:25 PM
EEL: bool GetTrackUIVolPan(MediaTrack* track, &volumeOut, &panOut)



from the API docs, what does the "&" mean?

Justin
02-08-2014, 12:48 PM
EEL: bool GetTrackUIVolPan(MediaTrack* track, &volumeOut, &panOut)



from the API docs, what does the "&" mean?

The & means "can be modified by the function"

Argitoth
02-08-2014, 01:03 PM
how do you escape from a loop and cancel a script in EEL? I just want to say "if no items are selected, end script" In python it's using the word "return" for ending the script, or "break" for stepping out of the loop.

Is there no relative action in EEL?

Justin
02-08-2014, 01:27 PM
how do you escape from a loop and cancel a script in EEL? I just want to say "if no items are selected, end script" In python it's using the word "return" for ending the script, or "break" for stepping out of the loop.

Is there no relative action in EEL?

Nope, you'd do a while(x) (y) loop and make the condition x something that detects the end.

spk77
02-08-2014, 01:39 PM
This is (very) slowly becoming almost usable :):

http://stash.reaper.fm/19715/eel%20item%20inspector3.gif

Argitoth
02-08-2014, 02:21 PM
Nope, you'd do a while(x) (y) loop and make the condition x something that detects the end.

How about surrounding the entire script in an if statement? If selected items >= 1, do script, else BLANK

Justin
02-08-2014, 02:25 PM
How about surrounding the entire script in an if statement? If selected items >= 1, do script, else BLANK

Sure:

CountSelectedMediaItems() > 0 ? (

MB("items are selected","hi",0);

);

musicbynumbers
02-08-2014, 04:44 PM
Thanks, hope this kind of ideas will pick the interest of talented coders here, because for me, the last time I wrote lines of code was in the dark ages, on a Thomson MO5 at school, and it was something like "Hello World"

That made me think.. If the internet ever becomes self-aware, I wonder if it will feel a sub-conscious urged for it's first words to be "...hello world" ;)

The amount of times that must have been written on the internet! ;)

Argitoth
02-08-2014, 05:19 PM
been at this for hours, why isn't it working?

num_items = CountSelectedMediaItems(0);

item_list[num_items];
i = 0;
loop(num_items,
item_list[i] = GetSelectedMediaItem(0, i);
i += 1;
);

Main_OnCommandEx(40289, 0, 0); // unselect all items

i = 0;
loop(num_items,
ShowMessageBox("set selected", "PAUSE", 0);
SetMediaItemInfo_Value(item_list[i], "B_UISEL", 1);
ShowMessageBox("set unselected", "PAUSE", 0);
SetMediaItemInfo_Value(item_list[i], "B_UISEL", 0);
i += 1;
);

Justin
02-08-2014, 05:39 PM
item_list[num_items];


This statement does nothing. Arrays are not initialized this way, instead you have a global address space of about 8,000,000. You can say:

item_list = 10000;
// addresses 10000, 10001, 10002, ... 10000+num_items-1 are all used for item_list

Using item_list as you are, it may appear to work, because item_list would by default be 0, so you'd be using memory slots 0..num_items-1 for the item pointers. That would work, until you accidentally overwrite that data with some other array.

ShowMessageBox("set selected", "PAUSE", 0);
SetMediaItemInfo_Value(item_list[i], "B_UISEL", 1);
ShowMessageBox("set unselected", "PAUSE", 0);
SetMediaItemInfo_Value(item_list[i], "B_UISEL", 0);
i += 1;


You may need to do an UpdateArrange() after the SetMediaItemInfo_Value(), in order for it to update the display.

spk77
02-08-2014, 05:39 PM
been at this for hours, why isn't it working?


Maybe it needs to be refreshed with UpdateArrange():

num_items = CountSelectedMediaItems(0);

item_list[num_items];
i = 0;
loop(num_items,
item_list[i] = GetSelectedMediaItem(0, i);
i += 1;
);

Main_OnCommandEx(40289, 0, 0); // unselect all items

i = 0;
loop(num_items,
ShowMessageBox("set selected", "PAUSE", 0);
SetMediaItemInfo_Value(item_list[i], "B_UISEL", 1);
UpdateArrange();
ShowMessageBox("set unselected", "PAUSE", 0);
SetMediaItemInfo_Value(item_list[i], "B_UISEL", 0);
UpdateArrange();
i += 1;
);

Argitoth
02-08-2014, 06:28 PM
the "Select one item at a time" thing works until I uncomment "crop items"

why is cropping items making my item list not work?

Justin
02-08-2014, 07:04 PM
Do the items in question intersect the time selection? You might want to check to see if the items are valid after the crop.. you can try querying something, or use ValidatePtr(item, "MediaItem*")

Justin
02-08-2014, 08:54 PM
Maybe ValidatePtr() != 0 ? ( ... run sws action etc... ); ? It's unclear what you're trying to accomplish...

Argitoth
02-08-2014, 10:09 PM
I apologize for stealing so much attention to my crappy scripting skills. I'm simply trying to:

1. select some items
2. store that selection as an array
3. crop the items
4. select one item at a time for running functions / reaper API

My best guess is that the media item id changes after cropping. Wait... that would make sense if "cropping" an item meant splitting it in two places and deleting the start and end, thereby making the item in the time selection a completely new item while deleting the original item.

ASCII demonstration:

item:
[_______]
split at time selection
[__|_|__]
delete trailing and leading items
[_]
now the original item has been deleted... this must be how cropping works



SOOO I guess I have to manipulate the item to achieve a cropping-like effect for my script to work.

EDIT: OK, yep, that worked. So the original item must be getting deleted during a crop action. So I made my own crop action that is actually just manipulating item start/length/offset rather than any sort of cutting/deleting.

schwa
02-09-2014, 06:39 AM
With audio accessor, can do you something like:

"if sample at cursor of any item is below 0 (below the zero crossing line), set item inverted"

Sure. (edited because originally I misinterpreted the question)

What will be fun is when API support is added to JS. Using the audioaccessor API from FX would allow the FX to manipulate audio at time positions other than "now", which opens up all sorts of read-ahead and analysis possibilities.

EvilDragon
02-09-2014, 07:38 AM
What will be fun is when API support is added to JS. Using the audioaccessor API from FX would allow the FX to manipulate audio at time positions other than "now", which opens up all sorts of read-ahead and analysis possibilities.

Sounds like ARA to me :D

Sexan
02-09-2014, 08:08 AM
Sounds like ARA to me :D

Yeeeeeeeeeeeeeeeeey!!!!! :)

Argitoth
02-09-2014, 09:41 AM
Sure. (edited because originally I misinterpreted the question)

What will be fun is when API support is added to JS. Using the audioaccessor API from FX would allow the FX to manipulate audio at time positions other than "now", which opens up all sorts of read-ahead and analysis possibilities.

Yes, I want an x/y color spectrogram display!

And I want to draw squares to remove frequency bits!

And I want those squares to have "soft edges"! Oh I'm probably describing Spectro (http://www.stillwellaudio.com/plugins/schwa-plugins/spectro/)

musicbynumbers
02-09-2014, 09:46 AM
Sure. (edited because originally I misinterpreted the question)

What will be fun is when API support is added to JS. Using the audioaccessor API from FX would allow the FX to manipulate audio at time positions other than "now", which opens up all sorts of read-ahead and analysis possibilities.

ooh!!! that sounds cool!

Once some one creates a JS EBU-R128 plugin, they might be able to have the loudness level drawn back into the reaper as an automation line on the track so we can get a really detailed overview of instant LU level at any point in the project! :)

Could it even get reaper to add markers at the exact time there's a peak overload/clip on the master! :)

Argitoth
02-09-2014, 10:04 AM
I want to create a TAKE audio accessor to return one sample value at cursor position for the first channel. This is from the documentation:
tr = RPR_GetTrack(0, 0)
aa = RPR_CreateTrackAudioAccessor(tr)
buf = list([0]*2*1024) # 2 channels, 1024 samples each, initialized to zero
pos = 0.0
(ret, buf) = GetAudioAccessorSamples(aa, 44100, 2, pos, 1024, buf)
# buf now holds the first 2*1024 audio samples from the track.
# typically GetAudioAccessorSamples() would be called within a loop, increasing pos each time.

But this code is in python. What would this look like if written for EEL? I don't understand the syntax of variableA = variableB([int]*int*int). Is that some sort of multidimensional array? What is the EEL equivalent? Also, (variableA, variableB) = variableC doesn't seem like EEL-compatible syntax as well.

mwe
02-09-2014, 12:05 PM
buf = list([0]*2*1024) # 2 channels, 1024 samples each, initialized to zero


buf = 10000; // 10000 is arbitrary memory address
memset(buf, 0, 2 * 1024);


(ret, buf) = GetAudioAccessorSamples(aa, 44100, 2, pos, 1024, buf)
# buf now holds the first 2*1024 audio samples from the track.
# typically GetAudioAccessorSamples() would be called within a loop, increasing pos each time.

Don't know about this. I assume you do
GetAudioAccessorSamples(aa, 44100, 2, pos, 1024, buf);
and the sample values will be copied to buf.

Argitoth
02-09-2014, 12:25 PM
memset(offset,value,length)
// Sets length items of memory at offset to value.


Could you tell me more about memtest? Is that just an easy way to ensure your arrays don't overlap?

mwe
02-09-2014, 01:12 PM
memset(offset,value,length)
// Sets length items of memory at offset to value.


Could you tell me more about memtest? Is that just an easy way to ensure your arrays don't overlap?

No, memset is used to set a range of memory slots to some value. Probably the most common way I've seen it used is to zero out a range used as an array. There are ways to manage memory addresses though. A lot of folks use something like


buf_size = 10;
buf1 = 10000;
memset(buf1, 0, buf_size);
buf2 = buf1 + buf_size;
memset(buf2, 0, buf_size);
buf3 = buf2 + buf_size;
memset(buf3, 0, buf_size);
//...etc


I've been thinking of doing something along these lines

function init(size)
(
this = cur_ptr;
memcpy(this, 0, size);
cur_ptr += size;
);

init_ptr = cur_ptr = 10000;
buf1.init(10);
buf2.init(8);
buf3.init(256);

James HE
02-09-2014, 01:48 PM
I have a very crude VCA/DCA thing going with EEL script. Where the slave faders actually move with the Masters automation

https://drive.google.com/file/d/0B8W7rjGAC00eaVlyTUNkeDBZU2s/edit?usp=sharing

master track has to be in read mode, slave in trim. Even once I flush out the interface and setup, I can't really see a way to re-load it properly with the project.

EEL scripts would need something akin to @serialize. If we get ReaScript / JS communication, I actually see this as being easier to do.. just would need a single JS in the project to store all the values and pop them into the script when ever you open a project...

I'm having a heck of a time with the window and displaying strings and getting that to persist and update correctly... but the mechanism itself works, just no visual feedback from the script. oh well, lot to do. :)

Argitoth
02-09-2014, 01:59 PM
I need to create an array of strings in EEL from a txt document list. The document would look like this:

item1
item2
item3
...etc.

How do I use EEL's string functions to split this by "newline"?

Argitoth
02-09-2014, 02:49 PM
item = GetSelectedMediaItem(0, 0);
take = GetActiveTake(item);

accessor = CreateTakeAudioAccessor(take);

samplerate = 44100;
channels = 1;
//FIXED!!
position = GetCursorPosition() - GetMediaItemInfo_Value(item,"D_POSITION");
samples = 1;
buffer = 0;

memset(buffer, 0, channels * samples);
GetAudioAccessorSamples(accessor, samplerate, channels, position, samples, buffer);
DestroyAudioAccessor(accessor);

buffer[0] < 0 ? (
ShowConsoleMsg("Less than zero!");
);

buffer[0] > 0 ? (
ShowConsoleMsg("More than zero!");
Main_OnCommandEx(40181, 0, 0); // toggle item phase
);

buffer[0] == 0 ? (
ShowConsoleMsg("Zero!");
);


Is there no media item value for phase/invert? There's "B_PHASE" for track, but it is not listed for media item.

Justin
02-09-2014, 04:29 PM
For take polarity, you should flip the sign of the take's volume.

Also, you should use GetCursorPosition() - GetMediaItemInfo_Value(item,"D_POSITION") for the time position....

Argitoth
02-09-2014, 05:27 PM
Thank you, Justin! Works!

Edit:
GetMediaSourceNumChannels(PCM_source* source)
GetMediaSourceSampleRate(PCM_source* source)

How do you fulfill the "PCM_source* source" requirement?

found it!: GetMediaItemTake_Source(MediaItem_Take* take)

Anton9
02-10-2014, 12:39 AM
Sure:

CountSelectedMediaItems() > 0 ? (

MB("items are selected","hi",0);

);


Thank you for posting this little snip. It got me thinking and I came up with this.., NOTE: This script is not really pratical, but I thought I would share as maybe it will give others ideas on the cool possibilities.


So basically what this does is.., it toggles the selected items' "MUTE" property on/off. You can select a different item while it's running and that items mute will be toggled..., the script will stay in a persistent state until you select 0 items, then it terminates.

Select one item before running the script:
function TEST()
(
CountSelectedMediaItems() > 0 ? (
x = GetSelectedMediaItem(0, 0);
val = 1 - val;
SetMediaItemInfo_Value(x, "B_MUTE", val);
UpdateArrange();

defer("TEST();");
);
);

TEST();

Anton9
02-10-2014, 01:12 AM
Quick question fot the devs,

So the UI thread is updated around every 30ms..., is this anything running in the UI thread (such as scripts) or just the graphical refresh of the UI?

Meaning can a parameter be updating at say 10ms, but it's just that the UI won't reflect this?

sstillwell
02-10-2014, 10:48 AM
Sure. (edited because originally I misinterpreted the question)

What will be fun is when API support is added to JS. Using the audioaccessor API from FX would allow the FX to manipulate audio at time positions other than "now", which opens up all sorts of read-ahead and analysis possibilities.

<mindboggle>Holy moly</mindboggle>

Argitoth
02-10-2014, 11:26 AM
function TEST()
(
CountSelectedMediaItems() > 0 ? (
x = GetSelectedMediaItem(0, 0);
val = 1 - val;
SetMediaItemInfo_Value(x, "B_MUTE", val);
UpdateArrange();

defer("TEST();");
);
);

TEST();
can be rewritten:

//CORRECTED
function TEST()
(
CountSelectedMediaItems() > 0 ? (
x = GetSelectedMediaItem(0, 0);
SetMediaItemInfo_Value(x, "B_MUTE", val = 1 - val);
UpdateArrange();

defer("TEST();");
);
);

TEST();

Argitoth
02-10-2014, 02:06 PM
Made a script that measures time selection and inserts tempo marker. The current actions weren't doing what I wanted... But what would be REALLY nice is to create a gfx interface for the beats number. Any takers? I might take a stab at it.


// get time selection
GetSet_LoopTimeRange(0, 0, time_start, time_end, 0);
time_length = time_end - time_start;

beats = 8; // change to any number of beats you count. the start of a beat to right before the next beat = 1 beat

bpm = beats * 4 * (60/time_length);

sprintf(str, "%{beats}i * (60/%{time_length}f) = %{bpm}f");
ShowConsoleMsg(str);

SetTempoTimeSigMarker(0, -1, time_start, -1, -1, bpm, 0, 0, 0);
UpdateTimeline();


Edit: The one problem is that I have not seen anyone have an eel script that you can input some text or numbers. Is it possible?
Edit: So I guess the solution would be to create a slider-like behavior and display numbers.

Anton9
02-10-2014, 02:40 PM
can be rewritten:


function TEST()
(
CountSelectedMediaItems() > 0 ? (
x = GetSelectedMediaItem(0, 0);
SetMediaItemInfo_Value(x, "B_MUTE", 1 - val);
UpdateArrange();

defer("TEST();");
);
);

TEST();


hmmm, not quite the same, yours seems to set and keep at mute while mine toggles on/off. I did that just so there was visual and audible cue that the script was running. :)

spk77
02-10-2014, 02:55 PM
Almost ready:

http://stash.reaper.fm/19740/eel%20item%20inspector4.gif

Anton9
02-10-2014, 03:15 PM
Almost ready:

Can't wait..., looks awesome!

spk77
02-10-2014, 03:41 PM
Can't wait..., looks awesome!

Thanks. Currently all adjustments (vol/pan/fontsize) have to be made with "modifier key + mousewheel up/down". I think I know how to do a "pan slider" (it's a "linear" slider), but I have to think how to do the vol sliders (have to convert logar. to linear and linear to logar. etc.). I think I'll add a "lock to selected item" -button. It's possible to open multiple instances of EEL GUI-windows -> this script can be used as a per-item take mixer, for example. :)

Argitoth
02-10-2014, 07:53 PM
hmmm, not quite the same, yours seems to set and keep at mute while mine toggles on/off. I did that just so there was visual and audible cue that the script was running. :)what? but I tested it! ok, well I was wrong. But NOW I'm right when I say can be rewritten as such:
SetMediaItemInfo_Value(x, "B_MUTE", val = 1 - val);

...

Is there no way to type in a new item name with EEL because there's no input box stuff? You'd have to do a GetUserInputs() which I don't like because is freezes REAPER until you deal with the input box, and it's not customizeable.

...

How do you open a txt file for reading into a string?

//WORKS! I'm getting somewhere.
#needle = "*FILENAME \"*\"*";
#haystack = "TakeName \"ThisIsATake\"\nFILENAME \"This is a file name.wav\"\nOther Stuff";
match(#needle,#haystack) == 1 ? (ShowConsoleMsg("matched!\n");):(ShowConsoleMsg("no match\n"););


Is there any way to return what is matched instead of just true or false?

Justin
02-10-2014, 08:58 PM
Usually done something like this:

fp = fopen("D:\\whatever\\file.txt","r");
fp > 0 ? (
fgets(fp, #mystr);
fclose(fp);
MB(#mystr,"I read a string",0);
);

Justin
02-10-2014, 09:00 PM
//WORKS! I'm getting somewhere.
#needle = "*FILENAME \"*\"*";
#haystack = "TakeName \"ThisIsATake\"\nFILENAME \"This is a file name.wav\"\nOther Stuff";
match(#needle,#haystack) == 1 ? (ShowConsoleMsg("matched!\n");):(ShowConsoleMsg("no match\n"););


Is there any way to return what is matched instead of just true or false?

Try:


#haystack = "TakeName \"ThisIsATake\"\nFILENAME \"This is a file name.wav\"\nOther Stuff";
match("*?FILENAME \"%S\"*",#haystack,#filename) ? ShowConsoleMsg(#filename);

Argitoth
02-10-2014, 09:11 PM
Hell yes! Thank you Justin. My EEL script "Search for takes by name with wildcards" now has a seed.

//AWESOOOOOM!!@!!
#haystack = "TAKENAME bla FILENAME bleee! OTHER STUFF weeheeeoohooo!";
match("TAKENAME %S FILENAME %S OTHER STUFF %S",#haystack,#takename,#filename,#coolcatname) ? ShowConsoleMsg("We got a match, babyies!\n");

#string += #takename;
#string += #filename;
#string += #coolcatname;
ShowConsoleMsg(#string += "\n");


//Same code as above, but written more regexish!
#haystack = "TAKENAME bla FILENAME bleee! OTHER STUFF weeheeeoohooo!";
match("TAKENAME %{#takename}S FILENAME %{#filename}S OTHER STUFF %{#coolcatname}S",#haystack) ? ShowConsoleMsg("We got a match, babyies!\n");

#string += #takename;
#string += #filename;
#string += #coolcatname;
ShowConsoleMsg(#string += "\n");

James HE
02-10-2014, 10:23 PM
Is there no way to type in a new item name with EEL because there's no input box stuff? You'd have to do a GetUserInputs() which I don't like because is freezes REAPER until you deal with the input box, and it's not customizeable.



I haven't successfully done that in reascript with EEL, but this seems to work in JS. I can't get ENTER to properly make a new line, but...

http://forum.cockos.com/showthread.php?t=135189

Argitoth
02-11-2014, 12:34 AM
Hey Justin (or anyone else), why return 0 for true and -1 for false? Isn't that dang confusing?

x = strcmp("blee","bloo");
y = strcmp("bla","bla");

sprintf(#string, "true: %{y}f\nfalse: %{x}f\n");
ShowConsoleMsg(#string);

prints this to console:

true: 0.000000
false: -1.000000

schwa
02-11-2014, 06:05 AM
Hey Justin (or anyone else), why return 0 for true and -1 for false? Isn't that dang confusing?

That's standard behavior. The purpose is to support ordering strings consistently (strcmp returns -1 or 1 if the strings are different, indicating which string should be ordered first).

http://pubs.opengroup.org/onlinepubs/009695399/functions/strcmp.html

Argitoth
02-11-2014, 10:21 AM
What's the difference between printf and sprintf besides the syntax/format? printf opens up ReaScript Output window instead of console message box. That's the only difference I can really see. OH and ReaScript Output window ignores newline characters. Printf does not return a string, it only causes ReaScript Output window to appear. Does printf return anything?

sstillwell
02-11-2014, 04:19 PM
What's the difference between printf and sprintf besides the syntax/format? printf opens up ReaScript Output window instead of console message box. That's the only difference I can really see. OH and ReaScript Output window ignores newline characters. Printf does not return a string, it only causes ReaScript Output window to appear. Does printf return anything?

printf is for generating output - typically to standard output (the console) in a C program. sprintf is a function that takes similar parameters and generates a null-terminated string instead of outputting it.

Normally printf returns the number of characters printed.

Scott

Argitoth
02-11-2014, 05:36 PM
Thanks, stillwell!

But the questions must continue.

if I have loops using variable i counter as a counter, do I need to set i = 0 first? If I don't, will I run the risk of having other scripts setting i to another number? What about persistent scripts? So the question is... is variable i a different memory slot every time the script is run or is it shared between all scripts?

James HE
02-11-2014, 06:11 PM
Thanks, stillwell!

But the questions must continue.

if I have loops using variable i counter as a counter, do I need to set i = 0 first? If I don't, will I run the risk of having other scripts setting i to another number? What about persistent scripts? So the question is... is variable i a different memory slot every time the script is run or is it shared between all scripts?

i don't think it would be shared between scripts.

even still, if your loop is within a function, you can just declare the i as a local () value


function thingamajig() local(i) .. etc

WyattRice
02-11-2014, 06:26 PM
Quick question guys.
Could any python script for Reaper be converted to eel?
Just curious.
Thanks, Wyatt

Argitoth
02-11-2014, 06:42 PM
Quick question guys.
Could any python script for Reaper be converted to eel?
Just curious.
Thanks, Wyatt

probably... just post the script and I'll see what I can does. (or someone else more skilled than me could do it if I fail).

Argitoth
02-11-2014, 07:02 PM
Is there any way to sort in EEL?

I'm trying to conceive of a script that checks if any media items overlap in time, and I'm thinking I need to sort their positions, only because if an item is on a different track, it is at a completely different place in order of "GetSelectedMediaItem()"

Edit:
function sort(array, length)
(
index = 1;
loop(length,
value = array[index];
i = index - 1;
while (i >= 0)(
value < array[i] ? (
array[i+1] = array[i];
array[i] = value;
i = i - 1;
):(i = -1;);
);
index +=1;
);
);


edit: oh sh** I f***in did it! Well I copied this (https://www.khanacademy.org/science/computer-science-subject/computer-science/v/insertion-sort-in-python) python code to eel, but I'm still proud! (took me hours to do even with code right infront of me...)

Edit: HOW DO YOU CALL FUNCTIONS FROM OTHER EEL SCRIPTS? :)
nevermind, it's explained in the js sdk

WyattRice
02-11-2014, 08:21 PM
probably... just post the script and I'll see what I can does. (or someone else more skilled than me could do it if I fail).

Thanks for the response. It's a script that gofer wrote for preparing items for cd burning on one track. Here's a link to some info on what the script does along with an animated gif.

http://forum.cockos.com/showpost.php?p=745535&postcount=54

And the script I would most be interested in is not in that bundle, but located in this post.
http://forum.cockos.com/showpost.php?p=782133&postcount=135

It's a complicated script for me to figure out programming wise, and I'm still just a newbie when it comes to python.

Man, it would be awesome to get this to work natively with Reaper with eel. Maybe a couple of you gurus would find this challenging.

Many thanks, Wyatt

Argitoth
02-11-2014, 09:22 PM
I'm not a guru, but this is definitely not a challenge. All the code is using 90% reaper API, so only 10% of the code really needs to be translated (statistics brought to you by my child brain!). I'll work on it today or tomorrow.

Anton9
02-11-2014, 10:32 PM
Man I'm having such a major brain fart.., how do I iterate through a track index to see which ones are selected and then mute them?

Argitoth
02-11-2014, 10:55 PM
CountSelectedTracks(ReaProject* proj)
GetSelectedTrack(ReaProject* proj, int seltrackidx)
SetMediaTrackInfo_Value(MediaTrack* tr, const char* parmname, double newvalue)


// create array of items
i = 0; loop(CountSelectedTracks(0), track_list[i] = GetSelectedtrack(0, i); i += 1;);

Anton9
02-11-2014, 11:54 PM
Right on! ...thank you Argitoth

Argitoth
02-12-2014, 12:16 AM
What is the syntax for color?


EEL: int AddProjectMarker2(ReaProject* proj, bool isrgn, pos, rgnend, "name", int wantidx, int color)

Returns the index of the created marker/region, or -1 on failure.
Supply wantidx>=0 if you want a particular index number,
but you'll get a different index number a region and wantidx is already in use.
color should be 0 or RGB(x,y,z)|0x1000000


my code:

AddProjectMarker2(0, 0, b, 0, "OVERLAP!", 0, 0x1ff0000); // blue instead of red
AddProjectMarker2(0, 0, b, 0, "OVERLAP!", 0, RGB(ff,0,0)); // throws error
AddProjectMarker2(0, 0, b, 0, "OVERLAP!", 0, RGB(255,0,0)); // throws error

I tried RGB(255,0,0) (255,0,0) 255,0,0 RGB(ff,0,0) (ff,0,0) ff,0,0

everything throws an error.

Tale
02-12-2014, 12:25 AM
I guess it is more or less the same as for gfx_clear, see:
http://www.reaper.fm/sdk/js/gfx.php#gfx_clear

Anton9
02-12-2014, 12:50 AM
If you want red it's AddProjectMarker2(0, 0, b, 0, "OVERLAP!", 0, 0x10000ff);
I think instead of being RGB it's BGR.

Argitoth
02-12-2014, 01:06 AM
how do you write an if, else if, else statement? is it like this?:

x > y ? (
a = b;
):( x == y ? (
a = c;
):(
a = d
);

IXix
02-12-2014, 03:20 AM
how do you write an if, else if, else statement? is it like this?...
Yes. I prefer to keep the conditions on separate lines but people have different tastes.

x > y ?
(
a = b;
)
: x == y ?
(
a = c;
)
:
(
a = d
);


Note that for simple things like the above, you can express it more concisely like this...

a = (x > y) ? b : (x == y) ? c : d;


Most of your questions in this thread have been about basic programming or API specifics which would be better placed in the JS/ReaScript forum..
Edit: Scratch that. I'm letting my grump get the better of me. Sorry.

Argitoth
02-12-2014, 07:45 AM
No, that's ok, you can let your grump out. I should have just tested it instead of asking, but it was late and I just wanted to ask before going to bed, lol. Thanks for presenting those differen syntaxes, it helps.

Argitoth
02-12-2014, 09:14 AM
I converted almost all code. The only thing left to convert is python's round() function and userinput stuff.

How do you mimick python's round() function in EEL? There's:

floor(x) -- rounds the value to the lowest integer possible (floor(3.9)==3, floor(-3.1)==-4).
ceil(x) -- rounds the value to the highest integer possible (ceil(3.1)==4, ceil(-3.9)==-3).

but, round() has number of decimals as an option instead of just whole numbers.


// Prepare Track 1 for DDP export
// removes all markers and regions!
//todo:
//-probably needs security from faulty user input (negatives, floats in the frames entries, alphabethics etc)

//clean this thing up!! Order of stuff at the start could be better, and I have some variable names used twice for different tasks (I believe).

framesize = 1.0 / 75.0;
TrackOne = GetTrack(0, 0); //The left 0 is "current project" (not clear on this), the right 0 means "the track with the lowest track number (track 1)"
ItemCount = CountTrackMediaItems(TrackOne);

ItemCount ==0 ? (
ShowMessageBox("No items on track 1", "Sorry", 0)
):(
//setting up the user input field
////names = "Pause at project start (sec):,Pause between tracks (sec):,Pregap on first track (frames):,Pregap on other tracks (frames):,Postgap on all tracks (frames):,Postgap on last track (frames):"
////defvalues = "2,2,30,9,1,60"; // default values
////maxreturnlen = 22; // one more than what you expect to get back
////nitems = len(defvalues.split(',')) // number of items in the dialog (6 in this case, but I left the counting in for future ease and because it's nice)
////Query = GetUserInputs('CD setup',nitems,names,defvalues,maxreturnlen); // call dialog and get result

////if Query[0] == 1: // user clicked OK
////////UserValues = Query[4].split(','); // the fourth item holds the input values
////////StartPauseSec = float(UserValues[0]);
////////TrackPauseSec = float(UserValues[1]);
////////FirstTrackStartSilenceSec = int(UserValues[2]) * framesize;
////////TrackStartSilence = int(UserValues[3]);
////////TrackStartSilenceSec = TrackStartSilence * framesize;
////////TrackEndSilence = int(UserValues[4]);
////////TrackEndSilenceSec = TrackEndSilence * framesize;
////////LastEndSilenceSec = int(UserValues[5]) * framesize;

// actual DDP routine starts here
Undo_BeginBlock();
//Remove all markers:
Marker = EnumProjectMarkers2(0, 0, 0, 0, 0, 0, 0); //(integer, proj, idx, is region, position, region end position, name, markerindexnumber)
IDX = Marker[0];
while (IDX > 0) (
is_Region = Marker[3];
MarkIdx = Marker[7]; // gets the last (seventh) item of the return array, the marker index number
DeleteProjectMarker( 0, MarkIdx, is_Region);
Marker = EnumProjectMarkers2(0, 0, 0, 0, 0, 0, 0);
IDX = Marker[0];
);
Main_OnCommand(40898, 0); // Renumbers marker ids in order (of course there are none at this point) Was the only workaround I know so far to get the undo behave nice.. If I delete this line, Undo has to be hit twice to get to the previous state.

Main_OnCommand(41238, 0); // save selection set 10
Main_OnCommand(40289, 0); // unselects all items

// unselect all tracks, select the first track. Just for convenience. Not necessary to make the script work:
TrackCount = CountTracks(0);
TrackIndex = 0;
while (TrackIndex < TrackCount) (
track = GetTrack(0, TrackIndex);
SetTrackSelected(track, 0);
TrackIndex += 1;
);
SetTrackSelected(TrackOne, 1);

StartMarker = StartPauseSec; // defaults to first track marker start time
StartOfNext = StartMarker + FirstTrackStartSilenceSec; // defaults the first item start time
ItemIndex = 0;
ItemNumber = 0;
CurrentItem = 0;
Message = 0;

AddProjectMarker(0, 0, 0, 8, "!", 0); //add a "!" marker at zero position

item = GetMediaItem(0, ItemIndex);
SetMediaItemInfo_Value(item, "B_UISEL", 1) ; // "physically" selects the first of the items.

while (ItemIndex < ItemCount) (
while (ItemNumber < ItemIndex) ( // this small inner loop does the "select 1st, 2nd, 3rd ... part. With each iteration of the outer loop, this loop will run through one more time - resulting in one item further right to be the selected one.
SetMediaItemInfo_Value(item, "B_UISEL", 0); //unselect the current item
item = GetMediaItem(0, ItemNumber + 1);
SetMediaItemInfo_Value(item, "B_UISEL", 1);
...TOO LONG TO POST ENTIRE CODE...

mwe
02-12-2014, 10:18 AM
How do you mimick python's round() function in EEL?

You can do this
function round(value,digits) local(y)
(
y = floor(value * pow(10, digits) + 0.5) / pow(10, digits);
);

I'm not sure it's the most efficient, I dug it up somewhere on the net to use in some JS code.

Argitoth
02-12-2014, 10:33 AM
Cool! Well, I just made a round() that expands upon floor() and ceil(). It basically decides whether to use one or the other. Is this more efficient than the above code (when not needing a decimal place function)?

EEL functions can have the same name, and EEL will know which function to used based on number of parameters, so our round functions can exist in the same script.


function round(number) local(a b)
(
b = abs(number);
a = ceil(b-1);
a = b - a;
a >= .5 ? b = ceil(b) : b = floor(b);
number < 0 ? number = -b : number = b;
number;
);

spk77
02-12-2014, 11:35 AM
First version of the "Item Inspector" posted to:

http://forum.cockos.com/showthread.php?t=135268

http://stash.reaper.fm/19763/eel%20item%20inspector5.gif

James HE
02-12-2014, 12:05 PM
faster would be to use bitwise operations, i think. (although floor and cieling are very fast too - just avoid the power operator if you can)

If you want to simply convert to integer (rounding down). just OR it and store it with 0.


x=3.14;
x|=0; //x=3

Argitoth
02-12-2014, 01:23 PM
Thanks James HE, you just solved a problem I had before I knew I had it!

Can you include functions form other scripts? I already asked this and then un-asked it, but can't figure it out actually...

Also, I created a sorting function that could sort one array and I'm sure I could expand on it to have it sort one array and apply what it does to a second array (to do a pseudo-multi-dimensional-array sort)... but one thing I use CONSTANTLY is sorting strings. I use it to reposition media items by name.

Any chance there will be a sort string array feature added? And maybe a number array sort while you're at it... whoever "you" refers to. Justin?

Justin
02-12-2014, 02:03 PM
You can use
@import filename.eel

to import the functions from a file.

Justin
02-12-2014, 02:04 PM
Is there any way to sort in EEL?


There's a set of list functions someone made for JS, I think I even made a QuickSort for it too... lemme try to find it

Lawrence
02-12-2014, 02:46 PM
Reading, lurking, thinking (while hopefully brain not exploding)....

Re: The colors and all that, can you define and use constants for all that so you can use color name text instead of the hex codes?

IXix
02-12-2014, 02:55 PM
There's a set of list functions someone made for JS, I think I even made a QuickSort for it too... lemme try to find it
That was me and yes you did :)

http://stash.reaper.fm/v/15072/ixlist.zip.zip

Argitoth
02-12-2014, 03:50 PM
How does Quicksort work when EEL does not have any definitive array lengths? How do I know it won't start sorting a nearby set of values? Are memory slots null until given a value? or actually 0? Edit: So if this is the case, then each array needs to be at least 1 null boundary away from one other. yes?

Oh and IXix (and Justin), thanks! I'm not sure I see a string sorting functions though.

Edit: oops, double posted. old post deleted.

Tale
02-12-2014, 03:57 PM
faster would be to use bitwise operations, i think. (although floor and cieling are very fast too - just avoid the power operator if you can)
The last time I checked |0 was indeed faster than floor(). Note for negative numbers |0 and floor() do different things.

rounding up, OR it and store it with 1

x=3.14;
x|=1; //x=4

That won't work, 3.14|1 == 3, not 4. The bitwise or operation converts 3.14 to an integer, rounding it down, no matter with what number you or it.

James HE
02-12-2014, 04:35 PM
The last time I checked |0 was indeed faster than floor(). Note for negative numbers |0 and floor() do different things.


That won't work, 3.14|1 == 3, not 4. The bitwise or operation converts 3.14 to an integer, rounding it down, no matter with what number you or it.

bah... I knew I was doing something wrong there.. ha ha...

yep, to round up just add 1 to it first.

Argitoth
02-12-2014, 05:47 PM
EEL: int SetTakeStretchMarker(MediaItem_Take* take, int idx, pos, unsupported srcposInOptional)


What does "unsupported" mean in this context? Does it mean I can't set original position differently from stretched position with EEL? Will that be added in the near future?

Argitoth
02-12-2014, 06:22 PM
I am somewhat disappointed by stretch marker API because I think it doesn't work.....
...............I don't think it's taking take start offset into account. :(

[animated gif removed, no longer relevant]


item = GetSelectedMediaItem(0,0);
take = GetActiveTake(item);

length = GetMediaItemInfo_Value(item, "D_LENGTH");
position = GetMediaItemInfo_Value(item,"D_POSITION");

//SetEditCurPos(position + offset, 0, 0);
insert = GetCursorPosition() - position;
SetTakeStretchMarker(take, -1, insert, insert);
UpdateArrange();
UpdateTimeline();
UpdateItemInProject(item);

Justin
02-12-2014, 06:48 PM
That was me and yes you did :)

http://stash.reaper.fm/v/15072/ixlist.zip.zip

<3 <3 <3

Justin
02-12-2014, 07:01 PM
Ah the "unsupported" thing will be fixed soon, as well as some related issues.

Re: take start offset -- yes, you should manually compensate for that.

Argitoth
02-12-2014, 07:05 PM
Ah the "unsupported" thing will be fixed soon, as well as some related issues.

Re: take start offset -- yes, you should manually compensate for that.

I tried! What would be the formula for this? :) Just can't seem to get it right.

Justin
02-12-2014, 07:26 PM
I tried! What would be the formula for this? :) Just can't seem to get it right.

Actually you should not need to look at the take start offset, just the item position (the position is in item coordinates). The EEL API for this is broken, related to the srcpos, though...

Tale
02-13-2014, 02:04 AM
bah... I knew I was doing something wrong there.. ha ha...

yep, to round up just add 1 to it first.
That still won't work like ceil(), because it will round up 3.0 to 4, while ceil() would leave 3.0 alone.

IXix
02-13-2014, 07:17 AM
IXix (and Justin), thanks! I'm not sure I see a string sorting functions though.
I don't think the existing sort functions will work for strings. They were implemented before strings became possible so they work based on numeric values. I might have to add dedicated string versions.

The list structure makes managing lists of things in JS/EEL memory easier. The syntax is a bit clunky and the documentation is thin but it works well. I use it in my perennially unfinished GUI library to maintain lists of control structures in memory, which would be a complete nightmare without it.

I should probably write a simple demo...

Argitoth
02-13-2014, 12:47 PM
I'm trying to split a comma-separated list, but I don't know if EEL can store arrays of strings. How do I store each item into a new variable?

i tried:
#string[x]
string[x]



#haystack = "this,that,theotherthing,one,two,thrice,A#3,B#4,A#2 ,56,57,12,14";

// get length of string
length = strlen(#haystack);

// what should we match?
#needle = ",";

// what is the length of character we need to see at once?
length2 = strlen(#needle);

i = 0;
loop(length,
strcpy_substr(#a,#haystack,i,length2);
i+=1;
match(#needle,#a) != 1 ? (
//ShowConsoleMsg(#a);
strcat(string[x],#a);
ShowConsoleMsg(#a)
):(
x+=1;
ShowConsoleMsg("\n");
);
);

Justin
02-13-2014, 05:31 PM
There isn't really a array-of-string syntax...

The magic values from 0 to 1023 are usable as strings, so you can do:

strcpy(57, "hello world");

a = 32+25;
gfx_printf("%s %s %s\n",a,57,58-1);


(you could also split a string and set parts of it to these strings, which would do what you want, but it is limited to 1024 slots for the whole plug-in).


Also, you can use an array of pointers to strings, such as:
myarray = 1000; // this is a normal EEL memory offset
myarray[0] = #myarray_1;
myarray[1] = #myarray_2;
myarray[2] = #myarray_3;
myarray[3] = #myarray_4;

i=0;
loop(4,
sprintf(myarray[i], "value is %d",i);
i+=1;
);

gfx_printf("%s", myarray[2]); // prints "value is 2"



EEL/JSFX is still a very numeric-oriented language, despite having some basic string functionality...

Argitoth
02-14-2014, 04:59 PM
I can't get this to work.


#names = "Pause at project start (sec):,"
"Pause between tracks (sec):,"
"Pregap on first track (frames):,"
"Pregap on other tracks (frames):,"
"Postgap on all tracks (frames):,"
"Postgap on last track (frames):";
#defvalues = "2,2,30,9,1,60";
GetUserInputs("CD setup",6,#names,#defvalues,22); // remove code in red


Error: D:\Portables\REAPER\Scripts\eeltester.eel:66: 'GetUserInputs' needs 4 parms: 'GetUserInputs("CD setup",6 <!> ,#names,#defvalues,22)'


Edit: Oh... I guess I can't read error messages. Clearly it needs 4 params...
Edit: Ok there's no maxlength in EEL version. oops!

Edit: How do you convert string to int? Got it!

// set x to a string
x = "5";
// match for floating point number in x and apply it to x
match("%f",x,x);
// convert x back to a string
ShowConsoleMsg(sprintf(#,"%f",x));


Why does this throw an error? :(
Edit: figured it out!

//remove the code in red

Framerem < 0.008 ? (
item_endframe = (FrameCount) * framesize;
):(
Framerem >= 0.5 ? (
item_endframe = (FrameCount) * framesize;
);
):(
item_endframe = (FrameCount + 1.0) * framesize;
);

Argitoth
02-17-2014, 03:19 AM
I made a script that compares media items, finds items that share the same name, and adds a number to the name. Basically like "increment file to avoid overwriting" except done with media items, nice to have before export.. But there's a weird thing about this code.

Why do I have to initialize strings to null for the script to work? I thought sprintf REAPER's "GetTakeName" function would overwrite the string completely, but it's not. Is this a bug?

// Increment duplicate names of selected items

/* by Elan Hickler
www.Soundemote.com
www.elanhickler.com */

items = CountSelectedMediaItems();
items !=0 ? (
PreventUIRefresh(1);
Undo_BeginBlock2(0);

i=0;loop(items, item[i] = GetSelectedMediaItem(0, i); i+=1;); // create item list

i=0;
while (i <= items) ( // loop through selected items
#nameA=""; // clear string before GetActiveTake
takeA = GetActiveTake(item[i]);
GetTakeName(#nameA, takeA);
x=1;
b=0;
loop(items-i, // loop through all items after current item
b+=1;
#nameB=""; // clear string before GetActiveTake
takeB = GetActiveTake(item[i+b]);
GetTakeName(#nameB, takeB);
strcmp(#nameA, #nameB) == 0 ? (
x+=1;
sprintf(#nameB, "%{#nameB}s_TAKE-%{x}i"); // *append duplicate number to name
GetSetMediaItemTakeInfo_String(takeB, "P_NAME", #nameB, 1);
item[i+b] = 0; // mark item processed
);
);
#nameC = #nameA; // remember old name for console message
sprintf(#nameA, "%{#nameA}s_TAKE-1");
GetSetMediaItemTakeInfo_String(takeA, "P_NAME", #nameA, 1); // *append _TAKE-1 first occurance of name
item[i]=0; // mark item processed
while (item[i] == 0 && i <= items) ( // check if item has been processed
ShowConsoleMsg(""); // clear console message
ShowConsoleMsg(sprintf(#temp, "comparing %i/%i items\n%s\n", i,items,#nameC));
i+=1;
);
);

Undo_EndBlock2(0, sprintf(#,"Increment duplicate names of %{items}i items"), -1);
PreventUIRefresh(-1);
);


// Remove 'Increment duplicate names' from selected items

/* by Elan Hickler
www.Soundemote.com
www.elanhickler.com */

items = CountSelectedMediaItems();
items !=0 ? (
PreventUIRefresh(1);
Undo_BeginBlock2(0);

i=0;
loop(items,
#nameA=""; // clear string
#nameB="";
item = GetSelectedMediaItem(0, i);
take = GetActiveTake(item);
GetTakeName(#nameA, take);
match("%S_TAKE-%i", #nameA, #nameB) == 1 ? (
GetSetMediaItemTakeInfo_String(take, "P_NAME", #nameB, 1);
x+=1; // for undo message
);
i+=1;
ShowConsoleMsg("");
ShowConsoleMsg(sprintf(#, "processing %{i}i/%{items}i\n%{#nameA}s"));
);

Undo_EndBlock2(0, sprintf(#,"Remove 'Increment duplicate names' from %{x}i items"), -1);
PreventUIRefresh(-1);
);

musicbynumbers
02-17-2014, 04:58 AM
^^^^^^^^^^^^^^^^^^^

Very useful script! thanks :) Will be using this one when it's done for sure :)

Teddy
02-17-2014, 08:01 AM
I have used python ReaScript to send OSC with the pyOSC library. It's been a nice addition to the OSC functionality already present in Reaper.

Would it be possible to somehow send OSC with eel ReaScript?

Argitoth
02-17-2014, 08:51 AM
^^^^^^^^^^^^^^^^^^^

Very useful script! thanks :) Will be using this one when it's done for sure :)

HEY THATS MINE!!! :(

spent hours on this to make it efficient... Edit: ...only because I suck at programming/logic/math, any decent programmer could have probably done this in much less time.

I could make this more useful, what else would you like it to do? I might be able to script it.

spk77
02-17-2014, 11:52 AM
I'm always using jnif's "undoable" (decorator?) function to make sure that RPR_Undo_EndBlock2 is executed even if a script fails at some point. I suppose this isn't possible with EEL currently?

from reaper_python import *
from contextlib import contextmanager

@contextmanager
def undoable(message):
RPR_Undo_BeginBlock2(0)
try:
yield
finally:
RPR_Undo_EndBlock2(0, message, -1)

def f():
# code here
pass

with undoable("Undo point text here"):
f()

...and to make sure that RPR_PreventUIRefresh(-1) is executed, I use:

@contextmanager
def noUIRefresh():
RPR_PreventUIRefresh(1)
try:
yield
finally:
RPR_PreventUIRefresh(-1)

timlloyd
02-17-2014, 12:20 PM
I'm going to be lazy and ask a question instead of trying to work it out ... :)

GetTrack() returns a pointer like this:

(MediaTrack*)0x0F1A5E00

(at least, this is what it looks like when printed to the reascript console using a python reascript)

So, if I want to save such a pointer with SetExtState() it seems necessary to convert the hex part to a string. How would I go about doing this with match()?

Here's the bones of a demo script -- at the moment, it's printing out only some of the hex value.


// loop over all tracks in project,
// convert hex from track pointer to
// string and print to the reascript console

// does not work

function test() (
i = 0;
loop(CountTracks(0),

pattern = "%x";
match(pattern, GetTrack(0, i), #tr);
string = sprintf(#, pattern, #tr);

ShowConsoleMsg(string);
ShowConsoleMsg("\n");

i += 1;
);
);

test();


cheeeeers :p

Argitoth
02-17-2014, 12:46 PM
play with this, it works. don't try to store a hexadecimal as a string!

Edit: it seems the track id is a combination of a string and a hexadecimal so you gotta do some sprintf and match magic.

*removed unhelpful code

timlloyd
02-17-2014, 01:05 PM
I don't think that works. It prints out:

(MediaTrack*)80000000

for me, even after removing all tracks and creating new ones which should have different pointers.

Btw, you're not 'matching' the string "(MediaTrack*)" in your script. You can put any string there and it will be prepended to the value returned for "%X".

Justin
02-17-2014, 01:10 PM
I'm going to be lazy and ask a question instead of trying to work it out ... :)

GetTrack() returns a pointer like this:

(MediaTrack*)0x0F1A5E00

(at least, this is what it looks like when printed to the reascript console using a python reascript)

So, if I want to save such a pointer with SetExtState() it seems necessary to convert the hex part to a string. How would I go about doing this with match()?

Here's the bones of a demo script -- at the moment, it's printing out only some of the hex value.


// loop over all tracks in project,
// convert hex from track pointer to
// string and print to the reascript console

// does not work

function test() (
i = 0;
loop(CountTracks(0),

pattern = "%x";
match(pattern, GetTrack(0, i), #tr);
string = sprintf(#, pattern, #tr);

ShowConsoleMsg(string);
ShowConsoleMsg("\n");

i += 1;
);
);

test();


cheeeeers :p


A couple of things here:

1) Python returns that string, but EEL does not (it returns the value directly).

2) You probably shouldn't save those pointers using SetExtState, they are not guaranteed to be valid for very long.

Justin
02-17-2014, 01:12 PM
tr = GetTrack(0, i);
match("(MediaTrack\*)%X", tr);
string = sprintf(#, "(MediaTrack\*)%X", tr);

That doesn't work -- tr is a pointer value and not a string, match() doesn't modify tr, and then you sprintf tr back into the string. Probably best not to put comments like "// works" in that code, it could be confusing to others... :)

timlloyd
02-17-2014, 01:29 PM
A couple of things here:

1) Python returns that string, but EEL does not (it returns the value directly).

OK, that's what I thought originally but couldn't get it to work. I get the error:

"strcpy: bad format specifier passed"

if it's passed in via reference:


tr = GetTrack(0, i);
SetExtState("bla", "more_bla", tr, 1);


or

"SetExtState: string parameter got invalid string index"


if it's passed in directly:


SetExtState("bla", "more_bla", GetTrack(0, i), 1);


That ^^ plus the fact that the API doesn't say that GetTrack() returns string value in Python when it actually does prompted the match() experiment :)

So if I did want to save the pointer with SetExtState, how would I do it?

2) You probably shouldn't save those pointers using SetExtState, they are not guaranteed to be valid for very long.

I think they may be valid for long enough for what I need them for. The py script(s) I was going to convert have been working as expected.

A bit more info would be interesting/helpful -- what operations will change a track's pointer, apart from reloading the project?

If they're really not suitable for saving beyond the life of a non-deferred script, can you give us the ability to work with GUIDs in Reascripts?

Edit -- Btw, it would be useful if ShowConsoleMsg() could take *any* type and output to the console, preferably also indicating the type.

Justin
02-17-2014, 03:17 PM
OK, that's what I thought originally but couldn't get it to work. I get the error:

"strcpy: bad format specifier passed"

if it's passed in via reference:


tr = GetTrack(0, i);
SetExtState("bla", "more_bla", tr, 1);


or

"SetExtState: string parameter got invalid string index"


if it's passed in directly:


SetExtState("bla", "more_bla", GetTrack(0, i), 1);


That ^^ plus the fact that the API doesn't say that GetTrack() returns string value in Python when it actually does prompted the match() experiment :)

So if I did want to save the pointer with SetExtState, how would I do it?



I think they may be valid for long enough for what I need them for. The py script(s) I was going to convert have been working as expected.

A bit more info would be interesting/helpful -- what operations will change a track's pointer, apart from reloading the project?

If they're really not suitable for saving beyond the life of a non-deferred script, can you give us the ability to work with GUIDs in Reascripts?



If you _REALLY_ want to store pointers in JS, you could use SetExtState("Bla","more blah", sprintf(#,"%.0f",tr)).

BUT -- I would definitely just use GUIDs and not worry about the pointers. EEL implements GUIDs as strings, so:


GetTrackGUID(#guid,GetTrack(0,i));
SetExtState("bla", "more_bla", #guid, 1);


Should do the trick! On the other end you'd just scan all tracks and stricmp() the GUIDs...



Edit -- Btw, it would be useful if ShowConsoleMsg() could take *any* type and output to the console, preferably also indicating the type.


With EEL anyway, things don't have a discrete type. I suppose it could say "value XYZ passed, if this was a string it would be "xyz", it is also a valid file handle blah blah blah"...

timlloyd
02-17-2014, 03:25 PM
BUT -- I would definitely just use GUIDs and not worry about the pointers. EEL implements GUIDs as strings, so:


GetTrackGUID(#guid,GetTrack(0,i));
SetExtState("bla", "more_bla", #guid, 1);


Should do the trick! On the other end you'd just scan all tracks and stricmp() the GUIDs...

Oh ... so it does :) Thanks.

mim
02-18-2014, 11:26 AM
I'm looking for a way to give to background scripts, values from outside of REAPER. (node.js)

One way I was thinking was to use OSC but is there a way to receive osc from background scripts ?
especially with eel, since it seems much faster ?

For now I was going to use beyond.python - with a (presumely useless) added OSC library, but this seems like a bigger setup than I'd like for the simple things I want REAPER to do. (show/hide envelopes or tracks)

Anyone is doing something similar ?

Teddy
02-18-2014, 12:40 PM
I'm looking for a way to give to background scripts, values from outside of REAPER. (node.js)

One way I was thinking was to use OSC but is there a way to receive osc from background scripts ?
especially with eel, since it seems much faster ?

For now I was going to use beyond.python - with a (presumely useless) added OSC library, but this seems like a bigger setup than I'd like for the simple things I want REAPER to do. (show/hide envelopes or tracks)

Anyone is doing something similar ?

I'm doing almost the exact same thing by
1. sending an OSC command to start a ReaScript.
2. The Reascript sends OSC back to node.js (using PyOSC). Something like /im/ready/for/action/ ;)
3. When Node.js receives this message it sends the values back to the script using OSC.

It's a real workaround, so I'm also looking for a simpler way. And while we're at the subject: wouldn't it be nice with a SendOSC( toDeviceInControllerList, action, value ) reascript action...?

Justin
02-18-2014, 12:40 PM
I'm looking for a way to give to background scripts, values from outside of REAPER. (node.js)

One way I was thinking was to use OSC but is there a way to receive osc from background scripts ?
especially with eel, since it seems much faster ?

For now I was going to use beyond.python - with a (presumely useless) added OSC library, but this seems like a bigger setup than I'd like for the simple things I want REAPER to do. (show/hide envelopes or tracks)

Anyone is doing something similar ?

You can do things using Python or EEL, such as listen on a TCP socket for commands... in EEL, you can use tcp_listen() etc for this purpose.

Argitoth
02-18-2014, 01:08 PM
I find myself creating a lot of scripts that work depending on item index, but the item index is returned as such:

by track by position
*all items in first track is first in the index, and then in order of position

What I need is this:

by position by track
*all items appearing first in project is first, and then in order of track in cases where items have the same position.

I tried using a sort function:

function sort(array, length) local(index i array value length)
(//insertionsort
index = 1;
loop(length,
value = array[index];
i = index - 1;
while (i >= 0)(
value < array[i] ? (
array[i+1] = array[i];
array[i] = value;
i = i - 1;
):(i = -1;);
);
index +=1;
);
);


I then expanded the code to be this:

function sort(arrayA, arrayB, length) local(index i value)
(//insertionsort
index = 1;
loop(length,
valueA = arrayA[index];
valueB = arrayB[index];
i = index - 1;
while (i >= 0)(
valueA < arrayA[i] ? (
arrayA[i+1] = arrayA[i];
arrayA[i] = valueA;
arrayB[i+1] = arrayB[i];
arrayB[i] = valueB;
i = i - 1;
):(i = -1;);
);
index +=1;
);
);


i made sure to offset my arrays

item_list = items * 0;
position_list = items * 1;


but the problem is the arrays end up all messed up

KEY:
position float (arrayA) - item id hexadecimal [ item id float ] (arrayB)

unsorted
------
sorted

green = missing from sorted

16.000000 - 62AB720 [ 103462688.000000 ] <-- track 1 starts here
20.054912 - 64004C0 [ 104858816.000000 ]
24.879104 - 63FFBC0 [ 104856512.000000 ]
26.727807 - 64001C0 [ 104858048.000000 ]
43.229856 - 62AA520 [ 103458080.000000 ]
47.583749 - 64007C0 [ 104859584.000000 ]
51.453876 - 63FD7C0 [ 104847296.000000 ]
18.624128 - 6400AC0 [ 104860352.000000 ] <-- track 2 starts here
22.879104 - 63FE6C0 [ 104851136.000000 ]
25.865079 - 63FF8C0 [ 104855744.000000 ]
41.229856 - 63FE0C0 [ 104849600.000000 ]
46.132451 - 63FECC0 [ 104852672.000000 ]
49.357557 - 64010C0 [ 104861888.000000 ]
-------
0.000000 - 64010C0
104847296.000000 - 64010C0
18.624128 - 64010C0
20.054912 - 64010C0
22.879104 - 64010C0
24.879104 - 64010C0
25.865079 - 64010C0
26.727807 - 64010C0
41.229856 - 64010C0
43.229856 - 64010C0
46.132451 - 64010C0
47.583749 - 64010C0
49.357557 - 64010C0


Where am I going wrong? It's gotta be a memory management / array offset problem, right?

WyattRice
02-18-2014, 08:37 PM
I know this is probably a long shot, but sometime in the future would it be possible for eel to run a cmd line program like cdrecord, and redirect the standard out into a textbox?

timlloyd
02-19-2014, 12:34 AM
I know this is probably a long shot, but sometime in the future would it be possible for eel to run a cmd line program like cdrecord, and redirect the standard out into a textbox?

For reference, we can already do that in Python Reascripts with something like this:


args = shlex.split("command as a string")
output, error = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE).communicate()


Then you might need to call decode("utf-8") on output or error.

Seems like if you really wanted, you could send the command from EEL over TCP with tcp_send() and listen for the result? Thought I'm not 100% of how to go about that on the other end without involving Python again ...

Anton9
02-19-2014, 10:26 AM
You can do things using Python or EEL, such as listen on a TCP socket for commands... in EEL, you can use tcp_listen() etc for this purpose.

Hey Justin,

If you get a spare moment could you maybe post an example of this?
Say I have another program sending an OSC message on 127.0.0.1 port 8008 "/update/value 0.5" and I want the EEL script to continuously store just the changing value into the variable 'x'.

Thank you :)

fingers
02-20-2014, 04:07 AM
Possible interesting use of deferred.


from reaper_python import *
from wsgiref.simple_server import make_server
from cgi import parse_qs, escape

def hello_reaper(environ, start_response):
parameters = parse_qs(environ.get('QUERY_STRING', ''))
if 'insert_track' in parameters:
start_response('200 OK', [('Content-Type', 'text/html')])
index = int(parameters['insert_track'][0])
RPR_InsertTrackAtIndex(index, True)
RPR_TrackList_AdjustWindows(True)
return ["Track added at %s\n" % (index)]
start_response('200 OK', [('Content-Type', 'text/html')])
return [""]

# start a http server to run our wsgi app
httpd = make_server('', 8000, hello_reaper)
# tweak for best performance
httpd.timeout = 0.1

# deferred loop
def http_serve():
httpd.handle_request()
RPR_defer("http_serve()")

http_serve()


Now click on http://localhost:8000/?insert_track=0

rstockm
03-01-2014, 03:49 AM
Possible interesting use of deferred.


from reaper_python import *
from wsgiref.simple_server import make_server
from cgi import parse_qs, escape

def hello_reaper(environ, start_response):
parameters = parse_qs(environ.get('QUERY_STRING', ''))
if 'insert_track' in parameters:
start_response('200 OK', [('Content-Type', 'text/html')])
index = int(parameters['insert_track'][0])
RPR_InsertTrackAtIndex(index, True)
RPR_TrackList_AdjustWindows(True)
return ["Track added at %s\n" % (index)]
start_response('200 OK', [('Content-Type', 'text/html')])
return [""]

# start a http server to run our wsgi app
httpd = make_server('', 8000, hello_reaper)
# tweak for best performance
httpd.timeout = 0.1

# deferred loop
def http_serve():
httpd.handle_request()
RPR_defer("http_serve()")

http_serve()


Now click on http://localhost:8000/?insert_track=0

OMFG.
O_O
\o/

Argitoth
03-25-2014, 06:04 PM
I'm always using jnif's "undoable" (decorator?) function to make sure that RPR_Undo_EndBlock2 is executed even if a script fails at some point. I suppose this isn't possible with EEL currently?

from reaper_python import *
from contextlib import contextmanager

@contextmanager
def undoable(message):
RPR_Undo_BeginBlock2(0)
try:
yield
finally:
RPR_Undo_EndBlock2(0, message, -1)

def f():
# code here
pass

with undoable("Undo point text here"):
f()

...and to make sure that RPR_PreventUIRefresh(-1) is executed, I use:

@contextmanager
def noUIRefresh():
RPR_PreventUIRefresh(1)
try:
yield
finally:
RPR_PreventUIRefresh(-1)

bumb/seconded

Breeder
03-26-2014, 12:17 PM
bumb/seconded

eel doesn't have exceptions so I don't know why would you need that :D

to be honest, I find those contextmanagers over the top anyway...if script is working all right (and you're not debugging it anymore), all it does is spend resources and increase interpretation time

Lazarus
03-29-2014, 06:56 PM
Is or could there be any way of managing windows? For example the window position doesn't appear to be managed automatically at the moment but being able to set window size/position might be nicer. Having an always on top pin and the ability to create the window as frameless ala floating toolbars would be ideal.

Viente
04-14-2014, 09:09 PM
So can we use SNM functions with EEL? :)

spk77
04-18-2014, 02:08 AM
This is from sws_util.cpp:

void* GetConfigVar(const char* cVar)
{
int sztmp;
void* p = NULL;
if (int iOffset = projectconfig_var_getoffs(cVar, &sztmp))
{
p = projectconfig_var_addr(EnumProjects(-1, NULL, 0), iOffset);
}
else if (p = get_config_var(cVar, &sztmp))
{
}
else
{
p = get_midi_config_var(cVar, &sztmp);
}
return p;
}

I'm not sure how to use these functions, but f.ex. projectconfig_var_getoffs() in EEL always returns "0".

ohmyreaper
12-18-2014, 11:15 PM
Hi all,

Could anyone point me to the official EEL manual where I can read about the flow controls (if, for, repeat, while, etc.), how to write functions, standard fuctions, etc etc?

This is the Python equivalent of what I'm looking for:
http://docs.python.org/

Thanks!

Argitoth
12-18-2014, 11:40 PM
http://www.reaper.fm/sdk/js/js.php

in REAPER --> Help --> Reascript Documentation