Skip to content

Instantly share code, notes, and snippets.

@mzandvliet
Last active July 23, 2022 00:13
Show Gist options
  • Save mzandvliet/c0e6260d332c5cf9820773b8f087cd39 to your computer and use it in GitHub Desktop.
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
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