"""
04_mel_hearing.py — Как компьютер видит частоты vs как их слышит ухо.

Строит бок о бок два спектрограммных представления одной и той же записи:
линейное (равные Гц на равные пиксели — как считает БПФ) и мел-шкалу
(психоакустическая шкала, приближающая нелинейное восприятие высоты звука
человеческим ухом — см. THEORY.md, раздел "Мел-шкала: почему ухо — не БПФ").

Запуск:
    python3 04_mel_hearing.py [/dev/ttyUSB0]
"""
import sys

import numpy as np
import matplotlib.pyplot as plt

from serial_stream import open_port, read_block_blocking, EFFECTIVE_RATE_HZ

try:
    import librosa
    import librosa.display
    LIBROSA_OK = True
except ImportError:
    LIBROSA_OK = False

PORT = sys.argv[1] if len(sys.argv) > 1 else "/dev/ttyUSB0"


def main():
    if not LIBROSA_OK:
        print("Нужна librosa: pip install librosa")
        return

    ser = open_port(PORT)
    duration_s = 4.0
    print(f"Запись {duration_s:.0f} секунд звука. Говорите/свистите разными тонами, чтобы был виден диапазон.")
    input("Нажмите Enter, чтобы начать запись...")

    n_samples = int(EFFECTIVE_RATE_HZ * duration_s)
    block = read_block_blocking(ser, n_samples=n_samples, timeout_s=duration_s + 3.0)
    ser.close()

    if block.size < n_samples // 2:
        print("Слишком мало данных — проверьте, что стенд в режиме Plotter ('p' в Serial Monitor).")
        return

    y = (block / 32768.0).astype(np.float32)
    sr = EFFECTIVE_RATE_HZ

    linear_spec = np.abs(librosa.stft(y, n_fft=1024, hop_length=256))
    linear_db = librosa.amplitude_to_db(linear_spec, ref=np.max)

    mel_spec = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=1024, hop_length=256, n_mels=64)
    mel_db = librosa.power_to_db(mel_spec, ref=np.max)

    fig, (ax_lin, ax_mel) = plt.subplots(1, 2, figsize=(12, 5))
    fig.suptitle("Линейная шкала частот (БПФ) vs мел-шкала (модель слуха человека)")

    img1 = librosa.display.specshow(linear_db, sr=sr, hop_length=256, x_axis="time", y_axis="linear", ax=ax_lin)
    ax_lin.set_title("Линейная шкала — равные Гц занимают равное место")
    fig.colorbar(img1, ax=ax_lin, format="%+2.0f дБ")

    img2 = librosa.display.specshow(mel_db, sr=sr, hop_length=256, x_axis="time", y_axis="mel", ax=ax_mel)
    ax_mel.set_title("Мел-шкала — сжато на высоких, растянуто на низких частотах")
    fig.colorbar(img2, ax=ax_mel, format="%+2.0f дБ")

    fig.tight_layout(rect=[0, 0, 1, 0.94])
    plt.show()


if __name__ == "__main__":
    main()
