Old 05-03-2021, 08:34 PM   #1
thecargocult
Human being with feelings
 
Join Date: Apr 2021
Posts: 9
Default Looping in DLLs

Please excuse me if the question is ridiculous - it's a little hard to find info on how the reaper extension system works and how to get started.

I have a basic reaper plugin working (C++, dylib/DLL) , but now want to run a timer loop, in which I'll check the filesystem for new audio files and then spot them to Reaper tracks.

I’ve been using a “controller” class, one of which I instantiate in main.cpp. This creates a loop inside a thread and is supposed to bail out when the object is being destroyed.
There are a couple of problems:

1. REAPER intermittently crashes after I call InsertMedia - the stack usually shows that it’s trying to create the reapeaks files.
I’m guessing InsertMedia can’t safely be called from a background thread. Does that ring a bell to anyone?

2. On windows the controller object is never destroyed so can’t clean up - resulting in hung REAPER processes hanging around causing trouble.
So how do these dylib/DLLs get loaded into REAPER?
How might I safely create/destroy an object and also have it monitor the OS while it’s alive?

I’ve tried a loop inside a lambda launched with std::async, = lots of crashing in InsertMedia
… and I’ve tried using JUCE with a juce::Timer object = fail on Windows due to messageManager never working to produce callbacks on the timer object.
… and I’ve tried triggering the InserMedia via JUCE InterprocessCommunication instead of a perpetual loop = fail due to messageManager issues + impossibility of closing namedPipe since my controller object never gets it’s destructor called.

main.cpp:

#define REAPERAPI_IMPLEMENT
#include "reaper_plugin_functions.h"

#include "NVCR_Controller.hpp"

NVCR_Controller controller; //timer or IPC pipe created here

extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT( REAPER_PLUGIN_HINSTANCE instance, reaper_plugin_info_t *rec)
{
if(!rec) { return 0; }
if(rec->caller_version != REAPER_PLUGIN_VERSION) return 0;

InsertMedia = (decltype(InsertMedia))rec->GetFunc("InsertMedia");

return 1;
}
thecargocult is offline   Reply With Quote
Old 05-03-2021, 09:00 PM   #2
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 3,551
Default

Quote:
Originally Posted by thecargocult View Post
1. REAPER intermittently crashes after I call InsertMedia - the stack usually shows that it’s trying to create the reapeaks files.
I’m guessing InsertMedia can’t safely be called from a background thread. Does that ring a bell to anyone?
The API is only safe to use from the main thread (except for the few functions that are explicitly marked as thread-safe).

The worker thread can notify the main thread when it's time to insert the new files (eg. post a message to a window's event queue or have the main thread poll some variable using a timer in a thread-safe way).

Quote:
Originally Posted by thecargocult View Post
2. On windows the controller object is never destroyed so can’t clean up - resulting in hung REAPER processes hanging around causing trouble.
So how do these dylib/DLLs get loaded into REAPER?
The extension's entry point is called again with a null reaper_plugin_info_t* when it's time to unload.

Last edited by cfillion; 05-03-2021 at 11:20 PM.
cfillion is offline   Reply With Quote
Old 05-03-2021, 10:20 PM   #3
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 10,597
Default

Just for case of clarity:
What exactly do you mean by DLL ?

A "standard" Reaper extension or a standard plugin (such as VST and then which kind ? ), even something else ?
-Michael
mschnell is offline   Reply With Quote
Old 05-03-2021, 10:23 PM   #4
thecargocult
Human being with feelings
 
Join Date: Apr 2021
Posts: 9
Default

Quote:
Originally Posted by cfillion View Post
The worker thread can notify the main thread when it's time to insert the new files (eg. post a message to a window's event queue or have the main thread poll some variable using a timer in a thread-safe way).
By mainThread you mean the thread that called my entryPoint function? I can't run a polling loop there tho can I? How else would I access the main thread or post to the window's event queue?

Quote:
Originally Posted by cfillion View Post
The extension's entry point is called again with a null reaper_plugin_info_t when it's time to unload.
Very helpful. Thanks.
thecargocult is offline   Reply With Quote
Old 05-03-2021, 10:26 PM   #5
thecargocult
Human being with feelings
 
Join Date: Apr 2021
Posts: 9
Default

Quote:
Originally Posted by mschnell View Post
Just for case of clarity:
What exactly do you mean by DLL ?
A reaper extension. (not a VST plugin).
Compiled C++ code as a dynamic library on both Mac and Win.
Including JUCE classes at the moment but I have branches using pure stl for the polling loop.
thecargocult is offline   Reply With Quote
Old 05-03-2021, 10:34 PM   #6
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 3,551
Default

Quote:
Originally Posted by thecargocult View Post
By mainThread you mean the thread that called my entryPoint function?
Yes.

Quote:
Originally Posted by thecargocult View Post
I can't run a polling loop there tho can I?
SetTimer/KillTimer (Win32/SWELL) or plugin_register("timer"/"-timer", callback). The later hooks into REAPER's existing 33Hz timer.

Quote:
Originally Posted by thecargocult View Post
How else would I access the main thread or post to the window's event queue?
If your extension has a window, PostMessage (Win32/SWELL).
cfillion is offline   Reply With Quote
Old 05-04-2021, 04:28 AM   #7
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 10,597
Default

Quote:
Originally Posted by cfillion View Post
The worker thread can notify the main thread when it's time to insert the new files (eg. post a message to a window's event queue
Does the appropriate library (WDL ?) not provide OS independent means for save inter-Thread communication (including sending to the main event thread queue) ?

Quote:
Originally Posted by thecargocult View Post
A reaper extension. (not a VST plugin).
Compiled C++ code as a dynamic library on both Mac and Win.
Including JUCE classes at the moment but I have branches using pure stl for the polling loop.
I did not suppose that it is possible/viable to do reaper extensions with JUCE, as this is not a target they advertise,

I in fact gave up on WDL and Reaper extensions and switched over to using JUCE to do VSTs (for which right now a patch is available that allows for accessing the Reaper API in a VST3 - while possible with VST2 since years).
I do hope that JUCE provides something like this and hence the code should be unmodified workable on Windows, Mac and Linux, X86 X86/64 and ARM.

BTW.: ReaLearn is a great example of a VST that uses the Reaper API - and supposedly thread communication. But same does not use JUCE nor C++.

-Michael

Last edited by mschnell; 05-04-2021 at 04:34 AM.
mschnell is offline   Reply With Quote
Old 05-04-2021, 04:50 AM   #8
thecargocult
Human being with feelings
 
Join Date: Apr 2021
Posts: 9
Default

plugin_register("timer") was the answer.
Still including JUCE for various other things but the Reaper timer solves the threading issues nicely.
thecargocult is offline   Reply With Quote
Old 05-04-2021, 04:56 AM   #9
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 3,551
Default

Quote:
Originally Posted by mschnell View Post
Does the appropriate library (WDL ?) not provide OS independent means for save inter-Thread communication (including sending to the main event thread queue) ?
SWELL imitates a Win32-like event queue on macOS and Linux. Not quite 1:1 but close enough (eg. PostMessage works across threads but SendMessage doesn't because it always runs the window proc in the caller thread).

(SWELL and WDL together also provide some generic thread synchronization utilities (mutex, event...), but those have std equivalents anyway.)

Last edited by cfillion; 05-04-2021 at 05:11 AM.
cfillion is offline   Reply With Quote
Old 05-04-2021, 05:24 AM   #10
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 10,597
Default

Quote:
Originally Posted by thecargocult View Post
plugin_register("timer") was the answer.
Hmm. This smells a bit like polling for a state change in an idle loop.

If possible it is better to use events instead of polling, as events provide both less latency and lower CPU resource waste.

But of course there are instances where events can't be generated.

-Michael

Last edited by mschnell; 05-05-2021 at 03:23 AM.
mschnell is offline   Reply With Quote
Old 05-04-2021, 02:10 PM   #11
thecargocult
Human being with feelings
 
Join Date: Apr 2021
Posts: 9
Default

yeah, this is definitely a case for polling. The Reaper timer is ~33Hz so its trivial to reduce the actual polling activity to any sensible lower rate.
thecargocult is offline   Reply With Quote
Old 05-04-2021, 06:25 PM   #12
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 3,551
Default

In that case, creating a timer with a custom interval using SetTimer might be a better solution.

Code:
void CALLBACK timerTick(HWND hwnd, UINT msg, UINT_PTR timerId, DWORD time)
{
}

UINT_PTR timerId { SetTimer(nullptr, 0, 1000, &timerTick) };
// KillTimer(nullptr, timerId);

Last edited by cfillion; 05-04-2021 at 06:31 PM.
cfillion 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 01:35 AM.


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