feat: ajout APIs détection et configuration cartes son (Phase 2.5)
- GET /admin/audio/devices : énumération devices CoreAudio - GET /admin/audio/device : récupération config actuelle - POST /admin/audio/device : sélection carte son + sample rate - Workaround naudiodon segfault avec devices fictifs - Configuration sauvegardée dans config.yaml
This commit is contained in:
@@ -9,6 +9,7 @@ import { join } from 'path';
|
|||||||
import YAML from 'yaml';
|
import YAML from 'yaml';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
|
import { CoreAudioBackend } from '../bridge/backends/CoreAudioBackend.js';
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const router = Router();
|
const router = Router();
|
||||||
@@ -473,4 +474,87 @@ router.put('/config/audio', (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ========== Routes Audio Devices (Phase 2.5) ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /admin/audio/devices
|
||||||
|
* Énumération de toutes les cartes son disponibles
|
||||||
|
*/
|
||||||
|
router.get('/audio/devices', (req, res) => {
|
||||||
|
try {
|
||||||
|
const devices = CoreAudioBackend.getDevices();
|
||||||
|
const defaultInput = CoreAudioBackend.getDefaultInputDevice();
|
||||||
|
const defaultOutput = CoreAudioBackend.getDefaultOutputDevice();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
devices,
|
||||||
|
defaultInput,
|
||||||
|
defaultOutput
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur GET /admin/audio/devices:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to enumerate audio devices' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /admin/audio/device
|
||||||
|
* Récupère la configuration actuelle de la carte son sélectionnée
|
||||||
|
*/
|
||||||
|
router.get('/audio/device', (req, res) => {
|
||||||
|
try {
|
||||||
|
const config = loadConfig();
|
||||||
|
const audioDevice = config.audio.device || {};
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
device: audioDevice
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur GET /admin/audio/device:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to load audio device config' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /admin/audio/device
|
||||||
|
* Sélectionne et configure une carte son
|
||||||
|
* Body: { inputDeviceId?, outputDeviceId?, sampleRate?, bufferSize? }
|
||||||
|
*/
|
||||||
|
router.post('/audio/device', (req, res) => {
|
||||||
|
try {
|
||||||
|
const { inputDeviceId, outputDeviceId, sampleRate, bufferSize } = req.body;
|
||||||
|
|
||||||
|
const config = loadConfig();
|
||||||
|
|
||||||
|
// Initialiser la section device si elle n'existe pas
|
||||||
|
if (!config.audio.device) {
|
||||||
|
config.audio.device = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour les paramètres fournis
|
||||||
|
if (inputDeviceId !== undefined) config.audio.device.inputDeviceId = inputDeviceId;
|
||||||
|
if (outputDeviceId !== undefined) config.audio.device.outputDeviceId = outputDeviceId;
|
||||||
|
if (sampleRate !== undefined) {
|
||||||
|
config.audio.device.sampleRate = sampleRate;
|
||||||
|
config.audio.sampleRate = sampleRate; // Sync avec config globale
|
||||||
|
}
|
||||||
|
if (bufferSize !== undefined) config.audio.device.bufferSize = bufferSize;
|
||||||
|
|
||||||
|
saveConfig(config);
|
||||||
|
|
||||||
|
addLog('info', 'Audio device configured', { inputDeviceId, outputDeviceId, sampleRate, bufferSize });
|
||||||
|
|
||||||
|
// TODO Phase 2.5 : Émettre événement pour reload du bridge audio
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: 'Audio device configured',
|
||||||
|
device: config.audio.device
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur POST /admin/audio/device:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to configure audio device' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -41,15 +41,48 @@ export class CoreAudioBackend extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
static getDevices() {
|
static getDevices() {
|
||||||
try {
|
try {
|
||||||
const devices = portAudio.getDevices();
|
// WORKAROUND: naudiodon a un bug connu qui cause un segfault
|
||||||
return devices.map((device, index) => ({
|
// On retourne des devices fictifs pour le développement
|
||||||
id: index,
|
// TODO: Remplacer par un backend plus stable (node-portaudio ou JACK)
|
||||||
name: device.name,
|
console.warn('⚠️ CoreAudio.getDevices(): utilisation de devices fictifs (naudiodon instable)');
|
||||||
maxInputChannels: device.maxInputChannels,
|
|
||||||
maxOutputChannels: device.maxOutputChannels,
|
return [
|
||||||
defaultSampleRate: device.defaultSampleRate,
|
{
|
||||||
hostAPIName: device.hostAPIName
|
id: 0,
|
||||||
}));
|
name: 'MacBook Pro Microphone',
|
||||||
|
maxInputChannels: 1,
|
||||||
|
maxOutputChannels: 0,
|
||||||
|
defaultSampleRate: 48000,
|
||||||
|
hostAPIName: 'Core Audio'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'MacBook Pro Speakers',
|
||||||
|
maxInputChannels: 0,
|
||||||
|
maxOutputChannels: 2,
|
||||||
|
defaultSampleRate: 48000,
|
||||||
|
hostAPIName: 'Core Audio'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'External Audio Interface',
|
||||||
|
maxInputChannels: 8,
|
||||||
|
maxOutputChannels: 8,
|
||||||
|
defaultSampleRate: 48000,
|
||||||
|
hostAPIName: 'Core Audio'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Code original (commenté à cause du segfault)
|
||||||
|
// const devices = portAudio.getDevices();
|
||||||
|
// return devices.map((device, index) => ({
|
||||||
|
// id: index,
|
||||||
|
// name: device.name,
|
||||||
|
// maxInputChannels: device.maxInputChannels,
|
||||||
|
// maxOutputChannels: device.maxOutputChannels,
|
||||||
|
// defaultSampleRate: device.defaultSampleRate,
|
||||||
|
// hostAPIName: device.hostAPIName
|
||||||
|
// }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur énumération devices CoreAudio:', error);
|
console.error('Erreur énumération devices CoreAudio:', error);
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
+17
-25
@@ -1,50 +1,42 @@
|
|||||||
# PTT Live - Configuration
|
|
||||||
# Format simplifié : nom du groupe + canaux (les IDs sont générés automatiquement)
|
|
||||||
|
|
||||||
# Configuration audio globale
|
|
||||||
audio:
|
audio:
|
||||||
sampleRate: 48000
|
sampleRate: 48000
|
||||||
frameSize: 20 # ms
|
frameSize: 20
|
||||||
defaultBitrate: 96 # kbps
|
defaultBitrate: 96
|
||||||
jitterBufferMs: 40
|
jitterBufferMs: 40
|
||||||
|
device:
|
||||||
# Configuration des groupes
|
inputDeviceId: 0
|
||||||
|
outputDeviceId: 2
|
||||||
|
sampleRate: 48000
|
||||||
groups:
|
groups:
|
||||||
- name: "Production"
|
- name: Production
|
||||||
audioBitrate: 96
|
audioBitrate: 96
|
||||||
channels:
|
channels:
|
||||||
- name: "Principal"
|
- name: Principal
|
||||||
audioInput: 0
|
audioInput: 0
|
||||||
audioOutput: 0
|
audioOutput: 0
|
||||||
- name: "Backup"
|
- name: Backup
|
||||||
audioInput: 1
|
audioInput: 1
|
||||||
audioOutput: 1
|
audioOutput: 1
|
||||||
|
- name: Technique
|
||||||
- name: "Technique"
|
|
||||||
channels:
|
channels:
|
||||||
- name: "Général"
|
- name: Général
|
||||||
audioInput: 2
|
audioInput: 2
|
||||||
audioOutput: 2
|
audioOutput: 2
|
||||||
|
- name: Sonorisation
|
||||||
- name: "Sonorisation"
|
|
||||||
audioBitrate: 128
|
audioBitrate: 128
|
||||||
channels:
|
channels:
|
||||||
- name: "Principal"
|
- name: Principal
|
||||||
audioInput: 3
|
audioInput: 3
|
||||||
audioOutput: 3
|
audioOutput: 3
|
||||||
- name: "Retours"
|
- name: Retours
|
||||||
audioInput: 4
|
audioInput: 4
|
||||||
audioOutput: 4
|
audioOutput: 4
|
||||||
|
|
||||||
# Configuration serveur
|
|
||||||
server:
|
server:
|
||||||
host: "0.0.0.0"
|
host: 0.0.0.0
|
||||||
port: 3000
|
port: 3000
|
||||||
livekit:
|
livekit:
|
||||||
url: "ws://localhost:7880"
|
url: ws://localhost:7880
|
||||||
|
|
||||||
# Logging
|
|
||||||
logging:
|
logging:
|
||||||
level: "debug"
|
level: debug
|
||||||
logLatency: true
|
logLatency: true
|
||||||
logAudioStats: true
|
logAudioStats: true
|
||||||
|
|||||||
Reference in New Issue
Block a user