using System.Runtime.InteropServices;
using System.Text.Json.Nodes;
namespace Sandbox.Audio;
///
/// 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.
///
public abstract partial class AudioProcessor
{
///
/// Is this processor active?
///
[Group( "Processor Settings" )]
public bool Enabled { get; set; } = true;
///
/// Should we fade the influence of this processor in?
///
[Range( 0, 1 )]
[Group( "Processor Settings" )]
public float Mix { get; set; } = 1;
private MultiChannelBuffer scratch = new MultiChannelBuffer( 8 );
internal Transform _listener;
///
/// The listener's position in this frame.
///
[Hide]
protected Transform Listener => _listener;
///
/// Should process input into output
///
internal virtual void Process( MultiChannelBuffer input, MultiChannelBuffer output )
{
Assert.True( input.ChannelCount <= output.ChannelCount );
output.CopyFrom( input );
ProcessEachChannel( output );
}
///
/// Will process the buffer, and copy it back to output
///
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 ) );
}
}
///
/// Called internally to process each channel in a buffer
///
private unsafe void ProcessEachChannel( MultiChannelBuffer buffer )
{
for ( int i = 0; i < buffer.ChannelCount; i++ )
{
using ( buffer.Get( i ).DataPointer( out var ptr ) )
{
Span memory = new Span( (float*)ptr, AudioEngine.MixBufferSize );
ProcessSingleChannel( new AudioChannel( i ), memory );
}
}
}
///
/// For implementations that process each channel individually
///
protected virtual unsafe void ProcessSingleChannel( AudioChannel channel, Span 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();
}
}
///
/// 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.
///
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;
}
///
/// Stores a variable per channel
///
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Compiler", "CS0169" )]
public struct PerChannel
{
#pragma warning disable CS0169, CS0649
private T _0, _1, _2, _3, _4, _5, _6, _7;
#pragma warning restore CS0169, CS0649
private Span Values => MemoryMarshal.CreateSpan( ref _0, 8 );
[Obsolete]
public T[] Value => Values.ToArray();
public PerChannel()
{
}
public static implicit operator PerChannel( T v )
{
PerChannel x = new PerChannel();
for ( int i = 0; i < 8; i++ )
{
x.Values[i] = v;
}
return x;
}
///
/// Get the value in a channel
///
public T Get( AudioChannel i ) => Values[i.channel];
///
/// Set the value in a channel
///
public void Set( AudioChannel i, T value ) => Values[i.channel] = value;
}