#!/usr/bin/env python3
"""Create an original four-second tone-mixing exercise, not speech or an AI dub.
Python 3.10+, standard library only. Writes WAVs to a new destination directory.
"""
import argparse
from array import array
import math
from pathlib import Path
import sys
import wave

RATE = 16000
SECONDS = 4


def envelope(t, start, end, fade=0.02):
    if t <= start or t >= end:
        return 0.0
    return min(1.0, (t - start) / fade, (end - t) / fade)


def tone(hz, t):
    return math.sin(2 * math.pi * hz * t)


def create(destination):
    # Refuse existing destinations to avoid overwriting a reader's media.
    destination.mkdir(parents=True, exist_ok=False)
    bed, old, new = [], [], []
    for i in range(RATE * SECONDS):
        t = i / RATE
        edge = envelope(t, 0, SECONDS, 0.06)
        background = edge * (0.045 * tone(240, t) + 0.025 * tone(360, t))
        # Two soft chimes stand in for effects, including one near the end.
        for start in (0.25, 3.70):
            background += 0.045 * envelope(t, start, start + 0.15) * tone(1640, t)
        pulse = envelope(t, 1, 1.6) + envelope(t, 2.2, 2.8)
        bed.append(background)
        old.append(0.10 * pulse * tone(750, t))
        new.append(0.12 * pulse * tone(1000, t))
    signals = {
        'bed.wav': bed,
        'old-marker.wav': old,
        'new-marker.wav': new,
        'source-mix.wav': [b + o for b, o in zip(bed, old)],
        'new-only.wav': new,
        'wrong-overlay.wav': [b + o + n for b, o, n in zip(bed, old, new)],
        'bed-plus-new.wav': [b + n for b, n in zip(bed, new)],
    }
    for name, values in signals.items():
        if max(abs(v) for v in values) >= 1:
            raise ValueError('Fixture would clip; no output should be used.')
        pcm = array('h', (round(v * 32767) for v in values))
        if sys.byteorder != 'little':
            pcm.byteswap()
        with wave.open(str(destination / name), 'wb') as f:
            f.setparams((1, 2, RATE, len(values), 'NONE', 'not compressed'))
            f.writeframes(pcm.tobytes())


if __name__ == '__main__':
    p = argparse.ArgumentParser(description=__doc__)
    p.add_argument('new_folder', type=Path)
    create(p.parse_args().new_folder)
