enzienaudio / heavy

heavy public issue tracker and static dsp code releases
https://enzienaudio.com
ISC License
71 stars 4 forks source link

Instantiation, Sample-Loading and Performance #194

Open diemildefreude opened 7 years ago

diemildefreude commented 7 years ago

I'm trying to instantiate multiple instances of a GameObject sequentially which each have their own 3D Audio Source and same HeavyPlugin attached. Said HeavyPlugin originally had 2 samples to be loaded and, when the instances got beyond a certain number, there would be some DSP spikes and clicking noise at the time of instantiation/component addition/sample loading. The DSP and sound would then return to normal.

Sample Loading does seem to be the problem, because doing the same thing with 4 samples per plugin leads to glitchy clicking noises right away... whereby I can't see any spikes in Unity's DSP display in this case.

My question is if you have any tips for how to optimize what I'm trying to do. E.g. perhaps preloading all of the samples into plugins and then reparenting/-positioning them as needed.

diplojocus commented 7 years ago

You will definitely get audio dropouts if you're loading samples while trying to process the plugin. Accessing the tables is not thread-safe and is not really recommended while the audio thread is running.

You should try and make sure that your samples are loaded first before processing the plugin, and if you need to reload it with different samples that you disable the plugin first, and re-enable it afterwards.

Basically loading samples while generating audio from the plugin will have a high chance of noise/glitches/drop outs.

diemildefreude commented 7 years ago

@diplojocus OK, in the following example, I've tried to add the component, disable it, set all the parameters and load the samples and then re-enable it, but it doesn't get rid of the clicks:

_audio = gameObject.AddComponent<Hv_sustainplayertest_AudioLib>();
_audio.enabled = false;
_audio.samples = (float)one_clip.samples;
_audio.FillTableWithMonoAudioClip("buffer", one_clip);
_audio.FillTableWithMonoAudioClip("section", two_clip2);
_audio.one_mix = one_mix;
_audio.two_mix = two_mix;
_audio.one_sustain_volume = one_sustain_volume;
_audio.two_sustain_volume = two_sustain_volume;
_audio.one_readeronoff = one_readeronoff;
_audio.two_readeronoff = two_readeronoff;
_audio.one_transpose = one_transpose;
_audio.two_transpose = two_transpose;
_audio.transition_envelope = transition_envelope;
_audio.start_delay = start_delay;
_audio.ord_vol = ord_vol;
_audio.enabled = true;

Now, I tried making it a Coroutine and adding a WaitForSeconds(1f) before "_audio.enabled = true", but this only makes the clicking worse (at the time of re-enabling). What procedure would you recommend?

diemildefreude commented 7 years ago

OK, the re-enabling clicking can be eliminated by setting the Audio Source's volume to 0 in the prefab and turning it back up to 1 after re-enablement of the HV plugin. However, the spikes in DSP are still happening, going as high as 75% before falling down into the 20%s. Note that I'm producing about 30 instances in this test and, because it's a branching fractal, the last few instances are spawned close to eachother. So when the amount of instances gets into the 20s is when the clicking starts. I can try spacing out the instantiation more, but I just want to make sure I'm handling sample-loading correctly otherwise. Here is the current version, with the volume-change added. It seems the Hv-script would be active for a moment since it's first added first and then disabled, however I think I can't call gameObject.AddComponent<Hv_sustainplayertest_AudioLib>().enabled = false because I also need to assign it to a variable (_audio):

_audio = gameObject.AddComponent<Hv_sustainplayertest_AudioLib>();
_audio.enabled = false;
_audio.samples = (float)one_clip.samples;
_audio.FillTableWithMonoAudioClip("buffer", one_clip);
_audio.FillTableWithMonoAudioClip("section", two_clip2);
_audio.one_mix = one_mix;
_audio.two_mix = two_mix;
_audio.one_sustain_volume = one_sustain_volume;
_audio.two_sustain_volume = two_sustain_volume;
_audio.one_readeronoff = one_readeronoff;
_audio.two_readeronoff = two_readeronoff;
_audio.one_transpose = one_transpose;
_audio.two_transpose = two_transpose;
_audio.transition_envelope = transition_envelope;
_audio.start_delay = start_delay;
_audio.ord_vol = ord_vol;
yield return new WaitForSeconds(1f); //if using as a coroutine
_audio.enabled = true;
GetComponent<AudioSource>().volume = 1f;
diplojocus commented 7 years ago

Typically you'll never want to be loading samples in the middle of a game because of the computational cost and memory needed to copy over the contents.

It might be worth re-thinking your system design so you can avoid doing this. For example instantiate everything at the start and then enable/disable components as needed.

diplojocus commented 7 years ago

You'll find games tend to get round this by loading content at the start of the level for example.

diemildefreude commented 7 years ago

Yes, that's much better, thank you. I'm loading and then activating everything at the start and then repositioning/banging/turning up the volume it as needed. There are still glitchy sounds sometimes despite the DSP not being overloaded and this seems to happen because of (near-)simultaneous bangs in different instances of the plugin. If I can stagger them, everything should work.