# Hagerman & Olofsson 4 phases - RSB en sortie d'aide auditive (R / Shiny)
# ========================= VERSION R DE REFERENCE ==============================

VERSION R DE REFERENCE du portage MATLAB (HO_4phases_MATLAB). Les RSB des deux
mesures (jusqu'a 5 RSB x 2 mesures = 10 taches independantes) sont calcules en
PARALLELE sur plusieurs coeurs (future / future.apply, meme pattern que
App_OIQ_R_parallel) ; "Coeurs = 1" donne le mode sequentiel (progression
detaillee par RSB). S'y ajoutent, par rapport au portage initial : l'alignement
sous-echantillon (precision de phase) et le RSB pondere SII (voir plus bas).

NB : l'ancienne version sequentielle HO_4phases_R_v2 est ABANDONNEE (archivee
dans HAtest/HO_4phases_R_v2_ARCHIVE_2026-08-20.zip). Son deploiement shinyapps
pointait sur la MEME app en ligne que celle-ci ; c'est desormais ce dossier qui
fait foi, en local comme en ligne.

Gain typique (Mac 8 coeurs, 2 mesures 45 min) : ~53 s -> ~15-20 s a 22 kHz ;
l'ecart croit avec des fichiers 44,1 kHz / 24 bits (~2 min -> ~30 s).

# Principe de la mesure (Hagerman & Olofsson, 4 phases)
-------------------------------------------------------------------------------
But : mesurer le RSB REEL en SORTIE d'aide auditive, et le comparer au RSB
d'ENTREE. Un simple niveau ne suffit pas : en sortie, parole et bruit sont
melanges. La methode 4 phases les SEPARE par annulation.

Pattern .wav STEREO :
    piste 1 / left  = micro de reference    -> RSB d'ENTREE
    piste 2 / right = sortie aide auditive  -> RSB de SORTIE

Pour chaque RSB (+10, +5, 0, -5, -10 dB), on rejoue la meme scene 4 fois en
inversant la phase de la parole (S/mS) et du bruit (N/mN) :
    SpN  = (+S) + (+N)      mSpN = (-S) + (+N)
    SmN  = (+S) + (-N)      mSmN = (-S) + (-N)

Extraction (par RSB et par piste) : les combinaisons lineaires annulent l'une
ou l'autre composante.
    Speech = (SpN + SmN - mSpN - mSmN) / 4      -> le bruit s'annule
    Noise  = (SpN - SmN + mSpN - mSmN) / 4      -> la parole s'annule
    Error  = (SpN + SmN + mSpN + mSmN) / 4      -> doit tendre vers 0
    RSB    = 20*log10(rms(Speech)) - 20*log10(rms(Noise))

Le terme Error est l'INDICATEUR QUALITE : tout ce qui ne s'annule pas
(desalignement, bruit de fond, non-linearites de l'aide) s'y retrouve. L'app en
tire error_dB, une MARGE (= min(Speech,Noise) - Error) et une note. Marge
elevee = extraction fiable. C'est aussi pourquoi l'alignement est critique
(voir la section dediee plus bas).

Deroulement : clap de depart pour le decoupage ; calibration stereo (defaut
92,1 dB SPL : pistonphone B&K 4230 a 94 dB via reducteur PA 100 -> le micro
recoit 92,1 dB SPL) ; alignement double (debut + fin, sur SpN) pour corriger la
derive d'horloge ; fenetre de mesure t1/t2 qui retire le lead-in. Toutes les
formules et reglages sont fideles au moteur MATLAB (voir ho_params()).

# Fichiers
-------------------------------------------------------------------------------
  ho_core_v2.R   moteur (portage 1:1 des ho_*.m MATLAB) + alignement
                 sous-echantillon + RSB pondere SII (ho_sii_critical / ho_snr_sii)
  ho_plots_v2.R  graphiques ggplot2 (+ option sii sur le trace RSB de sortie)
  ho_parallel.R  worker 1-RSB + orchestrateur (future multisession)
  app.R          interface Shiny (bslib) + selecteur "Coeurs" + case SII

# Fonctionnement du parallelisme
-------------------------------------------------------------------------------
- Le parent detecte le clap de chaque enregistrement (rapide), puis construit
  la liste des taches (mesure x RSB).
- Chaque worker R re-source le moteur lui-meme (future.globals = FALSE) : on ne
  lui envoie que des chemins et des scalaires. Il lit sa plage du .wav, filtre,
  aligne, extrait, ecrit ses .wav de sortie, et renvoie la ligne de tableau +
  les segments (pour l'onglet Enveloppes).
- Les GAINS 1/3 d'octave et l'EDI sont aussi PRE-CALCULES dans les workers
  (et non recalcules a chaque affichage des onglets) : l'affichage des
  onglets Gains/EDI est instantane.
- Graphique Gains : echelle Y commune aux deux panneaux (signal / bruit),
  lignes zero alignees.
- Repli automatique en sequentiel si future/future.apply absents ou Coeurs = 1.
- Reglage par defaut : 5 workers (10 taches -> 2 vagues ; bon compromis RAM).
  Chaque worker consomme ~0,5-1 Go en 44,1 kHz : eviter 8 workers si < 16 Go.

# Alignement : methode + raffinement sous-echantillon
-------------------------------------------------------------------------------
Panneau "Analyse" : menu "Alignement" (Enveloppe de Hilbert / Structure fine /
Aucun) + case "Raffinement sous-echantillon (precision de phase)".

Pourquoi : l'extraction H&O procede par ANNULATION (somme/difference des 4
phases), qui exige une coherence de PHASE. Un alignement a l'echantillon ENTIER
laisse un residu jusqu'a +/-0,5 echantillon ; l'annulation residuelle vaut alors
2*sin(pi*f*dt), soit a 48 kHz environ 18 dB a 1 kHz mais 0 dB a 8 kHz. La case
affine le recalage SOUS l'echantillon (lag par pic parabolique + retard
fractionnaire par interpolation de Lagrange). Ce n'est PAS un filtrage : aucune
frequence n'est supprimee.

Mesure (decalage vrai 3,4 ech., signal large bande) : annulation en entier
8,6 dB -> 25,3 dB avec le raffinement (>4 kHz : 6,8 -> 23,3 dB).

Quand l'activer :
  - si la MARGE d'alignement / error_dB signale une annulation mediocre ;
  - en basse frequence d'echantillonnage (22,05 / 24 kHz), ou le plancher
    +/-0,5 echantillon pese davantage ;
  - si l'aigu compte (gains et EDI vont jusqu'a 8 kHz).
Sinon, marge confortable = inutile ; laisser decoche (comportement d'origine).
Le cout calcul est negligeable et le lissage induit est proportionnel au residu
(donc ~nul sur un signal deja aligne).

A privilegier : Enveloppe + case cochee. On cumule la robustesse de l'enveloppe
(immunite au faux pic de xcorr sur S+N vs S-N deja synchrones) et la precision
sous-echantillon. "Structure fine + case" fonctionne mais herite du faux pic.

# RSB pondere SII (importance frequentielle)
-------------------------------------------------------------------------------
Panneau "Analyse" : case "Afficher le RSB pondere SII (importance
frequentielle)". En plus du RSB large bande standard, le moteur calcule TOUJOURS
un RSB pondere par l'importance de chaque zone frequentielle dans
l'intelligibilite, comme l'equipe de Nancy (Maillou, Ducourneau et al.,
"Evaluation objective des performances des aides auditives", Les Cahiers de
l'Audition 35(3), 2022, pp. 20-35) :

    RSB estime PAR BANDE CRITIQUE (PSD Welch sur les composantes separees par
    H&O), puis moyenne avec les coefficients d'importance de la norme SII
    (ANSI S3.5-1997, procedure a 21 bandes critiques 100-9500 Hz, somme des
    poids = 1) :    RSB_SII = somme( Ii * RSB_i )

Le gain de calibration s'annule dans le rapport -> le RSB pondere ne depend pas
de la calibration. Les bandes au-dela de fs/2 sont ecartees et les poids
renormalises. La case ne pilote que l'AFFICHAGE (colonnes in_snr_sii /
out_snr_sii dans la table et serie pointillee + triangles sur le graphique
"RSB de sortie") : on peut la cocher/decocher apres l'analyse sans recalcul.
Les colonnes sont toujours presentes dans les CSV exportes.

Table + calcul : ho_sii_critical() / ho_snr_sii() dans ho_core_v2.R (valeurs
verifiees contre le package CRAN 'SII' de G. Warnes, transcription de la norme).

# Audibilite ponderee SII a DYNAMIQUE REELLE (onglet "Audibilite SII")
-------------------------------------------------------------------------------
Variante du facteur d'audibilite du SII ou la dynamique de la parole n'est pas
POSTULEE (+/-15 dB autour du LTASS) mais MESUREE par analyse percentile des
niveaux court-terme, selon les conventions IEC 60118-15 (celles des Speechmap) :
trames de 125 ms, centiles calcules sur le signal complet, pauses comprises.
Par bande critique ANSI, sur les composantes separees par H&O :
    L99 = crete (centile 99), L30 = vallees (centile 30),
    bruit = niveau long-terme du bruit extrait,
    a_i = clip( (L99 - max(bruit, L30)) / (L99 - L30), 0, 1 )
    indice = somme( Ii * a_i ) sur les bandes VALIDES (poids renormalises).
L'interet vs le RSB pondere : la compression WDRC reduit la dynamique reelle
de la parole ; la mesurer capture cet effet la ou le bornage fixe du SII ne le
voit pas. L'indice est dans [0,1] (analogue SII), PAS un RSB en dB.

SEUILS D'AUDITION (panneau "Audiogramme") : l'audibilite de la SORTIE AA est
l'emergence au-dessus de max(bruit, SEUIL SPL AU TYMPAN) -- audibilite reelle
pour le malentendant appareille. Transposition (convention de l'app "influence
event", HL obtenus aux inserts) :
    seuil SPL tympan = HL + RETSPL(HA-1, ANSI S3.6-2010)
                          + RECD adulte (ANSI/ASA S3.46-2013 Fig. C.1)
interpolee en log(f) aux 21 centres de bandes critiques. Audiogrammes standards
IEC 60118-15 : N1-N7 et S1-S3 (Bisgaard, Vlaming & Dahlquist 2010, Trends
Amplif 14(2), Tables 2 et 4) + "Personnalise" (10 HL a 250-6000 Hz) + "Aucun"
(bruit seul). Le seuil de l'audiogramme s'applique a la sortie AA (mesuree au
tympan du mannequin). Quand un audiogramme est actif, le graphique montre
QUATRE conditions (une seule legende, chaque courbe dessinee comme tracee ;
code couleur : APPAREILLE = couleurs pleines, OREILLE NUE = gris pointilles) :
  1. Sortie AA - mesure 2 / algos autos (ROUGE plein), seuils audiogramme
  2. Sortie AA - mesure 1 / algos off  (BLEU plein),  seuils audiogramme
  3. Malentendant oreille nue (GRIS FONCE tirets) : champ + REUG, seuils de
     l'audiogramme -> situation NON APPAREILLEE (colonne in_audib_hl)
  4. Normo-entendant oreille nue (GRIS CLAIR pointille) : champ + REUG,
     seuils 0 dB HL
Le REUG = Bentler & Pavlovic 1989 (Table 1 col. A, 18 bandes SII), MEMES
valeurs que l'app "influence event", interpole en log(f) aux 21 centres de
bandes critiques (ho_reug_ansi()) ; les seuils 0 dB HL = RETSPL + RECD. Lectures : bleu -> rouge = benefice d'audibilite de l'appareillage ;
rouge vs gris = ecart residuel au normo-entendant. Sans audiogramme, les deux
canaux restent en emergence au-dessus du bruit seul. Le choix est pris en
compte au moment de l'ANALYSE (relancer si on en change).
Les niveaux de bande sont en dB SPL ABSOLUS (normalisation Parseval des trames
+ gain de calibration) -- l'audibilite avec seuils depend donc de la
calibration, contrairement au RSB pondere.

Garde-fous (bande invalidee -> a_i = NA, poids renormalises) :
  - vallees < residu d'extraction + 3 dB (on mesurerait le bruit d'extraction) ;
  - dynamique L99-L30 < 6 dB (fluctuation statistique d'un signal stationnaire
    dans les bandes etroites ; la parole reelle est tres au-dessus).

Affichage : onglet "Audibilite SII" = indice vs RSB d'entree reel, sortie AA
(rouge) et micro de reference (gris), m1 plein / m2 pointille, echelle 0-1.
Table : colonnes in_audib / out_audib (2 decimales) dans les CSV *_outSNR.csv.
Detail par bande : CSV dedie *_audibilite_bandes.csv (snr_nom, canal,
audiogramme, freq, Ii, L99, L30, bruit, seuil, residu, a, valide -- niveaux en
dB SPL). Calcul : ho_audibility_sii() + ho_seuil_tympan() +
ho_std_audiograms() dans ho_core_v2.R, pre-calcule dans les workers.

NB moteur : la lecture des .wav normalise desormais correctement les fichiers
FLOTTANTS (pcm=FALSE, deja en [-1,1] -> plus de division par 2^31 ; les PCM
entiers sont inchanges). Les RSB n'etaient pas affectes (rapports), mais les
niveaux absolus des wav float etaient faux.

# Lancement
-------------------------------------------------------------------------------
Dans R/RStudio : ouvrir app.R et cliquer "Run App", ou :
    shiny::runApp("app.R")
Les dependances (shiny, bslib, ggplot2, patchwork, tuneR, seewave, signal,
future, future.apply) s'installent automatiquement au premier lancement.

Etapes dans l'interface : fichiers, calibration, aide auditive, analyse
(alignement, raffinement sous-echantillon, case SII, nombre de coeurs).

NB deploiement shinyapps.io : les petites instances n'ont que 1-2 coeurs ->
peu ou pas de gain en ligne ; le parallele vise surtout l'usage local.

# Onglets de resultats
-------------------------------------------------------------------------------
- RSB de sortie : points aux valeurs REELLES (RSB d'entree mesure piste 1 en
  abscisse, RSB de sortie piste 2 en ordonnee), axe -15..+15 dB, ligne y=x.
  Au-dessus de y=x = l'aide ameliore le RSB ; en dessous = elle le degrade.
  La mesure 2 se superpose a la mesure 1 (comparaison de deux reglages).
- Gains : gain du SIGNAL (gauche) et du BRUIT (droite) cote a cote, une courbe
  par RSB (degrade rouge -> bleu), lissage 1/3 d'octave, echelle log
  125 - 8000 Hz, echelle Y commune aux deux panneaux. Trace pour la mesure
  aidee (m2 si presente, sinon m1).
- EDI : distorsion d'enveloppe (Envelope Difference Index) du signal et du
  bruit, en fonction du RSB d'entree reel, echelle 0 a 1 (0 = pas de
  distorsion).
- Audibilite SII : indice d'audibilite a dynamique reelle (voir la section
  dediee), en fonction du RSB d'entree reel, echelle 0 a 1.
- Enveloppes : sortie d'aide auditive (gauche) et micro de reference (droite) ;
  Signal+Bruit en pointilles gris, signal extrait en rouge, bruit extrait en
  bleu (dB SPL), pour un RSB et un intervalle au choix.

# Sorties (dossier results_HO/ a cote de l'enregistrement 1)
-------------------------------------------------------------------------------
  m1_off/ , m2_on/   CSV des RSB (*_outSNR.csv), CSV de l'audibilite par bande
                     (*_audibilite_bandes.csv) + .wav des composantes extraites
                     (prefixes in_ = entree, out_ = sortie)
  PNG des planches (RSB de sortie, Gains, EDI, Audibilite SII) auto-sauves en
  fin d'analyse.
En version deployee sur serveur, les sorties sont regroupees dans un ZIP
telechargeable (l'ecriture a cote du fichier source n'y est pas possible).

# Reference
-------------------------------------------------------------------------------
Methode : Hagerman, B. & Olofsson, A. (2004), "A method to measure the effect of
noise reduction algorithms using simultaneous speech and noise", Acta Acustica
united with Acustica, 90(2), 356-361.

Ce panel est LA version R du projet MATLAB HO_4phases_MATLAB (moteur + app) :
memes formules, memes reglages pour la chaine H&O de base. Toute evolution du
moteur validee en MATLAB est a reporter ici (ho_params() / ho_core_v2.R).
Les ajouts propres a cette version R (alignement sous-echantillon, RSB pondere
SII) n'existent PAS dans le MATLAB.
