Last active
August 4, 2024 18:17
-
-
Save mzandvliet/c0e6260d332c5cf9820773b8f087cd39 to your computer and use it in GitHub Desktop.
A quick example of how to play a mouse-controlled sine wave using the DSPGraph preview package
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using Unity.Collections; | |
using Unity.Audio; | |
using Unity.Mathematics; | |
using Unity.Burst; | |
using System.Collections; | |
/* | |
For allocations inside AudioKernels, use Allocator.AudioKernel | |
*/ | |
public class DspApplication : MonoBehaviour { | |
private DSPGraph _graph; | |
private DefaultDspGraphDriver _driver; | |
private AudioOutputHandle _outputHandle; | |
private DSPNode _node; | |
private NativeArray<float> _output; | |
private const int SAMPLERATE = 48000; | |
private const int NUMFRAMES = 1024; | |
private const int NUMCHANNELS = 2; | |
private bool _isRunning; | |
private void Awake() { | |
var audioConfig = AudioSettings.GetConfiguration(); | |
Debug.Log("Unity Audio Config:"); | |
Debug.Log(audioConfig.sampleRate); | |
Debug.Log(audioConfig.dspBufferSize); | |
Debug.Log(audioConfig.speakerMode); | |
_graph = DSPGraph.Create(SoundFormat.Stereo, NUMCHANNELS, NUMFRAMES, SAMPLERATE); | |
_driver = new DefaultDspGraphDriver(); | |
_driver._graph = _graph; | |
_driver.Initialize(NUMCHANNELS, SoundFormat.Stereo, SAMPLERATE, NUMFRAMES); | |
var cmd = _graph.CreateCommandBlock(); | |
_node = cmd.CreateDSPNode<OscParams, OscProviders, Osc>(); | |
cmd.AddOutletPort(_node, NUMCHANNELS, SoundFormat.Stereo); | |
cmd.Connect(_node, 0, _graph.RootDSP, 0); | |
cmd.SetFloat<OscParams, OscProviders, Osc>(_node, OscParams.Frequency, 440f); | |
cmd.Complete(); | |
_output = new NativeArray<float>(NUMCHANNELS * NUMFRAMES, Allocator.Persistent, NativeArrayOptions.ClearMemory); | |
} | |
private IEnumerator Start() { | |
/* | |
Note: when we try to start drawing audio from the graph right at application start | |
we get some errors related to NativeArray buffers with invalid state. If we wait | |
a tiny bit we're fine. | |
*/ | |
yield return new WaitForSeconds(0.5f); | |
_driver.BeginMix(NUMFRAMES); | |
_isRunning = true; | |
} | |
private void OnDestroy() { | |
var cmd = _graph.CreateCommandBlock(); | |
cmd.ReleaseDSPNode(_node); | |
cmd.Complete(); | |
_driver.Dispose(); | |
_output.Dispose(); | |
} | |
private void Update() { | |
var cmd = _graph.CreateCommandBlock(); | |
float pitch = 440f + 220f * Input.GetAxis("Mouse Y"); | |
cmd.SetFloat<OscParams, OscProviders, Osc>(_node, OscParams.Frequency, pitch); | |
cmd.Complete(); | |
} | |
private void OnAudioFilterRead(float[] data, int channels) { | |
if (!_isRunning) { | |
return; | |
} | |
_driver.EndMix(_output, NUMFRAMES); | |
// Todo: use low level memcopy | |
for (int i = 0; i < data.Length; i++) { | |
data[i] = _output[i]; | |
} | |
_driver.BeginMix(NUMFRAMES); | |
} | |
private const float TWOPI = math.PI * 2f; | |
public enum OscParams { | |
Frequency | |
} | |
public enum OscProviders { | |
Provider | |
} | |
[BurstCompile] | |
public struct Osc : IAudioKernel<OscParams, OscProviders> { | |
private float _phase; | |
private float _phaseStep; | |
private float _freq; | |
public void Initialize() {} | |
public void Dispose() {} | |
public void Execute(ref ExecuteContext<OscParams, OscProviders> context) { | |
for (int outIdx = 0; outIdx < context.Outputs.Count; outIdx++) { | |
var sampleBuffer = context.Outputs.GetSampleBuffer(outIdx); | |
var buffer = sampleBuffer.Buffer; | |
for (int i = 0; i < NUMFRAMES; i++) { | |
/* | |
Manually interpolate frequency parameter (have not yet been able to get the | |
automatic parameter interpolation system to behave yet) | |
*/ | |
_freq = math.lerp(_freq, context.Parameters.GetFloat(OscParams.Frequency, i), 0.001f); | |
_phaseStep = (TWOPI * _freq) / (float)SAMPLERATE; | |
buffer[i*NUMCHANNELS] = math.sin(_phase); | |
buffer[i*NUMCHANNELS+1] = buffer[i * NUMCHANNELS]; | |
_phase += _phaseStep; | |
if (_phase > TWOPI) { | |
_phase -= TWOPI; | |
} | |
} | |
} | |
} | |
} | |
} | |
[BurstCompile] | |
public struct DefaultDspGraphDriver : IAudioOutput { | |
public DSPGraph _graph; | |
private int _channelCount; | |
public void Initialize(int channelCount, SoundFormat format, int sampleRate, long dspBufferSize) { | |
_channelCount = channelCount; | |
} | |
public void BeginMix(int frameCount) { | |
_graph.BeginMix(frameCount); | |
} | |
public void EndMix(NativeArray<float> output, int frames) { | |
_graph.ReadMix(output, frames, _channelCount); | |
} | |
public void Dispose() { | |
_graph.Dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment