Acouvero
Télécharger
TECHNICAL DOC · v0.3.1

Acoustique des pièces,
de la mesure
au plan.

Acouvero est un outil d'analyse acoustique et de planification de traitement pour iPhone / iPad. Ce document s'adresse aux lecteurs intéressés par l'ingénierie acoustique ou l'implémentation client : algorithmes, système géométrique, méthodes de mesure, moteur de recommandation et notes d'ingénierie.

Bandes d'octave
6bandes
Formes
4géométries
Réflexions
1–3ordres
§ 01

Vue d'ensemble

Entrée : l'utilisateur saisit dimensions / forme / matériaux, positions des enceintes et du point d'écoute — ou la géométrie est capturée par LiDAR (RoomPlan).

Sortie
  • Localisation des premières réflexions
  • Jeu complet RT60 / EDT / C50 / C80 / D50 (mesuré + simulé)
  • Liste des fréquences modales
  • Plan d'implantation des panneaux (position, nombre, SKU)
  • Visualisation 3D + carte d'énergie des réflexions
  • Export PDF de niveau manuel d'installation
  • Mesure in situ via le micro de l'iPhone (balayage / claquement)

Pièces rectangulaires, en L, en U et tout polygone simple sont pris en charge ; les calculs utilisent le vrai polygone, pas sa boîte englobante.

§ 02

Modèle acoustique

2.1 Sabine RT60

La formule de Sabine classique :

SABINE · RT60 $$ RT_{60} = \frac{0.161 \cdot V}{\sum_i S_i \alpha_i} $$
$V$
Volume (m³) ; pour les polygones, aire de Shoelace × hauteur, moins les obstacles (poteaux / poutres).
$\sum_i S_i \alpha_i$
Surface × coefficient d'absorption par face. Coefficients sur 6 bandes (125 / 250 / 500 / 1k / 2k / 4k Hz).
$0.161$
= 24·ln(10)/c, avec c = 343 m/s (20 °C).

L'app fournit 5 préréglages de finition (vide / dur / mobilier / déjà traité / studio), chacun avec une courbe d'absorption par bande. Le RT60 cible est aussi une courbe à 6 bandes, conforme aux normes EBU Tech 3276 / THX qui tolèrent une traîne plus longue dans le grave.

2.2 Méthode des sources images (ISM)

Les premières réflexions utilisent le miroir : on reflète la source par rapport au mur ; l'intersection du segment « source miroir → auditeur » avec le mur est le point de réflexion.

  • Pièce rectangulaire : miroir sur les 6 plans (4 murs + sol + plafond) — solution analytique.
  • Non rectangulaire (PolygonReflectionCalculator) : miroir sur chaque segment de RoomPolygon.walls(), validation du paramètre s ∈ [0, 1] et point XY dans le polygone.

Les réflexions multi-ordres (1–3) sont implémentées par MultiOrderReflectionCalculator selon l'approche récursive de pyroomacoustics :

  1. Énumérer toutes les séquences de murs [s₁, …, sₙ], murs adjacents distincts
  2. Itération source-image : S₀ = source, Sₖ = mirror(Sₖ₋₁, sₖ)
  3. Tracé inverse depuis l'auditeur : Sₙ → auditeur coupe sₙ en Pₙ ; Sₙ₋₁ → Pₙ coupe sₙ₋₁ en Pₙ₋₁ ; et ainsi de suite
  4. Chaque intersection doit être dans la plage valide du mur et t ∈ (0, 1)
APPROXIMATION Pour les pièces non rectangulaires, le multi-ordre approxime par la boîte englobante (« réflexions fantômes » près des angles rentrants). L'analyse rejette tout trajet dont le point est > 0,3 m de tout segment réel ; seuls les trajets valides alimentent la détection de flutter / slapback.

2.3 Énergie des trajets

Énergie large bande selon le nombre de réflexions et l'atténuation de distance :

PATH ENERGY $$ E_{rel} = (1 - \bar{\alpha})^N \cdot \frac{d_{direct}^2}{d_{total}^2} $$
  • α est borné à [0, 0.99] pour éviter que α = 1 produise log(0) ou pow(négatif, N) = NaN
  • L'atténuation suit la divergence sphérique 1/r²
  • Conversion en dB via 10·log₁₀ (domaine énergétique)

L'énergie par bande est calculée pour les 6 bandes (coefficient de finishProfile.absorptionByBand).

2.4 Estimation RT60 (mesurée)

ImpulseResponseEstimator :

  • Filtrage adapté : le balayage enregistré de 0,45 s est filtré en inverse (ESS / Farina) en une impulsion, gain SNR ~+40 dB
  • rmsEnvelope : enveloppe RMS à fenêtre glissante
  • Intégration inverse de Schroeder : E_back(t) = ∫ₜ^∞ s²(τ) dτ, normalisée en 10·log₁₀ pour la courbe EDC
  • Régression linéaire de pente : T30 [−5, −35 dB] selon SNR, repli T20 [−5, −25] ou T10 [−5, −15]
  • RT₆₀ = −60 / pente

RT60 par bande : sur les échantillons filtrés, passe-bande aux centres ISO (250 / 500 / 1k / 2k / 4k), Q = √2, puis Schroeder par bande.

2.5 Simulation de RIR

SimulatedRIRGenerator convertit les trajets multi-ordres en impulsions discrètes écrites dans un buffer PCM 8 kHz × 0,5 s :

SimulatedRIRGeneratorpseudocode
for path in reflectionPaths:
    t   = path.totalDistance / speedOfSound
    amp = 10^(path.relativeEnergyDB / 20)
    impulse[round(t * sampleRate)] += amp

Ajouter l'impulsion du son direct, puis intégration inverse de Schroeder pour EDT / RT60 / C50 / C80 / D50.

Comparaison avant / après Chaque trajet est atténué par les panneaux des murs traversés, produisant un RIR « traité ». L'UI bascule A/B pour montrer le gain EDT / C50, et AVAudio convolue un signal sec pour comparer à l'oreille.

2.6 Modes propres

Fréquences modales d'une pièce rectangulaire :

ROOM MODES $$ f_{n_x, n_y, n_z} = \frac{c}{2} \sqrt{\left(\frac{n_x}{L_x}\right)^2 + \left(\frac{n_y}{L_y}\right)^2 + \left(\frac{n_z}{L_z}\right)^2} $$

Classés selon le nombre de nₓ, n_y, n_z non nuls : 1 = axial, 2 = tangentiel, 3 = oblique.

Les pièces non rectangulaires estiment les modes via la boîte englobante (les modes exacts exigent un FEM) ; indiqué dans l'UI.

§ 03

Système géométrique

3.1 Formes prises en charge

  • Rectangle — largeur × longueur × hauteur
  • Forme en L — retire une encoche nw × nl à un coin choisi
  • Forme en U — retire une encoche nw × nl au milieu d'un mur
  • Personnalisé — tout polygone simple (≥ 3, ≤ 24 sommets)

Toutes les formes non rectangulaires deviennent une liste de sommets CCW via RoomPolygon.from(shape:, bboxWidth:, bboxLength:). Les calculs utilisent ces sommets, pas la boîte englobante.

3.2 Outils de géométrie polygonale

  • Aire — formule de Shoelace
  • Périmètre — somme des longueurs d'arêtes
  • contains(x, y) — lancer de rayon + epsilon de bord (évite les faux négatifs aux coins)
  • clamped(point) — les rectangles utilisent [0, w] × [0, l] ; les autres utilisent le vrai [minₓ, maxₓ] × [min_y, max_y] de polygon.boundingBox, testent polygon.contains, et dichotomie vers le centre si hors polygone
  • walls() — découpe le polygone en segments classés par normale intérieure (leftWall / rightWall / frontWall / backWall), réutilisant le recommandeur rectangulaire
  • PolygonValidator.isSimple — valide un polygone simple non auto-sécant

3.3 Numérisation RoomPlan

Sur iOS 17+, RoomCaptureSession est utilisé et CapturedRoomMapper mappe le scan en ScannedRoom (wallSegments + openings + furniture). Mappage des coordonnées :

ARKit (x, y, z) → y est vertical App (x, y, z) → z est vertical Extrémités de mur (z=0 sol) App.x = ARKit.x App.y = ARKit.z App.z = 0 Centre ouverture / mobilier  App.x = ARKit.x App.y = ARKit.z App.z = ARKit.y

Les pièces non rectangulaires scannées passent par RectangularRoomSimplifier.polygonShapeFromScan, chaînant les wallSegments en sommets. Les extrémités RoomPlan ont une erreur de 3–8 cm ; une boucle de 12 segments peut cumuler 5 cm, d'où un epsilon de fermeture de 12 cm. Si la boucle ne ferme pas, repli sur la boîte englobante et invitation à rescanner.

3.4 Éditeur de sommets libres

FreeVertexEditorView permet de glisser les sommets sur un Canvas SwiftUI :

  • Appui long pour supprimer (si sommets ≥ 3)
  • Le glissement enregistre le dernier état valide dans dragSnapshot ; une position auto-sécante revient en arrière silencieusement
  • Accrochage à la grille de 0,05 m
  • Une marge max(0,5, bbox × 0,2) m hors bbox permet de tirer les sommets au-delà de la bbox (la vue parente agrandit room.width / length)
§ 04

Zones d'écoute & multi-auditeur

AcousticConfiguration.listeners gère 1..N positions d'écoute. L'analyse exécute la méthode des images par auditeur et fusionne les points dans un tableau (l'auditeur principal d'abord). Le recommandeur pondère chaque mur par son nombre de points (cap = max(6, speakerCount × listenerCount)) ; plusieurs sièges partageant un mur le poussent vers le plafond du budget.

Visuel UI
  • En 3D, les auditeurs secondaires : opacité 0,45 + échelle 0,75
  • Vue de dessus (RoomPlanView) : points primaires pleine couleur r=5, secondaires opacité 0,45 r=3
  • La liste (RecommendationView) n'affiche que les points primaires ; note de bas de page « + N réflexions déjà comptées »

Réflexions multi-ordres / RIR / carte sont calculées une fois pour l'auditeur principal (O(speakers × surfaces³) par auditeur est trop lourd, et le RIR est ponctuel).

§ 05

Moteur de recommandation

PanelPlacementRecommender produit [PanelRecommendation], chacun avec : position (mur gauche / droit / plafond / avant / arrière / coin), nombre, priorité (essentiel / fort / recommandé / optionnel), effectivenessMultiplier, targetReflectionPointId.

5.1 Répartition proportionnelle (plus fort reste)

L'ancien add() glouton affamait des murs : le mur gauche vidait le budget → mur droit à 0 ; budget serré, plafond / avant / arrière affamés. La nouvelle version : méthode du plus fort reste :

  • Calculer le « raw desired » par mur (nombre de points + profondeur + cas d'usage)
  • floor(raw × budget / total) comme allocation initiale
  • les murs avec raw > 0 reçoivent ≥ 1 panneau (aucun mur en demande n'est affamé)
  • si somme > budget, retrancher en partant du plus petit reste fractionnaire
  • passe de symétrie G/D : si raw[0] == raw[1], forcer alloc[0] == alloc[1]
  • redistribuer le reliquat selon la plus grande fraction

5.2 Modèles par cas d'usage

Sans enceintes placées, des modèles spécifiques s'appliquent :

  • Salle de réunion : plafond 35 % + mur arrière 20 % + chaque mur latéral 12 %
  • Podcast : devant le micro 25 % + chaque côté 18 % + plafond 20 %
  • Studio : amortissement symétrique 4 murs + 4 pièges à graves d'angle forcés
  • Écoute générale : base symétrique + invitation à ajouter des enceintes

5.3 Les nécessités physiques contournent le budget

Les pièges à graves d'angle sont forcés à 4 quand les modes 80–150 Hz sont denses, contournant le plafond totalPanel. Raison : la pression est maximale aux angles et les pièges y sont ~1,5× plus efficaces — une nécessité physique.

5.4 Pondération proche auditeur

Une passe finale marque les recommandations à moins d'1,5 m d'un auditeur en .nearListener, effectivenessMultiplier ×1,15, et l'indique dans le titre / la raison.

§ 06

Mesure

6.1 Balayage ESS

D'après la méthode de Farina :

  • Balayage log 180 Hz → 4 kHz, 0,45 s
  • Filtre inverse (makeInverseSweep) = inversion temporelle + compensation 1/freq
  • Enregistrement via AVAudioRecorder en AAC m4a (44,1 kHz mono)
  • Relecture → filtrage adapté → analyzeImpulse
Garde-fou d'échantillonnage Si le taux réel ≠ 44,1 kHz (interfaces USB-C / certaines configs Bluetooth), le filtrage adapté est ignoré et l'UI signale une faible fiabilité, conseillant de déconnecter le matériel externe.

6.2 Impulsion par claquement

Sans lecture — l'utilisateur claque fort une fois, 3 s de traîne enregistrées. Directement vers analyzeImpulse (pas de filtre adapté ; un claquement est déjà une impulsion étroite). Couvre la bande 125 Hz que le balayage (dès 180 Hz) rate.

6.3 Mesuré vs simulé

MeasuredVsSimulatedCard récupère le dernier RT60 mesuré et affiche l'écart avec le RT60 simulé :

< 15%modèle et mesure concordent
15–30%écart (mobilier / matériaux non modélisés)
> 30%écart important — vérifier géométrie / matériaux

6.4 Bruit ambiant & calibrage

AmbientNoiseEstimator enregistre un segment silencieux pour le bruit de fond dBFS / spectre, évaluant le SNR. DelayCalibrationService fait lecture + enregistrement en champ proche pour la latence lecture-enregistrement de l'iPhone (80–150 ms).

§ 07

Visualisation & export

7.1 Vue 3D

Room3DController rend via SceneKit : murs / sol / plafond / enceintes (caisson fin + repère + pastille canal) / auditeur (glyphe FE + tige) / obstacles (SCNBox translucide) / panneaux recommandés / segments de trajets multi-ordres / superposition de carte d'énergie.

DESIGN · DESSIN TECHNIQUE Style « dessin technique » (arêtes fines, éclairage constant, sans PBR), distinct du rendu photoréaliste.

7.2 Carte d'énergie des réflexions

ReflectionEnergyMapGenerator accumule l'énergie de chaque trajet sur la grille uCells × vCells du mur (12×6 par défaut). Une carte par mur, superposée en 3D (bleu → rouge).

EnergyHeatmapRenderer convertit la grille en UIImage : suréchantillonnage bicubique + colormap Magma + alpha selon l'énergie absolue.

7.3 Aperçu par convolution RIR

RIRPreviewPlayer (AVAudioEngine + AVAudioPlayerNode) convolue tout signal sec (voix / batterie / piano) avec le RIR simulé pour l'écoute « non traité vs traité ».

La convolution temporelle RIRConvolver.convolve(dry:rir:) est directe O(N × M) (N = échantillons secs, M = RIR), normalisée à 0,9 pour éviter l'écrêtage.

7.4 Manuel d'installation PDF

ReportExporter rend un PDF A4 (595 × 842 pt) via UIGraphicsPDFRenderer :

  • Aperçu de la pièce + capture 3D
  • Histogramme RT60 6 bandes mesuré vs cible
  • Tableau des points + tableau des modes + résumé des risques
  • Tableau des panneaux + nomenclature (BOM)
  • Police Geist + repli PingFangSC (CoreText cascadeList, pour éviter les □□□ en CJK)

7.5 Persistance des projets

ProjectStore (actor) écrit ~/Library/Application Support/AcouVero/projects.json : [RoomProject], chacun avec Room + enceintes + auditeurs + recommandations + panelCountOverrides.

MeasurementStore (actor) écrit measurements.json : [MeasurementResult], lié au projet par roomId.

Les deux ont une récupération de décodage par élément (wrapper Throwable<T>) — un enregistrement corrompu ne fait pas échouer toute la liste.

§ 08

Notes d'ingénierie

8.1 SwiftUI + @Observable

Le ViewModel (AcouVeroViewModel) utilise la macro @Observable de Swift 5.9 — champs observés directement, sans Combine. Les résultats AcousticAnalysis sont mis en cache par comparaison Equatable de AcousticConfiguration :

AcouVeroViewModel.analysisswift
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 Isolation par acteur

ProjectStore, MeasurementStore et RoomPlanService sont des actor, sérialisant l'IO / l'async. L'UI utilise async / await ; le thread principal n'est jamais bloqué par l'IO disque.

8.3 Cycle de vie AVAudio

AudioSessionManager centralise la catégorie de session (.playback pour l'aperçu RIR, .playAndRecord pour la mesure). AVAudioSession.interruptionNotification écoute appels / Siri et arrête le moteur, réinitialisant isPlaying.

8.4 Annulation de tâche

La lecture par convolution est une tâche de fond ; les appuis rapides déclenchent pendingPlayTask?.cancel() + double vérif Task.isCancelled pour éviter des écritures MainActor désordonnées.

8.5 Couverture de tests

AllShapesAndUseCasesTests (framework Swift Testing) exécute la matrice complète — chaque forme × cas d'usage × combinaisons auditeur / enceinte — en vérifiant :

  • analysis.reflectionPaths.count >= 1
  • analysis.warnings contient la note d'approximation pour les pièces non rectangulaires
  • le total des recommandations correspond au budget
  • l'allocateur garantit sum ≤ budget et la symétrie
  • fermeture du polygone / sommets &lt; 3 et autres cas limites
§ 09

Limites connues

  1. Multi-ordre en pièce non rectangulaire = boîte englobante — le risque flutter / slapback d'ordre 2-3 près des angles rentrants peut dévier. Les points d'ordre 1 utilisent le vrai polygone.
  2. Modes en pièce non rectangulaire estimés via boîte englobante — modes exacts = FEM.
  3. Mesure au micro intégré de l'iPhone — non calibré ; réponse / sensibilité non plates. Fiabilité toujours .low, en recoupement de l'estimation géométrique.
  4. Hypothèse ISM source unique — ignore diffraction du caisson, directivité du pavillon, etc.
  5. Pas de modélisation des pièges à graves — les problèmes sous 80 Hz sortent du modèle de Sabine et exigent un traitement dédié.
  6. Précision du scan AR — extrémités RoomPlan à 3-8 cm près ; boucle de 12 segments peut cumuler 5+ cm. Épaisseur des murs non modélisée.
§ 10

Références

  1. Sabine, W. C. (1922). Collected Papers on Acoustics. Harvard University Press.
  2. Allen, J. B., & Berkley, D. A. (1979). Image method for efficiently simulating small-room acoustics. JASA, 65(4), 943–950.
  3. Schroeder, M. R. (1965). New method of measuring reverberation time. JASA, 37(3), 409–412.
  4. Farina, A. (2000). Simultaneous measurement of impulse response and distortion with a swept-sine technique. Audio Engineering Society Convention 108.
  5. Scheibler, R., Bezzam, E., & Dokmanić, I. (2018). Pyroomacoustics: A Python package for audio room simulation and array processing algorithms. ICASSP 2018.
  6. EBU Tech 3276. Listening conditions for the assessment of sound programme material.
  7. ITU-R BS.1116. Methods for the subjective assessment of small impairments in audio systems.
  8. BB93 — Acoustic design of schools (UK).
  9. THX Pro Cinema Certification requirements.
Vous préférez la version simple ?

« Pourquoi votre pièce réécrit la musique »

Lire la version détaillée