#!/usr/bin/env python3
# 「99%」の効果音・音楽を numpy で合成して wav にする（外部素材なし）
import sys, wave
import numpy as np
sys.path.insert(0, __import__("os").path.dirname(__file__))
from render import BUBBLES, pct_value, DUR

SR = 44100
N = int(DUR * SR)
L = np.zeros(N)
rng = np.random.default_rng(7)

def add(t, sig, gain=1.0):
    i = int(t * SR)
    if i >= N: return
    j = min(N, i + len(sig))
    L[i:j] += sig[: j - i] * gain

def tone(f, d, wave_="sine", decay=8.0, attack=0.005):
    t = np.arange(int(d * SR)) / SR
    if wave_ == "sine": s = np.sin(2 * np.pi * f * t)
    elif wave_ == "square": s = np.sign(np.sin(2 * np.pi * f * t)) * 0.5
    elif wave_ == "tri": s = 2 / np.pi * np.arcsin(np.sin(2 * np.pi * f * t))
    env = np.minimum(1, t / attack) * np.exp(-decay * t)
    return s * env

def sweep(f0, f1, d, decay=6.0):
    t = np.arange(int(d * SR)) / SR
    f = np.linspace(f0, f1, len(t))
    ph = 2 * np.pi * np.cumsum(f) / SR
    return np.sin(ph) * np.minimum(1, t / 0.005) * np.exp(-decay * t)

# パソコンのファンのような低いノイズ（全編、エンディング前まで）
fan = np.cumsum(rng.normal(0, 1, N)); fan -= np.convolve(fan, np.ones(2000) / 2000, "same")
fan /= np.abs(fan).max() + 1e-9
end_i = int(58.6 * SR)
fan[end_i:] = 0
L += fan * 0.05

# 進捗のカチカチ（1%ごと）
last = 0
for i in range(int(8 * 30)):
    t = i / 30
    v = int(pct_value(t))
    if v > last:
        add(t, tone(2400, 0.03, decay=120), 0.18); last = v

# 99%で止まる：小さな「ピ」
add(7.6, tone(880, 0.25, decay=10), 0.25)
# 残り時間が変わるたび
for t, g in [(9.5, 0.25), (11.0, 0.3), (12.6, 0.4)]:
    add(t, tone(660, 0.12, "square", decay=20), g)
add(12.6, sweep(500, 120, 0.9, decay=3), 0.35)  # 約14年：がっかり音

# 吹き出しが出るたびの「ポッ」
for a, b, txt in BUBBLES:
    add(a, sweep(500, 900, 0.09, decay=30), 0.3)

# カーソルが近づく緊張音（低いパルス）
for k in range(0, 10):
    add(21.0 + k * 0.5, tone(110, 0.25, "tri", decay=10), 0.25 + k * 0.02)
# 待って待って：小刻みなビープ
for k in range(18):
    add(22.6 + k * 0.25, tone(1200 + 80 * (k % 2), 0.08, "square", decay=30), 0.12)
# カーソルが引き返す：ほっとする下降音
add(26.2, sweep(900, 400, 0.6, decay=4), 0.25)

# 力む：上がり続ける音
t0, t1 = 31.9, 39.0
n = int((t1 - t0) * SR)
tt = np.arange(n) / SR
f = 120 * (2 ** (tt / (t1 - t0) * 3.5))
ph = 2 * np.pi * np.cumsum(f) / SR
strain = (np.sin(ph) + 0.3 * np.sign(np.sin(ph))) * np.minimum(1, tt / 0.5)
strain *= 1 + 0.3 * np.sin(2 * np.pi * 9 * tt)
add(t0, strain, 0.12)
# 39秒：力尽きて元に戻る「ボヨン」
add(39.0, sweep(700, 90, 0.5, decay=5), 0.5)

# 100%：ファンファーレ（ド・ミ・ソ・ド→和音）
notes = [523.25, 659.25, 783.99, 1046.5]
for k, fr in enumerate(notes):
    add(46.5 + k * 0.12, tone(fr, 0.4, "square", decay=6), 0.13)
for fr in [523.25, 659.25, 783.99, 1046.5, 261.63]:
    add(47.0, tone(fr, 2.5, "tri", decay=1.2), 0.15)
    add(47.0, tone(fr * 2, 2.0, "sine", decay=2.0), 0.05)

# 次のウィンドウ：「ポン」（Windows風ではない、単純な低い音）
add(52.0, tone(330, 0.6, "sine", decay=5), 0.35)
add(52.08, tone(247, 0.8, "sine", decay=4), 0.3)
# 「……え？」
add(56.5, sweep(300, 520, 0.25, decay=6), 0.25)

add(57.4, tone(660, 0.12, "square", decay=20), 0.4)
add(57.4, sweep(500, 100, 1.0, decay=3), 0.35)

# エンディング：静かな和音
for fr in [220.0, 277.18, 329.63, 440.0]:
    add(59.2, tone(fr, 3.6, "sine", decay=0.8, attack=0.4), 0.09)

L = np.tanh(L * 1.2) * 0.85
fade = int(0.6 * SR)
L[-fade:] *= np.linspace(1, 0, fade)
data = (L * 32767).astype(np.int16)
with wave.open(sys.argv[1], "wb") as w:
    w.setnchannels(1); w.setsampwidth(2); w.setframerate(SR)
    w.writeframes(data.tobytes())
