Code/SFXR.cs
using System;
using System.Collections.Generic;
using Sandbox;

namespace SFXR;

public static partial class SFXR
{
    public const short MaxAmplitude = 32767;  // Maximum amplitude for a 16-bit audio

    /// <summary>
    /// Gets the sample of a waveform at a given time and frequency
    /// </summary>
    /// <param name="waveform">The waveform</param>
    /// <param name="t">Time in seconds</param>
    /// <param name="frequency">Frequency in Hz</param>
    /// <returns>Sample of the waveform at the given time and frequency</returns>
    public static short GetWaveformSample( Waveform waveform, float t, float frequency )
    {
        switch ( waveform )
        {
            case Waveform.Square:
                return GetSquareWaveSample( t, frequency );
            case Waveform.Sawtooth:
                return GetSawtoothWaveSample( t, frequency );
            case Waveform.Sine:
                return GetSineWaveSample( t, frequency );
            case Waveform.Noise:
                return GetNoiseWaveSample( t, frequency );
            default:
                throw new ArgumentException( "Invalid waveform" );
        }
    }

    public static short GetCurveSample( Curve curve, float t, float frequency )
    {
        float period = 1.0f / frequency;
        return (short)(curve.Evaluate( t / period ) * MaxAmplitude);
    }

    /// <summary>
    /// Gets the sample of a square waveform at a given time and frequency
    /// </summary>
    /// <param name="t">Time in seconds</param>
    /// <param name="frequency">Frequency in Hz</param>
    /// <returns>Sample of the square waveform at the given time and frequency</returns>
    public static short GetSquareWaveSample( float t, float frequency )
    {
        float period = 1.0f / frequency;
        float halfPeriod = period / 2;

        // Determine the current position in the waveform cycle
        float cyclePosition = t % period;

        // Output the maximum amplitude for the first half of the period,
        // and the minimum amplitude for the second half.
        return (short)(cyclePosition < halfPeriod ? MaxAmplitude : -MaxAmplitude);
    }

    /// <summary>
    /// Gets the sample of a sawtooth waveform at a given time and frequency
    /// </summary>
    /// <param name="t">Time in seconds</param>
    /// <param name="frequency">Frequency in Hz</param>
    /// <returns>Sample of the sawtooth waveform at the given time and frequency</returns>
    public static short GetSawtoothWaveSample( float t, float frequency )
    {
        float period = 1.0f / frequency;
        return (short)(MaxAmplitude * 2 * (t % period) / period - MaxAmplitude);
    }

    /// <summary>
    /// Gets the sample of a sine waveform at a given time and frequency
    /// </summary>
    /// <param name="t">Time in seconds</param>
    /// <param name="frequency">Frequency in Hz</param>
    /// <returns>Sample of the sine waveform at the given time and frequency</returns>
    public static short GetSineWaveSample( float t, float frequency )
    {
        float period = 1.0f / frequency;
        return (short)(MaxAmplitude * MathF.Sin( t * MathF.PI * 2 / period ));
    }

    /// <summary>
    /// Gets the sample of a noise waveform at a given time and frequency
    /// </summary>
    /// <param name="t">Time in seconds</param>
    /// <param name="frequency">Frequency in Hz</param>
    /// <returns>Sample of the noise waveform at the given time and frequency</returns>
    public static short GetNoiseWaveSample( float t, float frequency )
    {
        if ( MathF.Abs( t - lastNoiseT ) >= 1.0f / frequency )
        {
            noisebuffer.Clear();
            for ( int i = 0; i < 32; i++ )
            {
                noisebuffer.Add( (short)Random.Shared.Int( short.MinValue, short.MaxValue ) );
            }
            lastNoiseT = t;
        }

        // LUA EXAMPLE: sample = noisebuffer[trunc(phase * 32 / period) % 32 + 1]
        float period = 1.0f / frequency;
        int index = (int)(t * 32 / period) % 32;
        return noisebuffer[index];
    }
    static List<short> noisebuffer = new();
    static float lastNoiseT = -999f;

}