Old 10-11-2019, 10:05 AM   #1
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default Need ideas for a Lua wrapper around the Reaper API.

I'm interested in writing an intuitive Lua wrapper around the Reaper API. I've been toying around with ideas of a good way to do that.

The first thing I tried was an OOP approach where I began wrapping Reaper pointers inside objects that stored info about the state and methods to manipulate that state. I started running into problems with circular requires, for example: a media item (which should know about the track that owns it) can be created from the selection, except the track that owns it can have media items added to it so it knows about media items as well.

Basically, I'm not so good with OOP yet so this approach fell apart pretty fast.

The other idea I had was to wrap API functions in closures that keep track of the state. The idea is you could call getSelectedMediaItems(), and the first time you call it, since the data does not exist yet, it will get that data from the Reaper API. Any future calls to the function would simply reference the data stored in the closure. If you need to refresh the data and call the Reaper API again, you can simply pass in a 'refresh' parameter to the function.

The problem is, I can't seem to get this to work right. See the example below:

Code:
local Alk = {}

local function apiFn(fn, refresh)
    local state = nil
    return function()
        if state == nil or refresh then
            state = fn()
        end
        return state
    end
end

-- This is the idea, but it doesn't work.
-- I don't know how to pass refresh into the function.
Alk.getSelectedMediaItems = apiFn(function()
    local items = {}
    for i = 1, reaper.CountSelectedMediaItems(0) do
        table.insert(items, reaper.GetSelectedMediaItem(0, i - 1))
    end
    return items
end,
refresh)

-- This ends up returning a function, which kind of defeats the purpose
-- since you end up having to imperatively assign it to a value later.
--function Alk.getSelectedMediaItems(refresh)
--    return apiFn(function()
--        local items = {}
--        for i = 1, reaper.CountSelectedMediaItems(0) do
--            table.insert(items, reaper.GetSelectedMediaItem(0, i - 1))
--        end
--        return items
--    end,
--    refresh)
--end

-- If you return a resolved function, the closure doesn't preserve
-- the data properly across calls.
--function Alk.getSelectedMediaItems(refresh)
--    return apiFn(function()
--        local items = {}
--        for i = 1, reaper.CountSelectedMediaItems(0) do
--            table.insert(items, reaper.GetSelectedMediaItem(0, i - 1))
--        end
--        return items
--    end,
--    refresh)()
--end

return Alk
If you have any ideas to help me out I would appreciate it!
Alkamist is offline   Reply With Quote
Old 10-11-2019, 10:41 AM   #2
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

It's definitely a tricky problem.

- There's nothing inherently wrong with a circular class structure (item -> parent track -> child items) as long as you know that there will be circular structures to account for - GraphQL, for instance, has them all over the place since it lets you traverse a relational database.

I had the same issue with the upcoming release of my GUI, since it does the same thing for managing elements. An element has a pointer to the layer (think Photoshop) it's on, and layers have a table of pointers to their child elements. If you try to deep-copy a layer or element, or print all of its contents, it will freeze Reaper when it gets stuck in that circle.

My solution has been to add a specific boolean flag at any point in a structure where it would be problematic to keep "digging", and then have my copying and stringifying code check for that before going any further. So far it works pretty well.

- Xenakios had a proof-of-concept for a class-based approach with metatables, etc. that was pretty cool - he had methods on the item object that mapped to the appropriate reaper.Get/SetMediaItemInfo_Value call. I can't find the thread right now, and I don't remember why he gave up on it aside from it being a lot of work.

- A simpler approach that can take care of many uses cases is to wrap Reaper's types in Lua iterators. I have an example in the ReaScript template repo and I'm planning to include that in v3 of my GUI as well.

Part of the problem is that Lua isn't inherently an OOP language, so it can be a bit tricky to implement something like this.

If you're interested in stuff like this I would love to have some help extending the "helpers for Reaper stuff" aspect of my library. I've got a number of ideas already mapped out, just haven't had time to get to them.
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-11-2019, 01:28 PM   #3
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by Lokasenna View Post
It's definitely a tricky problem.

- There's nothing inherently wrong with a circular class structure...
Thanks for the info!

I think what I was going for in my OOP approach had promise. Basically, you can construct an item from a Reaper pointer and there would be methods associated with any Reaper API calls pertaining to that type of pointer. When you call a method that needs data from the Reaper API, it will call the Reaper API the first time to get the data and store it in a member. Any future calls to the method will return that member instead of calling the API again, unless you explicitly tell it to refresh the member.

My end goal was essentially to trim down the amount of imperative code I would have to write when dealing with Reaper stuff. I'd like to just define an item and call members on it instead of having to always call the Reaper API a ton and store data whenever I need it.

I've got all of that worked out, it's just the circular require thing is really bumming me out. The problem comes when you need two classes to manufacture each other in some way. How do you usually go about dealing with that in Lua?

Quote:
Originally Posted by Lokasenna View Post
If you're interested in stuff like this I would love to have some help extending the "helpers for Reaper stuff" aspect of my library. I've got a number of ideas already mapped out, just haven't had time to get to them.
Well if I ever follow through with this Lua wrapper thing and it is actually helpful, I certainly don't mind you including it or using the ideas from it in your library.
Alkamist is offline   Reply With Quote
Old 10-11-2019, 01:45 PM   #4
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

Quote:
Originally Posted by Alkamist View Post
I've got all of that worked out, it's just the circular require thing is really bumming me out. The problem comes when you need two classes to manufacture each other in some way. How do you usually go about dealing with that in Lua?
Hard to say without seeing an example, but as a first step I would try moving all of the construction to a separate place that all of them can use to request a new X. This could be a "registry" that takes in the raw pointer from Reaper, uses the API to figure out the type (MediaItem, MediaTrack...), and instantiates a new instance of the appropriate Lua wrapper. It could also use tostring(pointerFromReaper) to memoize the instances and return the same one anytime you ask for that object.

I would also suggest having your wrappers use the API every time a script needs to access something on that item rather than caching the data, since someone using your library might do something that affects those items without going through your wrapper and you have no way of knowing.

Quote:
Well if I ever follow through with this Lua wrapper thing and it is actually helpful, I certainly don't mind you including it or using the ideas from it in your library.
As an incentive, my library also has testing tools that might make your life easier.
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-11-2019, 01:47 PM   #5
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

Some reading material on resolving circular dependencies:
https://softwareengineering.stackexc...lar-dependency
https://medium.com/@mearns.b/circula...n-403b790daebb
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-11-2019, 01:55 PM   #6
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

cfillion found the thread I mentioned: https://forum.cockos.com/showthread.php?t=176838
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-11-2019, 02:24 PM   #7
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by Lokasenna View Post
Hard to say without seeing an example, but as a first step I would try moving all of the construction to a separate place that all of them can use to request a new X. This could be a "registry" that takes in the raw pointer from Reaper, uses the API to figure out the type (MediaItem, MediaTrack...), and instantiates a new instance of the appropriate Lua wrapper. It could also use tostring(pointerFromReaper) to memoize the instances and return the same one anytime you ask for that object.
I started coming to a conclusion along these lines upon further thinking. I know globals are usually bad but I wonder if that registry should be a global that comes with the library or something. Otherwise you would probably have to pass it along to everything. I haven't thought too hard about it yet though.

Quote:
Originally Posted by Lokasenna View Post
I would also suggest having your wrappers use the API every time a script needs to access something on that item rather than caching the data, since someone using your library might do something that affects those items without going through your wrapper and you have no way of knowing.
My idea behind caching the data was that anywhere you wanted to be sure that you were getting the most up to date info (probably in the beginning of a function call) you would call the member function with a 'refresh' parameter to ensure it calls the API. If you are certain that a part of your code will be fine with using the cached data (further along in a function where you previously already refreshed the data), then you can just not refresh it to save calling the API.

Not sure if this is even worth it though. I haven't really benchmarked how fast calling the API is, I just assumed that it would be better to check cached data when you can instead of calling it repeatedly.
Alkamist is offline   Reply With Quote
Old 10-11-2019, 02:40 PM   #8
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by Lokasenna View Post
cfillion found the thread I mentioned: https://forum.cockos.com/showthread.php?t=176838
That looks interesting. I'll have to look over it and see if I can take some ideas from it and implement them.

I like the operator overloading stuff. I've tended to stay away from that in Lua since it's so strange compared to other languages, but I should probably get more familiar with it.
Alkamist is offline   Reply With Quote
Old 10-11-2019, 02:40 PM   #9
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

Quote:
Originally Posted by Alkamist View Post
I started coming to a conclusion along these lines upon further thinking. I know globals are usually bad but I wonder if that registry should be a global that comes with the library or something. Otherwise you would probably have to pass it along to everything. I haven't thought too hard about it yet though.
The big problem with globals is that any part of the code can modify things or peek at internal values whenever it wants, and so every other part of the code needs to not break when that happens. It also means that when you have a bug because a global got into an invalid state, you might have to go looking through half of your codebase to find all of the places that access the global - it may not necessarily have been the last function that touched it.

Using a registry avoids this because all interaction with the "global" has to go through a very specific, limited interface. Only the registry itself gets to handle the instantiation, tracking, and handing out of class instances, and the rest of the app has no access to any of it. In this case, the other parts of your code only need to know that a) a registry exists, b) they can give it pointers, and c) it will return a valid wrapper for it.
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-11-2019, 02:52 PM   #10
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

I use the same idea to manage a script's available graphics buffers in my library:
https://github.com/jalovatt/scythe/b...lic/buffer.lua

There's no way to actually prevent a script from doing whatever it wants with the buffers, since they're just numbers, but if a script only uses the Buffer module to do so it can't possibly run into trouble.
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-11-2019, 03:10 PM   #11
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by Lokasenna View Post
The big problem with globals is that any part of the code can modify things or peek at internal values whenever it wants, and so every other part of the code needs to not break when that happens. It also means that when you have a bug because a global got into an invalid state, you might have to go looking through half of your codebase to find all of the places that access the global - it may not necessarily have been the last function that touched it.

Using a registry avoids this because all interaction with the "global" has to go through a very specific, limited interface. Only the registry itself gets to handle the instantiation, tracking, and handing out of class instances, and the rest of the app has no access to any of it. In this case, the other parts of your code only need to know that a) a registry exists, b) they can give it pointers, and c) it will return a valid wrapper for it.
So essentially you pass around the registry and get class instances from it? I suppose the registry would stem from the given project, since it would own most of the information.

I guess what would be a good open question to anyone regarding this whole API wrapper thing is: what is the ideal syntax you can think of to access it? If a good example can be thought of and it is plausible to recreate, I think it would be a good goal to strive toward.

I think Xenakios' idea was pretty good. Are there any improvements you can think of?

Maybe something like:

Code:
local currentProject = Rpr.getProject(0)

for _, item in ipairs(currentProject.selectedItems) do
    item.pitch = 24.0 * math.random() - 12.0 
end
Quote:
Originally Posted by Lokasenna View Post
I use the same idea to manage a script's available graphics buffers in my library:
https://github.com/jalovatt/scythe/b...lic/buffer.lua

There's no way to actually prevent a script from doing whatever it wants with the buffers, since they're just numbers, but if a script only uses the Buffer module to do so it can't possibly run into trouble.
I'll take a look, thanks!
Alkamist is offline   Reply With Quote
Old 10-12-2019, 08:14 AM   #12
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Should Reaper pointer wrappers validate their pointers on their own?

So say you call 'item.length = 5' after you shoved the wrong kind of pointer into the wrapper, should I let the program crash or just have the wrapper detect it and not run the function?
Alkamist is offline   Reply With Quote
Old 10-12-2019, 08:46 AM   #13
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

Have the registry validate it when you first pass the pointer in. You can use:
Code:
boolean reaper.ValidatePtr2(ReaProject proj, identifier pointer, string ctypename)

Return true if the pointer is a valid object of the right type in proj (proj is ignored if pointer is itself a project). Supported types are: ReaProject*, MediaTrack*, MediaItem*, MediaItem_Take*, TrackEnvelope* and PCM_source*.
The registry can run through the list of types until it finds a match and then instantiate the appropriate wrapper.

It won't prevent people from making stupid mistakes though:
Code:
local tr = reaper.GetTrack(0, 0)
local wrappedItem = instantiateFancyWrapper(tr)
local value = wrappedItem.getSomeItemValue -- will be nil
You could instead let them specify the type they're expecting and validate against that:
Code:
local tr = reaper.GetTrack(0, 0)
local wrappedItem = instantiateFancyWrapper('item', tr)
-- throw an error telling the user they're an idiot
This would also have a small performance benefit because you wouldn't have to call ValidatePtr2 multiple times.
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-12-2019, 08:53 AM   #14
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

Another option would be to have the registry provide functions that get the track/item/whatever themselves:
Code:
local wrappedTrack = getWrappedTrack(projNumber, trNumber)
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-12-2019, 09:15 AM   #15
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by Lokasenna View Post
Have the registry validate it when you first pass the pointer in.
I like that idea. It's better to get rid of the problem as soon as possible instead of trying to prevent it on every method call.
Alkamist is offline   Reply With Quote
Old 10-12-2019, 03:34 PM   #16
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Is there a good way to get the name of the type of Reaper pointer something is without just knowing beforehand or running through a bunch of ValidatePtr calls to try to figure it out?
Alkamist is offline   Reply With Quote
Old 10-12-2019, 08:22 PM   #17
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

Not in the current API. I think I requested reaper.getTypeFromPointer once and never got a response.
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-13-2019, 07:03 AM   #18
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by Lokasenna View Post
Not in the current API. I think I requested reaper.getTypeFromPointer once and never got a response.
Ah that's a shame. Not sure I'll need it anyway.

It is coming along nicely though. I've achieved a really clean looking syntax.

One thing that I would maybe like to figure out though is how to have a sense of uniqueness among pointer wrappers. So currently you can have this 'factory' hand out wrappers around pointers, that's the only way to get a wrapper. The wrappers can manufacture other wrappers by simply having a pointer to the factory without knowing what it actually is. That's how I stay away from that circular require problem. The thing is, if you happen to arrive at the same pointer by different means (for example, getting a media item by selected items and items in the project), the wrappers you get will be two different tables around the same pointer.

I wonder if there is an elegant way to always get the same table when arriving at the same pointer.

It is also currently possible with what I've written to infinitely chain dots. I think that's what you were talking about with your GFX library.

Like currently this is possible:

Code:
track.item.track.item.track.item
Another concern I have is how is a wrapper supposed to know when a pointer has been deleted out from under it? Do I need to have some sort of garbage collection function that runs during GUI scripts to check?
Alkamist is offline   Reply With Quote
Old 10-13-2019, 08:34 AM   #19
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

Quote:
Originally Posted by Alkamist View Post
I wonder if there is an elegant way to always get the same table when arriving at the same pointer.
Memoization is the computery term for remembering some previous output when given the same input again. It's great for intense calculations and also for something like this.

Code:
local function newWrapper(ptr)
  return { myPointer = ptr }
end

local function memoize(func)
  -- The returned function will always have access to this
  local previous = {}

  -- This will only memoize based on the first argument to a function
  -- You can do it with more if you want i.e. key = tostring(arg1)..tostring(arg2)
  return function(arg)
    local key = tostring(arg)

    if previous[key] then return previous[key] end

    local new = func(arg)
    previous[key] = new

    return new
  end
end

local memoizedNewWrapper = memoize(newWrapper)

local wrapper1 = memoizedNewWrapper(12345)
local wrapper2 = memoizedNewWrapper(67890)
local otherWrapper1 = memoizedNewWrapper(12345)

reaper.ShowConsoleMsg("same wrapper? " .. tostring(otherWrapper1 == wrapper1))
It occurs to me that this might not play nicely with the way Lua passes self as an extra argument with method calls. Not sure though.

Quote:
Like currently this is possible:

Code:
track.item.track.item.track.item
As I had mentioned above, there's nothing wrong that as long as you make sure it can't get into an infinite loop. This function works through a table, printing out the contents and recursively digging into any child tables. I add .__noRecursion = true to my elements and layers, so if I try to print a layer the output stops before it can get into trouble:
Code:
name = Layer3
elements = table:
  my_btn2 = table:
  my_picker = table:
  my_mnu = table:
  my_frm = table:
  my_lbl = table:
  my_knob = table:
  my_txt = table:
Quote:
Another concern I have is how is a wrapper supposed to know when a pointer has been deleted out from under it? Do I need to have some sort of garbage collection function that runs during GUI scripts to check?
You could, but IMO it wouldn't be worth the time unless you had code you wanted to run if a pointer happens to go missing. It's largely a question of whether you want to assume that users are only accessing Reaper items through your wrapper, or if you want to worry about things happening outside your control.

Wrappers' methods could validate their pointer every single time you call them, depending on how much overhead this creates. For user-friendliness this could even extend to a trackWrapper:validate method that checks itself and any child wrappers it has for items, if you wanted.
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-13-2019, 09:05 AM   #20
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by Lokasenna View Post
Memoization is the computery term for remembering some previous output when given the same input again. It's great for intense calculations and also for something like this.
Thanks for the advice! I'll try to study what you posted and see if I can figure it out.

Quote:
Originally Posted by Lokasenna View Post
As I had mentioned above, there's nothing wrong that as long as you make sure it can't get into an infinite loop. This function works through a table, printing out the contents and recursively digging into any child tables. I add .__noRecursion = true to my elements and layers, so if I try to print a layer the output stops before it can get into trouble:
I'll experiment with some things and see if I can get something working.

Quote:
Originally Posted by Lokasenna View Post
You could, but IMO it wouldn't be worth the time unless you had code you wanted to run if a pointer happens to go missing. It's largely a question of whether you want to assume that users are only accessing Reaper items through your wrapper, or if you want to worry about things happening outside your control.

Wrappers' methods could validate their pointer every single time you call them, depending on how much overhead this creates. For user-friendliness this could even extend to a trackWrapper:validate method that checks itself and any child wrappers it has for items, if you wanted.
Well the example I'm thinking of is this pitch correction GUI script I'm working on. It keeps track of media items and does pitch corrections on them. There's nothing stopping the user from deleting a media item in Reaper while the script is running, and there is no way for me to know that a user did that without constantly checking every frame I think.

I suppose the wrapper could validate itself on method calls, but that still won't tell the GUI to update the fact that an item was deleted until you attempt to call one of these methods.
Alkamist is offline   Reply With Quote
Old 10-13-2019, 09:35 AM   #21
Lokasenna
Human being with feelings
 
Lokasenna's Avatar
 
Join Date: Sep 2008
Location: Calgary, AB, Canada
Posts: 6,551
Default

Quote:
Originally Posted by Alkamist View Post
I suppose the wrapper could validate itself on method calls, but that still won't tell the GUI to update the fact that an item was deleted until you attempt to call one of these methods.
You could poll reaper.CountMediaItems(proj) every X loops or milliseconds and watch for it to change, then validate all of your wrappers.
__________________
I'm no longer using Reaper or working on scripts for it. Sorry. :(
Default 5.0 Nitpicky Edition / GUI library for Lua scripts / Theory Helper / Radial Menu / Donate
Lokasenna is offline   Reply With Quote
Old 10-13-2019, 09:54 AM   #22
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by Lokasenna View Post
You could poll reaper.CountMediaItems(proj) every X loops or milliseconds and watch for it to change, then validate all of your wrappers.
That sounds like a good idea. It have very little overhead I would think. Thanks for the idea!
Alkamist is offline   Reply With Quote
Old 10-13-2019, 11:02 AM   #23
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

There's something confusing I've come across and I don't know if it's just from me not seeing something or what.

In the ValidatePtr2 description, there seems to exist a pointer type of "ReaProject*". When you call functions that reference a project you seem to do it by project index instead of by pointer. Is there actually some way to get a pointer to a project?
Alkamist is offline   Reply With Quote
Old 10-13-2019, 11:08 AM   #24
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Only EnumProjects takes a project index (and returns a ReaProject*). The other functions take a ReaProject* (0 = null pointer = current project).
cfillion is offline   Reply With Quote
Old 10-13-2019, 11:30 AM   #25
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 506
Default

Quote:
Originally Posted by cfillion View Post
Only EnumProjects takes a project index (and returns a ReaProject*). The other functions take a ReaProject* (0 = null pointer = current project).
Ohhh, that makes sense. I had never actually done anything in ReaScript regarding a project other than the current one, so for some reason I assumed that passing numbers other than 0 to API functions that want a project would use the index.

Thanks for the info though, that cleared it up.
Alkamist is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 06:58 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.