Files
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

171 lines
3.7 KiB
C#

using System.Runtime.InteropServices;
using System.Text.Json.Nodes;
namespace Sandbox.Audio;
/// <summary>
/// Takes a bunch of samples and processes them. It's common for these to be chained together.
/// It's also common for the processor to store state between calls.
/// </summary>
public abstract partial class AudioProcessor
{
/// <summary>
/// Is this processor active?
/// </summary>
[Group( "Processor Settings" )]
public bool Enabled { get; set; } = true;
/// <summary>
/// Should we fade the influence of this processor in?
/// </summary>
[Range( 0, 1 )]
[Group( "Processor Settings" )]
public float Mix { get; set; } = 1;
private MultiChannelBuffer scratch = new MultiChannelBuffer( 8 );
internal Transform _listener;
/// <summary>
/// The listener's position in this frame.
/// </summary>
[Hide]
protected Transform Listener => _listener;
/// <summary>
/// Should process input into output
/// </summary>
internal virtual void Process( MultiChannelBuffer input, MultiChannelBuffer output )
{
Assert.True( input.ChannelCount <= output.ChannelCount );
output.CopyFrom( input );
ProcessEachChannel( output );
}
/// <summary>
/// Will process the buffer, and copy it back to output
/// </summary>
internal void ProcessInPlace( MultiChannelBuffer inputoutput )
{
scratch.Silence();
Process( inputoutput, scratch );
for ( int i = 0; i < inputoutput.ChannelCount; i++ )
{
inputoutput.Get( i ).CopyFrom( scratch.Get( i ) );
}
}
/// <summary>
/// Called internally to process each channel in a buffer
/// </summary>
private unsafe void ProcessEachChannel( MultiChannelBuffer buffer )
{
for ( int i = 0; i < buffer.ChannelCount; i++ )
{
using ( buffer.Get( i ).DataPointer( out var ptr ) )
{
Span<float> memory = new Span<float>( (float*)ptr, AudioEngine.MixBufferSize );
ProcessSingleChannel( new AudioChannel( i ), memory );
}
}
}
/// <summary>
/// For implementations that process each channel individually
/// </summary>
protected virtual unsafe void ProcessSingleChannel( AudioChannel channel, Span<float> input )
{
}
public JsonObject Serialize()
{
var js = Json.SerializeAsObject( this );
js["__type"] = GetType().Name;
return js;
}
public void Deserialize( JsonObject node )
{
Json.DeserializeToObject( this, node );
}
public override string ToString() => GetType().Name;
protected virtual void OnDestroy()
{
}
internal virtual void OnRemovedInternal()
{
OnDestroy();
}
}
/// <summary>
/// Represents an audio channel, between 0 and 7. This is used to index into buffers.
/// This is used rather than an int to avoid unfortuate bugs.
/// </summary>
public struct AudioChannel
{
public static AudioChannel Left => new AudioChannel( 0 );
public static AudioChannel Right => new AudioChannel( 1 );
internal int channel;
public AudioChannel( int i )
{
channel = i;
}
public int Get() => channel;
}
/// <summary>
/// Stores a variable per channel
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Compiler", "CS0169" )]
public struct PerChannel<T>
{
#pragma warning disable CS0169, CS0649
private T _0, _1, _2, _3, _4, _5, _6, _7;
#pragma warning restore CS0169, CS0649
private Span<T> Values => MemoryMarshal.CreateSpan( ref _0, 8 );
[Obsolete]
public T[] Value => Values.ToArray();
public PerChannel()
{
}
public static implicit operator PerChannel<T>( T v )
{
PerChannel<T> x = new PerChannel<T>();
for ( int i = 0; i < 8; i++ )
{
x.Values[i] = v;
}
return x;
}
/// <summary>
/// Get the value in a channel
/// </summary>
public T Get( AudioChannel i ) => Values[i.channel];
/// <summary>
/// Set the value in a channel
/// </summary>
public void Set( AudioChannel i, T value ) => Values[i.channel] = value;
}