Search for double Plus sign (++) to navigate the sections in this document. ++ Introduction I made a quick test to see if ReaScripts or Reaper scripts can be written with the help of AI if you have little or no programming knowledge. And although I do have programming knowledge, I am not familiar enough with Reaper API functions, and I decided to behave like my programming knowledge is minimal and, well, AI always makes coding fun and it makes life easier even for us coders, so why not to use it. To do the tests, I've used currently the best AI models for code generation, O1 by OpenAI in ChatGPT, and 3.5 Sonnet in Claude. I haven't tested Gemini, since according to my knowledge it currently generates the worst code, at least until version 2.0 comes out. Note, in ChatGPT, I've made my custom instructions for code formatting and commenting, and that's why the final result has so many comments in the code. ++ The prompt Here's my prompt, and I wrote the same prompt for both ChatGPT and Claude: I am using Cockos Reaper DAW version 7.X to make my music and audio productions. I am also using Osara Reaper Plugin for blind and visually impaired users. I like to use a reference track when mixing my tracks to easily compare my mix to the reference track's mix. However, toggling between the reference track and my mix is not a one-step process, because you need to disable master send on your master track, then move focus from your current track to a reference track, then solo it, then listen to it, then unsolo it, then switch focus to your current track. This is boring if your project has lots of tracks. Your task is to write a custom Reaper Lua script for me, that will contain one action, which is toggle between my mix and a reference track. This means: Toggle between 2 states. State1: Mute all tracks and unmute the reference track. State2: Unmute all tracks and mute the reference track. When script action runs, the code must also make the following checks: By default, it must assume that Track 1 AKA the first track in our Reaper project is our reference track. Next, in case if Master send is checked on a reference track, automatically uncheck it, so that the effects on the Master track cannot affect the sound of the reference track. Then remember the state and toggle the state between reference track or our mix. All the code must be organized in respective functions for orderliness and transparency. If possible, add Osara spoken messages instead of regular message boxes, so that Osara directly speaks toggle state between the reference tracks and my mix. ++ Test1: OpenAI ChatGPT Model used: O1 ChatGPT's response: Below is a **Reaper Lua** script example that provides a **one-click toggle** between your mix and a reference track (assumed to be track #1). The script follows these goals: 1. **Toggle between two states**: \t**State 1**: Mute all tracks and unmute (enable) the reference track. \t**State 2**: Unmute all tracks and mute (disable) the reference track. 2. **Master Send Handling**: If the reference track has its master send enabled (meaning it’s sending audio to the master bus), automatically disable that send so that master bus effects do **not** affect your reference track. 3. **OSARA Speech**: If possible, we use a small helper function that attempts to speak messages so that you know which state you are in. (If OSARA doesn’t directly provide a specialized function, a `reaper.ShowMessageBox` or `reaper.ShowConsoleMsg` call is often read by OSARA.) 4. **Organization**: The code is broken into multiple functions to illustrate separation of responsibilities (similar to SOLID principles in OOP). Even though this is Lua, we are still trying to keep the code well-structured, commented, and easy to modify. 5. **Tabs for Indentation**: The code uses tabs rather than spaces for indentation. 6. **Explanation**: We include comments that explain each piece of code in detail (like you’re 5 years old). We also show how to handle errors gracefully if, for example, there are no tracks in the project. --- ## Script Files Overview Since Reaper scripts are typically stored in **one file** per script action, we’ll demonstrate how you might separate your code logically. In practice, you may keep them in one file or place helper functions in a separate `.lua` file and load them via `dofile(...)`. Below is an example of a main script file (`ToggleReferenceTrack.lua`) and a supporting file (`OsaraSpeechHelper.lua`). The latter is optional; you could merge it into one file if that’s simpler for your workflow. ### `OsaraSpeechHelper.lua` ```lua -- OsaraSpeechHelper.lua --[[ This Lua file contains a helper function to speak messages using OSARA. We call 'SpeakMessage' below to provide user feedback via OSARA. Disclaimer: If OSARA doesn't have a direct function available in Reaper's Lua environment, fallback to show a message box or console message. ]] --[[ SpeakMessage function @param message: The text you want OSARA to speak. ]] function SpeakMessage(message) -- Try a direct OSARA function if it exists (fictitious for demonstration): -- If OSARA doesn't offer a direct function, we can use Reaper's ShowMessageBox -- since OSARA generally reads message boxes or console output as well. -- We first try a fictional OSARA function: -- if reaper.osara_outputMessage then -- reaper.osara_outputMessage(message) -- else -- Fallback to message box reaper.ShowMessageBox(message, "OSARA Speech", 0) -- end end ``` --- ### `ToggleReferenceTrack.lua` ```lua -- ToggleReferenceTrack.lua --[[ ToggleReferenceTrack.lua A Reaper Lua script that toggles between: 1) Listening to your reference track only (track #1) 2) Listening to your full mix (all tracks except reference track unmuted) We also ensure that, if the reference track has the master send enabled, we disable it so the master FX chain doesn't affect the reference audio. ]] -- Use dofile if you put 'OsaraSpeechHelper.lua' in the same Scripts folder: -- (Adjust the path to match your Reaper scripts directory) dofile(reaper.GetResourcePath().."/Scripts/OsaraSpeechHelper.lua") --[[ Global Variables (if needed): We store a toggle state in the Reaper project using ExtState, so that the state is remembered each time you run the action. ]] local SCRIPT_NAME = "ToggleReferenceTrack" local STATE_KEY = "RefTrackToggled" --[[ getToggleState Reads the current toggle state from the Reaper ExtState. @return (boolean) : Whether the reference track is currently active ]] local function getToggleState() -- Reaper stores states as strings, so we'll interpret "1" as true, else false. local storedValue = reaper.GetExtState(SCRIPT_NAME, STATE_KEY) if storedValue == "" then -- If there's no value set yet, assume "mix" mode is active (return false). return false end return (storedValue == "1") end --[[ setToggleState Writes the toggle state to the Reaper ExtState. @param newState (boolean) : The new toggle state; true if reference track is active, false if normal mix is active. ]] local function setToggleState(newState) local val = newState and "1" or "0" reaper.SetExtState(SCRIPT_NAME, STATE_KEY, val, true) end --[[ checkAndDisableMasterSend This function checks if the given track has its master send enabled; if it does, it disables it. @param track (MediaTrack) : The track you want to check. ]] local function checkAndDisableMasterSend(track) -- "B_MAINSEND" is a Reaper property for enabling master send local isMasterSendEnabled = reaper.GetMediaTrackInfo_Value(track, "B_MAINSEND") if isMasterSendEnabled == 1 then reaper.SetMediaTrackInfo_Value(track, "B_MAINSEND", 0) end end --[[ muteAllTracksExceptReference Iterates over all tracks in the project, mutes them all, then unmutes the reference track (track #1). It also disables master send on the reference track. ]] local function muteAllTracksExceptReference() -- Reaper uses zero-based indexing for tracks internally local trackCount = reaper.CountTracks(0) if trackCount == 0 then SpeakMessage("No tracks found in this project.") return end for i = 0, trackCount - 1 do local track = reaper.GetTrack(0, i) if i == 0 then -- i == 0 means the first track is the reference track reaper.SetMediaTrackInfo_Value(track, "B_MUTE", 0) -- unmute reference track checkAndDisableMasterSend(track) -- ensure master send is disabled else reaper.SetMediaTrackInfo_Value(track, "B_MUTE", 1) -- mute all other tracks end end SpeakMessage("Reference track only.") reaper.UpdateArrange() end --[[ unmuteAllTracksAndMuteReference Iterates over all tracks, unmutes them all, then mutes the reference track (track #1). For demonstration, we do not automatically re-enable master send for the reference track (since the user wants reference unaffected by master bus effects). If you want to re-enable it, you can add a line below. ]] local function unmuteAllTracksAndMuteReference() local trackCount = reaper.CountTracks(0) if trackCount == 0 then SpeakMessage("No tracks found in this project.") return end for i = 0, trackCount - 1 do local track = reaper.GetTrack(0, i) if i == 0 then reaper.SetMediaTrackInfo_Value(track, "B_MUTE", 1) -- mute reference track -- If you want to re-enable master send here, remove the comment below: -- reaper.SetMediaTrackInfo_Value(track, "B_MAINSEND", 1) else reaper.SetMediaTrackInfo_Value(track, "B_MUTE", 0) -- unmute other tracks end end SpeakMessage("All tracks unmuted. Reference track muted.") reaper.UpdateArrange() end --[[ toggleReferenceTrack This function reads the current toggle state, then switches between the reference track state and the normal mix state. ]] local function toggleReferenceTrack() -- Reaper must begin an undo-block so we can undo/redo the entire action in one go reaper.Undo_BeginBlock() local currentState = getToggleState() if currentState == false then -- If currently in "mix mode," switch to reference track only muteAllTracksExceptReference() setToggleState(true) else -- If currently in "reference track mode," switch to full mix unmuteAllTracksAndMuteReference() setToggleState(false) end -- Reaper must end the undo-block reaper.Undo_EndBlock("Toggle Reference Track", -1) end -------------------------------------------------------------------------------- -- Main Entry Point -------------------------------------------------------------------------------- --[[ Because this is a single-action script, Reaper calls whatever is at the bottom of the script. Here, we simply call toggleReferenceTrack() as our main function. ]] toggleReferenceTrack() ``` --- ## How to Install and Use 1. **Save the Files** \t1. In your Reaper scripts folder (or anywhere else you store scripts), create two files: \t\t- `OsaraSpeechHelper.lua` \t\t- `ToggleReferenceTrack.lua` 2. **Load the Script into Reaper** \t- Go to `Actions` > `Show Action List` in Reaper. \t- Click on the **ReaScript** button (bottom right). \t- Click **Load**. \t- Select `ToggleReferenceTrack.lua` (make sure `OsaraSpeechHelper.lua` is in the same or a known path). 3. **Assign a Keyboard Shortcut (Optional but Recommended)** \t- In the Action List, search for `ToggleReferenceTrack`. \t- Assign a convenient keyboard shortcut. 4. **Run It** \t- Press the shortcut or select the action to toggle between the reference track and your mix. --- ## Explanation (Like You’re 5) - **A “function”** is like a little helper that does a specific job. - **“Mute”** means “don’t let sound go through.” **“Unmute”** means “do let sound go through.” - **The reference track** is the first track in your project. - **The mix** is all your other tracks. - When you run our script, it checks if you’re listening to your **mix** now. If you are, it will **mute** your entire project and **unmute** your reference track. Then next time you run the script, it does the opposite: it **unmutes** everything and **mutes** the reference track. - If the reference track is sending audio to the master track’s effects, we **turn that off** so those effects don’t change the reference track. --- ## Unit Testing Example Below is a small snippet that conceptually “tests” some parts of our script. Lua “unit tests” aren’t as common in Reaper scripting, but you can adapt something like this for your environment or for manual verification: ```lua -- UnitTest_ToggleReferenceTrack.lua --[[ This is a conceptual test file to demonstrate how one might test the logic of our toggle script. In Reaper, you'd typically run manual tests because ReaScripts interact with the project tracks. ]] dofile(reaper.GetResourcePath().."/Scripts/OsaraSpeechHelper.lua") dofile(reaper.GetResourcePath().."/Scripts/ToggleReferenceTrack.lua") -- A mock track environment for testing (you won't have real tracks in a test environment) local function mockTest() -- In actual Reaper environment, you'd have some project loaded. -- The calls below won't do much unless you have actual tracks. -- But let's demonstrate the function usage: -- Start with "mix mode" by default setToggleState(false) -- Toggle once -> should go to reference track toggleReferenceTrack() -- Assert or confirm that track #1 is unmuted, others are muted, master send on track #1 is disabled -- Toggle again -> should go back to mix toggleReferenceTrack() -- Assert or confirm that all tracks are unmuted, track #1 is muted end -- Run the test mockTest() SpeakMessage("Unit test completed.") ``` > **Note**: Because ReaScript logic operates on actual project tracks, you typically do “real-world” tests in a Reaper project rather than purely automated tests. The above snippet is just a conceptual example to show how one could structure test code. --- ### Final Thoughts - This script is an **example** that illustrates the main idea. Feel free to adjust it to match exactly what you need in your environment (for example, if you want the reference track to re-enable its master send after toggling back, or if you prefer a different track as the reference track). - **OSARA** might handle speaking differently depending on the version. If you find that `ShowMessageBox` isn’t read aloud, look for an alternative function or approach in the OSARA docs for speaking text. You can rename `SpeakMessage` to directly call whatever OSARA’s official function is. Enjoy faster A/B comparisons between your reference track and your mix! ++ Test 2: Anthropic Claude Model used: 3.5 Sonnet Claude's response: I'll help you create a Lua script for Reaper that will make A/B comparison with a reference track much easier. This is a perfect case for an artifact since it's a complete, reusable script. ```lua -- Reference Track Toggle Script for Reaper -- Designed to work with Osara for accessibility -- Default reference track is Track 1 local last_state = false -- false = mix state, true = reference state -- Function to get the reference track (Track 1) function GetReferenceTrack() return reaper.GetTrack(0, 0) -- Get first track (0-based index) end -- Function to check and disable master send if needed function DisableMasterSend(track) local master_send = reaper.GetMediaTrackInfo_Value(track, "B_MAINSEND") if master_send == 1 then reaper.SetMediaTrackInfo_Value(track, "B_MAINSEND", 0) end end -- Function to speak message through Osara function SpeakMessage(message) if reaper.osara_outputMessage then reaper.osara_outputMessage(message) end end -- Function to mute all tracks except reference function MuteAllExceptReference(ref_track) local track_count = reaper.CountTracks(0) for i = 0, track_count - 1 do local track = reaper.GetTrack(0, i) if track == ref_track then reaper.SetMediaTrackInfo_Value(track, "B_MUTE", 0) -- Unmute reference else reaper.SetMediaTrackInfo_Value(track, "B_MUTE", 1) -- Mute others end end end -- Function to unmute all tracks and mute reference function UnmuteAllExceptReference(ref_track) local track_count = reaper.CountTracks(0) for i = 0, track_count - 1 do local track = reaper.GetTrack(0, i) if track == ref_track then reaper.SetMediaTrackInfo_Value(track, "B_MUTE", 1) -- Mute reference else reaper.SetMediaTrackInfo_Value(track, "B_MUTE", 0) -- Unmute others end end end -- Main function to toggle between states function ToggleReferenceState() -- Start undo block reaper.Undo_BeginBlock() local ref_track = GetReferenceTrack() if not ref_track then SpeakMessage("No reference track found. Please ensure Track 1 exists.") return end -- Ensure master send is disabled on reference track DisableMasterSend(ref_track) -- Toggle state if not last_state then -- Switch to reference track MuteAllExceptReference(ref_track) SpeakMessage("Reference track active") last_state = true else -- Switch to mix UnmuteAllExceptReference(ref_track) SpeakMessage("Mix active") last_state = false end -- End undo block reaper.Undo_EndBlock("Toggle Reference Track", -1) -- Update UI reaper.UpdateArrange() end -- Main script execution function main() ToggleReferenceState() end -- Execute script main() ``` I've created a comprehensive Lua script that handles reference track toggling in Reaper with Osara support. Here's how to use it: 1. Save this script with a `.lua` extension (e.g., `reference_track_toggle.lua`) 2. Import it into Reaper's Actions list 3. Assign a keyboard shortcut to it for quick access Key features of the script: - Automatically uses Track 1 as the reference track - Disables master send on the reference track if needed - Uses Osara's speech output instead of message boxes - Includes proper undo functionality - Maintains state between toggles - Organized into clear, separate functions for maintainability The script will announce "Reference track active" when switching to the reference track and "Mix active" when returning to your mix. Would you like me to explain any part of the script in more detail or make any modifications to suit your workflow better? ++ Outro and conclusions As you can see, I didn't write any follow up questions, because there was no need for it. I didn't have any errors, but I didn't wanna have Osara speech code in a separate file, so I've excluded dofile statement and moved the code to the same .lua file where the main script code is written. Here are my results: ChatGPT O1: Code generated with ChatGPT worked as expected, but it showed regular message boxes instead of speaking messages with Osara. I found out later that this was because the lines of code containing reaper.osara_outputMessage were commented out. Just uncommenting the lines will make Osara speak the message, as requested. Anthropic Claude 3.5 Sonnet: Code generated with Claude also worked, and messages were spoken by Osara, but it worked partially, because it toggled reference track on, but not off. This can be fixed in a follow up prompt, but with ChatGPT's O1 it it worked from the first attempt. Many reviewers said that Claude is currently best at code generation, but ChatGPT with O1 is also quite impressive. End of document.