feat: initial commit - Carinha Control Panel v1.0 com UI glassmorphism, watch-bar, emojis, braco robotico e conversa IA
This commit is contained in:
+248
@@ -0,0 +1,248 @@
|
||||
/**
|
||||
* audio-robot.js — Sintetizador procedural de efeitos sonoros robóticos (Web Audio API)
|
||||
* Gera bips, assobios, trinados alegres e sons estilo WALL-E / Eilik diretamente no navegador.
|
||||
* Sem arquivos de áudio externos, sem dependências, 100% autônomo e instantâneo.
|
||||
*/
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
let audioCtx = null;
|
||||
let enabled = localStorage.getItem('carinha_sfx') !== 'false';
|
||||
let volume = parseFloat(localStorage.getItem('carinha_volume') || '0.7');
|
||||
|
||||
function getContext() {
|
||||
if (!audioCtx) {
|
||||
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
if (AudioContext) {
|
||||
audioCtx = new AudioContext();
|
||||
}
|
||||
}
|
||||
if (audioCtx && audioCtx.state === 'suspended') {
|
||||
audioCtx.resume();
|
||||
}
|
||||
return audioCtx;
|
||||
}
|
||||
|
||||
const RobotAudio = {
|
||||
get enabled() { return enabled; },
|
||||
set enabled(val) {
|
||||
enabled = Boolean(val);
|
||||
localStorage.setItem('carinha_sfx', enabled ? 'true' : 'false');
|
||||
window.dispatchEvent(new CustomEvent('carinha:audio-toggle', { detail: { enabled } }));
|
||||
},
|
||||
|
||||
get volume() { return volume; },
|
||||
set volume(val) {
|
||||
volume = Math.max(0, Math.min(1, parseFloat(val) || 0.7));
|
||||
localStorage.setItem('carinha_volume', volume.toString());
|
||||
},
|
||||
|
||||
toggle() {
|
||||
this.enabled = !this.enabled;
|
||||
if (this.enabled) this.bleep();
|
||||
return this.enabled;
|
||||
},
|
||||
|
||||
// Bip simples de confirmação / clique
|
||||
click() {
|
||||
if (!enabled) return;
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
const now = ctx.currentTime;
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(800, now);
|
||||
osc.frequency.exponentialRampToValueAtTime(1400, now + 0.04);
|
||||
gain.gain.setValueAtTime(volume * 0.15, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.05);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.05);
|
||||
},
|
||||
|
||||
// Bip robótico positivo
|
||||
bleep() {
|
||||
if (!enabled) return;
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
const now = ctx.currentTime;
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(659.25, now); // E5
|
||||
osc.frequency.setValueAtTime(880, now + 0.06); // A5
|
||||
gain.gain.setValueAtTime(volume * 0.25, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.16);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.16);
|
||||
},
|
||||
|
||||
// Trinado alegre (estilo WALL-E / Eilik feliz)
|
||||
happy() {
|
||||
if (!enabled) return;
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const notes = [523.25, 659.25, 783.99, 1046.50]; // C5, E5, G5, C6
|
||||
const now = ctx.currentTime;
|
||||
notes.forEach((freq, idx) => {
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
const start = now + idx * 0.07;
|
||||
osc.type = 'triangle';
|
||||
osc.frequency.setValueAtTime(freq, start);
|
||||
osc.frequency.exponentialRampToValueAtTime(freq * 1.08, start + 0.06);
|
||||
gain.gain.setValueAtTime(0.001, start);
|
||||
gain.gain.linearRampToValueAtTime(volume * 0.3, start + 0.02);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, start + 0.1);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(start);
|
||||
osc.stop(start + 0.1);
|
||||
});
|
||||
},
|
||||
|
||||
// Curiosidade / interrogação
|
||||
curious() {
|
||||
if (!enabled) return;
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
const now = ctx.currentTime;
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(440, now);
|
||||
osc.frequency.exponentialRampToValueAtTime(950, now + 0.22);
|
||||
gain.gain.setValueAtTime(volume * 0.22, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.25);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.25);
|
||||
},
|
||||
|
||||
// Surpreso / Alerta
|
||||
surprise() {
|
||||
if (!enabled) return;
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
const now = ctx.currentTime;
|
||||
osc.type = 'square';
|
||||
osc.frequency.setValueAtTime(800, now);
|
||||
osc.frequency.linearRampToValueAtTime(1400, now + 0.08);
|
||||
osc.frequency.linearRampToValueAtTime(1800, now + 0.15);
|
||||
gain.gain.setValueAtTime(volume * 0.2, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.18);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.18);
|
||||
},
|
||||
|
||||
// Triste / Melancólico
|
||||
sad() {
|
||||
if (!enabled) return;
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
const now = ctx.currentTime;
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(600, now);
|
||||
osc.frequency.linearRampToValueAtTime(320, now + 0.35);
|
||||
gain.gain.setValueAtTime(volume * 0.2, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.4);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.4);
|
||||
},
|
||||
|
||||
// Acordando (Power on)
|
||||
wake() {
|
||||
if (!enabled) return;
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const freqs = [350, 440, 587.33, 880];
|
||||
const now = ctx.currentTime;
|
||||
freqs.forEach((f, i) => {
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
const t = now + i * 0.06;
|
||||
osc.type = 'triangle';
|
||||
osc.frequency.setValueAtTime(f, t);
|
||||
gain.gain.setValueAtTime(volume * 0.25, t);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.12);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(t);
|
||||
osc.stop(t + 0.12);
|
||||
});
|
||||
},
|
||||
|
||||
// Dormindo (Power down)
|
||||
sleep() {
|
||||
if (!enabled) return;
|
||||
const ctx = getContext();
|
||||
if (!ctx) return;
|
||||
const freqs = [700, 520, 380, 220];
|
||||
const now = ctx.currentTime;
|
||||
freqs.forEach((f, i) => {
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
const t = now + i * 0.08;
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(f, t);
|
||||
gain.gain.setValueAtTime(volume * 0.2, t);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, t + 0.14);
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(t);
|
||||
osc.stop(t + 0.14);
|
||||
});
|
||||
},
|
||||
|
||||
// Tocar som de acordo com o gesto/emoção
|
||||
tocarGesto(gesto) {
|
||||
if (!enabled) return;
|
||||
switch ((gesto || '').toLowerCase()) {
|
||||
case 'feliz':
|
||||
case 'alegre':
|
||||
case 'coracao':
|
||||
this.happy();
|
||||
break;
|
||||
case 'surpreso':
|
||||
this.surprise();
|
||||
break;
|
||||
case 'triste':
|
||||
this.sad();
|
||||
break;
|
||||
case 'dormir':
|
||||
this.sleep();
|
||||
break;
|
||||
case 'acordar':
|
||||
this.wake();
|
||||
break;
|
||||
case 'olhar_esq':
|
||||
case 'olhar_dir':
|
||||
case 'olhar_baixo':
|
||||
this.curious();
|
||||
break;
|
||||
case 'piscar':
|
||||
case 'piscar2':
|
||||
case 'piscadinha':
|
||||
this.click();
|
||||
break;
|
||||
default:
|
||||
this.bleep();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.RobotAudio = RobotAudio;
|
||||
})(window);
|
||||
Reference in New Issue
Block a user