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;
}