DirectSound question

As I experience audio stuttering from buffer underruns (even with a next to nothing sweeped sine wave), I checked the DS impl. How come there are so many sleep(1)'s ? That is most probably what causes the hickups (f.i. when minimizing/maximizing the application window).

It looks like the impl is polled instead of using the IDirectSoundNotify interface which would let the juce impl just sit and wait on an event when it’s time to fill up the buffers? I’m starting such an effort but just wanted to went the idea here before I do. Any objections?

/R

It’s very old code, written when I was first developing tracktion, and I don’t exactly remember all the ins and outs. (Could even be that IDirectSoundNotify didn’t exist back then!) I’d certainly be interested to see what you come up with!

Cool! Let you know when I got somethin’ runnin’ !

Ok, tried the IDirectSoundNotify method and it works a little better, but for the heck of it I tried portaudio (with same DS buffer setup) and it works flawlessly. No stuttering at all. The portaudio implementation uses timeSetEvent to setup a timer callback which is used to fill up the buffer via the user callback function. So I’ll be going with portaudio for now…

/R

So you mean it’s just using a timer instead of a thread? Those windows timers are useless, I’m surprised that it’d work at all!

Yup, timeSetEvent with callback. Windows timers via WM_TIMER are useless, but I guess timeSetEvent runs some other scheme. With 3 x 960 samples length in the secondary buffer it runs real smooth. I think the timeSetEvent periodicity was set to 16 ms in portaudio with this buffer size. Cool thing with portaudio is that they have WDM KS and WASAPI backends aswell, haven’t tested WDM KS but I guess it enables much lower latencies (Sonar uses WDM KS and with Sonar I can get below 10 ms latency with my Roland PCR-A30 USB unit). Not that I need that right now…

[quote=“robiwan”]Ok, tried the IDirectSoundNotify method and it works a little better, but for the heck of it I tried portaudio (with same DS buffer setup) and it works flawlessly. No stuttering at all. The portaudio implementation uses timeSetEvent to setup a timer callback which is used to fill up the buffer via the user callback function. So I’ll be going with portaudio for now…

/R[/quote]

Hi Robiwan, did you reimplement some the juce_win32_DirectSound.cpp functions to test the IDirectSoundNotify method ? If so, i’d be interested in looking at what you’ve done and testing it in my JUCE based audio app :slight_smile:
I already had a look at this code but i’m totally unfamiliar with the DirectSound API…

Thanx in advance !

Yes, but I never got it to run as smoothly as the portaudio implementation, so rather than “reinventing the wheel” I opted for the portaudio implementation (on Windows mind you, still use JUCE coreaudio impl on Mac).

/R

Hi Robiwan, we also switched to the PortAudio DirectSound implementation too, but it seems like the DirectSound latency can be made lower in the Juce implementation than in the PortAudio one.

On my computer, using the builtin (crappy Realtek) soundcard, here are my DirectSound minimum buffer sizes:

Juce Implementation: 960 samples (21,8 ms @ 44100 Hz)
PortAudio Implementation: 1280 samples (29 ms @ 44100 Hz)

Do you have similar numbers?

Any ideas on the reason why Juce manages to get tighter latencies ?

I changed “suggestedLatency” to 0.04 s which makes portaudio use identical directsound buffer sizes, i.e. 960 samples so the latency is the same as in JUCE (V1.45 I might add) but much more stable. Specially noticed it on a low CPU power laptop…

/R

0.04s * 44100 == 1764 samples

What did I miss ?

[quote=“grossebete”]0.04s * 44100 == 1764 samples

What did I miss ?[/quote]

Not quite sure why I did exactly 0.04, but when portaudio opens DS it does it with exactly the same buffer sizes as JUCE. It was a trial-n-error thingy.

I took a look at what is done in the Juce DSoundAudioDevice and the port audio DirectSound code.

In our application, PortAudio support has been added as full-blown AudioIODevice / AudioIODeviceType classes and a minor tweak to the AudioDeviceType machinery that allows to override the default implementations or to add new ones from client code. Therefore, the two DirectSound devices can be compared fairly, since the AudioDeviceManager code, hasn’t been changed.

Here are my findings:

It seems like Juce allocates a DirectSound buffer 3 times larger than the buffer size set in the AudioDeviceManager (but stills calls the audio callback with the requested buffer size).

OTOH, PortAudio allocates directly the buffer size requested, hence the apparent discrepancy in the minimum acceptable latencies, as displayed in the audio settings.

But in the end, the REAL latencies are the same…

Jules, do you remember the reason for this triple buffer size in the DirectSound code ?

Can’t remember exactly why I made it 3 times the size, but would be amazed if it’d work with less than 2x the latency. The device has to have enough space to fill the next section of the buffer while you’re processing the last bit.

Are you sure that the number portaudio uses isn’t referring to the entire buffer size instead of than actual latency you want?

You are right, I misinterpreted my results. PortAudio creates double size buffers. Anyway, there is still a strange bit that I will try to explain below.

I looked a the secondary CreateSoundBuffer calls in both Juce and PortAudio. When setting the buffer size to 960 samples (the default) in the AudioDeviceManager, here’s what I get for these calls:

Juce: secondaryDesc->dwBufferBytes == 11520 == 2 (chans) x 2 (16 bits) x 960 x 3

PortAudio: secondaryDesc->dwBufferBytes == 7680 == 2 (chans) x 2 (16 bits) x 960 x 2

Here’s the funny thing: With my soundcard, 960 samples is too short for the Port Audio Device. The actual minimum acceptable buffer size that does not produce garbled audio is 1280 samples which amounts to secondaryDesc->dwBufferBytes == 8 x 1280 == 10240, very close to what happens when using the juce device at 960 samples.

I’m by no means a DirectSound expert, and I’m just trying to make the PortAudio and Juce devices behave the same, from an audio device manager buffer size slider user point of view. Any ideas ?

Not really a question I could help with, as I know nothing about portaudio…