# Church Acoustic Analysis Suite — Project Reference ## Project Purpose A React single-file web application for acoustic analysis of a Plymouth Brethren Christian Church (PBCC) "Universal Hall" — specifically the MS6 ceiling speaker system. The tool models the hall geometry, computes speaker coverage per row, checks Haas window compliance, analyses flutter echo risk, connects to REW (Room EQ Wizard) API for measurements, and sends all data to a local Ollama LLM (or Claude API) for structured acoustic recommendations. --- ## Hall Geometry — Confirmed Measurements ### Critical geometry rule — read carefully **The CEILING IS FLAT.** It is one horizontal plane at a constant absolute height of **4300mm above the centre floor datum** across the entire main seating zone. **The FLOOR IS DISHED** (bowl shape). It is lowest at the centre (communion table area) and rises outward toward the perimeter. The apparent "ceiling height" (floor-to-ceiling clearance) decreases toward the perimeter solely because the floor rises. There is no slope in the ceiling itself over the main seating area. ### Plan dimensions | Parameter | Value | Source | |---|---|---| | Seating plan | 20,914mm × 20,914mm | SK-303 + confirmed | | Half-width (centre to wall) | 10,457mm | 20914 / 2 | | Row 1 front edge (inner radius) | 2,210mm from centre | Confirmed | | Row pitch (FFL to FFL) | 883mm | Confirmed | | Number of main rows | 8 | Confirmed | | Row 8 back face | 8,411mm from centre | SK-303 drawing | | Back aisle width | 2,046mm | 10457 − 8411 = 2046 ✓ | | Corner rows per corner | 4 rows | Confirmed | | Corner row pitch | 883mm (same as main) | Confirmed | | Corner row 1 front | ~620mm from edge of circular seating | Confirmed | **Row mid-bench radii (ear position):** | Row | Front edge (mm) | Mid-bench ear (mm) | |---|---|---| | 1 | 2,210 | 2,651 | | 2 | 3,093 | 3,534 | | 3 | 3,976 | 4,418 | | 4 | 4,859 | 5,301 | | 5 | 5,742 | 6,184 | | 6 | 6,625 | 7,067 | | 7 | 7,508 | 7,950 | | 8 | 8,391 | 8,833 → back face 8,411 | ### Section — ceiling and floor **Flat ceiling (main seating zone):** - Absolute height: **4300mm** above centre floor datum (0mm reference) - This is constant across the entire main seating zone - Floor-to-ceiling clearance at any radius = `4300 − floorRise(r)` **Dished floor:** - Centre (communion table zone, r ≈ 0–2210mm): **0mm** (lowest, flat zone) - Rises linearly from row 1 outward to row 8 - Total dish rise from centre to row 8 back (r=8411mm): **700mm** - Dish slope: 700mm over 8411mm ≈ **1:12** (approximately 1:8 per SK-303, measured from row 1) - Rise per row ≈ 87.5mm **Floor-to-ceiling clearance per row (flat ceiling @ 4300mm):** | Row | Floor rise (mm) | Floor-to-ceiling (mm) | |---|---|---| | Centre | 0 | 4,300 | | Row 1 mid | ~55 | ~4,245 | | Row 2 mid | ~140 | ~4,160 | | Row 3 mid | ~220 | ~4,080 | | Row 4 mid | ~310 | ~3,990 | | Row 5 mid | ~400 | ~3,900 | | Row 6 mid | ~480 | ~3,820 | | Row 7 mid | ~565 | ~3,735 | | Row 8 mid | ~650 | ~3,650 | | Row 8 back | 700 | 3,600 | **Corner area ceiling:** - The ceiling drops in the corner zones due to building structure (lower roof soffit) - Floor-to-ceiling behind last corner row: **2,950mm** - This is a structural change, not a continuation of the main flat ceiling **Corner geometry — 45° chamfer:** - The building corners are cut at 45° behind the last corner seating row - The perimeter wall in each corner is not a right-angle but a diagonal plane at 45° to the two adjacent side walls - This means corner listeners face the hall centre across the chamfered geometry, not across a square corner - Acoustic implication: the 45° angled surface redirects reflections diagonally inward rather than straight back — less flutter risk than a 90° corner, but creates oblique reflections toward the main seating zone - The geometry code currently approximates corner rows by radial position only; the 45° chamfer is not yet explicitly modelled in plan view **Corner staging void (HVAC):** - Below the corner seating there is a structural void — the corner seating is raised staging - This void is used as a plenum/duct for the HVAC ventilation system - The surfaces of this void (floor and underside of staging) are insulated to reduce sound transmission into and out of the ventilation path - Acoustic implication: the insulation damps what would otherwise be hard reflective surfaces; effectively the corner underfloor is acoustically treated - HVAC noise ingress from this void into the seating area is a risk factor — any REW measurement showing low-frequency noise floor elevation in the corner zone should be investigated against HVAC operating state **Ear height above local floor:** 1,100mm (seated listener, consistent throughout) **Ceiling tile grid:** 600mm × 600mm acoustic tiles — constrains speaker positions to 600mm increments from the grid --- ## Speaker System ### Layout — all flush mounted, NO drop rods All speakers sit flush with the ceiling (acoustic face at ceiling level). There are no drop rods anywhere. | Ring | Count | Radius from centre | Ceiling height at radius | Notes | |---|---|---|---|---| | Centre | 1 | 0mm | ~4,300mm | Over communion table | | Inner ring | 8 | 5,720mm | ~4,060mm | Between rows 4–5 | | Outer ring | 8 | 8,600mm | ~3,580mm | Just inside row 8 back (8,411mm) | | Corner | 8 (2 per corner) | 10,310mm | ~2,950mm | 1,500mm from side walls | **Total: 25 speakers, all flush with ceiling.** Speaker height above centre floor datum = ceiling height at that radius (since drop rod = 0). ### Corner speaker position - Radius: 10,310mm from centre - 1,500mm from each side wall (wall at 10,457mm → one axis coordinate = 8,957mm) - Cartesian: approximately (8,957mm, 5,107mm) from centre (solving 10,310² = 8,957² + y²) ### MS6 Speaker — Technical Specification (ISS 1, June 2024, Dudley Wilkin) **Drivers:** - LF: Visaton FRS8M — 8Ω, 30W, 88dB/1W/1m, resonant freq 125Hz, response 100Hz–20kHz - HF: Visaton G20SC — 8Ω, 20mm soft dome tweeter, 88dB/1W/1m, response 1,200Hz–30kHz - Driver spacing: 90mm centre-to-centre **Crossover innovation (key design element):** - Conventional crossovers cause destructive interference lobes at 2–5kHz — exactly the consonant intelligibility band (P, T, F, S) - MS6 uses MIDAS MR12 digital mixer as active crossover with a **hard frequency gap** - LF driver hard cut-off: **2,792Hz** - HF driver starts: **3,225Hz** - Gap: **433Hz** wide, centred around 3kHz — inaudible but eliminates driver overlap - Result: zero horizontal lobes, near-circular polar response to 55° off-axis **Polar response (measured):** - At 30° off-axis: near-perfect circular across 200Hz–10kHz - At 55° off-axis: still excellent circular response - **Maximum coverage angle: 55° off-axis from directly below** **Signal chain:** ``` Mono audio source → MIDAS MR12 Ch1 (Acoustic TEQ insert for room EQ) → Bus 1 → AUX1 → LF PEQ (crossover cut) → LF amplifier → 100V line → LF transformers → Bus 2 → AUX2 → HF PEQ (crossover cut) → HF amplifier → 100V line → HF transformers ``` **MR12 AUX1 (LF driver) PEQ crossover settings:** - Low: −15dB @ 3k09, Q=10 - Low2: −15dB @ 3k31, Q=10 - LoMid: −15dB @ 3k43, Q=10 - HiMid: +4dB @ 2k79, Q=10 **MR12 AUX2 (HF driver) PEQ crossover settings:** - Low: −15dB @ 2k79, Q=10 - Low2: −15dB @ 2k99, Q=10 - LoMid: +5.5dB @ 3k20, Q=10 - HiMid: +0.5dB @ 3k55, Q=10 **Acoustic EQ (Channel 1 TEQ insert):** Applied full-range before the crossover split, corrects room acoustics per installation. **Measurement protocol:** - Use ARTA software with impulse window (minimum 10ms window) - Never use TrueRTA or any continuous-signal method - Mic minimum 2m from speaker (far field), at 90° horizontal so both drivers are equidistant ### Historical system context (Dudley Wilkin documentation, 1970–2024) - **1970s:** 4 corner column speakers — poor coverage, excited all room echoes - **1982:** First ceiling ring — 8 speakers at row 5 radius + 4 diagonal + corner staging speakers - **1986:** Anti-phasing breakthrough — quadrant anti-phasing creates null lines along aisle centrelines; each listener hears only one speaker segment → major intelligibility improvement - **2010:** Proposed new layout — centre speaker (rows 1–3) + main ring (rows 4+) + outer ring fill; reduces count from 21 to 13 - **MS6 (2024):** New bespoke two-driver speaker with gap crossover replacing earlier drivers **Anti-phasing principle:** Adjacent speaker segments operate in opposite polarity. Null lines form along aisle centrelines. A listener on either side of an aisle hears only the speaker from their segment. This eliminates cross-segment interference and dramatically improves STI (Speech Transmission Index). --- ## Acoustic Analysis Objectives ### Key metrics (in priority order) 1. **STIPA** — Speech Transmission Index for PA systems (IEC 60268-16); the single number that captures all intelligibility-degrading effects; target ≥ 0.75 (Excellent) 2. **Direct-to-Reverberant ratio** per seat — primary intelligibility driver; improved by anti-phasing (each listener hears only their nearest segment) 3. **Haas window compliance** — speakers reaching a listener more than 30ms after the nearest speaker cause echo/colouration; 10–30ms causes tonal colouration; both degrade STI modulation depth 4. **Off-axis angle** per speaker per row — must be within 55° MS6 coverage cone 5. **SPL at ear** per row — from inverse square law using speaker sensitivity and power 6. **Flutter echo** — flat ceiling + rising floor creates parallel surfaces at decreasing clearance toward perimeter; harmonics in 500–4kHz band directly degrade STI ### STIPA measurement **STIPA** (Speech Transmission Index for PA systems) is the simplified single-measurement STI method defined in IEC 60268-16. It uses a pre-recorded test signal with specific modulation at frequencies across seven octave bands (125 Hz–8 kHz), played through the live PA system and measured with a handheld analyser. **IEC 60268-16 rating scale:** | Rating | STI range | Colour | | ------ | --------- | ------ | | Excellent | 0.75–1.00 | Green | | Good | 0.60–0.75 | Blue | | Fair | 0.45–0.60 | Amber | | Poor | 0.30–0.45 | Orange | | Bad | 0.00–0.30 | Red | Target for this hall: ≥ 0.75 (Excellent) Measurement equipment: NTI Acoustilyzer AL1 or equivalent STIPA-capable analyser. Key degradation mechanisms in this hall (computed risk factors in Analysis tab): - Haas echo rows (>30ms) — directly reduce modulation depth in all STI bands - Off-axis coverage gaps (outside 55° cone) — reduces signal level, lowering D/R ratio - Flutter harmonics in 500–4kHz speech band — reduce modulation depth at those frequencies - Tile diffraction at ~571Hz — may introduce a spectral dip in a critical STI band - HVAC noise via corner staging void — raises background noise floor in corner seating STIPA is valid for this hall type (it uses a modulated test signal, not raw SLM levels). Standard pink noise SLM readings are still invalid for checking individual speaker coverage due to anti-phase null lines. --- ### How to measure STIPA in this hall #### Equipment - **STIPA analyser** — NTI Acoustilyzer AL1 (or XL2 with STIPA option), or any IEC 60268-16 compliant handheld STI meter. The analyser contains the standardised STIPA test signal internally; no separate playback source is required. - **Measurement microphone** — the analyser's own omni mic capsule at ear height (1,100 mm above local floor level, which rises with the dish). #### Operating conditions before measuring 1. **All speakers active** — every ring that will be in use during a meeting must be powered on. STIPA measures the whole system as the listener experiences it. 2. **Anti-phasing active** — the MIDAS MR12 must be configured with the normal quadrant (or per-speaker) anti-phasing wiring. Do not bypass or disable anti-phasing for the measurement. 3. **HVAC running** — measure with the ventilation system in its normal operating state. This captures the real background noise contribution from the corner staging voids and ductwork. Record a separate background noise level (HVAC on, no audio signal) as reference. 4. **Hall otherwise empty** — audience absorption changes the reverb time; an occupied-hall measurement requires people in seats. 5. **Signal level** — set the PA to normal meeting operating level before starting. The STIPA signal is output at whatever level the system is set to, so level calibration matters. #### Measurement positions The anti-phasing null lines run along the aisle centrelines. A microphone placed exactly on a null line will read an artificially low STI — this is acoustically correct at that point (it is genuinely poor) but is not representative of normal seating positions. Measure **between** null lines. Recommended positions (minimum set): | Zone | Mic position | Speaker dominant | Notes | | ---- | ------------ | ---------------- | ----- | | Centre zone | Row 2, mid-bench, left of aisle | Centre (×1) | Single speaker — no null lines | | Inner ring | Row 5, mid-bench, between two inner speakers | Inner ring | Avoid aisle centreline | | Outer ring | Row 8 mid, between two outer speakers | Outer ring | Near row 8 back face | | Back aisle | At back aisle centreline, between outer speakers | Outer ring | Check rear coverage | | Corner C1 | Corner row 2, mid-bench | Corner (×2/corner) | One side of null between corner pairs | | Corner C2 | Corner row 2, opposite corner | Corner (×2/corner) | Verify symmetry | For a full survey, take at least two readings per row (one each side of the aisle centreline nearest to that seating position) to confirm the null-line boundary is where the geometry predicts it. #### Step-by-step procedure 1. Set up the PA at normal operating level. Confirm all rings are active and anti-phasing is correct. 2. Start the HVAC, wait 2 minutes for the system to stabilise, then measure and record the background noise level (A-weighted dBA and NC curve if possible) at each measurement position. This is used by the STIPA algorithm internally but is worth recording separately. 3. Position the mic at Row 2 mid-bench, ear height (local floor + 1,100 mm). Start the STIPA measurement. Wait for the analyser to complete its averaging period (typically 15 seconds for STIPA). Record the STI value and IEC rating. 4. Move to each position in the table above, repeating the measurement. Allow 15 seconds settling time after moving before starting each measurement. 5. At any position rated Fair or below, take a second measurement 300–400 mm to the left and right of the original position to distinguish between a genuine coverage gap and an accidental null-line hit. 6. Record all results in a log: position, STI value, IEC rating, HVAC state, PA level, date/time, operator. 7. Photograph the MR12 fader positions, AUX send levels, and PEQ settings at the time of measurement. #### Interpreting results - **Excellent (≥ 0.75) everywhere** — system is performing correctly. - **One zone consistently Good (0.60–0.75)** — likely a coverage or level issue in that zone; check off-axis angles and SPL in the Analysis tab. - **Fair or below in any seat** — investigate: Haas violations (check delay values in Coverage tab), off-axis angles, flutter (compare flutter harmonic frequencies against any spectral dip visible in REW), or background noise. - **Corner seating consistently lower than main seating** — HVAC noise ingress via staging void is the most likely cause; re-measure with HVAC off to isolate. - **STI varies widely left-to-right at same row** — null-line boundaries may be misaligned with seating positions; review anti-phasing wiring orientation. #### What STIPA does not replace STIPA gives a single-number intelligibility score but does not identify the cause. Use it alongside: - The **Coverage tab** to diagnose Haas violations and off-axis angles - **REW impulse measurements** (ARTA method, minimum 10 ms window) to see the actual frequency response and early reflection pattern at each position - The **Flutter analysis** section to correlate any spectral dips with flutter harmonic frequencies --- ### Haas window rules applied in code - **Primary:** nearest speaker to that row (0ms excess) — green - **OK:** 0–10ms Haas excess — blue (slight colouration, acceptable) - **Warn:** 10–30ms Haas excess — amber (audible colouration, degrades STI) - **Danger:** >30ms Haas excess — red (distinct echo, significantly degrades STI) ### Flutter echo analysis - Flutter frequency at each zone = speed of sound / (2 × floor-to-ceiling clearance at ear level) - At row 8 (clearance ≈ 2,500mm ear-to-ceiling): flutter fundamental ≈ 69Hz, harmonics at 137Hz, 206Hz — in speech fundamental range - 600mm tile grid creates diffraction grating effect at ~571Hz - Harmonics landing in 500–4kHz range are flagged as STI risk factors in the Analysis tab --- ## Codebase ### Files | File | Description | |---|---| | [church-acoustic-suite.jsx](church-acoustic-suite.jsx) | Complete single-file React application — all components, geometry engine, UI | | [CLAUDE.md](CLAUDE.md) | This project reference document | | [CLAUDE_CHAT.md](CLAUDE_CHAT.md) | Full transcript of the original design chat (historical reference) | | [README.md](README.md) | Repository readme | ### App structure (church-acoustic-suite.jsx) **Constants:** - `C` — colour palette (dark theme) - `HALL_DEFAULTS` — confirmed hall geometry (20,914mm sq, 8 rows, dish floor, flat ceiling) - `SPEAKER_DEFAULTS` — 4 rings: centre, inner (×8, r=5720mm), outer (×8, r=8600mm), corner (×8, r=10310mm) - `SETTINGS_DEFAULTS` — Ollama/Claude API config, REW host/port, system prompt **Geometry engine:** - `ceilHeightAtRadius(r, hall)` — returns floor-to-ceiling clearance at radius r - `floorHeightAtRadius(r, hall)` — returns floor rise above centre datum at radius r - `computeHallGeometry(hall)` — builds rowData array with per-row geometry - `computeCoverage(hall, speakers, rowData)` — computes slant distance, delay, Haas status, off-axis angle, SPL per row×ring combination - `computeFlutter(hall)` — calculates flutter echo fundamentals and harmonics **Components:** - `SectionDiagram` — SVG cross-section showing ceiling, floor dish, speaker positions and coverage rays - `CoverageHeatmap` — SVG grid (rows × speaker rings) colour-coded by Haas status - `HallTab` — Hall dimension inputs, ceiling/dish inputs, row geometry table, flutter analysis - `SpeakerTab` — Per-ring editor (radius, power, sensitivity, phase, drop rod) - `CoverageTab` — Coverage heatmap + full data table - `AnalysisTab` — REW API connection + Ollama/Claude analysis with streaming - `SettingsTab` — LLM provider selection (Ollama or Anthropic), REW host/port, system prompt - `App` — Root with tab navigation and log ### Known code issue to fix The `ceilHeightAtRadius()` function currently stores three separate ceiling heights (`ceilAtRow1Front: 4338`, `ceilAtRow8Back: 3600`, `ceilAtCornerBack: 2950`) and interpolates between them — this incorrectly models a **sloped ceiling**. **Correct model:** ceiling is flat at a single absolute height (`ceilFlat = 4300mm`) above centre datum. Floor-to-ceiling at any radius = `4300 − floorHeightAtRadius(r)`. The corner area ceiling (2950mm) is a separate structural change that should be applied only in the corner zone. The `HALL_DEFAULTS` and geometry engine need to be refactored to: ```js ceilFlat: 4300, // flat ceiling, mm above centre floor datum cornerCeilClearance: 2950, // floor-to-ceiling in corner zone (structural drop) dishRise: 700, // floor rise from centre to row 8 back face ``` --- ## REW API Integration - REW runs locally on port 4735 by default - REW must be started with API enabled (Preferences → API, or `-api` launch flag) - Browser must run on same machine as REW (localhost only by design) - Full API spec: https://www.roomeqwizard.com/help/help_en-GB/html/api.html ### Endpoints used | Endpoint | Purpose | | -------- | ------- | | `GET /application` | Connection test | | `PUT /roomsim/room-size` | Push hall dimensions to REW room simulator | | `GET /roomsim/frequency-response?micposition=Main&ppo=24` | Simulated frequency response (Base64 big-endian float32) | | `GET /measurements` | List all saved measurements (returns array with uuid, title) | | `GET /measurements/:id/rt60?octaveFrac=1` | Per-octave RT60 values for STI calculation | | `GET /measurements/:id/impulse-response` | Raw impulse response (Base64 big-endian float32) | | `GET /measurements/:id/frequency-response` | Frequency response for a saved measurement | ### STI from REW impulse response REW can calculate STI internally (via its STI panel, IEC 60268-16:2020 indirect method). The app implements the same calculation: 1. Connect to REW — the app fetches the saved measurement list automatically 2. Select a measurement from the dropdown in the Analysis tab 3. Click **Compute STI** — the app calls `GET /measurements/:id/rt60?octaveFrac=1` and runs the IEC 60268-16 Schroeder/Houtgast–Steeneken calculation 4. The result shows overall STI, per-band Transmission Index (TI), and per-band T60 **Calculation method** (implemented in `src/sti.js`): - For each of the 7 STI octave bands (125 Hz–8 kHz) and 14 modulation frequencies (0.63–12.5 Hz): - MTF from T60: `1 / √(1 + (2π × Fm × T60 / 13.8)²)` (Schroeder formula) - Optional noise correction: `MTF_eff = MTF × 10^(SNR/10) / (1 + 10^(SNR/10))` - Apparent SNR: `10 × log10(MTF_eff / (1 − MTF_eff))`, clamped to [−15, +15] dB - Band TI: `(SNR_apparent + 15) / 30`, averaged over 14 modulation frequencies - STI = `Σ(α_k × TI_k) − Σ(β_k × √(TI_k × TI_{k+1}))` using male speech weights (IEC Table B.1) - No background noise correction is applied unless per-band SNR is provided **Important:** The RT60 method gives a theoretically exact STI only for a diffuse reverberant field. In this hall with anti-phasing, each listener hears primarily one speaker, so the effective RT60 driving that listener's intelligibility is different from the room RT60. Treat the REW-derived STI as an upper bound; compare with measured STIPA at representative seat positions. --- ## LLM Integration **Ollama (preferred — local/private):** - Requires `OLLAMA_ORIGINS=* ollama serve` for CORS - Models: pull e.g. `ollama pull llama3.2`, `mistral`, `qwen2.5` - Endpoint: `POST /api/chat` with streaming **Anthropic Claude (fallback):** - API key entered in Settings tab - Endpoint: `POST https://api.anthropic.com/v1/messages` - Default model: claude-sonnet-4-20250514 **Analysis prompt:** Sends complete hall geometry, per-row coverage data (slant, delay, Haas, off-axis, SPL), flutter echo analysis, and optional REW frequency data. Requests structured sections: hall assessment, coverage issues, speaker position optimisation, Haas/delay analysis, anti-phasing assessment, MR12 EQ recommendations, ARTA measurement protocol. **Analysis prompt:** You are an expert acoustic consultant specialising in distributed ceiling speaker systems for large assembly halls. You have deep knowledge of accoustic ceiling speaker layout and design, including the MS6 speaker (Visaton FRS8M + G20SC drivers), the MIDAS MR12 active crossover with its 433 Hz gap at 3 kHz, anti-phasing techniques, and the Haas effect in multi-speaker environments. Analyse the provided coverage data and give specific, technical, actionable recommendations. --- ## Build / Run This is a standalone React JSX file intended for use in a CodeSandbox, Vite React project, or similar. It uses only React hooks (`useState`, `useCallback`, `useRef`, `useEffect`) with no external dependencies beyond React itself. To run locally: 1. Create a Vite React project: `npm create vite@latest -- --template react` 2. Replace `src/App.jsx` with `church-acoustic-suite.jsx` 3. `npm run dev` 4. Start REW on same machine with API enabled 5. Start Ollama with CORS enabled: `OLLAMA_ORIGINS=* ollama serve`