# Copyright (C) 2026 Nathan Cerisara <https://github.com/nath54/nasong>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.


"""Synthesizer instrument definitions.

This module provides various subtractive and additive synthesizers, including
thick unison leads, punchy sub-basses, and atmospheric pads.
"""

#
### Import Modules. ###
#
import math

#
import nasong.core.all_values as lv


#
def SynthLead(
    time: lv.Value,
    frequency: float,
    start_time: float,
    duration: float = 1.0,
    velocity: float = 1.0,
) -> lv.Value:
    """Creates a smooth, powerful "Unison" lead synthesizer.

    Uses three detuned band-limited sawtooth oscillators to create a thick
    stereo-ready sound, modulated by a subtle vibrato and a standard ADSR
    envelope.

    Args:
        time (lv.Value): The global time provider.
        frequency (float): The base fundamental frequency (Hz).
        start_time (float): Activation time in seconds.
        duration (float, optional): Length of the note in seconds. Defaults to 1.0.

    Returns:
        lv.Value: The audio value graph for the synth lead.
    """

    #
    ### ADSR envelope: Slightly softer attack for a smoother feel ###
    #
    amp_env: lv.Value = lv.ADSR2(
        time,
        note_start=start_time,
        note_duration=duration,
        attack_time=0.05,
        decay_time=0.2,
        sustain_level=0.6,
        release_time=0.3,
    )

    #
    ### Vibrato LFO: Subtle 4.0 Hz rate, 0.5% depth (reduced from 2%) ###
    #
    vibrato_lfo: lv.Value = lv.LFO(
        time,
        rate_hz=lv.c(1.0),
        waveform_class=lv.Sin,
        amplitude=lv.c(0.00005 * frequency),  # 0.5% frequency deviation
    )
    #
    base_freq: lv.Value = lv.Sum(lv.c(frequency), vibrato_lfo)

    #
    ### Oscillators: 3 Detuned Sawtooths (Unison Effect) ###
    ### This creates a "thick" and "smooth" sound instead of a thin buzz. ###
    #

    # 1. Center Oscillator
    osc_center: lv.Value = lv.BandLimitedSawtooth(
        time, frequency=base_freq, num_harmonics=30
    )

    # 2. Left Oscillator (Slightly Flat)
    freq_flat: lv.Value = lv.Product(base_freq, lv.c(0.9999))
    osc_flat: lv.Value = lv.BandLimitedSawtooth(
        time, frequency=freq_flat, num_harmonics=30
    )

    # 3. Right Oscillator (Slightly Sharp)
    freq_sharp: lv.Value = lv.Product(base_freq, lv.c(1.0001))
    osc_sharp: lv.Value = lv.BandLimitedSawtooth(
        time, frequency=freq_sharp, num_harmonics=30
    )

    #
    ### Mix the oscillators: Center is dominant, sides add "smoothness" ###
    #
    mixed_signal: lv.Value = lv.Sum(
        lv.Product(osc_center, lv.c(0.5)),
        lv.Product(osc_flat, lv.c(0.25)),
        lv.Product(osc_sharp, lv.c(0.25)),
    )

    #
    ### Final = 0.4 * velocity * AmpEnv * MixedSignal ###
    #
    return lv.Product(lv.c(0.4 * velocity), amp_env, mixed_signal)


#
def SynthBass(
    time: lv.Value,
    frequency: float,
    start_time: float,
    duration: float = 0.5,
    velocity: float = 1.0,
) -> lv.Value:
    """Creates a punchy, anti-aliased synthesizer bass.

    Combines a band-limited square wave with a sine sub-oscillator one octave
    below, shaped by a quick-attack exponential decay envelope.

    Args:
        time (lv.Value): The global time provider.
        frequency (float): Fundamental frequency (Hz).
        start_time (float): activation time in seconds.
        duration (float, optional): Length of the note in seconds. Defaults to 0.5.

    Returns:
        lv.Value: The audio value graph for the synth bass.
    """

    #
    ### Envelope: 0.01s attack, then exp(-t * 5) decay ###
    #
    amp_env: lv.Value = lv.ExponentialADSR(
        time,
        note_start=start_time,
        note_duration=duration,
        attack_time=0.01,
        decay_time=duration - 0.01,
        sustain_level=0.0,
        release_time=0.01,
        attack_curve=1.0,  # Linear attack
        decay_curve=2.5,  # Exponential decay (approx. exp(-t*5))
    )

    #
    ### Oscillator: Replaced "naive" square with anti-aliased version. ###
    #
    osc_square: lv.Value = lv.BandLimitedSquare(
        time, frequency=lv.c(frequency), amplitude=lv.c(0.6), num_harmonics=20
    )

    #
    ### Sub-oscillator: Sine wave one octave down ###
    #
    relative_time: lv.Value = lv.BasicScaling(time, lv.c(1), lv.c(-start_time))
    #
    osc_sub: lv.Value = lv.Sin(
        relative_time,
        frequency=lv.c(frequency / 2.0 * 2 * math.pi),
        amplitude=lv.c(0.4),
    )

    #
    ### Signal = (Square * 0.6 + Sub * 0.4) ###
    #
    signal: lv.Value = lv.Sum(osc_square, osc_sub)

    #
    ### Final = 0.35 * velocity * AmpEnv * Signal ###
    #
    return lv.Product(lv.c(0.35 * velocity), amp_env, signal)


#
def SynthPad(
    time: lv.Value,
    frequency: float,
    start_time: float,
    duration: float = 4.0,
    velocity: float = 1.0,
) -> lv.Value:
    """Creates an atmospheric synthesizer pad.

    Features a very slow attack and release, utilizing three delicately
    detuned sine oscillators to create a wide, shimmering texture.

    Args:
        time (lv.Value): The global time provider.
        frequency (float): Fundamental frequency (Hz).
        start_time (float): Activation time in seconds.
        duration (float, optional): Gated duration of the pad. Defaults to 4.0.

    Returns:
        lv.Value: The audio value graph for the synth pad.
    """

    #
    ### Slow ASR envelope ###
    #
    amp_env: lv.Value = lv.ADSR2(
        time,
        note_start=start_time,
        note_duration=duration,
        attack_time=0.5,
        decay_time=0.001,
        sustain_level=1.0,
        release_time=1.0,
    )

    #
    ### Three detuned oscillators ###
    #
    relative_time: lv.Value = lv.BasicScaling(time, lv.c(1), lv.c(-start_time))
    pi2: float = 2 * math.pi
    #
    osc1: lv.Value = lv.Sin(relative_time, frequency=lv.c(frequency * pi2))
    #
    osc2: lv.Value = lv.Sin(relative_time, frequency=lv.c(frequency * 1.003 * pi2))
    #
    osc3: lv.Value = lv.Sin(relative_time, frequency=lv.c(frequency * 0.997 * pi2))

    #
    ### Signal = Average of the three oscillators ###
    #
    signal: lv.Value = lv.Product(lv.Sum(osc1, osc2, osc3), lv.c(1.0 / 3.0))

    #
    ### Final = 0.2 * velocity * AmpEnv * Signal ###
    #
    return lv.Product(lv.c(0.2 * velocity), amp_env, signal)
