Added wine-devel to cc
Op 26-10-12 13:12, Joerg-Cyril.Hoehle@t-systems.com schreef:
Maarten,
I'll try and come up with a review of your winepulse patches yet I'm not familiar with the PA API.
Regarding your dsound ones, I'm sorry I've not enough experience with the DSound API to be really useful. I was happy that Alexey Loukianov and A.Eikum worked on DSound.
One issue that struck me is the 5ms period downgrade in winepulse. I advise you keep that as a separate patch. Halving the externally visible SetEVentHandle rate is certainly not something that should be in the one big core patch introducing winepulse.
Yeah this was explicitly to make skyrim work better, I return the updated latency in GetStreamLatency, and with the fixes wine dsound uses that value for calculating mixing fraglen.
Even if it causes no test failure, that trick would certainly be visible in the log lines. Native shows: render.c:2084: Should play 1000ms continuous tone with fragment size 480. render.c:2136: Released 48000=100x480 -960 frames at 48000 worth 980ms in 1008ms
Yeah I seem to fire more events, so less data gets played.
This requires some rethinking though, winepulse doesn't attempt to hide latency at all, and so far the mmdevapi tests have failed on my usb headphones because it didn't always have a smooth buffer increment.
The tests don't fail, but because of the extra firing processing sometimes gets skipped..
Woops, some more digging revealed I also seemed to have done a SetEvent in pulse_started_callback, which is entirely bogus, presumably a leftover from before I hooked up the latency callback for when in underrun mode.
With that SetEvent part of the latency hack disabled and the bogus SetEvent removed, I get:
render.c:2127: Released 44541=101x441 -441 frames at 44100 worth 1000ms in 1003ms
Can't get better than that. :-)
I disabled the exclusive mode on winepulse because I felt that mode was sufficiently different in behavior, and I don't think the mmdevapi tests cover it properly.
I'll do some more digging if the SetEvent is still needed for the hack, or if the period lowering is enough. I would suspect lowering the period is sufficient now. Quick testing of skyrim seems to indicate I can leave the SetEvent part out, so I pass all tests properly again. :-)
BTW, I'd still be pleased if you could upload somewhere to bugzilla results from runtest -v -i mmdevapi/render and capture from your winepulse driver. I think I asked you that favour already but found nothing.
My tree removes todo_wine from the tests, but anyway:
capture.c:501: Returned periods: 10.0000 ms 0.4988 ms capture.c:513: pwfx: 0xdccb0 capture.c:514: Tag: fffe capture.c:515: bits: 32 capture.c:516: chan: 2 capture.c:517: rate: 44100 capture.c:518: align: 8 capture.c:519: extra: 22 capture.c:524: Res: 32 capture.c:525: Mask: 3 capture.c:526: Alg: FLOAT capture.c:585: Returned latency: 10.0000 ms capture.c:185: Wait'ed position 0 pad 0 flags 0, amount of frames locked: 441 capture.c:247: Sleep.1 position 441 pad 15435 flags 0, amount of frames locked: 441 capture.c:291: GetBufferSize 22050 period size 441 capture.c:301: Overrun position 1323 pad 22050 flags 1, amount of frames locked: 441 capture.c:328: Cont'ed position 1764 pad 21609 flags 0, amount of frames locked: 441 capture.c:352: Restart position 2205 pad 21168 flags 0, amount of frames locked: 441 capture.c:383: Reset position -1 pad 0 flags abadcafe, amount of frames locked: 0 capture.c:409: Running position 23373 pad 7938 flags 0, amount of frames locked: 441 capture: 255 tests executed (0 marked as todo, 0 failures), 0 skipped.
render.c:185: Returned periods: 10.0000 ms 2.0000 ms render.c:197: pwfx: 0xdccb0 render.c:198: Tag: fffe render.c:199: bits: 32 render.c:200: chan: 6 render.c:201: rate: 48000 render.c:202: align: 24 render.c:203: extra: 22 render.c:208: Res: 32 render.c:209: Mask: 3f render.c:210: Alg: FLOAT render.c:268: Initialize(duration=0) GetBufferSize is 192 render.c:333: Returned latency: 10.0000 ms render.c:411: IsSupported(shared , 8000x 8x1) render.c:411: IsSupported(shared , 8000x 8x2) render.c:411: IsSupported(shared , 8000x16x1) render.c:411: IsSupported(shared , 8000x16x2) render.c:411: IsSupported(shared , 11025x 8x1) render.c:411: IsSupported(shared , 11025x 8x2) render.c:411: IsSupported(shared , 11025x16x1) render.c:411: IsSupported(shared , 11025x16x2) render.c:411: IsSupported(shared , 12000x 8x1) render.c:411: IsSupported(shared , 12000x 8x2) render.c:411: IsSupported(shared , 12000x16x1) render.c:411: IsSupported(shared , 12000x16x2) render.c:411: IsSupported(shared , 16000x 8x1) render.c:411: IsSupported(shared , 16000x 8x2) render.c:411: IsSupported(shared , 16000x16x1) render.c:411: IsSupported(shared , 16000x16x2) render.c:411: IsSupported(shared , 22050x 8x1) render.c:411: IsSupported(shared , 22050x 8x2) render.c:411: IsSupported(shared , 22050x16x1) render.c:411: IsSupported(shared , 22050x16x2) render.c:411: IsSupported(shared , 44100x 8x1) render.c:411: IsSupported(shared , 44100x 8x2) render.c:411: IsSupported(shared , 44100x16x1) render.c:411: IsSupported(shared , 44100x16x2) render.c:411: IsSupported(shared , 48000x 8x1) render.c:411: IsSupported(shared , 48000x 8x2) render.c:411: IsSupported(shared , 48000x16x1) render.c:411: IsSupported(shared , 48000x16x2) render.c:411: IsSupported(shared , 96000x 8x1) render.c:411: IsSupported(shared , 96000x 8x2) render.c:411: IsSupported(shared , 96000x16x1) render.c:411: IsSupported(shared , 96000x16x2) render.c:2174: Output to a MS-DOS console is particularly slow and disturbs timing. render.c:2175: Please redirect output to a file. render.c:870: Testing shared mode render.c:902: Latency: 10.0000 ms render.c:921: BufferSize 24000 estimated fragment 480 x 50 = 24000 render.c:945: Clock Frequency 1152000 render.c:974: data at 0x530030 render.c:1023: padding 14302 past sleep #2 render.c:1042: padding 14302 position 232752 past stop #2 render.c:1076: data at 0x530030 render.c:1100: position 115416 past 100ms sleep #3 render.c:1117: padding 18711 position 126936 past stop #3 render.c:1145: data at 0x530030 for prefill 22500 render.c:1175: hpctime 381 after 370ms render.c:1194: hpctime 481 pcpos 492 render.c:1199: padding 1250 position 510000/21250 slept 470ms iteration 0 render.c:1227: data at 0x530030 render.c:1194: hpctime 589 pcpos 599 render.c:1199: padding 18551 position 634776/26449 slept 570ms iteration 1 render.c:1232: data at 0x530030 (small 5449) render.c:1194: hpctime 691 pcpos 701 render.c:1199: padding 19321 position 747072/31128 slept 670ms iteration 2 render.c:1232: data at 0x530030 (small 4679) render.c:1194: hpctime 793 pcpos 803 render.c:1199: padding 18727 position 873624/36401 slept 770ms iteration 3 render.c:1232: data at 0x530030 (small 5273) render.c:1194: hpctime 895 pcpos 905 render.c:1199: padding 19089 position 991488/41312 slept 870ms iteration 4 render.c:1232: data at 0x530030 (small 4911) render.c:1194: hpctime 996 pcpos 1007 render.c:1199: padding 19279 position 1104792/46033 slept 970ms iteration 5 render.c:1232: data at 0x530030 (small 4721) render.c:1194: hpctime 1098 pcpos 1109 render.c:1199: padding 18961 position 1225728/51072 slept 1070ms iteration 6 render.c:1232: data at 0x530030 (small 5039) render.c:1194: hpctime 1200 pcpos 1211 render.c:1199: padding 19326 position 1337904/55746 slept 1170ms iteration 7 render.c:1232: data at 0x530030 (small 4674) render.c:1194: hpctime 1302 pcpos 1312 render.c:1199: padding 18970 position 1458624/60776 slept 1270ms iteration 8 render.c:1232: data at 0x530030 (small 5030) render.c:1248: position 1458624 render.c:1257: position 2034624 past underrun, 0 padding left, 84776 frames written render.c:1279: hpctime 2304 after underrun render.c:882: Testing exclusive mode at 48000 render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -480 frames at 48000 worth 1000ms in 1002ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -480 frames at 48000 worth 1000ms in 1004ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -960 frames at 48000 worth 990ms in 1003ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -480 frames at 48000 worth 1000ms in 1007ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -480 frames at 48000 worth 1000ms in 1002ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -480 frames at 48000 worth 1000ms in 1005ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -960 frames at 48000 worth 990ms in 997ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -960 frames at 48000 worth 990ms in 999ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -330 frames at 48000 worth 1003ms in 1009ms render.c:2075: Should play 1000ms continuous tone with fragment size 480. render.c:2127: Released 48480=101x480 -960 frames at 48000 worth 990ms in 1003ms render: 4996 tests executed (0 marked as todo, 0 failures), 0 skipped.
Sometimes up to 2 tests fail due to position updates not being regular, as you can see from this line:
render.c:2127: Released 48480=101x480 -330 frames at 48000 worth 1003ms in 1009ms
I suspect this is because it's nearly done playing though, but it can get worse when you use usb headphones, not much I can do about that though, except if I started lying, but it's so far been much simpler when I don't..
Exclusive mode tests aren't done, I disabled exclusive mode because I felt tests were insufficient. If you can point me to an application using it I'll work on it though, but I didn't feel mmdevapi tests were complete enough for handling this.
*BONUS*
My favorite capture results (a2dp phone as "microphone"):
capture.c:501: Returned periods: 135.2834 ms 135.2834 ms capture.c:513: pwfx: 0xdccb0 capture.c:514: Tag: fffe capture.c:515: bits: 32 capture.c:516: chan: 2 capture.c:517: rate: 44100 capture.c:518: align: 8 capture.c:519: extra: 22 capture.c:524: Res: 32 capture.c:525: Mask: 3 capture.c:526: Alg: FLOAT capture.c:585: Returned latency: 135.2834 ms capture.c:168: Test failed: Waiting on event handle failed! capture.c:185: Wait'ed position -1 pad 0 flags abadcafe, amount of frames locked: 0 capture.c:217: Test failed: GetNextPacketSize 0 vs. GCP 0 capture.c:220: Test failed: Valid IAudioCaptureClient_GetBuffer returns 08890001 capture.c:247: Sleep.1 position -1 pad 0 flags abadcafe, amount of frames locked: 0 capture.c:286: Test failed: GetNextPacketSize 0 vs. GetDevicePeriod 5966 capture.c:291: GetBufferSize 23864 period size 5966 capture.c:299: Test failed: Valid IAudioCaptureClient_GetBuffer returns 08890001 capture.c:301: Overrun position -1 pad 0 flags abadcafe, amount of frames locked: 0 capture.c:326: Test failed: Valid IAudioCaptureClient_GetBuffer returns 08890001 capture.c:328: Cont'ed position -1 pad 0 flags abadcafe, amount of frames locked: 0 capture.c:347: Test failed: Valid IAudioCaptureClient_GetBuffer returns 08890001 capture.c:352: Restart position -559038737 pad 0 flags abadcafe, amount of frames locked: 0 capture.c:354: Test failed: restarted GCP 0 capture.c:383: Reset position -1 pad 0 flags abadcafe, amount of frames locked: 0 capture.c:408: Test failed: Valid IAudioCaptureClient_GetBuffer returns 08890001 capture.c:409: Running position -1 pad 0 flags abadcafe, amount of frames locked: 0 capture.c:426: Test failed: Wait(event) after Stop gave 102 capture.c:434: Test failed: Wait(event) after Reset gave 102 capture.c:440: Test failed: Wait(NULL event) gave 102 capture: 224 tests executed (0 marked as todo, 12 failures), 0 skipped.
I love this one because it shows that when you have a source that's not capturing, winepulse still gets the correct results. Even though obviously the tests completely fail because there's no data. ;-)
~Maarten
Hi,
Maarten Lankhorst kindly posted mmdevapi test results for render and capture gathered using his winepulse driver: http://www.winehq.org/pipermail/wine-devel/2012-October/097602.html
render.c:1199: padding 1250 position 510000/21250 slept 470ms iteration 0
I've run your data through some MS-Excel analysis of mine. Here are my observations.
Exclusive mode tests aren't done, I disabled exclusive mode because I felt tests were insufficient.
It's funny that you say this, because your winepulse driver behaves much like an exclusive mode driver, not a shared mode one ;-)
More specifically,
a) winepulse and exclusive mode do not decrease padding in multiples of the period size.
b) winepulse and exclusive mode appear to equate GetPosition with released frames minus GetCurrentPadding exactly like ALSA hw:0 and dmix do: delay + avail_update = buffer_size (~= 8192 frames)
IOW, the driver pretends that the speaker position is exactly the frame that just fell off the circular buffer. This may be ok for ALSA hw:0 and MS-Windows exclusive mode drivers, but that's wrong for anything with a filter chain in the back.
Hence I assume that winepulse's GetPosition is lying. The question is: does it matter?
If using PA API calls of some sort, you manage to keep the PA-internal latency well below what's the human brain can perceive, I'd say that's ok. Likewise, hw:0 ignores Digital->Analog and wire->LC->speaker times.
Our gripe with PA is precisely that using the regular ALSA->PA path via the ALSA plugin, nobody found a means to keep PA's latency small.
(I'll repeat myself saying that what actually matters is the sum of all latencies and buffers: mmdevapi buffer, PA transformation chain, DAC etc.)
The caveat is that with increasing latency, the behaviour of GCP and GetPosition will differ more and more from native's mixer, until it becomes too much for application X to bear, causing a bug report.
That was about the relationship between GCP and GetPosition. Now what about GetPosition and wall time (as seen by the HighPerformanceTimer)? The largest delta is 8ms, which suggests that your GetPosition is not particularly regular, but as it stays below one 10ms period, it's presumably good enough. By contrast, native's largest delta is < 1ms.
Sometimes up to 2 tests fail due to position updates not being regular,
Indeed, the data reveals it. I guess the reported GetPosition is more related to packet size job completion than actual sample accurate stream position.
I suspect this is because it's nearly done playing though, but it can get worse when you use usb headphones, not much I can do about that though, except if I started lying,
What do you mean with "started lying"? I inferred above that it's already lying.
but it's so far been much simpler when I don't..
The data points look very reasonable -- if audible latency is really low, which the tests cannot reveal.
BTW, I'd really like to see native test results with a USB headphone. IF somebody has such data, pleas show them to me.
I'm not saying that it's bad that winepulse's GetCurrentPadding and GetPosition behave like an exclusive mode driver. After all, winecoreaudio's GCP does the same. Just be aware of the differences.
Your capture results look good.
My favorite capture results (a2dp phone as "microphone"): capture.c:247: Sleep.1 position -1 pad 0 flags abadcafe, amount of frames locked: 0 I love this one because it shows that when you have a source that's not capturing, winepulse still gets the correct results.
This is an area that needs more tests. I've seen native machines behave similarly, OTOH I've read msdn blog entries mentioning that no event is delivered when there's nothing to capture. So the expected behaviour when there's nothing to capture is unclear to me so far.
I'd recommend you simply enable (non-event) exclusive mode, that'll yield twice as many data points per test run. ;-)
BTW, I recommend that you play & increase the worst case test duration to last for 5, 30 or 3000 seconds and see if figures are still regular (e.g. with ALSA->PA, you'd see with 5 seconds that it eats too much data) and whether it still plays without audible glitches.
Regards, Jörg Höhle
Op 03-12-12 15:42, Joerg-Cyril.Hoehle@t-systems.com schreef:
Hi,
Maarten Lankhorst kindly posted mmdevapi test results for render and capture gathered using his winepulse driver: http://www.winehq.org/pipermail/wine-devel/2012-October/097602.html
render.c:1199: padding 1250 position 510000/21250 slept 470ms iteration 0
I've run your data through some MS-Excel analysis of mine. Here are my observations.
Exclusive mode tests aren't done, I disabled exclusive mode because I felt tests were insufficient.
It's funny that you say this, because your winepulse driver behaves much like an exclusive mode driver, not a shared mode one ;-)
More specifically,
a) winepulse and exclusive mode do not decrease padding in multiples of the period size.
It typically will keep it in multiples if pulseaudio is run in timer driven mode (glitchfree audio), however if you create a buffer that's 2x period size or less it seems to be more aggressive with updates.
b) winepulse and exclusive mode appear to equate GetPosition with released frames minus GetCurrentPadding exactly like ALSA hw:0 and dmix do: delay + avail_update = buffer_size (~= 8192 frames)
IOW, the driver pretends that the speaker position is exactly the frame that just fell off the circular buffer. This may be ok for ALSA hw:0 and MS-Windows exclusive mode drivers, but that's wrong for anything with a filter chain in the back.
Hence I assume that winepulse's GetPosition is lying. The question is: does it matter?
Well from how I understand with winepulse pulseaudio will only add 1 more period on top of that. I do or did have code for IAudioClock::GetPosition, winepulse v18 commit removed it entirely. I could reinstate it, but I didn't find the mmdevapi tests strict enough to justify it.
If using PA API calls of some sort, you manage to keep the PA-internal latency well below what's the human brain can perceive, I'd say that's ok. Likewise, hw:0 ignores Digital->Analog and wire->LC->speaker times.
Our gripe with PA is precisely that using the regular ALSA->PA path via the ALSA plugin, nobody found a means to keep PA's latency small.
(I'll repeat myself saying that what actually matters is the sum of all latencies and buffers: mmdevapi buffer, PA transformation chain, DAC etc.)
The caveat is that with increasing latency, the behaviour of GCP and GetPosition will differ more and more from native's mixer, until it becomes too much for application X to bear, causing a bug report.
That was about the relationship between GCP and GetPosition. Now what about GetPosition and wall time (as seen by the HighPerformanceTimer)? The largest delta is 8ms, which suggests that your GetPosition is not particularly regular, but as it stays below one 10ms period, it's presumably good enough. By contrast, native's largest delta is < 1ms.
Delta shouldn't be so high, are you using the timestamps from IAudioClock2::GetPosition ? Those are lies, I could probably fix that by recording them in pulse_wr_callback. But you could try testing with my tree, I enable rtkit in there which should decrease jitter regardless.
Sometimes up to 2 tests fail due to position updates not being regular,
Indeed, the data reveals it. I guess the reported GetPosition is more related to packet size job completion than actual sample accurate stream position.
I suspect this is because it's nearly done playing though, but it can get worse when you use usb headphones, not much I can do about that though, except if I started lying,
What do you mean with "started lying"? I inferred above that it's already lying.
Not directly passing the value in GetCurrentPadding through as was reported by pulseaudio, but hide the irregular pointner advancements. I prefer to just report the values from pulseaudio directly.
but it's so far been much simpler when I don't..
The data points look very reasonable -- if audible latency is really low, which the tests cannot reveal.
BTW, I'd really like to see native test results with a USB headphone. IF somebody has such data, pleas show them to me.
Unless I'm mistaken, because I use early requests, pulseaudio will only have 1 more period internally than the buffer length of my sound buffer, so if the buffer is 30 ms, period is 10 ms, and I only fill it with 10 ms data, max latency would be 20 ms at that point. If you say jitter is 8 ms (which I find very high), you wouldn't notice that extra 10 ms..
I'm not saying that it's bad that winepulse's GetCurrentPadding and GetPosition behave like an exclusive mode driver. After all, winecoreaudio's GCP does the same. Just be aware of the differences.
Your capture results look good.
My favorite capture results (a2dp phone as "microphone"): capture.c:247: Sleep.1 position -1 pad 0 flags abadcafe, amount of frames locked: 0 I love this one because it shows that when you have a source that's not capturing, winepulse still gets the correct results.
This is an area that needs more tests. I've seen native machines behave similarly, OTOH I've read msdn blog entries mentioning that no event is delivered when there's nothing to capture. So the expected behaviour when there's nothing to capture is unclear to me so far.
I guessed that it just has a bunch of packets that are re-used, like the way I'm implementing capture in winepulse. See ACPacket, it's stuffed in locked_ptr, empty ones in packet_free_head, and filled in packet_filled_head.
I'd recommend you simply enable (non-event) exclusive mode, that'll yield twice as many data points per test run. ;-)
BTW, I recommend that you play & increase the worst case test duration to last for 5, 30 or 3000 seconds and see if figures are still regular (e.g. with ALSA->PA, you'd see with 5 seconds that it eats too much data) and whether it still plays without audible glitches.
render.c:2290: Released 441441=1001x441 -441 frames at 44100 worth 10000ms in 9972ms render.c:2290: Released 4410441=10001x441 -441 frames at 44100 worth 100000ms in 99900ms
Some interruption of the audio stream, that sleep(i % 10) is suspect... what are you really testing there?
And that test seems to be sensitive to not being run in realtime, it seems to work slightly better if I change that to if I set THREAD_PRIORITY_TIME_CRITICAL on the thread, Sleep(0) (for i%10 = 0) looks kind of wrong though
Oops and another bugfix[1], pulse_wr_callback can sometimes be called with no update, which can be a contributing factor why sometimes the tests finished early..
with that fixed and no sleep: render.c:2296: Released 4410441=10001x441 -441 frames at 44100 worth 100000ms in 99998ms
And with rt prio set, and sleep: render.c:2291: Released 4410441=10001x441 -882 frames at 44100 worth 99990ms in 100014ms
I may also be hitting some pulseaudio bug though, if I play some music on the background when one of those tests start the sound during the test works ok, even if I pause it after that. Come to think of it, what is more likely is that on underrun the device is paused since nothing else is playing, and it starts again when being fed again, that would explain why I'm getting interruptions like the +14 ms above..
Also played skyrim for a few hours just fine, it wanted a 20 ms buffer for some reason.. Still not sure why, but who am I to object..
~Maarten
[1] winepulse don't SetEvent on no pad change..
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 554a9fc..a4575d5 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -531,7 +531,10 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata) else This->pad = 0;
- assert(oldpad >= This->pad); + if (oldpad == This->pad) + return; + + assert(oldpad > This->pad);
This->clock_written += oldpad - This->pad; TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
Maarten Lankhorst wrote:
That was about the relationship between GCP and GetPosition. Now what about GetPosition and wall time (as seen by the HighPerformanceTimer)? The largest delta is 8ms, which suggests that your GetPosition is not particularly regular, but as it stays below one 10ms period, it's presumably good enough. By contrast, native's largest delta is < 1ms.
Delta shouldn't be so high, are you using the timestamps from IAudioClock2::GetPosition ?
I'm using the data that you sent me, nothing invisible. So it's simply GetPosition vs. the HighPerformanceTimer output, as seen in: render.c:1194: hpctime 481 pcpos 492 render.c:1199: padding 1250 position 510000/21250 slept 470ms iteration 0
If you say jitter is 8 ms (which I find very high), you wouldn't notice that extra 10 ms..
I simply compared GetPosition advancement with wall time as reported by the HPC (relying on file buffering to ignore delays introduced by logging output).
I prefer to just report the values from pulseaudio directly.
Reasonable and probably good enough.
Some interruption of the audio stream, that sleep(i % 10) is suspect... what are you really testing there?
That sleep serves to prove my hypothesis that upon receiving an event from mmdevapi, any app has nearly one period time to feed (at least) one period worth of samples to mmdevapi. I.e. conceptually, mmdevapi calls SetEvent() after performing one round of mixing and decrementing padding. It then goes to sleep until the next period tick.
Native plays that test glitch free on real hardware.
And that test seems to be sensitive to not being run in realtime, it seems to work slightly better if I change that to if I set THREAD_PRIORITY_TIME_CRITICAL on the thread
RT probably helps, however I claim that *IF* Linux is able to deliver a sustained 10ms rate to any app without RT, then Wine (as an app) should be able to feed data to the audio every 10ms. The mmdevapi design is such that the client app has nearly 10ms to feed new data -- by design! That is why the sleep(8ms) is perfectly valid -- given a 10ms period -- albeit stressing (and sensitive to the Linux scheduler delaying execution of the client thread).
What I want to emphasize is that there would be a design bug in a wineXYZ.drv mmdevapi driver if it would depend on a client app to react within say 2ms upon SetEvent. I repeat that the app has nearly 10ms time, e.g. DSound can consume nearly 10ms to perform its mixing and HQ resampling in mmdevapi event based mode. I think this is a nice property of mmdevapi.
That is why we've the silence lead-in in winealsa.drv... It shall guarantee that ALSA has enough samples to play until the next period tick (if the Linux scheduler then calls the driver in time).
Here and then I've also reported that since the introduction of the completely fair scheduler, Linux appears *UNABLE* to sustain a periodic 10ms or even a 50ms rate without RT. This affects not only audio. The former scheduler found IIRC in 2.6.18 performed much better at games, both audio and video glitches. If you are a gamer, use one of the old kernels!
Sleep(0) (for i%10 = 0) looks kind of wrong though
That's just ok and the typical scenario where the client app react immediately to SetEvent.
I may also be hitting some pulseaudio bug though, if I play some music on the background when one [...] that would explain why I'm getting interruptions like the +14 ms above..
I'm sorry but I did not understand that whole paragraph.
Regards, Jörg Höhle