same postions RT60 topts jump alot in the 64 hz band(1/3 octave setting) . 50 hz band and all other good reproducable. wy better precision as 1/3 ?

user44455555

Registered
Thread Starter
Joined
Jan 16, 2021
Posts
241
In topts display in REW can also see much diffrence between speakers at same place and microphone positions. is topts better as clarity for small rooms ?. Problem i notice since long time in my measure always the 64 hz band in topts jump randomly alot between repeat measures and diffrent speaker levels. so i ignore topts and trust clarity. because clarity give reproducable results with diffrent speakers or foam placement

But there is tell clarity is not good in small room so i look for another measure method that is accepted for small rooms and can be usefull to speaker case problems and speaker resonances and is usefull for treaten room
topts show als worse case resonances only problem is that on all speakers the 64 hz band jump alot even when only repeat the measure or with less or more level. the measures mdat is are here


here can see the results. only the 64 hz band jump so much. happen on most speakers. between 0.166 and 0.4 sec. the speakers stand firmly on the tripod, but it happen also in other places diffrent speakers. clarity did not have such problems and also show case resonances
topt.jpg
 
The measurements have a sharp dip at around 60 Hz so results in that band will not be reliable as there is little energy in that band. The measurements also have very different patterns of reflections, here's the JBL, for example:

1777894126838.png


RT60 measurements are based on a linear fit of the Schroeder integral. In acoustically large spaces the Schroeder integral is very close to a straight line on a dB scale as the resonances and reflections are much more dense, forming a diffuse field. RT60 is a meaningful measure above frequencies where that diffuse field exists. In small spaces the modal resonances are sparse at low frequencies and there are dominant reflections so there is no diffuse field and the assumptions underlying the RT60 calculation are not valid. Here's a measurement from a large space to illustrate the difference:

1777894643185.png


RT60, like clarity, is a measure that is appropriate for characterising large spaces. Waterfall, spectrogram and decay graphs are more useful for small spaces, whilst the impulse and ETC graphs show reflections.
 
I liike the measure also to decide speaker quality. In the spectrogram can see that the focal have worse resonances. But it show strange results depend on levels. i do the measures with measure ramped levels settings and set the level of speaker with check levels so they are same in db. The JBl 104 with 66 db volume measure have longer decay time as the jbl 104 with 76 db measure. I carefully set the level and SNR is also good.

or a diffrent ask . You should tell which speaker is the best with fewest case resonances. On which REW display do you think you see this best ?

focal 66 db

focal 65 spectrogram.jpg

jbl 76 db
jbl 104 76 db spectrogram.jpg

jbl 66 db have a 130 hz 180 hz resonance which is not in 76 db measure
jbl 104 66 db spectrogram.jpg


The focal have more resonances with 76 db (700 ms at 45 hz) level as with 66 db level which can be logical because of more case vibration

focal 76 db spectrogram.jpg
 
You should tell which speaker is the best with fewest case resonances. On which REW display do you think you see this best ?
To reliably detect enclosure resonances you would need an accelerometer attached to the enclosure. For driver resonances (and perhaps some indication of enclosure resonances) use the waterfall in CSD mode with suitably short time spans and windows, a near field measurement is required to minimise room contributions. The result is probably easier to examine in 3D mode, here's an example:

1777913144406.png


1777913241354.png
 
the CSD mode with 3d i not see before. I test and it look good, but problem i notice with diffrent level at same frequency. there is a normalize to peak at each frequency feature to avoid this, but it create huge noise. Is a normalize to maybe 60 db(or a choosable db values) not better ?. this do not boost noise on low frequencies. and when do this for all speakers they can compare
To reliably detect enclosure resonances you would need an accelerometer attached to the enclosure. For driver resonances (and perhaps some indication of enclosure resonances) use the waterfall in CSD mode with suitably short time spans and windows, a near field measurement is required to minimise room contributions.

Problem on speaker is the enclosure emit noise on all sides. also the thin metal backplate on active speakers is a high resonance risc. So to see how this really interact a nearfield measure is not usefull, and i gues this is the problem with the focal and kali. they maybe test it only the way you tell. speaker developers should use a small case as the JBL 104 and put it in a room and compare so that their speaker did not much more resonances as the small speakers

you see level is diffrent for both but because the focal is so worse you can still see the jbl 104 is better. but for good compare when can normalize to a db count is more usefull. I have do check levels in REW both same, but because every speaker have a diffrent FR it differ alot in the peak and average

focal.jpg

job 104.jpg

because in the JBL is weak on bass and need much boost, the noise is also much boost
jbl normalize to peak.jpg
normalize to peak.jpg
 
the AI have a CSD comparision tool i tell features it should have. there can clear see now, the focal have more resonances. the readme also create the google AI.

that a high resolution clarity not work good in small rooms i think the scientiest use bad speakers, and because of the small room the worse reflections from case(the metal backplate on active speakers) hit on a small room earlier the wall and is reflect and influence the room modes more see screenshot.
On the jbl the decay is smooth in bass . with the focal not with same settings . maybe such a CSD or CSD 3d compare tool can add in REW, because to use this program need save the impulse as 32 bvit float and load it in. it can not read direct REW data. this help to get better speakers when speaker developers also use it to design their case


screenshot.jpg




Python:
import numpy as np
import librosa
import librosa.display
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from scipy.ndimage import gaussian_filter1d
import os
import warnings

warnings.filterwarnings("ignore")

class CSDViewer:
    def __init__(self, root):
        self.root = root
        self.root.title("Acoustic Pro Analyzer: CSD & Dynamic Lag")

        screen_w = self.root.winfo_screenwidth()
        screen_h = self.root.winfo_screenheight()
        self.root.geometry(f"{int(screen_w*0.8)}x{int(screen_h*0.8)}")

        self.data_raw = {'A': None, 'B': None}
        self.sr = {'A': None, 'B': None}
        self.filenames = {'A': "Impulse A", 'B': "Impulse B"}
        self.diff_window = None
        
        self.norm_val = tk.StringVar(value="70.0")
        self.decay_range = tk.DoubleVar(value=-40.0)
        self.smoothing = tk.StringVar(value="1/12")

        ctrl_frame = tk.Frame(root)
        ctrl_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=5)

        tk.Button(ctrl_frame, text="Load A (Ref)", command=lambda: self.load_file('A'), width=12).pack(side=tk.LEFT, padx=2)
        tk.Button(ctrl_frame, text="Load B", command=lambda: self.load_file('B'), width=12).pack(side=tk.LEFT, padx=2)
        
        ttk.Separator(ctrl_frame, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=10)
        
        tk.Label(ctrl_frame, text="Normalize dB:").pack(side=tk.LEFT)
        ttk.Combobox(ctrl_frame, textvariable=self.norm_val, values=["60.0", "70.0", "80.0", "None"], width=7).pack(side=tk.LEFT, padx=5)

        tk.Label(ctrl_frame, text="Range dB:").pack(side=tk.LEFT, padx=(10,0))
        ttk.Combobox(ctrl_frame, textvariable=self.decay_range, values=[-20.0, -30.0, -40.0, -50.0, -60.0], width=7).pack(side=tk.LEFT, padx=5)

        tk.Label(ctrl_frame, text="Smoothing:").pack(side=tk.LEFT, padx=(10,0))
        ttk.Combobox(ctrl_frame, textvariable=self.smoothing, values=["None", "1/48", "1/24", "1/12", "1/6", "1/3"], width=7).pack(side=tk.LEFT, padx=5)

        self.norm_val.trace_add("write", lambda *a: self.update_all())
        self.decay_range.trace_add("write", lambda *a: self.update_all())
        self.smoothing.trace_add("write", lambda *a: self.update_all())

        tk.Button(ctrl_frame, text="Analyse Time-Lag", command=self.plot_time_diff, bg="#add8e6", font=("Arial", 10, "bold")).pack(side=tk.RIGHT, padx=20)

        self.fig, self.axs = plt.subplots(1, 2, figsize=(12, 6), sharey=True, sharex=True)
        self.canvas = FigureCanvasTkAgg(self.fig, master=root)
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        self.toolbar = NavigationToolbar2Tk(self.canvas, root)

    def load_file(self, slot):
        path = filedialog.askopenfilename(filetypes=[("Audio", "*.wav")])
        if path:
            try:
                self.filenames[slot] = os.path.basename(path)
                y, sr = librosa.load(path, sr=None)
                y = y / np.max(np.abs(y))
                C = librosa.cqt(y, sr=sr, fmin=30, n_bins=120, bins_per_octave=24)
                self.data_raw[slot] = np.abs(C)
                self.sr[slot] = sr
                self.update_all()
            except Exception as e: messagebox.showerror("Error", str(e))

    def get_processed_db(self, slot):
        data = self.data_raw[slot].copy()
        s_val = self.smoothing.get()
        if s_val != "None":
            factors = {"1/48": 0.5, "1/24": 1.0, "1/12": 2.0, "1/6": 4.0, "1/3": 8.0}
            data = gaussian_filter1d(data, sigma=factors[s_val], axis=0)
        
        S_db = librosa.amplitude_to_db(data, ref=np.max)
        norm = self.norm_val.get()
        if norm != "None":
            start_level = float(norm)
            for j in range(S_db.shape[0]):
                S_db[j, :] = S_db[j, :] - S_db[j, 0] + start_level
        return S_db

    def update_all(self):
        self.update_previews()
        if self.diff_window and self.diff_window.winfo_exists(): self.plot_time_diff()

    def update_previews(self):
        norm = self.norm_val.get()
        decay = self.decay_range.get()
        for i, slot in enumerate(['A', 'B']):
            if self.data_raw[slot] is not None:
                self.axs[i].clear()
                db_data = self.get_processed_db(slot)
                vmax = float(norm) if norm != "None" else 0
                vmin = vmax + decay
                librosa.display.specshow(db_data, x_axis='time', y_axis='cqt_hz', sr=self.sr[slot], ax=self.axs[i], vmin=vmin, vmax=vmax, fmin=30)
                self.axs[i].set_title(self.filenames[slot])
                self.axs[i].grid(True, which='both', alpha=0.2, color='white')
        self.canvas.draw()

    def plot_time_diff(self):
        if self.data_raw['A'] is None or self.data_raw['B'] is None: return
        thresh = self.decay_range.get()
        times = {'A': [], 'B': []}
        
        for slot in ['A', 'B']:
            spec = self.get_processed_db(slot)
            norm_offset = float(self.norm_val.get()) if self.norm_val.get() != "None" else 0
            spec -= norm_offset
            time_axis = librosa.frames_to_time(np.arange(spec.shape[1]), sr=self.sr[slot], hop_length=512)
            for freq_bin in spec:
                idx = np.where(freq_bin < thresh)[0]
                times[slot].append(time_axis[idx[0]] if len(idx) > 0 else time_axis[-1])

        diff = np.array(times['B']) - np.array(times['A'])
        freqs = librosa.cqt_frequencies(120, fmin=30, bins_per_octave=24)
        
        if not self.diff_window or not self.diff_window.winfo_exists():
            self.diff_window = tk.Toplevel(self.root)
            self.d_fig, self.d_ax = plt.subplots(figsize=(9, 8))
            self.d_canvas = FigureCanvasTkAgg(self.d_fig, master=self.diff_window)
            self.d_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
            self.d_toolbar = NavigationToolbar2Tk(self.d_canvas, self.diff_window)
        else: self.d_ax.clear()

        self.diff_window.title(f"Lag Analysis: Smoothing {self.smoothing.get()}")
        colors = ['#ff4d4d' if x < 0 else '#4d4dff' for x in diff]
        
        s_val = self.smoothing.get()
        h_factors = {"None": 0.02, "1/48": 0.03, "1/24": 0.04, "1/12": 0.06, "1/6": 0.1, "1/3": 0.2}
        bar_h = freqs * h_factors.get(s_val, 0.05)
        
        self.d_ax.barh(freqs, diff, height=bar_h, color=colors, alpha=0.8)
        
        max_l = np.max(np.abs(diff))
        limit = max_l * 1.3 if max_l > 0.01 else 0.05
        self.d_ax.set_xlim(-limit, limit)
        self.d_ax.set_yscale('log')
        self.d_ax.set_yticks([31.5, 63, 125, 250, 500, 1000, 2000, 4000, 8000])
        self.d_ax.get_yaxis().set_major_formatter(plt.ScalarFormatter())
        
        # Labels and Title (FIXED)
        self.d_ax.set_title(f"Lag: {self.filenames['B']} vs {self.filenames['A']} @ {thresh}dB\n(Blue = {self.filenames['B']} decays slower)")
        self.d_ax.set_xlabel("Time-Lag in Seconds (Right = Blue decays slower)")
        
        self.d_ax.axvline(0, color='black', lw=1.5)
        self.d_ax.grid(True, which='both', alpha=0.3)
        self.d_canvas.draw()

if __name__ == "__main__":
    root = tk.Tk(); app = CSDViewer(root); root.mainloop()

README: Acoustic Time-Lag & CSD Analyzer
Overview
This Python-based tool is designed as a specialized extension for loudspeaker impulse response analysis. While REW (Room EQ Wizard) is excellent for individual measurements, this tool focuses on the direct comparison of two impulses (e.g., comparing a reference speaker with a modified version or a different model).
Key Features
  • Normalized CSD View: Every frequency is locked to a selectable start level (Normalize dB). This allows for a pure comparison of the decay behavior (ringing) regardless of the absolute SPL.
  • Time-Lag Analysis: The core innovation of this tool. It calculates the time difference it takes for each frequency to drop below a certain threshold (Decay Range).
  • Visual Lag Overlay: A bar chart clearly shows which speaker is "slower" at specific frequencies.
    • Blue bars (right): Impulse B (Target) decays slower than Impulse A.
    • Red bars (left): Impulse A (Reference) decays slower than Impulse B.
  • High-Resolution Analysis: Uses Constant-Q Transform (CQT) with 24 bins per octave for superior low-frequency detail compared to standard STFT.
  • Synchronized Navigation: Zooming or panning in one CSD plot automatically updates the other for perfect side-by-side comparison.
Requirements
To run the source code, you need Python installed and the following libraries:
pip install numpy librosa matplotlib scipy
How to use
  1. Run the script: python csd_viewer.py
  2. Load your reference impulse (WAV) into Slot A.
  3. Load your target impulse (WAV) into Slot B.
  4. Adjust Smoothing (e.g., 1/12) and Normalize dB to align the visuals.
  5. Click "Analyse Time-Lag Overlay" to open the specialized comparison window.
Technical Note
The Time-Lag analysis is particularly useful for identifying narrow-band resonances that might be hard to spot in a standard waterfall plot but become obvious when compared to a cleaner reference.
 
The acoustic contribution of radiation from the enclosure is very, very small compared to the output of the drivers, even in a badly designed enclosure, and is not visible in the IR or in decay plots. That's why accelerometers are used to directly measure panel behaviour. The differences in the measurements are not related to the speaker enclosures.
 
The acoustic contribution of radiation from the enclosure is very, very small compared to the output of the drivers, even in a badly designed enclosure, and is not visible in the IR or in decay plots. That's why accelerometers are used to directly measure panel behaviour. The differences in the measurements are not related to the speaker enclosures.

and what can be the reason then ?. bad driver ? . the focal sound with Eq correct really very boomy and muddy with a EQ correct other speaker. the csd compare only show what i hear. I can hear that in blind test easy.
 
I compare with better resolution now also headphone and focal at 10 cm. only very few focal is slower(the time is scale, only 0.04 sec focal is slower. but on hearing pos focal is much slower as
A near field measurement is required to minimise room contributions. The result is probably easier to examine in 3D mode, here's an example:

I do a near field measure and compare headphone and the focal. here the diffrence is very small. only 40 ms (0.04 sec)is the focal slower as headphone. but on hearing place the jbl is much faster. . maybe the speaker case resonances are 1 time not much, but when the case is hit 10 times from the audio waves it summed up. the jbl 104 4.5 inch have a very rounded case. the kali have not such bass slowness measure , the kali 6,5 inch have a tweeter diffusor is more round in front and have a not so width case as the 6.5 inch focal. both have front bassports . this have now increased resolution for time. there happen really magic room interactions between diffrent speakers. maybe i should ask AI to add audio input output so it can do every second such a measure and display results and move with foam or basstraps to see how the results change in realtime


headphone focal 10 cm compare.jpg


kali focal compare.jpg

jbl focal compare.jpg
 
Back
Top