Alex Villacís Lasso wrote:
The good news: the patch sort of works (in my setup, at least, with Fedora Core 4). All the games I have (Japanese RPGs) now have smooth sound, unless the CPU load is too high. The bad news: the patch does nothing to make the dsound tests pass in Wine (but they were already failing before the patch :-)
In a previous post, I commented about DirectSound tests failing when ALSA is used with full hardware acceleration. Now I know the reason why.
As far as I can glean from the code, DirectSound is supposed to report the application several properties of the sound system, including the size of the hardware buffer. This hardware buffer is queried by the snd_hw_params_get_buffer_size(), and is then converted from frame units to bytes. Then the application, or in this case, the test, expects the buffer size to remain constant for each and every hardware buffer created, regardless of requested format. Only this assertion fails in ALSA. For example, the capability query causes the following output to be shown:
trace:wave:DSDB_CreateMMAP format=U8 frames=11025 channels=2 bits_per_sample=8 bits_per_frame=16 trace:wave:DSDB_CreateMMAP created mmap buffer of 11025 frames (22050 bytes) at 0x7c0fd100
This reported size (22050 bytes) remains constant as long as the request is for U8 format with 2 channels (although the mmap address jumps between three different values in my setup; this may or may not be relevant). However, as soon as the requested format changes, the trace shows the following:
trace:wave:DSDB_CreateMMAP format=S16_LE frames=3763 channels=2 bits_per_sample=16 bits_per_frame=32 trace:wave:DSDB_CreateMMAP created mmap buffer of 3763 frames (15052 bytes) at 0x7c0fd6b8
There goes a fourth mmap address, but the truly interesting thing is that the buffer size also changes, and no longer matches the previously reported size of 22050 bytes. The test then goes and plays a 5-second sound with the (now incorrect) buffer size of 22050 bytes, not noting that the reported position is wrapping around at 15052 bytes:
ds3d.c:431: Playing 5 second 440Hz tone at 11025x16x2
...
trace:dsound:DSOUND_PrimaryGetPosition (0x7fe03ab8,0x7fa6fa80,(nil)) trace:wave:IDsDriverBufferImpl_GetPosition hw_ptr=0x00000000, playpos=0, writepos=-1, mmap_buflen_bytes=15052 trace:dsound:DSOUND_PrimaryGetPosition playpos = 0, writepos = 0 (0x7fe03ab8, time=1913) trace:dsound:PrimaryBufferImpl_GetCurrentPosition playpos = 0, writepos = 0 (0x7fe03ab8, time=1913) ds3d.c:230:buf size=22050 last_play_pos=0 play_pos=0 played=0 / 220500, fraction_played=0 ds3d.c:248:offset=15052 free=6998 written=15052 / 220500
... ds3d.c:230:buf size=22050 last_play_pos=10296 play_pos=11700 played=11700 / 220500, fraction_played=1404 ds3d.c:248:offset=10296 free=1404 written=32346 / 220500 ds3d.c:230:buf size=22050 last_play_pos=11700 play_pos=13104 played=13104 / 220500, fraction_played=1404 ds3d.c:248:offset=11700 free=1404 written=33750 / 220500 ds3d.c:230:buf size=22050 last_play_pos=13104 play_pos=14508 played=14508 / 220500, fraction_played=1404 ds3d.c:248:offset=13104 free=1404 written=35154 / 220500 ds3d.c:230:buf size=22050 last_play_pos=14508 play_pos=860 played=22910 / 220500, fraction_played=8402 ds3d.c:248:offset=14508 free=8402 written=36558 / 220500
Please note that the play_pos wrapped from 14508 to 860 because it exceeded 15052 bytes, but the test assumes it wrapped around at 22050 bytes, so it miscalculates the fraction_played, advancing it at an abnormally fast rate. That is how the following result ensues:
trace:dsound:DSOUND_PrimaryGetPosition playpos = 176, writepos = 0 (0x7fe03ab8, time=5366) trace:dsound:PrimaryBufferImpl_GetCurrentPosition playpos = 176, writepos = 0 (0x7fe03ab8, time=5366) ds3d.c:230:buf size=22050 last_play_pos=13824 play_pos=176 played=220676 / 220500, fraction_played=8402 ds3d.c:237:all the samples have been played, stopping... ds3d.c:279:stopping playback trace:dsound:PrimaryBufferImpl_Stop (0x7fe143b8) ds3d.c:628: Test failed: The sound played for 3453 ms instead of 5000 ms
Many other tests with formats other than U8, 11025Hz fail for the same reason. Any other DirectSound application that reuses the buffer size as the test does will fail in the same way (music mixing up and prematurely stopping). However, I am to understand that this test passes on Windows, in all versions.
Now that I have found the issue, I ask for help in suggesting a proper fix. I think that the ALSA implementation used to have a separate buffer from which samples were copied into the hardware buffer, but this implementation was scrapped for some reason (and I think, without re-running the ALSA tests). So I tried using snd_hw_params_set_buffer_size() - it fails to set the buffer size to the previously reported size. So I am still thinking about this. Any comments or suggestions for fixing this will be greatly appreciated.
BTW, does the DirectSound API allow the application to access the hardware mmap directly (that is, not just by specifying the buffer to be played)? If not, one possible solution would be to remember the first reported buffer size, and wrap the *reported* position around at that buffer size, even when the ALSA playback uses the real buffer size. However, when I tried to do this (by hardcoding 22050 as a wraparound value, just as a test), Wine promptly crashed, so I need more insight into this.
Alex Villacís Lasso
Resending because previous attempt probably was rejected due to gigantic attachment (my bad).
Alex Villacís Lasso wrote:
Alex Villacís Lasso wrote:
The good news: the patch sort of works (in my setup, at least, with Fedora Core 4). All the games I have (Japanese RPGs) now have smooth sound, unless the CPU load is too high. The bad news: the patch does nothing to make the dsound tests pass in Wine (but they were already failing before the patch :-)
In a previous post, I commented about DirectSound tests failing when ALSA is used with full hardware acceleration. Now I know the reason why.
As far as I can glean from the code, DirectSound is supposed to report the application several properties of the sound system, including the size of the hardware buffer. This hardware buffer is queried by the snd_hw_params_get_buffer_size(), and is then converted from frame units to bytes. Then the application, or in this case, the test, expects the buffer size to remain constant for each and every hardware buffer created, regardless of requested format. Only this assertion fails in ALSA. For example, the capability query causes the following output to be shown:
trace:wave:DSDB_CreateMMAP format=U8 frames=11025 channels=2 bits_per_sample=8 bits_per_frame=16 trace:wave:DSDB_CreateMMAP created mmap buffer of 11025 frames (22050 bytes) at 0x7c0fd100
This reported size (22050 bytes) remains constant as long as the request is for U8 format with 2 channels (although the mmap address jumps between three different values in my setup; this may or may not be relevant). However, as soon as the requested format changes, the trace shows the following:
trace:wave:DSDB_CreateMMAP format=S16_LE frames=3763 channels=2 bits_per_sample=16 bits_per_frame=32 trace:wave:DSDB_CreateMMAP created mmap buffer of 3763 frames (15052 bytes) at 0x7c0fd6b8
There goes a fourth mmap address, but the truly interesting thing is that the buffer size also changes, and no longer matches the previously reported size of 22050 bytes. The test then goes and plays a 5-second sound with the (now incorrect) buffer size of 22050 bytes, not noting that the reported position is wrapping around at 15052 bytes:
ds3d.c:431: Playing 5 second 440Hz tone at 11025x16x2
...
trace:dsound:DSOUND_PrimaryGetPosition (0x7fe03ab8,0x7fa6fa80,(nil)) trace:wave:IDsDriverBufferImpl_GetPosition hw_ptr=0x00000000, playpos=0, writepos=-1, mmap_buflen_bytes=15052 trace:dsound:DSOUND_PrimaryGetPosition playpos = 0, writepos = 0 (0x7fe03ab8, time=1913) trace:dsound:PrimaryBufferImpl_GetCurrentPosition playpos = 0, writepos = 0 (0x7fe03ab8, time=1913) ds3d.c:230:buf size=22050 last_play_pos=0 play_pos=0 played=0 / 220500, fraction_played=0 ds3d.c:248:offset=15052 free=6998 written=15052 / 220500
... ds3d.c:230:buf size=22050 last_play_pos=10296 play_pos=11700 played=11700 / 220500, fraction_played=1404 ds3d.c:248:offset=10296 free=1404 written=32346 / 220500 ds3d.c:230:buf size=22050 last_play_pos=11700 play_pos=13104 played=13104 / 220500, fraction_played=1404 ds3d.c:248:offset=11700 free=1404 written=33750 / 220500 ds3d.c:230:buf size=22050 last_play_pos=13104 play_pos=14508 played=14508 / 220500, fraction_played=1404 ds3d.c:248:offset=13104 free=1404 written=35154 / 220500 ds3d.c:230:buf size=22050 last_play_pos=14508 play_pos=860 played=22910 / 220500, fraction_played=8402 ds3d.c:248:offset=14508 free=8402 written=36558 / 220500
Please note that the play_pos wrapped from 14508 to 860 because it exceeded 15052 bytes, but the test assumes it wrapped around at 22050 bytes, so it miscalculates the fraction_played, advancing it at an abnormally fast rate. That is how the following result ensues:
trace:dsound:DSOUND_PrimaryGetPosition playpos = 176, writepos = 0 (0x7fe03ab8, time=5366) trace:dsound:PrimaryBufferImpl_GetCurrentPosition playpos = 176, writepos = 0 (0x7fe03ab8, time=5366) ds3d.c:230:buf size=22050 last_play_pos=13824 play_pos=176 played=220676 / 220500, fraction_played=8402 ds3d.c:237:all the samples have been played, stopping... ds3d.c:279:stopping playback trace:dsound:PrimaryBufferImpl_Stop (0x7fe143b8) ds3d.c:628: Test failed: The sound played for 3453 ms instead of 5000 ms
Many other tests with formats other than U8, 11025Hz fail for the same reason. Any other DirectSound application that reuses the buffer size as the test does will fail in the same way (music mixing up and prematurely stopping). However, I am to understand that this test passes on Windows, in all versions.
Now that I have found the issue, I ask for help in suggesting a proper fix. I think that the ALSA implementation used to have a separate buffer from which samples were copied into the hardware buffer, but this implementation was scrapped for some reason (and I think, without re-running the ALSA tests). So I tried using snd_hw_params_set_buffer_size() - it fails to set the buffer size to the previously reported size. So I am still thinking about this. Any comments or suggestions for fixing this will be greatly appreciated.
BTW, does the DirectSound API allow the application to access the hardware mmap directly (that is, not just by specifying the buffer to be played)? If not, one possible solution would be to remember the first reported buffer size, and wrap the *reported* position around at that buffer size, even when the ALSA playback uses the real buffer size. However, when I tried to do this (by hardcoding 22050 as a wraparound value, just as a test), Wine promptly crashed, so I need more insight into this.
Alex Villacís Lasso
Resending because previous attempt probably was rejected due to gigantic attachment (my bad).
Only the primary buffer supports hardware acceleration. The secondary buffer(s) are implemented in software and mixed into the primary buffer. The formats (mono/stereo, 8/16 bit samples, and sample rate) of the primary and secondary buffers are totally independent and can be anything.
One of the dsound tests keeps the primary buffer format constant and iterates through all secondary buffer formats and another keeps the secondary buffer format constant and iterates through all possible primary formats.
This is probably what you are seeing. The secondary buffer format has nothing to do with what is sent to the hardware.
Robert Reif wrote:
Only the primary buffer supports hardware acceleration. The secondary buffer(s) are implemented in software and mixed into the primary buffer. The formats (mono/stereo, 8/16 bit samples, and sample rate) of the primary and secondary buffers are totally independent and can be anything.
One of the dsound tests keeps the primary buffer format constant and iterates through all secondary buffer formats and another keeps the secondary buffer format constant and iterates through all possible primary formats.
This is probably what you are seeing. The secondary buffer format has nothing to do with what is sent to the hardware.
The first test that fails with the ALSA driver is the so-called "reference" tone, which, as far as I could see, is played with a primary buffer, and no secondary buffer. The reference tone uses the hardware position directly, which wraps around at a buffer size that changes unexpectedly when switching playback formats. I have prepared a patch that "fixes" this, by re-querying the buffer size in the case of a primary buffer, and displaying a warning if it detects a buffer size change. However, I don't know if the buffer size is supposed to remain constant across buffer format changes. If it does, then the patch would need to be modified to mark this as a TODO.
Changelog: * Re-query buffer capabilities after format change, and display warning if buffer size changed after format change. Fixes reference playback with ALSA.
Alex Villacís Lasso wrote:
The first test that fails with the ALSA driver is the so-called "reference" tone, which, as far as I could see, is played with a primary buffer, and no secondary buffer. The reference tone uses the hardware position directly, which wraps around at a buffer size that changes unexpectedly when switching playback formats. I have prepared a patch that "fixes" this, by re-querying the buffer size in the case of a primary buffer, and displaying a warning if it detects a buffer size change. However, I don't know if the buffer size is supposed to remain constant across buffer format changes. If it does, then the patch would need to be modified to mark this as a TODO.
Fix the wine ALSA driver rather than the test unless you can prove that the test fails on Windows.
Robert Reif wrote:
Alex Villacís Lasso wrote:
The first test that fails with the ALSA driver is the so-called "reference" tone, which, as far as I could see, is played with a primary buffer, and no secondary buffer. The reference tone uses the hardware position directly, which wraps around at a buffer size that changes unexpectedly when switching playback formats. I have prepared a patch that "fixes" this, by re-querying the buffer size in the case of a primary buffer, and displaying a warning if it detects a buffer size change. However, I don't know if the buffer size is supposed to remain constant across buffer format changes. If it does, then the patch would need to be modified to mark this as a TODO.
Fix the wine ALSA driver rather than the test unless you can prove that the test fails on Windows.
This is a modified patch that marks the problematic buffer resize as a todo_wine. Could somebody please test this patch and confirm that the buffer size actually remains constant across playback format changes?
Alex Villacís Lasso wrote:
Robert Reif wrote:
Alex Villacís Lasso wrote:
The first test that fails with the ALSA driver is the so-called "reference" tone, which, as far as I could see, is played with a primary buffer, and no secondary buffer. The reference tone uses the hardware position directly, which wraps around at a buffer size that changes unexpectedly when switching playback formats. I have prepared a patch that "fixes" this, by re-querying the buffer size in the case of a primary buffer, and displaying a warning if it detects a buffer size change. However, I don't know if the buffer size is supposed to remain constant across buffer format changes. If it does, then the patch would need to be modified to mark this as a TODO.
Fix the wine ALSA driver rather than the test unless you can prove that the test fails on Windows.
This is a modified patch that marks the problematic buffer resize as a todo_wine. Could somebody please test this patch and confirm that the buffer size actually remains constant across playback format changes?
The problem is that the wine ALSA driver creates a fixed time buffer rather than a fixed size buffer.
Please try your patched test on a real Windows 9X system and on a Windows 2k/xp system. Windows 2k up doesn't really have a hardware primary buffer because of kmixer but a Windows 9X vdx driver will communicate directly with the hardware.
If the test fails on Windows, then we need to change the direct sound implementation to read the new buffer size. Otherwise we need to change the ALSA driver to always create a fixed buffer size regardless of the requested format (like OSS).
We can fix the test and the direct sound dll to work around ALSA's current behavior but that won't help other apps that depend on the primary buffer size not changing if that's what Windows really does.