REW Beta Release Trying to write some macros...

moedra

Member
Thread Starter
Joined
Mar 1, 2021
Posts
151
Hi John. I need some help. I am not a coder, but I am trying to enlist the help of Gemini to write some macros for REW using the API and I am hitting a wall where REW will not release the data needed to make the macros function properly. The hard part is that I don't understand Python code or the API and therefore cannot troubleshoot. I've had Gemini scan this entire forum for anything that can be of assistance, and although we've learned a lot, this macro still doesn't fully work the way it's meant to (hands-free). Gemini even tried having the entire thing done in Python code, but REW won't release the measurement data, and it won't save out files in the scripts, so data cannot be extracted that way either...

In this first macro (of maybe four or five), I want to:
1. take the main measurements, which I'm calling Measurement-L and Measurement-R, and smooth them to 1/6
2. copy them via response copy, resulting in Measurement-L-copy and Measurement-R-copy
3. smooth both copies to 1/12
4. generate RMS Average measurement of Measurement-L and Measurement-L-copy, resulting in RMS Average 1
5. generate RMS Average measurement of Measurement-R and Measurement-R-copy, resulting in RMS Average 2
6. export RMS Average 1 as text, with no smoothing, max frequency capped to 22kHz, to filename Main-L.txt
7. export RMS Average 2 as text, with no smoothing, max frequency capped to 22kHz, to filename Main-R.txt
8. import Main-L and Main-R back into REW
9. generate minimum phase copies (new measurement versions) of Main-L and Main-R, resulting in Main-L-MP and Main-R-MP
10. delete/remove the copies and the RMS averages, as they are not needed any further

The objective of this macro is to have Main-L, Main-L-MP, Main-R, and Main-R-MP prepared for further operations in the following regard:
• They contain the relative smoothing value of 1/9, yet they are not smoothed as far as REW is concerned.
• The raw data is "smoothed", so subsequent arithmetic can be performed on the smoothed data rather than the original raw measurement data.
Of course, if 1/9 were an option I'd just smooth to that and be done with it, but this is great exercise to have a macro do, so I'm doing it!

The problem is that not all of these steps work correctly, and Gemini is having a hard time figuring out how to get the API to do what is being asked. Here's breakdown of what is and isn't working:
1. works great. 1/6 smoothed L and R
2. response copy works great
3. this works. same as step 1
4 and 5. This does not work. In the script I have resorted to having Gemini code this as a user step, so I have to generate the RMS average measurements manually
6 and 7. This does not work either. We've been able to get the parameters set but REW will not export any files. This has become another manual operation.
8. same as the last step, REW will not import the files I made (Main-L.txt and Main-R.txt). Another manual operation. The script waits for me to import the files, then continues.
9. this step works
10. this does not work properly. Seemingly random measurements got removed and so this, too, has become a manual operation

So, the macro has too many user prompts, and the solution is simply a matter of getting REW to generate the average measurements and export/import the files so that the macro can be completely hands-free. The idea is to make this automatic so that it's faster, and also easier for someone who has no patience to do everything manually!
And I like the idea of making macros, since the later steps of my overall procedure are full of time-consuming arithmetic functions that have to executed in sequence for both speakers' measurements.

The script for "macro 1" is here.
For reference, "V3.1 minimum phase logic" refers to the version of the script in which we successfully got it to generate the minimum phase versions we asked for, so we made it a point to use that code because it was proven to work.

======================

import urllib.request
import json
import time

REW_URL = "http://127.0.0.1:4735"

def send_rew_request(endpoint, data=None, method='POST'):
url = f"{REW_URL}{endpoint}"
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
try:
req_data = json.dumps(data).encode('utf-8') if data else None
req = urllib.request.Request(url, data=req_data, headers=headers, method=method)
with urllib.request.urlopen(req) as response:
return json.loads(response.read().decode('utf-8'))
except Exception: return None

def get_id(name):
# Quick check for ID
data = send_rew_request("/measurements", method='GET') or {}
for m_id, m_info in data.items():
if m_info.get('title') == name: return m_id
return None

def run_prep(name):
print(f" [+] Prepping {name} (Smooth 1/6 + Copy + Smooth 1/12)...")
sid = get_id(name)
if not sid:
print(f" [!] Error: {name} not found.")
return

# 1. Smooth Source to 1/6
send_rew_request(f"/measurements/{sid}/command", {"command": "Smooth", "parameters": [["smoothing", "1/6"]]})

# 2. Create Copy
send_rew_request(f"/measurements/{sid}/command", {"command": "Response copy"})
time.sleep(1) # Short pause to let copy register

# 3. Find and Smooth Copy to 1/12
# We check standard name variations for the copy
cid = get_id(f"{name}-copy") or get_id("Response copy") or get_id(f"{name} copy")
if cid:
send_rew_request(f"/measurements/{cid}/command", {"command": "Smooth", "parameters": [["smoothing", "1/12"]]})
else:
print(f" [!] Warning: Could not auto-smooth the copy for {name}. Please check it.")

def wait_for_import_and_finalize(target_name):
print(f"\n [...] Watching for '{target_name}' to appear in REW...")

# INFINITE LOOP: This prevents the script from failing if you take your time.
found_id = None
while not found_id:
found_id = get_id(target_name)
if not found_id:
time.sleep(2) # Poll every 2 seconds

print(f" [+] {target_name} detected! Applying V3.1 Minimum Phase logic...")

# THE PROVEN V3.1 COMMAND
send_rew_request(f"/measurements/{found_id}/command", {
"command": "Minimum phase version",
"parameters": [
["append lf tail", "false"], ["append hf tail", "false"],
["include cal", "false"], ["replicate data", "false"],
["apply frequency warping to hf tail", "false"]
]
})

# Verify it worked
time.sleep(1)
print(f" [√] {target_name} processing complete.")

if __name__ == "__main__":
print("--- PHASE 1: AUTOMATED PREP ---")
run_prep("Measurement-L")
run_prep("Measurement-R")

print("\n" + "="*60)
print(" PHASE 2: MANUAL INTERVENTION (The Missing Link)")
print(" 1. Go to 'All SPL' tab.")
print(" 2. Average 'Measurement-L' + Copy -> Export/Import as 'Main-L'")
print(" 3. Average 'Measurement-R' + Copy -> Export/Import as 'Main-R'")
print(" (The script is strictly waiting for 'Main-L' and 'Main-R' to appear)")
print("="*60)

# These functions will block (wait) until they see the files.
# They effectively act as the "Second Half" of the automation.
wait_for_import_and_finalize("Main-L")
wait_for_import_and_finalize("Main-R")

print("\nMacro 1 Complete. Safe to manually delete temp copies/averages.")
 
Last edited:
This may or may not help, but I don't have any other reference. Here is the terminal readout of the latest few iterations I've run on v10 of the script, along with Gemini's diagnosis below them. The latest version of said script is at the bottom. I am trying to get the steps to work one at a time systematically today rather than have the whole sequence in there at once. Are the issues evident here by chance? I think Gemini is also systematically trying to figure out how to make this work, so it's interesting to watch.

Oh, and I've implemented a color-coding scheme for left and right channels now. Fun.


PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
--- Macro 1 v10.05 (Indentation Fixed + Data Retries) ---

--- Processing L Channel ---
[+] Data Bridge: Fetching curves for Main-L...
[...] Data for ID 1 not ready yet, retrying (1/5)...
[...] Data for ID 1 not ready yet, retrying (2/5)...
[...] Data for ID 1 not ready yet, retrying (3/5)...
[...] Data for ID 1 not ready yet, retrying (4/5)...
[...] Data for ID 1 not ready yet, retrying (5/5)...
[...] Data for ID 3 not ready yet, retrying (1/5)...
[...] Data for ID 3 not ready yet, retrying (2/5)...
[...] Data for ID 3 not ready yet, retrying (3/5)...
[...] Data for ID 3 not ready yet, retrying (4/5)...
[...] Data for ID 3 not ready yet, retrying (5/5)...
[!] Error: Failed to pull data from REW for Main-L.

--- Processing R Channel ---
[+] Data Bridge: Fetching curves for Main-R...
[...] Data for ID 2 not ready yet, retrying (1/5)...
[...] Data for ID 2 not ready yet, retrying (2/5)...
[...] Data for ID 2 not ready yet, retrying (3/5)...
[...] Data for ID 2 not ready yet, retrying (4/5)...
[...] Data for ID 2 not ready yet, retrying (5/5)...
[...] Data for ID 4 not ready yet, retrying (1/5)...
[...] Data for ID 4 not ready yet, retrying (2/5)...
[...] Data for ID 4 not ready yet, retrying (3/5)...
[...] Data for ID 4 not ready yet, retrying (4/5)...
[...] Data for ID 4 not ready yet, retrying (5/5)...
[!] Error: Failed to pull data from REW for Main-R.
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py^C
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
--- Macro 1 v10.08 (Pre-Flight Audit) ---

--- Processing L Channel ---
[+] Bridging data for Main-L...

--- Processing R Channel ---
[+] Bridging data for Main-R...
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
--- Macro 1 v10.08 (Pre-Flight Audit) ---

--- Processing L Channel ---
[+] Bridging data for Main-L...

--- Processing R Channel ---
[+] Bridging data for Main-R...
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
--- Macro 1 v10.09 (Sidebar Refresh Logic) ---

--- Processing L Channel ---

--- Processing R Channel ---
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
--- Macro 1 v10.10 (Full Debug Mode) ---

>>> STARTING L CHANNEL <<<
[+] Triggering Text Export for ID: 1...
[DEBUG ERROR] API Request failed on /measurements/1/command: HTTP Error 400: Bad Request
[...] Waiting for file (1/5)...
[...] Waiting for file (2/5)...
[...] Waiting for file (3/5)...
[...] Waiting for file (4/5)...
[...] Waiting for file (5/5)...
[!] ERROR: REW failed to write the export file to C:\REW_Projects\temp_api_export.txt
[+] Triggering Text Export for ID: 3...
[DEBUG ERROR] API Request failed on /measurements/3/command: HTTP Error 400: Bad Request
[...] Waiting for file (1/5)...
[...] Waiting for file (2/5)...
[...] Waiting for file (3/5)...
[...] Waiting for file (4/5)...
[...] Waiting for file (5/5)...
[!] ERROR: REW failed to write the export file to C:\REW_Projects\temp_api_export.txt

>>> STARTING R CHANNEL <<<
[+] Triggering Text Export for ID: 2...
[DEBUG ERROR] API Request failed on /measurements/2/command: HTTP Error 400: Bad Request
[...] Waiting for file (1/5)...
[...] Waiting for file (2/5)...
[...] Waiting for file (3/5)...
[...] Waiting for file (4/5)...
[...] Waiting for file (5/5)...
[!] ERROR: REW failed to write the export file to C:\REW_Projects\temp_api_export.txt
[+] Triggering Text Export for ID: 4...
[DEBUG ERROR] API Request failed on /measurements/4/command: HTTP Error 400: Bad Request
[...] Waiting for file (1/5)...
[...] Waiting for file (2/5)...
[...] Waiting for file (3/5)...
[...] Waiting for file (4/5)...
[...] Waiting for file (5/5)...
[!] ERROR: REW failed to write the export file to C:\REW_Projects\temp_api_export.txt
PS C:\REW_Projects\REW-FilterGen-Scripts> python rew_filtergen_macro1_v10.py
--- Macro 1 v10.11 (API Syntax Fix) ---

>>> PROCESSING L SPEAKER <<<
[API ERROR] POST /measurements/1/command: HTTP Error 400: Bad Request
[+] Exporting ID 1 to C:/REW_Projects/temp_api_export.txt...
[API ERROR] POST /measurements/1/command: HTTP Error 400: Bad Request
[+] Exporting ID 3 to C:/REW_Projects/temp_api_export.txt...
[API ERROR] POST /measurements/3/command: HTTP Error 400: Bad Request

>>> PROCESSING R SPEAKER <<<
[API ERROR] POST /measurements/2/command: HTTP Error 400: Bad Request
[+] Exporting ID 2 to C:/REW_Projects/temp_api_export.txt...
[API ERROR] POST /measurements/2/command: HTTP Error 400: Bad Request
[+] Exporting ID 4 to C:/REW_Projects/temp_api_export.txt...
[API ERROR] POST /measurements/4/command: HTTP Error 400: Bad Request


=========================
Gemini's feedback on the above terminal results:


It sounds like we’ve hit a wall where the REW API is accepting our commands but refusing to actually "commit" the data to the UI. Based on forum discussions regarding the REW API, sometimes the import command requires a specific data structure or a "refresh" trigger to appear in the sidebar.

Let's try v10.09. In this version, I’ve adjusted the import payload to include the unit (SPL) and added a "Selection" trigger. Sometimes REW won't show a measurement in the sidebar until you tell the API to "Select" it.

Macro 1: v10.09 (Import Payload Fix + Selection Trigger)​

One more possibility:​

If the file temp_api_export.txt is being created but the import still isn't showing up, it might be that REW's API for import is restricted in the Free tier or specific builds.

Watch your REW screen as the script runs:

  1. Do you see the "Measurement-L-copy" appear and then disappear?
  2. Does a "Exporting" dialog box flash on screen?
If the copies appear and disappear, but "Main" never shows up, we may need to try importing the file using a command-line flag rather than a JSON body.

--------------------------------------------
If the script isn't giving feedback and the measurements aren't appearing, it usually means the script is crashing silently or hitting an "Except: pass" block without telling us why.

Let’s move to v10.10. I have stripped out the "silent" error handling and replaced it with High-Visibility Debugging. This version will print every single interaction to the terminal. If it fails, it will tell us the exact line and error.

I have also added a "Safe Import" check. If REW rejects the data upload, we'll see the raw error code.

Macro 1: v10.10 (High-Visibility Debugging)​

What to watch for in the terminal:​

  1. "API Request failed": This means the connection to REW dropped.
  2. "Waiting for file": If it counts to 5 and fails, REW is ignoring the "Export" command.
  3. "REW rejected the data import": This means our data format is wrong for your specific REW version.
------------------------------------
That HTTP Error 400: Bad Request means REW is receiving the command but rejecting the way the parameters are structured. Specifically, REW's API may be very picky about how file paths are passed in JSON.

The API expects the parameters to be an array of arrays, and it seems that it often dislikes single backslashes in Windows paths (e.g., C:\Projects).

Let’s move to v10.11. I have sanitized the path to use forward slashes (which REW may prefer) and simplified the parameter structure to match the exact syntax REW requires for file exports.

Macro 1: v10.11 (Path Sanitization + API Syntax Fix)​

Why this should fix the 400 Error:​

  • Forward Slashes: REW interprets C:/Path much better than C:\Path via JSON, as backslashes often act as escape characters.
  • Double Nesting: In the parameters, REW specifically looks for [ ["key", "value"] ]. Missing one set of brackets triggers a "Bad Request."
  • File Size Check: It ensures the file is actually on the disk and contains data before trying to read it.

--------------------------------------------



=========================
rew_filtergen_macro1_v10.py
Macro 1 v10.11 (API Syntax Fix)
=========================

import urllib.request
import json
import time
import os
import numpy as np

# --- CONFIGURATION ---
REW_URL = "http://127.0.0.1:4735"
# We use forward slashes to avoid "Bad Request" errors in the API
TEMP_EXPORT = "C:/REW_Projects/temp_api_export.txt"

COLOR_RED, COLOR_ORANGE = 16711680, 16747520
COLOR_GREEN, COLOR_BLUE = 65280, 49151

def send_rew_request(endpoint, data=None, method='POST'):
url = f"{REW_URL}{endpoint}"
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
try:
req_data = json.dumps(data).encode('utf-8') if data else None
req = urllib.request.Request(url, data=req_data, headers=headers, method=method)
with urllib.request.urlopen(req) as response:
return json.loads(response.read().decode('utf-8'))
except Exception as e:
# If this prints, REW is rejecting the command structure
print(f" [API ERROR] {method} {endpoint}: {e}")
return None

def force_export_and_read(m_id):
if os.path.exists(TEMP_EXPORT):
os.remove(TEMP_EXPORT)

# v10.11 Fix: Exact parameter nesting REW expects
print(f" [+] Exporting ID {m_id} to {TEMP_EXPORT}...")
payload = {
"command": "Export measurement as text",
"parameters": [ ["filename", TEMP_EXPORT] ]
}

send_rew_request(f"/measurements/{m_id}/command", payload)

# Wait for file arrival
for i in range(5):
if os.path.exists(TEMP_EXPORT):
# Check if file is actually written (not 0 bytes)
if os.path.getsize(TEMP_EXPORT) > 0:
print(f" [√] File ready.")
break
time.sleep(1)

if os.path.exists(TEMP_EXPORT):
try:
data = np.genfromtxt(TEMP_EXPORT, comments='*', invalid_raise=False)
return data[:, 0], data[:, 1]
except Exception as e:
print(f" [!] Parse Error: {e}")
return None, None

def run_channel(side):
src, cpy, target = (f"Measurement-{side}", f"Measurement-{side}-copy", f"Main-{side}")
s_col, t_col = (COLOR_RED, COLOR_ORANGE) if side == "L" else (COLOR_GREEN, COLOR_BLUE)

print(f"\n>>> PROCESSING {side} SPEAKER <<<")

# 1. Identify IDs
m_list = send_rew_request("/measurements", method='GET') or {}
sid = next((k for k, v in m_list.items() if v.get('title') == src), None)

if not sid:
print(f" [!] {src} not found.")
return

# 2. Prep & Copy
send_rew_request(f"/measurements/{sid}/command", {"command": "Smooth", "parameters": [["smoothing", "1/6"]]})
send_rew_request(f"/measurements/{sid}/command", {"command": "Set color", "parameters": [["color", s_col]]})
send_rew_request(f"/measurements/{sid}/command", {"command": "Response copy"})

time.sleep(3)
m_list = send_rew_request("/measurements", method='GET') or {}
cid = next((k for k, v in m_list.items() if v.get('title') == cpy), None)

if cid:
send_rew_request(f"/measurements/{cid}/command", {"command": "Smooth", "parameters": [["smoothing", "1/12"]]})

# 3. Data Bridge via Export
f1, s1 = force_export_and_read(sid)
f2, s2 = force_export_and_read(cid)

if s1 is not None and s2 is not None:
# RMS Math
avg = 20 * np.log10(np.sqrt((10**(s1/20)**2 + 10**(s2/20)**2) / 2))

# 4. Import Back to REW
print(f" [+] Uploading {target}...")
send_rew_request("/measurements/import", data={
"name": target, "freq": f1.tolist(), "spl": avg.tolist(), "unit": "dB"
})

time.sleep(2)
m_list = send_rew_request("/measurements", method='GET') or {}
mid = next((k for k, v in m_list.items() if v.get('title') == target), None)

if mid:
send_rew_request(f"/measurements/{mid}/command", {"command": "Set color", "parameters": [["color", t_col]]})
send_rew_request(f"/measurements/{mid}/command", {"command": "Minimum phase version", "parameters": [["append lf tail", "false"], ["append hf tail", "false"]]})
print(f" [√] {target} Generation Complete.")
send_rew_request(f"/measurements/{cid}", method='DELETE')
else:
print(f" [!] Error: REW did not name the copy '{cpy}'")

if __name__ == "__main__":
print("--- Macro 1 v10.11 (API Syntax Fix) ---")
run_channel("L")
run_channel("R")
 
Last edited:
I don't use Python, so I can't help with your script, sorry. I would not expect an AI to have much success in vibe coding an REW interface as there are very few example implementations for it to draw on and its ability to understand the help or the OpenAPI specification for the API is likely limited. You can find an example of using the API with Python at https://github.com/hyacin75/rew-cross-corr-align-automation (from a quick github search, I don't know the author or whether the script works).

I recommend you use the API in blocking mode which should avoid the need to poll for actions to complete or measurements to appear. Use the Swagger UI at localhost:4735 in a browser to see the responses REW generates and test command sequences.
 
I guess what's important, ultimately, is that the functions I am trying to execute can actually be done in a scripted macro. I don't necessarily care if it's Python or some other language, as long as it works. Python is just the language Gemini chose to use, and since I don't know any better, I let it go. Is there a specific scripting language that would work better?
 
The language doesn't matter. JavaScript is an alternative, albeit more cryptic (IMO), but has the advantage of being able to run in any browser by opening an html file containing the script.

The REW API does not have any endpoints for export.

I again suggest you use the SwaggerUI to go through the steps you want carried out. It doesn't require programming knowledge and REW's responses will be displayed so you can see what works or doesn't work. In the former case you can then explain to Gemini what steps it should follow, what API calls to use and what it should do with the responses, in the latter the response usually explains why the call was rejected.
 
The REW API does not have any endpoints for export
To explain this, the API has calls that allow the frequency or impulse response of measurements to be obtained, so the data can be processed externally in whatever manner is desired. There are also endpoints to put the result back in REW (e.g. import/frequency-response-data) so using files as an intermediate step is unnecessary.
 
Is there a way to simply commit smoothing data to a measurement? If so, I could simply have the macro do that instead of using export/import to obtain a raw measurement that has smoothed data baked into it.

The reason I'm doing what I'm doing is because I want the arithmetic calculations to be performed on smoothed data. As far as I am aware, the smoothing in REW is only a visual reference. The unsmoothed data seems to persist behind the smoothing, and trace arithmetic does appear to ignore whatever smoothing is applied.

The underlying reason for all of this is that I've worked out a personal process of generating correction filters and it cannot be done with the raw unsmoothed data. The math must be done on the smoothed data to avoid over-correcting the results of comb filtering (reflections).
 
Is there a way to simply commit smoothing data to a measurement? If so, I could simply have the macro do that instead of using export/import to obtain a raw measurement that has smoothed data baked into it.
A min phase copy of a smoothed measurement has the smoothed magnitude response.
 
A min phase copy of a smoothed measurement has the smoothed magnitude response.
Yes, but what if I want the smoothed data (as with minphase copy) but with the original phase response in the measurement (not minphase)? I'm trying to omit the "export frequency response as text with smoothing and bring it back in" approach, since there's no export function in the API.

I tried the "no save file" approach but Gemini doesn't know what to do with it. Resorted to manual export...

In the script I have successfully managed to do everything except the file import. I export the files manually because there is no export function, then have the script import the measurements but nothing happens. I have looked over the Swagger UI but I don't know enough about this to tell Gemini what to do.
 
here's no export function in the API
There isn't an export to file, but you can get the response from the /measurements/:id/frequency-response endpoint, there's no need for a file. Do what you want with the data (or nothing) then put it back using the /import/frequency-response-data endpoint.
 
Quick check in, as I am now taking over the scripting process for this set of macros.

I am going to save this thread and implement the suggestions made within.

I may continue using Python, integrating a terminal into an HTML file, or I may look into writing it in JavaScript instead.
 
Back
Top