Code/SFXRNote.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Sandbox;
namespace SFXR;
public class SFXRNote
{
public bool IsTriggered { get; private set; } = false;
public bool IsPlaying => CurrentTime < EndTime;
List<Part> Parts = new();
SoundHandle TriggeredHandle;
SFXRComponent Sfxr;
public float Frequency;
public float Volume;
public float CurrentTime = 0f;
float LoopStartTime = 0f;
float LoopEndTime = 0f;
float EndTime = 0f;
public SFXRNote( SFXRComponent sfxr, float frequency, float volume )
{
Sfxr = sfxr;
Frequency = frequency;
Volume = volume;
}
public void Trigger()
{
if ( IsTriggered )
{
TriggeredHandle.Stop( 0 );
Parts.Clear();
}
IsTriggered = true;
var effects = new List<SFXREffect>();
foreach ( var component in Sfxr.GameObject.Components.GetAll() )
{
if ( component is not SFXREffect effect || !effect.Enabled ) continue;
effects.Add( effect );
}
int sampleRate = (int)Sfxr.SampleRate;
SFXREnvelope envelope = null;
if ( effects.FirstOrDefault( x => x is SFXREnvelope ) is SFXREnvelope env )
{
envelope = env;
effects.Remove( env );
float cycle = 1f / Frequency;
int sampleCount = (int)((envelope.GetLength() + cycle) * sampleRate);
short[] samples = new short[sampleCount];
float t = 0f;
for ( int i = 0; i < sampleCount; i++ )
{
t += 1f / sampleRate;
short sampleValue = SFXR.GetWaveformSample( Sfxr.Waveform, t, Frequency );
sampleValue = (short)((float)sampleValue * Sfxr.MasterVolume * Volume);
samples[i] = sampleValue;
}
foreach ( var effect in effects )
{
if ( !effect.Enabled ) continue;
samples = effect.Apply( samples, Sfxr );
}
envelope?.Apply( samples, Sfxr );
// Attack Stream
int attackSampleCount = (int)(envelope.Attack * sampleRate);
short[] attackSamples = new short[attackSampleCount];
for ( int i = 0; i < attackSampleCount; i++ )
{
attackSamples[i] = samples[i];
}
Parts.Add( new Part( attackSamples, 0, 0 ) );
// Decay Stream
int decaySampleCount = (int)(envelope.Decay * sampleRate);
short[] decaySamples = new short[decaySampleCount];
for ( int i = 0; i < decaySampleCount; i++ )
{
decaySamples[i] = samples[i + attackSampleCount];
}
Parts.Add( new Part( decaySamples, envelope.Attack, 1 ) );
// Sustain Stream
int sustainSampleCount = (int)(envelope.SustainTime * sampleRate);
short[] sustainSamples = new short[sustainSampleCount];
for ( int i = 0; i < sustainSampleCount; i++ )
{
sustainSamples[i] = samples[i + attackSampleCount + decaySampleCount];
}
Parts.Add( new Part( sustainSamples, envelope.Attack + envelope.Decay, 2 ) );
// Release Stream
int releaseSampleCount = (int)(envelope.Release * sampleRate);
short[] releaseSamples = new short[releaseSampleCount];
for ( int i = 0; i < releaseSampleCount; i++ )
{
releaseSamples[i] = samples[i + attackSampleCount + decaySampleCount + sustainSampleCount];
}
Parts.Add( new Part( releaseSamples, envelope.Attack + envelope.Decay + envelope.SustainTime, 3 ) );
LoopStartTime = envelope.Attack + envelope.Decay;
LoopEndTime = envelope.Attack + envelope.Decay + envelope.SustainTime;
EndTime = envelope.Attack + envelope.Decay + envelope.SustainTime + envelope.Release;
}
else
{
int sampleCount = (int)(Sfxr.Length * sampleRate);
short[] samples = new short[sampleCount];
float t = 0f;
for ( int i = 0; i < sampleCount; i++ )
{
t += 1f / sampleRate;
short sampleValue = SFXR.GetWaveformSample( Sfxr.Waveform, t, Frequency );
sampleValue = (short)((float)sampleValue * Sfxr.MasterVolume * Volume);
samples[i] = sampleValue;
}
foreach ( var effect in effects )
{
if ( !effect.Enabled ) continue;
samples = effect.Apply( samples, Sfxr );
}
LoopStartTime = 0f;
LoopEndTime = Sfxr.Length;
EndTime = Sfxr.Length;
Parts.Add( new Part( samples, 0, 2 ) );
}
}
public void Release()
{
if ( !IsTriggered ) return;
TriggeredHandle?.Stop();
var last = Parts.LastOrDefault();
Parts.Clear();
if ( last is not null ) Parts.Add( last );
CurrentTime = LoopEndTime;
IsTriggered = false;
}
public void Update()
{
CurrentTime += Time.Delta;
if ( CurrentTime >= LoopEndTime && IsTriggered )
{
CurrentTime = LoopStartTime;
foreach ( var part in Parts )
{
if ( part.Index == 2 )
{
part.Played = false;
}
}
}
foreach ( var part in Parts )
{
if ( part.Played ) continue;
if ( CurrentTime >= part.Time )
{
TriggeredHandle = part.Play( (int)Sfxr.SampleRate );
part.Played = true;
}
}
}
// public void DestroyStreams()
// {
// TriggeredHandle.Stop(0);
// Parts.Clear();
// }
internal class Part
{
public short[] Samples;
public float Time;
public bool Played = false;
public int Index = 0;
public Part( short[] samples, float time, int index = 0 )
{
Samples = samples;
Time = time;
Index = index;
}
public SoundHandle Play( int sampleRate )
{
var stream = new SoundStream( sampleRate );
stream.WriteData( Samples );
return stream.Play();
}
}
}