Überblick
Eingabe: Der Nutzer gibt Raummaße / Form / Materialien, Lautsprecher- und Hörposition ein — oder die Geometrie wird per LiDAR (RoomPlan) erfasst.
- Lokalisierung früher Reflexionen
- Vollständige RT60 / EDT / C50 / C80 / D50 (gemessen + simuliert)
- Liste der Raummoden-Frequenzen
- Panel-Layout (Position, Anzahl, Produkt-SKU)
- 3D-Visualisierung + Reflexionsenergie-Heatmap
- PDF-Export in Montagehandbuch-Qualität
- Messung über das iPhone-Mikrofon (Sweep / Klatschen)
Rechteckige, L-, U-förmige und beliebige einfache Polygon-Räume werden unterstützt; die Berechnung nutzt das echte Polygon, nicht die Bounding Box.
Akustikmodell
2.1 Sabine RT60
Die klassische Sabine-Formel:
- $V$
- Raumvolumen (m³); bei Polygonen Shoelace-Fläche × Höhe, abzüglich Hindernissen (Stützen / Träger).
- $\sum_i S_i \alpha_i$
- Fläche × Absorptionsgrad je Fläche. Koeffizienten über 6 Oktavbänder (125 / 250 / 500 / 1k / 2k / 4k Hz).
- $0.161$
- = 24·ln(10)/c, mit c = 343 m/s (20 °C).
Die App liefert 5 Ausstattungs-Presets (leer / harte Oberflächen / Möbel / bereits behandelt / Studio), je mit Absorptionskurve pro Band. Auch das Ziel-RT60 ist eine 6-Band-Kurve, passend zu Normen wie EBU Tech 3276 / THX, die einen längeren Tiefbass-Nachhall zulassen.
2.2 Image-Source-Methode (ISM)
Erste Reflexionen per Spiegelung: Quelle an der Wand spiegeln; der Schnittpunkt der Strecke „Spiegelquelle → Hörer“ mit der Wand ist der Reflexionspunkt.
- Rechteckiger Raum: Spiegelung an allen 6 Ebenen — geschlossene Lösung.
- Nicht rechteckig (
PolygonReflectionCalculator): jedes Wandsegment ausRoomPolygon.walls()spiegeln, Parameter s ∈ [0, 1] prüfen, Reflexions-XY muss im Polygon liegen.
Mehrfachreflexionen (Ordnung 1–3) implementiert MultiOrderReflectionCalculator nach dem rekursiven Ansatz von pyroomacoustics:
- Alle Wandsequenzen [s₁, …, sₙ] aufzählen, benachbarte Wände verschieden
- Spiegelquellen-Iteration: S₀ = Quelle, Sₖ = mirror(Sₖ₋₁, sₖ)
- Rückverfolgung vom Hörer: Sₙ → Hörer trifft sₙ in Pₙ; Sₙ₋₁ → Pₙ trifft sₙ₋₁ in Pₙ₋₁; rekursiv zurück
- Jeder Schnittpunkt muss im gültigen Wandbereich liegen und t ∈ (0, 1)
2.3 Pfadenergie
Breitband-Energie als Funktion von Reflexionsanzahl und Distanzdämpfung:
- α wird auf [0, 0.99] begrenzt, damit α = 1 kein log(0) oder pow(negativ, N) = NaN erzeugt
- Distanzdämpfung folgt der sphärischen Ausbreitung 1/r²
- Umrechnung in dB über 10·log₁₀ (Energiedomäne)
Die Energie pro Band wird für die 6 Oktavbänder separat berechnet (Koeffizient aus finishProfile.absorptionByBand).
2.4 RT60-Schätzung (gemessen)
ImpulseResponseEstimator:
- Matched Filtering: der aufgenommene 0,45-s-Sweep wird invers gefiltert (ESS / Farina) zu einem Impuls, ~+40 dB effektiver SNR
- rmsEnvelope: gleitende RMS-Hüllkurve
- Schroeder-Rückwärtsintegration: E_back(t) = ∫ₜ^∞ s²(τ) dτ, mit 10·log₁₀ normiert ergibt die EDC-Kurve
- Lineare Steigungsregression: bevorzugt T30 [−5, −35 dB] je SNR, sonst T20 [−5, −25] oder T10 [−5, −15]
- RT₆₀ = −60 / Steigung
RT60 pro Band: auf den gefilterten Samples Bandpass an ISO-Oktavmitten (250 / 500 / 1k / 2k / 4k), Q = √2, dann Schroeder je Band.
2.5 RIR-Simulation
SimulatedRIRGenerator wandelt Mehrfachreflexionspfade in diskrete Impulse in einem 8 kHz × 0,5 s PCM-Puffer:
for path in reflectionPaths:
t = path.totalDistance / speedOfSound
amp = 10^(path.relativeEnergyDB / 20)
impulse[round(t * sampleRate)] += amp
Den Direktschall-Impuls hinzufügen, dann Schroeder-Integration für EDT / RT60 / C50 / C80 / D50.
2.6 Raummoden
Modale Frequenzen eines rechteckigen Raums:
Klassifiziert nach Anzahl der von null verschiedenen nₓ, n_y, n_z: 1 = axial, 2 = tangential, 3 = schräg.
Nicht rechteckige Räume schätzen Moden über die Bounding Box (exakte Moden brauchen FEM); in der UI ausgewiesen.
Geometriesystem
3.1 Unterstützte Formen
- Rechteck — Breite × Länge × Höhe
- L-Form — eine nw × nl-Kerbe an einer Ecke entfernen
- U-Form — eine nw × nl-Kerbe mittig an einer Wand entfernen
- Benutzerdefiniert — beliebiges einfaches Polygon (≥ 3, ≤ 24 Ecken)
Alle nicht rechteckigen Formen werden über RoomPolygon.from(shape:, bboxWidth:, bboxLength:) zur CCW-Eckenliste. Die Akustik nutzt diese Ecken direkt, nicht die Bounding Box.
3.2 Polygon-Geometrie-Werkzeuge
- Fläche — Shoelace-Formel
- Umfang — Summe der Kantenlängen
- contains(x, y) — Ray-Casting mit Rand-Epsilon (verhindert Fehler an Ecken)
- clamped(point) — Rechtecke nutzen den schnellen Pfad [0, w] × [0, l]; andere das echte [minₓ, maxₓ] × [min_y, max_y] aus
polygon.boundingBox, prüfen polygon.contains und Binärsuche zum Bbox-Zentrum, falls außerhalb - walls() — zerlegt das Polygon in nach Innennormalen klassifizierte Segmente (leftWall / rightWall / frontWall / backWall), sodass der rechteckige Panel-Empfehler wiederverwendet wird
- PolygonValidator.isSimple — prüft ein nicht selbstschneidendes einfaches Polygon
3.3 RoomPlan-Scan
Auf iOS 17+ wird RoomCaptureSession genutzt; CapturedRoomMapper bildet den Scan auf ScannedRoom ab (wallSegments + openings + furniture). Koordinaten-Mapping:
Gescannte nicht rechteckige Räume durchlaufen RectangularRoomSimplifier.polygonShapeFromScan, das wallSegments zu Polygon-Ecken verkettet. RoomPlan-Endpunkte haben typ. 3–8 cm Fehler; eine 12-Segment-Schleife kann 5 cm akkumulieren, daher 12 cm Schließ-Epsilon. Schließt die Kette nicht, Rückfall auf die Bounding Box und Aufforderung zum erneuten Scan.
3.4 Freier Vertex-Editor
FreeVertexEditorView erlaubt das Ziehen von Ecken auf einem SwiftUI-Canvas:
- Langes Drücken zum Löschen (bei ≥ 3 Ecken)
- Das Ziehen speichert den letzten gültigen Zustand in
dragSnapshot; eine selbstschneidende Position wird still zurückgesetzt - 0,05-m-Raster-Einrastung
- Ein Rand von max(0,5, bbox × 0,2) m außerhalb der Bbox erlaubt das Ziehen über die Bbox hinaus (die Elternansicht vergrößert room.width / length automatisch)
Hörzonen & Multi-Listener
AcousticConfiguration.listeners unterstützt 1..N Hörpositionen. Die Analyse führt die Image-Source-Methode je Hörer aus und fügt Reflexionspunkte in ein Array zusammen (primärer Hörer zuerst). Der Empfehler gewichtet jede Wand nach Punktanzahl (cap = max(6, speakerCount × listenerCount)); mehrere Sitze an einer Wand treiben sie ans Budgetlimit.
- In 3D nutzen sekundäre Hörer Opacity 0,45 + Scale 0,75
- Draufsicht (RoomPlanView): primäre Punkte volle Farbe r=5, sekundäre Opacity 0,45 r=3
- Die Liste (RecommendationView) zeigt nur primäre Punkte; Fußnote „+ N Reflexionen bereits berücksichtigt“
Mehrfachreflexionen / RIR / Heatmap werden weiterhin einmal für den primären Hörer berechnet (O(speakers × surfaces³) je Hörer ist zu schwer, RIR ist eine Einzelpunkt-Metrik).
Empfehlungssystem
PanelPlacementRecommender liefert [PanelRecommendation], je mit: Position (links / rechts / Decke / vorn / hinten / Ecke), Anzahl, Priorität (essenziell / stark / empfohlen / optional), effectivenessMultiplier, targetReflectionPointId.
5.1 Proportionale Zuteilung (größter Rest)
Das alte gierige add() ließ Wände verhungern: linke Wand verbrauchte das Budget → rechte 0; bei knappem Budget auch Decke / vorn / hinten. Neu: Methode des größten Rests:
- Raw-Desired je Wand berechnen (Punktanzahl + Raumtiefe + Anwendungsfall)
- floor(raw × budget / total) als Anfangszuteilung
- Wände mit raw > 0 erhalten ≥ 1 Panel (keine nachgefragte Wand verhungert)
- falls Summe > Budget, vom kleinsten Bruchrest aufwärts kürzen
- Links/Rechts-Symmetrie: bei raw[0] == raw[1] alloc[0] == alloc[1] erzwingen
- Restbestand nach größtem Bruchteil umverteilen
5.2 Anwendungsfall-Vorlagen
Ohne platzierte Lautsprecher gelten anwendungsspezifische Vorlagen:
- Besprechungsraum: Decke 35 % + Rückwand 20 % + je Seitenwand 12 %
- Podcast: direkt vor dem Mikrofon 25 % + je Seite 18 % + Decke 20 %
- Studio: symmetrische Vier-Wand-Dämpfung + erzwungene 4 Eck-Bassfallen
- Allgemeines Hören: symmetrische Basis + Hinweis, Lautsprecher hinzuzufügen
5.3 Physikalische Muss-Maßnahmen umgehen das Budget
Eck-Bassfallen werden bei dichten 80–150-Hz-Moden auf 4 erzwungen und umgehen das totalPanel-Limit. Grund: In Ecken ist der Schalldruck am höchsten, Bassfallen dort ~1,5× effizienter — physikalische Notwendigkeit.
5.4 Hörnahe Gewichtung
Ein Nachlauf markiert Empfehlungen innerhalb 1,5 m eines Hörers als .nearListener, setzt effectivenessMultiplier ×1,15 und vermerkt dies in Titel / Begründung.
Messung
6.1 ESS-Sweep
Nach Farinas Methode:
- Logarithmischer Sweep 180 Hz → 4 kHz, 0,45 s
- Inversfilter (
makeInverseSweep) = Zeitumkehr + 1/freq-Amplitudenkompensation - Aufnahme via AVAudioRecorder als AAC m4a (44,1 kHz mono)
- Zurücklesen → Matched Filter → analyzeImpulse
6.2 Klatsch-Impuls
Ohne Wiedergabe — der Nutzer klatscht einmal kräftig, 3 s Ausklang werden aufgenommen. Direkt zu analyzeImpulse (kein Matched Filter nötig; ein Klatschen ist bereits ein schmaler Impuls). Deckt das 125-Hz-Band ab, das der Sweep (ab 180 Hz) verfehlt.
6.3 Gemessen vs. simuliert
MeasuredVsSimulatedCard nimmt das letzte gemessene RT60 und zeigt die Abweichung zum simulierten RT60:
6.4 Umgebungsrauschen & Kalibrierung
AmbientNoiseEstimator nimmt ein stilles Segment auf, um Grundrauschen dBFS / Spektrum zu berechnen und den SNR zu bewerten. DelayCalibrationService bestimmt per Nahfeld-Wiedergabe + Aufnahme die Wiedergabe-zu-Aufnahme-Latenz (typ. 80–150 ms).
Visualisierung & Ausgabe
7.1 3D-Ansicht
Room3DController rendert mit SceneKit: Wände / Boden / Decke / Lautsprecher (dünnes Gehäuse + Akzent + Kanal-Pille) / Hörer (FE-Glyph + Stiel) / Hindernisse (transluzente SCNBox) / empfohlene Panels / Mehrfachreflexions-Pfadsegmente / Reflexionsenergie-Overlay.
7.2 Reflexionsenergie-Heatmap
ReflectionEnergyMapGenerator akkumuliert die Energie jedes Pfads auf das uCells × vCells-Raster der Wand (Standard 12×6). Eine Karte je Wand, in 3D als Farbtemperatur-Layer (blau → rot).
EnergyHeatmapRenderer wandelt das Raster in ein UIImage: bikubisches Upsampling + Magma-Colormap + Alpha nach Absolutenergie.
7.3 RIR-Faltungs-Vorschau
RIRPreviewPlayer (AVAudioEngine + AVAudioPlayerNode) faltet ein beliebiges trockenes Signal (Stimme / Drums / Piano) mit dem simulierten RIR — hörbar „unbehandelt vs. behandelt“.
Zeitbereichs-Faltung RIRConvolver.convolve(dry:rir:) direkt O(N × M) (N = trockene Samples, M = RIR), Peak-normiert auf 0,9 gegen Clipping.
7.4 PDF-Montagehandbuch
ReportExporter rendert ein A4-PDF (595 × 842 pt) via UIGraphicsPDFRenderer:
- Raumübersicht + 3D-Schnappschuss
- 6-Band-RT60-Balkendiagramm gemessen vs. Ziel
- Reflexionspunkt-Tabelle + Moden-Tabelle + Risikoübersicht
- Empfohlene-Panel-Tabelle + Einkaufsliste (BOM)
- Geist-Schrift + PingFangSC-Fallback (CoreText cascadeList, damit CJK nicht als □□□ erscheint)
7.5 Projekt-Persistenz
ProjectStore (Actor) schreibt ~/Library/Application Support/AcouVero/projects.json: [RoomProject], je mit Room + Lautsprechern + Hörern + Empfehlungen + panelCountOverrides.
MeasurementStore (Actor) schreibt measurements.json: [MeasurementResult], per roomId mit dem Projekt verknüpft.
Beide nutzen elementweise Decode-Wiederherstellung (Throwable<T>-Wrapper) — ein defekter Datensatz lässt nicht die ganze Liste scheitern.
Engineering-Hinweise
8.1 SwiftUI + @Observable
Das ViewModel (AcouVeroViewModel) nutzt Swift 5.9 @Observable — Statusfelder direkt beobachtet, kein Combine. AcousticAnalysis-Ergebnisse werden per Equatable-Vergleich von AcousticConfiguration gecacht:
var analysis: AcousticAnalysis {
let cfg = configuration
if let cached = cachedAnalysis, cachedConfiguration == cfg {
return cached
}
let result = analysisService.analyze(cfg)
cachedConfiguration = cfg
cachedAnalysis = result
return result
}
8.2 Actor-Isolation
ProjectStore, MeasurementStore und RoomPlanService sind actors, die IO / Async natürlich serialisieren. Die UI nutzt async / await; der Hauptthread wird nie durch Disk-IO blockiert.
8.3 AVAudio-Lebenszyklus
AudioSessionManager zentralisiert die Session-Kategorie (.playback für RIR-Vorschau, .playAndRecord für Messung). AVAudioSession.interruptionNotification erkennt Anruf / Siri und stoppt die Engine, isPlaying zurückgesetzt.
8.4 Task-Abbruch
Die Faltungs-Wiedergabe ist ein Hintergrund-Task; schnelle Taps lösen pendingPlayTask?.cancel() plus Task.isCancelled-Doppelprüfung aus, um Out-of-order-MainActor-Schreibvorgänge zu verhindern.
8.5 Testabdeckung
AllShapesAndUseCasesTests nutzt das Swift-Testing-Framework für die volle Matrix — jede Form × jeder Anwendungsfall × diverse Hörer-/Lautsprecher-Kombis — und prüft:
analysis.reflectionPaths.count >= 1analysis.warningsenthält den Näherungshinweis für nicht rechteckige Räume- Empfehlungssumme entspricht dem Budget
- der Allokator garantiert sum ≤ Budget und Symmetrie
- Polygon-Schließung / Ecken < 3 und weitere Randfälle
Bekannte Grenzen
- Multi-Order in nicht rechteckigen Räumen via Bounding Box — Flutter-/Slapback-Risiko 2./3. Ordnung nahe konkaven Ecken kann abweichen. Erste Ordnung nutzt das echte Polygon.
- Raummoden in nicht rechteckigen Räumen via Bounding Box geschätzt — exakte Moden brauchen FEM.
- Messung mit iPhone-Mikrofon — unkalibriert; nicht-flacher Frequenzgang / Empfindlichkeit. Zuverlässigkeit immer
.low, als Quervergleich zur geometrischen Schätzung. - Einzelquellen-ISM-Annahme — ignoriert Gehäusebeugung, Horn-Richtwirkung u. a.
- Keine Bassfallen-Modellierung — Probleme unter 80 Hz liegen außerhalb des Sabine-Modells und brauchen dedizierte Tieftonbehandlung.
- AR-Scan-Genauigkeit — RoomPlan-Endpunkte typ. 3-8 cm Fehler; 12-Segment-Schleife kann 5+ cm akkumulieren. Wanddicke nicht modelliert.
Referenzen
- Sabine, W. C. (1922). Collected Papers on Acoustics. Harvard University Press.
- Allen, J. B., & Berkley, D. A. (1979). Image method for efficiently simulating small-room acoustics. JASA, 65(4), 943–950.
- Schroeder, M. R. (1965). New method of measuring reverberation time. JASA, 37(3), 409–412.
- Farina, A. (2000). Simultaneous measurement of impulse response and distortion with a swept-sine technique. Audio Engineering Society Convention 108.
- Scheibler, R., Bezzam, E., & Dokmanić, I. (2018). Pyroomacoustics: A Python package for audio room simulation and array processing algorithms. ICASSP 2018.
- EBU Tech 3276. Listening conditions for the assessment of sound programme material.
- ITU-R BS.1116. Methods for the subjective assessment of small impairments in audio systems.
- BB93 — Acoustic design of schools (UK).
- THX Pro Cinema Certification requirements.