| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export const HYPE_LINES = { |
| hello: "Manos arriba — let's go!", |
| build: "Build it up… here it comes…", |
| drop: "¡Dale! Drop it!", |
| sweep: "Bring it up!", |
| mix: "Mix it — vamos!", |
| }; |
|
|
| function b64ToBuf(b64) { |
| const bin = atob(b64), u = new Uint8Array(bin.length); |
| for (let i = 0; i < bin.length; i++) u[i] = bin.charCodeAt(i); |
| return u.buffer; |
| } |
|
|
| export class HypeMan { |
| constructor(engine) { |
| this.engine = engine; |
| this.ctx = engine.ctx; |
| this.bank = {}; |
| this.last = {}; |
| this.enabled = true; |
| |
| this.source = ("speechSynthesis" in window) ? "speech" : "none"; |
| this.bus = this.ctx.createGain(); |
| this.bus.gain.value = 1.0; |
| this.bus.connect(this.ctx.destination); |
| } |
|
|
| |
| |
| async load() { |
| let loaded = await this._loadPrebaked(); |
| if (loaded < Object.keys(HYPE_LINES).length) loaded += await this._loadEndpoint(); |
| if (loaded) this.source = "voxcpm"; |
| return this.source; |
| } |
|
|
| async _loadPrebaked() { |
| let n = 0; |
| for (const key of Object.keys(HYPE_LINES)) { |
| try { |
| const r = await fetch(`hype/${key}.wav`, { cache: "force-cache" }); |
| if (r.ok) { this.bank[key] = await this.ctx.decodeAudioData(await r.arrayBuffer()); n++; } |
| } catch (_) { } |
| } |
| return n; |
| } |
|
|
| async _loadEndpoint() { |
| try { |
| const r = await fetch("api/hype"); |
| if (!r.ok) return 0; |
| const j = await r.json(); |
| let n = 0; |
| for (const item of (j.bank || [])) { |
| if (this.bank[item.key]) continue; |
| try { this.bank[item.key] = await this.ctx.decodeAudioData(b64ToBuf(item.wav_b64)); n++; } catch (_) {} |
| } |
| return n; |
| } catch (_) { return 0; } |
| } |
|
|
| fire(key, { cooldown = 5, duck = 0.4 } = {}) { |
| if (!this.enabled || !HYPE_LINES[key]) return; |
| const now = this.ctx.currentTime; |
| if (this.last[key] && now - this.last[key] < cooldown) return; |
| this.last[key] = now; |
|
|
| if (duck < 1) this.engine.duck(duck, 1.1); |
|
|
| const buf = this.bank[key]; |
| if (buf) { |
| const s = this.ctx.createBufferSource(); |
| s.buffer = buf; s.connect(this.bus); s.start(); |
| } else if (this.source === "speech") { |
| const u = new SpeechSynthesisUtterance(HYPE_LINES[key]); |
| u.rate = 1.08; u.pitch = 1.15; u.volume = 1.0; |
| window.speechSynthesis.cancel(); |
| window.speechSynthesis.speak(u); |
| } |
| } |
| } |
|
|