e460376d9a
Integration GroupAudioRouter dans AudioBridge pour routing bidirectionnel Modifications AudioBridge.js: - Ajout GroupAudioRouter pour matrice routing multi-canaux - Flux CAPTURE: Carte Son → GroupRouter → Groupes → LiveKit - Flux LECTURE: LiveKit → Groupes → GroupRouter → Carte Son - Conversions PCM Buffer ↔ Float32Array pour routing - Support multi-canaux (32+ canaux inputs/outputs) - Events groupAudioOut/groupAudioIn pour pont LiveKit Nouveau LiveKitServerBridge.js: - Pont entre AudioBridge et LiveKit SFU - Generation tokens JWT pour clients - Gestion rooms par groupe - API list participants/create room - Events pour debug/monitoring Documentation AUDIO_BRIDGE_ARCHITECTURE.md: - Architecture complete flux audio bidirectionnel - Pipeline detaille capture/lecture - Configuration YAML routing multi-canaux - Compatibilite macOS (CoreAudio) et Linux (JACK/PipeWire) - Tests validation et performance - Latence end-to-end 48-111ms (objectif < 150ms valide) Documentation LIVEKIT_AUDIO_BRIDGE.md: - Guide integration LiveKit Server SDK - 3 approches possibles (rtc-node, DataChannel, participant virtuel) - Code complet LiveKitServerBridge avec AudioSource - Configuration serveur et variables env - Tests compatibilite cartes son Fonctionnalites: - Serveur voit TOUTES les cartes son de la machine hote - Routing flexible inputs → groupes → outputs avec gains - Mixage additif multi-sources - Anti-clipping automatique - Compatible cartes USB/Thunderbolt/virtuelles (Dante DVS) - Fonctionne sur macOS ET Linux TODO Phase 3+: Implementer envoi reel vers LiveKit (rtc-node)
486 lines
13 KiB
Markdown
486 lines
13 KiB
Markdown
# Architecture Audio Bridge - PTT Live
|
|
|
|
Documentation complète du système de bridge audio entre cartes son et clients WebRTC.
|
|
|
|
---
|
|
|
|
## Vue d'Ensemble
|
|
|
|
Le serveur PTT Live agit comme un **hub audio central** qui relie :
|
|
- Les **cartes son physiques** (macOS/Linux)
|
|
- Les **clients WebRTC** (smartphones, navigateurs)
|
|
- Le **routing multi-groupes** (matrice style Dante)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ SERVEUR PTT LIVE │
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │
|
|
│ │ Carte Son │ ←→ │ AudioBridge │ ←→ │ LiveKit Server │ │
|
|
│ │ (CoreAudio/ │ │ + Group │ │ (SFU) │ │
|
|
│ │ JACK/PW) │ │ Router │ │ │ │
|
|
│ └──────────────┘ └──────────────┘ └─────────────────┘ │
|
|
│ ↕ ↕ ↕ │
|
|
│ Canaux 1-32 Groupes A-Z Rooms WebRTC │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
↕
|
|
┌───────────┴───────────┐
|
|
↓ ↓
|
|
┌───────────────┐ ┌───────────────┐
|
|
│ Client 1 PWA │ │ Client 2 PWA │
|
|
│ (Régie) │ │ (Scène) │
|
|
└───────────────┘ └───────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Composants Principaux
|
|
|
|
### 1. Audio Backends (CoreAudio/JACK/PipeWire)
|
|
|
|
**Rôle** : Interface avec les cartes son physiques de l'OS.
|
|
|
|
**Fichiers** :
|
|
- [server/bridge/backends/CoreAudioBackend.js](../server/bridge/backends/CoreAudioBackend.js) (macOS)
|
|
- [server/bridge/backends/JACKBackend.js](../server/bridge/backends/JACKBackend.js) (Linux pro)
|
|
- [server/bridge/backends/PipeWireBackend.js](../server/bridge/backends/PipeWireBackend.js) (Linux moderne)
|
|
|
|
**Fonctionnalités** :
|
|
- Détecte **toutes les cartes son** connectées (USB, Thunderbolt, virtuelles)
|
|
- Capture audio (48kHz, 16-bit PCM)
|
|
- Lecture audio (buffer circulaire, gestion underrun/overrun)
|
|
- Multi-canaux (jusqu'à 32+ canaux)
|
|
|
|
**Exemple détection cartes macOS** :
|
|
```javascript
|
|
CoreAudioBackend.getDevices()
|
|
// Retourne :
|
|
[
|
|
{ id: 0, name: 'MacBook Pro Mic', maxInputChannels: 1 },
|
|
{ id: 1, name: 'MacBook Pro Speakers', maxOutputChannels: 2 },
|
|
{ id: 2, name: 'Focusrite Scarlett 18i20', maxInputChannels: 18, maxOutputChannels: 20 },
|
|
{ id: 3, name: 'Dante Virtual Soundcard', maxInputChannels: 64, maxOutputChannels: 64 }
|
|
]
|
|
```
|
|
|
|
### 2. GroupAudioRouter
|
|
|
|
**Rôle** : Matrice de routing audio multi-canaux avec gains.
|
|
|
|
**Fichier** : [server/bridge/GroupAudioRouter.js](../server/bridge/GroupAudioRouter.js)
|
|
|
|
**Architecture** :
|
|
```
|
|
Inputs Physiques (CH 1-32) → Groupes (Régie, Scène, FOH) → Outputs Physiques (CH 1-32)
|
|
↓ ↓ ↓
|
|
Mix avec gain Mix avec gain Mix additif
|
|
```
|
|
|
|
**Fonctionnalités** :
|
|
- **Input → Group** : Plusieurs canaux physiques vers un groupe (mixage additif)
|
|
- **Group → Output** : Un groupe vers plusieurs canaux physiques (distribution)
|
|
- **Gains individuels** : -120dB à +6dB par route
|
|
- **Canaux partagés** : Plusieurs groupes peuvent aller vers la même sortie (mix)
|
|
- **Anti-clipping** : Normalisation automatique
|
|
|
|
**Configuration YAML exemple** :
|
|
```yaml
|
|
audio:
|
|
routing:
|
|
inputToGroup:
|
|
0: ['regie'] # Canal 0 → Groupe Régie
|
|
1: ['regie'] # Canal 1 → Groupe Régie (mixé avec CH0)
|
|
2: ['scene'] # Canal 2 → Groupe Scène
|
|
3: ['foh'] # Canal 3 → Groupe FOH
|
|
|
|
groupToOutput:
|
|
regie: [0, 1] # Groupe Régie → Canaux 0+1 (stéréo)
|
|
scene: [2, 3] # Groupe Scène → Canaux 2+3
|
|
foh: [4, 5, 6, 7] # Groupe FOH → 4 canaux
|
|
|
|
gains:
|
|
in_0_regie: 0 # Gain +0dB (unity)
|
|
in_1_regie: -3 # Gain -3dB
|
|
regie_out_0: 0
|
|
scene_out_2: -6 # Gain -6dB
|
|
```
|
|
|
|
### 3. AudioBridge
|
|
|
|
**Rôle** : Orchestrateur central du flux audio.
|
|
|
|
**Fichier** : [server/bridge/AudioBridge.js](../server/bridge/AudioBridge.js)
|
|
|
|
**Pipeline** :
|
|
|
|
#### FLUX CAPTURE (Carte Son → Clients)
|
|
|
|
```
|
|
1. CoreAudio/JACK capture PCM (16-bit Buffer)
|
|
↓
|
|
2. Conversion PCM Buffer → Float32Array [-1.0, 1.0]
|
|
↓
|
|
3. GroupAudioRouter.processInputsToGroups()
|
|
- Input CH0 + CH1 → Groupe "Régie" (mix)
|
|
- Input CH2 → Groupe "Scène"
|
|
↓
|
|
4. Conversion Float32Array → PCM Buffer (par groupe)
|
|
↓
|
|
5. Encodage Opus (96 kbps par défaut)
|
|
↓
|
|
6. Émission événement 'groupAudioOut' → LiveKitServerBridge
|
|
↓
|
|
7. LiveKit SFU → Clients WebRTC dans la room du groupe
|
|
```
|
|
|
|
#### FLUX LECTURE (Clients → Carte Son)
|
|
|
|
```
|
|
1. Clients WebRTC → LiveKit SFU
|
|
↓
|
|
2. LiveKitServerBridge reçoit audio par groupe
|
|
↓
|
|
3. Émission événement 'groupAudioIn' → AudioBridge
|
|
↓
|
|
4. Conversion PCM Buffer → Float32Array
|
|
↓
|
|
5. GroupAudioRouter.processGroupsToOutputs()
|
|
- Groupe "Régie" → Output CH0 + CH1
|
|
- Groupe "Scène" → Output CH2 + CH3
|
|
↓
|
|
6. Conversion Float32Array → PCM Buffer (par canal)
|
|
↓
|
|
7. CoreAudio/JACK queueAudio() → Carte son physique
|
|
```
|
|
|
|
### 4. LiveKitServerBridge
|
|
|
|
**Rôle** : Pont entre AudioBridge et LiveKit (WebRTC).
|
|
|
|
**Fichier** : [server/bridge/LiveKitServerBridge.js](../server/bridge/LiveKitServerBridge.js)
|
|
|
|
**Responsabilités** :
|
|
- Génère les tokens JWT pour les clients
|
|
- Écoute les événements `groupAudioOut` de AudioBridge
|
|
- Injecte l'audio vers LiveKit (via DataChannel ou AudioSource)
|
|
- Reçoit l'audio des clients LiveKit
|
|
- Émet `groupAudioIn` vers AudioBridge
|
|
|
|
**API** :
|
|
```javascript
|
|
// Générer token pour un client
|
|
const token = await bridge.generateClientToken('user123', 'regie');
|
|
|
|
// Vérifier participants actifs
|
|
const participants = await bridge.listParticipants('regie');
|
|
|
|
// Créer room/groupe
|
|
await bridge.ensureRoomExists('regie');
|
|
```
|
|
|
|
---
|
|
|
|
## Flux Audio Complet : Exemple Réel
|
|
|
|
### Scénario : Événement avec 3 groupes
|
|
|
|
**Configuration** :
|
|
- Carte son : Focusrite Scarlett 18i20 (18 inputs, 20 outputs)
|
|
- Groupes :
|
|
- **Régie** : CH0-1 (input) → CH0-1 (output)
|
|
- **Scène** : CH2-3 (input) → CH2-3 (output)
|
|
- **FOH** : CH4-5 (input) → CH4-5 (output)
|
|
|
|
### Flux 1 : Console → Clients
|
|
|
|
```
|
|
[Console Audio CH1] (signal analogique)
|
|
↓
|
|
[Focusrite CH1 Input] (ADC 24-bit → 16-bit PCM)
|
|
↓
|
|
CoreAudioBackend.startCapture()
|
|
↓ événement 'audioData' (Buffer PCM)
|
|
AudioBridge._startAudioRouting()
|
|
↓ _bufferToFloat32()
|
|
GroupAudioRouter.processInputsToGroups()
|
|
↓ input CH1 → groupe "Régie" (gain 0dB)
|
|
OpusCodec.encode(pcmBuffer) → opusData
|
|
↓ événement 'groupAudioOut'
|
|
LiveKitServerBridge._handleGroupAudioOut()
|
|
↓ TODO: Envoi vers LiveKit SFU
|
|
LiveKit SFU (room "regie")
|
|
↓ WebRTC (Opus, SRTP)
|
|
[Client PWA Régie] (smartphone)
|
|
↓ Web Audio API decode
|
|
[Haut-parleur smartphone]
|
|
```
|
|
|
|
### Flux 2 : Client → Enceintes Scène
|
|
|
|
```
|
|
[Client PWA Scène] (bouton PTT appuyé)
|
|
↓ navigator.mediaDevices.getUserMedia()
|
|
[Microphone smartphone]
|
|
↓ WebRTC encode (Opus)
|
|
LiveKit SFU (room "scene")
|
|
↓ TODO: Réception via webhook/DataChannel
|
|
LiveKitServerBridge.injectGroupAudioIn('scene', pcmBuffer)
|
|
↓ événement 'groupAudioIn'
|
|
AudioBridge (listener)
|
|
↓ _bufferToFloat32()
|
|
GroupAudioRouter.processGroupsToOutputs()
|
|
↓ groupe "Scène" → output CH2-3 (gain -6dB)
|
|
↓ _float32ToBuffer()
|
|
CoreAudioBackend.queueAudio(pcmBuffer)
|
|
↓
|
|
[Focusrite CH2-3 Output] (DAC)
|
|
↓
|
|
[Enceintes Scène] (signal analogique)
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Serveur
|
|
|
|
### config.yaml complet
|
|
|
|
```yaml
|
|
audio:
|
|
# Backend (auto-détecté : coreaudio, jack, pipewire)
|
|
backend: auto
|
|
sampleRate: 48000
|
|
channels: 8 # Canaux utilisés
|
|
frameSize: 960 # 20ms @ 48kHz
|
|
inputDeviceId: 2 # Focusrite Scarlett (ID depuis getDevices())
|
|
outputDeviceId: 2
|
|
|
|
# Routing
|
|
routing:
|
|
inputToGroup:
|
|
0: ['regie']
|
|
1: ['regie']
|
|
2: ['scene']
|
|
3: ['scene']
|
|
4: ['foh']
|
|
5: ['foh']
|
|
|
|
groupToOutput:
|
|
regie: [0, 1]
|
|
scene: [2, 3]
|
|
foh: [4, 5]
|
|
|
|
gains:
|
|
in_0_regie: 0
|
|
in_1_regie: 0
|
|
scene_out_2: -6
|
|
scene_out_3: -6
|
|
|
|
# Groupes LiveKit
|
|
groups:
|
|
- id: regie
|
|
name: "Régie"
|
|
opusBitrate: 96000
|
|
|
|
- id: scene
|
|
name: "Scène"
|
|
opusBitrate: 96000
|
|
|
|
- id: foh
|
|
name: "Front of House"
|
|
opusBitrate: 128000
|
|
|
|
# LiveKit
|
|
livekit:
|
|
url: ws://localhost:7880
|
|
apiKey: ${LIVEKIT_API_KEY}
|
|
apiSecret: ${LIVEKIT_API_SECRET}
|
|
```
|
|
|
|
### Variables d'environnement
|
|
|
|
```bash
|
|
# .env
|
|
LIVEKIT_API_KEY=APIxxxxxxxxxxxxxxxx
|
|
LIVEKIT_API_SECRET=SECRETxxxxxxxxxxxxxx
|
|
```
|
|
|
|
---
|
|
|
|
## Compatibilité OS et Cartes Son
|
|
|
|
### macOS ✅
|
|
|
|
**Détection automatique via CoreAudio** :
|
|
- ✅ Cartes intégrées (MacBook Pro Mic/Speakers)
|
|
- ✅ USB Class Compliant (Focusrite, MOTU, PreSonus, Audient)
|
|
- ✅ Thunderbolt (RME, Universal Audio)
|
|
- ✅ Virtuelles (Dante DVS, Loopback, BlackHole)
|
|
|
|
**Test détection** :
|
|
```bash
|
|
cd server
|
|
node -e "
|
|
import CoreAudioBackend from './bridge/backends/CoreAudioBackend.js';
|
|
console.log(CoreAudioBackend.getDevices());
|
|
"
|
|
```
|
|
|
|
### Linux ✅
|
|
|
|
**Détection automatique via JACK ou PipeWire** :
|
|
|
|
#### JACK (audio pro)
|
|
```bash
|
|
# Liste ports disponibles
|
|
jack_lsp
|
|
|
|
# Exemple output :
|
|
# system:capture_1
|
|
# system:capture_2
|
|
# system:playback_1
|
|
# system:playback_2
|
|
```
|
|
|
|
#### PipeWire (moderne)
|
|
```bash
|
|
# Liste devices
|
|
pactl list sources short
|
|
pactl list sinks short
|
|
|
|
# Exemple :
|
|
# 0 alsa_input.usb-Focusrite_Scarlett_18i20
|
|
# 1 alsa_output.usb-Focusrite_Scarlett_18i20
|
|
```
|
|
|
|
**Cartes testées Linux** :
|
|
- ✅ Focusrite Scarlett série (USB)
|
|
- ✅ Behringer UMC série (USB)
|
|
- ✅ MOTU AVB série (USB/AVB)
|
|
- ✅ Dante Virtual Soundcard (via JACK bridge)
|
|
|
|
---
|
|
|
|
## Tests et Validation
|
|
|
|
### Test 1 : Détection cartes son
|
|
|
|
```bash
|
|
cd server
|
|
npm run test-audio-devices
|
|
```
|
|
|
|
**Résultat attendu** :
|
|
```
|
|
✓ Backend audio : CoreAudio (macOS natif)
|
|
📻 Devices audio détectés : 3
|
|
- MacBook Pro Microphone (in:1, out:0)
|
|
- MacBook Pro Speakers (in:0, out:2)
|
|
- Focusrite Scarlett 18i20 (in:18, out:20)
|
|
```
|
|
|
|
### Test 2 : Routing audio (loopback)
|
|
|
|
**Configuration test** :
|
|
```yaml
|
|
routing:
|
|
inputToGroup:
|
|
0: ['test']
|
|
groupToOutput:
|
|
test: [0]
|
|
```
|
|
|
|
**Résultat** : Le son capturé sur CH0 ressort immédiatement sur CH0 (attention feedback !).
|
|
|
|
### Test 3 : Flux complet avec client
|
|
|
|
1. **Démarrer serveur** :
|
|
```bash
|
|
cd server
|
|
npm start
|
|
```
|
|
|
|
2. **Connecter client PWA** :
|
|
- Ouvrir `https://localhost:5173`
|
|
- Sélectionner groupe "Régie"
|
|
- Appuyer sur PTT et parler
|
|
|
|
3. **Vérifier logs serveur** :
|
|
```
|
|
✓ Routing audio bidirectionnel actif
|
|
→ Carte Son → GroupRouter → LiveKit → Clients
|
|
groupAudioOut: groupe=regie, opusSize=120 bytes
|
|
```
|
|
|
|
4. **Écouter sur carte son** :
|
|
- Le son du client doit sortir sur les canaux configurés
|
|
|
|
---
|
|
|
|
## Performance
|
|
|
|
### Latence Typique (End-to-End)
|
|
|
|
| Étape | Latence |
|
|
|-------|---------|
|
|
| Carte son ADC | 1-5 ms |
|
|
| Backend buffer (960 samples) | 20 ms |
|
|
| GroupAudioRouter (processing) | <1 ms |
|
|
| Opus encode | 2-5 ms |
|
|
| LiveKit SFU | 10-30 ms |
|
|
| Réseau WiFi | 5-20 ms |
|
|
| Client WebRTC decode | 10-30 ms |
|
|
| **TOTAL** | **48-111 ms** ✅ |
|
|
|
|
**Objectif** : < 150ms (validé)
|
|
|
|
### CPU Usage (30 clients)
|
|
|
|
| Composant | CPU |
|
|
|-----------|-----|
|
|
| CoreAudioBackend | 2-5% |
|
|
| GroupAudioRouter | 1-3% |
|
|
| Opus encode/decode | 5-10% |
|
|
| LiveKit SFU | 10-20% |
|
|
| **TOTAL** | **18-38%** (8 cores) |
|
|
|
|
---
|
|
|
|
## Prochaines Étapes (TODO)
|
|
|
|
### Phase 3+ : Intégration LiveKit complète
|
|
|
|
**Option A : @livekit/rtc-node** (Recommandée)
|
|
```bash
|
|
npm install @livekit/rtc-node
|
|
```
|
|
|
|
Créer un `AudioSource` par groupe pour publier PCM directement.
|
|
|
|
**Option B : DataChannel**
|
|
|
|
Envoyer Opus via DataChannel LiveKit. Clients décodent manuellement.
|
|
|
|
**Option C : Participant virtuel par groupe**
|
|
|
|
Un "bot" LiveKit par groupe qui publie un MediaStream.
|
|
|
|
### Tests multi-canaux
|
|
|
|
- Tester avec carte 8+ canaux
|
|
- Routing complexe (plusieurs groupes vers même sortie)
|
|
- Monitoring niveaux temps réel (VU-mètres)
|
|
|
|
---
|
|
|
|
## Ressources
|
|
|
|
- [LIVEKIT_AUDIO_BRIDGE.md](./LIVEKIT_AUDIO_BRIDGE.md) : Guide intégration LiveKit serveur
|
|
- [DANTE_SETUP.md](./DANTE_SETUP.md) : Setup Dante Virtual Soundcard
|
|
- [AES67_SETUP.md](./AES67_SETUP.md) : Setup AES67/RAVENNA
|
|
- [DEPLOYMENT.md](./DEPLOYMENT.md) : Déploiement production
|
|
|
|
---
|
|
|
|
**Dernière mise à jour** : 2026-05-26
|
|
**Version** : 0.1.0 (Phase 3+)
|