Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrading from v1.8.4 to v2.2.1 causes missing samples (choppy playback) with high channel count AsioDevice. #1161

Open
nitz opened this issue May 28, 2024 · 4 comments

Comments

@nitz
Copy link

nitz commented May 28, 2024

Hello all!

I have a project that has been running on v1.8.4 for many years now wonderfully. As part of looking to the future, I've been attempting to upgrade to v2.2.1 to hopefully soon be able to move away from .NET Framework. However, I've run into a confusing issue:

When playing back audio to an ASIO device with higher channel counts, I end up with missed samples (and therefore "crunchy" or "choppy" audio.)

I've witnessed this behavior before on v1.8.4, but only with specific scenarios (e.g.: 64 channel playback with a woefully underpowered processor.) However, when I'm trying the newer version of NAudio, I'm running into the issue starting at 17 channels and up, even on my development machine.

At 17 channels, the gaps/choppiness is almost completely unnoticeable. At 24 channels, the effect is more pronounced. At channel counts of 48, or 64, the output is completely unrecognizable. The result feels similar to when you have a sample provider that fills buffers with an incorrect stride.

The effect can occur more pronounced at lower channel counts if my application is built in debug. It's this fact that makes me believe it's due to timing (or the inability to fill out samples fast enough), but I'm not sure what would have changed with that between NAudio versions, as I see nothing in the changelogs.

My reproduction setup includes a 64 channel ASIO device, with it expecting 512 samples at 32 bits per sample, with a latency of 5 ms. I am providing samples with a rate of 48,000.

Initializing looks like this:

_asioDevice = new AsioOut(DanteName);
int desiredChannels = 32; // actual value set from elsewhere
_sampleProvider = new MyCustomSampleProvider(desiredChannels);
_waveProvider = new SampleToWaveProvider(_sampleProvider);
_asioDevice.Init(_waveProvider);
_asioDevice.Play();

Rolling back and forth changing nothing but which NAudio packages are referenced is enough to see (or hide) the issue for me.

I did notice that the latest patch notes specified WASAPI uses background threads, but I was unable to determine if a similar thing is true about ASIO devices; as I would think perhaps a background thread priority could be the root cause for what I'm experiencing.

Any thoughts or guidance would be greatly appreciated!

@markheath
Copy link
Contributor

hmm, strange, I don't think anything has really changed with the ASIO support in a while, and the threading with ASIO is not under .NET control. I wondered if perhaps I might have accidentally released a debug build but as far as I can see that doesn't seem to be the case.

Obviously when dealing with high channel counts and low latency you need to be sure that your sample provider can keep up. And if the .NET garbage collector were to kick in, that would likely disrupt filling a short buffer and cause an audible dropout

@nitz
Copy link
Author

nitz commented May 29, 2024

Hi Mark! Thanks for the incredibly quick reply :)

I've done a bit more digging, and I think you've definitely echoed my findings so far.

  • The GC definitely can cause a hiccup. One of the places I'm using this is in a Unity3D application. The GC triggers regularly in the editor and it definitely chugs there. Actual builds don't suffer the same issue (I'm assuming due to the much lower overhead when running standalone,) so that seems all good!
  • I have managed to produce the issue with v1.8.4 on my development machine (an i7-6700k). Using a software ASIO device (Dante Virtual Soundcard) with 32+ channels and a low customized latency setting (<= 3ms), I'm able to recreate the issue I was seeing elsewhere. If it's like you (and now I) suspect, any performance increases to the sample provider I imagine will reduce the choppy behavior! Looks like I've got some profiling to do.
  • There may be more overhead in v2+ vs v1.8.4 and that's just exposed what I'm seeing more regularly? I don't think it's fair for me to put the onus on NAudio though with no evidence, so I think it's perfectly fine to close this issue if you agree!

I'm sure it's out of the scope of what your fantastic project is here to provide, but I figured it's worth asking someone with far greater knowledge than I: Is there a way I can tell the driver/device to be more lenient on how long it will wait for me to fill a buffer? It's nice in scenarios like with DVS where I can control the expected latency manually, but I'd like to be able to plan ahead for devices with less configurability.

Cheers and thanks so much again for the reply, and for all that you do!

@markheath
Copy link
Contributor

the time you get to fill a buffer is simply your ASIO latency - so 5ms, which to be honest I'm impressed you're managing at all.
In an ideal world, I'd rework NAudio to use Span rather than byte arrays which would allow for more efficient of passing audio through pipelines. I prototyped it a while back, but sadly I don't really have the time to do any major NAudio work these days.

@nitz
Copy link
Author

nitz commented Jun 3, 2024

I prototyped it a while back, but sadly I don't really have the time to do any major NAudio work these days.

I feel this in my soul 😂

the time you get to fill a buffer is simply your ASIO latency - so 5ms, which to be honest I'm impressed you're managing at all.

Yeah, it's been tricky at some points, but most of the time the data is from some pretty quick signal generation or duplication to multiple channels. With as performant as NAudio already is, it's made so much already such a breeze!

I'll let you know if I come across any concrete examples for something that might make good "low hanging fruit" improvements, but until then I'm just gonna up the hardware requirements for when we need larger channel counts!

Again, thanks so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants