h
This commit is contained in:
parent
e00bfd8879
commit
f941200504
70 changed files with 1856 additions and 429 deletions
32
js/audio.js
32
js/audio.js
|
|
@ -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 });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue