Posted on

Reascript: How I fixed my OSX problem

I described earlier how I totally broke Reaper in trying to figure out how to solve an issue with pop-up windows on Mac OSX.

Well, I’ve solved (see: circumvented) that problem.  I’ll share with you my thought process and how I went about figuring it out today.

Some background

To explain this process I first have to give you a little background.  The scripts I created were built to solve a missing workflow problem in Reaper.  Said workflow has the following specific steps:

  1. Open plugin
  2. Have audio selected
  3. Hit “preview button” to begin playback loop
  4. Tweak FX
  5. Hit another button to instantly bake FX onto the audio

This process allows for rapid iteration/creation of sounds for sound designers.  It’s super useful, and admittedly very difficult to train yourself away from.  I wanted my version to work similarly, with the following workflow:

  1. Have plugin open
  2. Hit hotkey to begin playback loop and pop-up window to wait for input
  3. Tweak FX
  4. Click “OK” to bake, or “Cancel” to cancel

On Windows (what I’m developing with) this works fantastically.  As a playback loop begins, a pop-up window hits your screen and acts as a simple “wait for input” function (which doesn’t exist in Reascript’s version of Lua apparently).

Unfortunately, Mac OSX doesn’t do window handling the same way that Windows does.  If you have a pop-up dialogue on Mac – all other UI (user interface) behind it freezes.

Brainstorming

So admittedly, I felt like an idiot for not quickly being able to solve this.  My first inclination was to remove the pop-up box, but not change the workflow.  This would require a specific “wait for user input” or “wait for keypress” function.

Google searching “wait for input Lua” gave me a whole lot of Minecraft-related results that didn’t solve the problem (apparently there are built-in functions for Minecraft that don’t work in general Lua).  The closest I could get to figuring this out is that you could use a C++ import to Lua, in which you could provide yourself a wait for user input function.

Everything going down that route seemed entirely too complicated.

After that, the suggestion was made to me that I should make a UI custom to Reaper.  Reaper does have some graphics functions that you can do this with, but I had two problems:

  1. I had no guarantee window handling was going to be better simply because it was built into Reaper (technically the pop-up was too)
  2. It meant spending time learning how to build a GUI in Reaper.  I didn’t want to spend this time

So again – back to the drawing board.

Enter the defer() function

Eventually, the solution was presented to me via the Reaper API.  Each supported language has a function called defer().  Essentially this allows for a process to run in the background while the user does other things — pretty much exactly what I needed.

I’m not 100% sure that I ended up using defer() in the way it was intended, but the way I used it did end up working.  It also entirely changed the workflow, so when I release the scripts publicly I’m going to be including a “how to use this” video with them.

defer() turns out to be pretty neat in the way that it works, here’s a short example:

function myFunction()
-- This is how defer works

-- If the value I want is not equal to the value I need...
if valueIWant ~= valueINeed then
-- [[defer to myFunction (aka 
"go back to the beginning of myFunction")]]
defer(myFunction)
else
-- do the rest of your program here

end

So if you read through the above it may be a tad hard to understand because it’s a vague example.  I’ll do my best to explain it, though…

When you call defer(), the parameter you put in the parenthesis needs to be the function you’re deferring to.  In this case, it’s myFunction.  If your program’s flow hits this defer function, it goes to the beginning of that function.  So in essence above, we’ve built an infinite loop within an if statement.  Until the “value I want” equals the “value I need”, we’re going to keep going back to the start of the function.

In my case, my if statement is “if the user hasn’t stopped playback, then defer”.  Once the user stops playback, a popup window appears asking if they want to take the FX they just changed.  This effectively changes the workflow to:

  1. Select audio and FX
  2. Hit hotkey to start playback
  3. Tweak FX
  4. Stop playback and either take FX or Cancel

The flow is a little different than I originally envisionsed, but thanks to early help testing for OSX – I can confirm it works!

I hope this helps teach you through defer a bit, and “wait for input” in Reascripts in general.  Thanks to this code, you’ll see my scripts available to use very soon!


Copyright 2016-2021, NIR LLC, all rights reserved.