class ProcessorDTMF extends AudioWorkletProcessor { constructor() { super(); this.sampleRate = sampleRate; this.symbolDuration = 0.03; // 50 ms par symbole this.samplesPerSymbol = Math.floor(this.sampleRate * this.symbolDuration); this.encodeLowPrio = []; this.symbolSamples = []; this.lastBlackStep = 0; this.port.onmessage = (e) => { if (e.data.type === 'encode') { this.symbolSamples.push(...this.encodeSymbols(e.data.symbols)); this.symbolSamples.push(...this.encodeBlack(this.sampleRate * 0.02)); } if (e.data.type === 'encodeLowPrio') { this.encodeLowPrio.push(e.data.symbols); } }; } dtmfFrequencies = [ [697, 770, 852, 941], // Fréquences basses [1209, 1336, 1477, 1633] // Fréquences hautes ]; encodeSymbols(symbols) { const samples = []; for (const symbol of symbols) { const lf = this.dtmfFrequencies[0][symbol % 4]; // Fréquence basse const hf = this.dtmfFrequencies[1][Math.floor(symbol / 4)]; // Fréquence haute // console.log(`Symbol: ${symbol}, LF: ${lf} Hz, HF: ${hf} Hz`); for (let i = 0; i < this.samplesPerSymbol; i++) { const t = i / this.sampleRate; const t2 = (this.lastBlackStep + i) / this.sampleRate; samples.push(0.5 * Math.sin(2 * Math.PI * lf * t) + 0.5 * Math.sin(2 * Math.PI * hf * t) // Signal DTMF + Math.sin(2 * Math.PI * 150 * t2) * (0.0625 * (Math.sin(2 * Math.PI * 0.5 * t2) + 1))); // Ajouter un signal à 150 Hz pour le "black" } this.lastBlackStep += this.samplesPerSymbol; // ajouter un silence de 10 ms entre les symboles samples.push(...this.encodeBlack(this.sampleRate * 0.01)); // Silence } return samples; } encodeBlack(size) { const samples = []; for (let i = 0; i < size; i++) { const t = (this.lastBlackStep + i) / this.sampleRate; samples.push(Math.sin(2 * Math.PI * 150 * t) * (0.0625 * (Math.sin(2 * Math.PI * 0.5 * t) + 1))); // Signal à 350 Hz pour le "black" } this.lastBlackStep += size; this.lastBlackStep %= this.sampleRate * 2; // Réinitialiser tous les 2 secondes pour éviter les débordements return samples; } process(inputs, outputs, parameters) { const output = outputs[0]; // output est un tableau de canaux (ex: [Float32Array, ...]) const channelData = output[0]; // Accéder au premier canal (mono) if (this.symbolSamples.length === 0 && this.encodeLowPrio.length > 0) { this.symbolSamples.push(...this.encodeSymbols(this.encodeLowPrio.shift())); this.symbolSamples.push(...this.encodeBlack(this.sampleRate * 0.02)); } if (this.symbolSamples.length === 0) { const samples = this.encodeBlack(channelData.length) for (let i = 0; i < channelData.length; i++) { channelData[i] = samples[i] || 0; } return true; } for (let i = 0; i < channelData.length; i++) { channelData[i] = this.symbolSamples.shift() || 0; // Prendre le prochain échantillon ou 0 si vide } return true; } } registerProcessor('dtmf-processor', ProcessorDTMF);