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