This commit is contained in:
33333-33333 2026-06-30 10:57:06 +09:00
commit f941200504
70 changed files with 1856 additions and 429 deletions

View file

@ -20,6 +20,9 @@ const audio = {
samplePaths: {},
soundCategories: {},
soundMap: {},
_startupAudioDampenStarted: false,
_startupAudioDampenUntil: 0,
_currentPlayGainScale: 1,
applySoundPack(pack = window.TARINAI_SOUND_PACK) {
if (!pack || typeof pack !== "object") return false;
if (pack.categoryGain && typeof pack.categoryGain === "object") this.categoryGain = { ...this.categoryGain, ...pack.categoryGain };
@ -39,7 +42,8 @@ const audio = {
if (!this.masterGainNode) {
const ctx = this.ctx;
this.masterGainNode = ctx.createGain();
this.masterGainNode.gain.setValueAtTime(1.85, ctx.currentTime);
this.masterGainNode.gain.setValueAtTime(0.0001, ctx.currentTime);
this.masterGainNode.gain.linearRampToValueAtTime(0.78, ctx.currentTime + 0.18);
if (typeof ctx.createDynamicsCompressor === "function") {
const compressor = ctx.createDynamicsCompressor();
compressor.threshold.value = -18;
@ -135,6 +139,16 @@ const audio = {
this.sampleLast[id] = t;
return true;
},
startupGainScale() {
const now = performance.now() / 1000;
if (!this._startupAudioDampenStarted) {
this._startupAudioDampenStarted = true;
this._startupAudioDampenUntil = now + 0.85;
return 0.26;
}
if (now < (this._startupAudioDampenUntil || 0)) return 0.42;
return 1;
},
playSample(key, minGap = 0.16, category = null) {
const base = this.samples[key];
if (!base) return false;
@ -143,8 +157,9 @@ const audio = {
this.lastPlayedId = key;
window.TarinaiEvents.emit("audio:play", { id: key, category: cat, synthetic: false });
this.ensure();
const playScale = this.startupGainScale();
const node = base.cloneNode(true);
node.volume = Math.min(1, base.volume * (this.categoryGain?.[cat] ?? 1) * (this.idGain?.[key] ?? 1));
node.volume = Math.min(1, base.volume * (this.categoryGain?.[cat] ?? 1) * (this.idGain?.[key] ?? 1) * playScale);
this.beginVoice(Math.max(0.08, Number(node.duration) || 0.16));
node.play().catch(() => {});
return true;
@ -155,7 +170,7 @@ const audio = {
if (!ctx) return;
if ((this.activeVoices || 0) >= (this.maxConcurrent || 16) && category !== "notify") return;
this.beginVoice(dur);
gain = Math.min(0.95, Math.max(0.0002, gain * (this.categoryGain?.[category] ?? 1) * (this._synthIdGain || 1)));
gain = Math.min(0.95, Math.max(0.0002, gain * (this.categoryGain?.[category] ?? 1) * (this._synthIdGain || 1) * (this._currentPlayGainScale || 1)));
const t0 = ctx.currentTime;
const osc = ctx.createOscillator();
const g = ctx.createGain();
@ -175,7 +190,7 @@ const audio = {
if (!ctx) return;
if ((this.activeVoices || 0) >= (this.maxConcurrent || 16) && category !== "notify") return;
this.beginVoice(dur);
gain = Math.min(0.90, Math.max(0.0002, gain * (this.categoryGain?.[category] ?? 1) * (this._synthIdGain || 1)));
gain = Math.min(0.90, Math.max(0.0002, gain * (this.categoryGain?.[category] ?? 1) * (this._synthIdGain || 1) * (this._currentPlayGainScale || 1)));
const length = Math.max(1, Math.floor(ctx.sampleRate * dur));
const buffer = ctx.createBuffer(1, length, ctx.sampleRate);
const data = buffer.getChannelData(0);
@ -197,16 +212,19 @@ const audio = {
if (!this.canPlay(id, category, minGap)) return false;
this.lastPlayedId = id;
window.TarinaiEvents.emit("audio:play", { id, category, synthetic: !this.samples[id] });
const playScale = this.startupGainScale();
if (this.samples[id]) {
const base = this.samples[id];
this.ensure();
const node = base.cloneNode(true);
node.volume = Math.min(1, base.volume * (this.categoryGain?.[category] ?? 1) * (this.idGain?.[id] ?? 1));
node.volume = Math.min(1, base.volume * (this.categoryGain?.[category] ?? 1) * (this.idGain?.[id] ?? 1) * playScale);
this.beginVoice(Math.max(0.08, Number(node.duration) || 0.16));
node.play().catch(() => {});
return true;
}
this.synth(id, category);
const prevPlayScale = this._currentPlayGainScale || 1;
this._currentPlayGainScale = playScale;
try { this.synth(id, category); } finally { this._currentPlayGainScale = prevPlayScale; }
return true;
},
synth(id, category) {
@ -262,6 +280,8 @@ const audio = {
},
bubble(text) {
const s = String(text || "");
if (s.includes("ぶるぶる")) return this.playSample("voice_temp_cold", 1.20, "voice") || this.play("voice_temp_cold", { category: "voice", minGap: 1.20 });
if (s.includes("あちゅい")) return this.playSample("voice_temp_hot", 1.20, "voice") || this.play("voice_temp_hot", { category: "voice", minGap: 1.20 });
if (s.includes("\u3082\u3050")) return this.playSample("sfx_eat", 0.20, "voice") || this.play("sfx_eat", { category: "voice", minGap: 0.20 });
if (s.includes("\u3076\u308a")) return this.playSample("voice_poop", 0.28, "voice") || this.play("sfx_zunchi", { category: "ops", minGap: 0.28 });
if (s.includes("\u306f\u3063")) return this.playSample("voice_stress", 0.80, "voice") || this.play("sfx_damage_soft", { category: "voice", minGap: 0.80 });