Files
MeshChatX/meshchatx/src/frontend/js/ToneGenerator.js

157 lines
4.6 KiB
JavaScript

export default class ToneGenerator {
constructor() {
this.audioCtx = null;
this.oscillator = null;
this.gainNode = null;
this.timeoutId = null;
this.currentTone = null; // 'ringback', 'busy', or null
this.volume = 0.1; // Default volume (0.0 to 1.0 real gain)
}
setVolume(volumePercent) {
// volumePercent is 0-100
this.volume = (volumePercent / 100) * 0.2; // Cap at 0.2 real gain for safety
if (this.gainNode && this.audioCtx) {
this.gainNode.gain.setTargetAtTime(this.volume, this.audioCtx.currentTime, 0.1);
}
}
_initAudioContext() {
if (!this.audioCtx) {
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
}
playRingback() {
if (this.currentTone === "ringback") return;
this._initAudioContext();
this.stop();
this.currentTone = "ringback";
const play = () => {
const osc1 = this.audioCtx.createOscillator();
const osc2 = this.audioCtx.createOscillator();
const gain = this.audioCtx.createGain();
osc1.frequency.value = 440;
osc2.frequency.value = 480;
gain.gain.value = this.volume;
osc1.connect(gain);
osc2.connect(gain);
gain.connect(this.audioCtx.destination);
osc1.start();
osc2.start();
this.oscillator = [osc1, osc2];
this.gainNode = gain;
// Stop after 2 seconds
setTimeout(() => {
if (this.oscillator === osc1 || (Array.isArray(this.oscillator) && this.oscillator.includes(osc1))) {
gain.gain.exponentialRampToValueAtTime(0.001, this.audioCtx.currentTime + 0.5);
setTimeout(() => {
osc1.stop();
osc2.stop();
osc1.disconnect();
osc2.disconnect();
gain.disconnect();
}, 500);
}
}, 2000);
// Repeat every 6 seconds
this.timeoutId = setTimeout(play, 6000);
};
play();
}
playBusyTone() {
if (this.currentTone === "busy") return;
this._initAudioContext();
this.stop();
this.currentTone = "busy";
const play = () => {
const osc = this.audioCtx.createOscillator();
const gain = this.audioCtx.createGain();
osc.frequency.value = 480;
gain.gain.value = this.volume;
osc.connect(gain);
gain.connect(this.audioCtx.destination);
osc.start();
this.oscillator = osc;
this.gainNode = gain;
// Stop after 0.5 seconds
setTimeout(() => {
if (this.oscillator === osc) {
osc.stop();
osc.disconnect();
gain.disconnect();
}
}, 500);
// Repeat every 1 second
this.timeoutId = setTimeout(play, 1000);
};
play();
// Auto-stop busy tone after 4 seconds (4 cycles)
setTimeout(() => this.stop(), 4000);
}
stop() {
this.currentTone = null;
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
if (this.oscillator) {
if (Array.isArray(this.oscillator)) {
this.oscillator.forEach((osc) => {
try {
osc.stop();
} catch {
// Ignore errors if oscillator is already stopped or disconnected
}
try {
osc.disconnect();
} catch {
// Ignore errors if oscillator is already disconnected
}
});
} else {
try {
this.oscillator.stop();
} catch {
// Ignore errors if oscillator is already stopped or disconnected
}
try {
this.oscillator.disconnect();
} catch {
// Ignore errors if oscillator is already disconnected
}
}
this.oscillator = null;
}
if (this.gainNode) {
try {
this.gainNode.disconnect();
} catch {
// Ignore errors if gain node is already disconnected
}
this.gainNode = null;
}
}
}