There is a problem with Directsound and i810 soundcards. The test I just sent to wine-patches illustrates it very well.
Basically sounds are being played too fast.
Here's my understanding of the problem (if my memory and understanding of the logs is correct):
When DSound initializes it create a primary sound buffer and invokes winmm to actually get access to a sound device. The default format of the primary buffer is 22050x16x2 (i.e. 22050Hz, 16 bit sound, stereo). This means the device it's going to get will be handled by msacm.drv which will be responsible for performing the conversions on the fly.
However during the primary buffer creation DSDB_MapPrimary gets invoked in wineoss and does an mmap of the hardware sound buffer.
So when we then play sound in a secondary sound buffer, this sound gets mixed into the primary buffer, but because of the mmap this then goes straight to the sound card with no further conversion. So you end up with a chipmunk sound effect since you play a 22050x16x2 sound at 48000x16x2.
I will try to make more tests to confirm this but does it seem to make sense? Should I look in some other direction? Any suggestion about a fix?
If you have an i810 soundcard you can reproduce by doing the following: * apply "Add WINETEST_INTERACTIVE" from wine-patches * apply "Add 'audiophile' DirectSound tests" from wine-patches * cd dlls/dsound/tests * make * WINETEST_INTERACTIVE=1 ../../../tools/runtest -P wine -M dsound.dll -T ../../.. -p dsound_test.exe.so dsound.c
(you can also set WINETEST_DEBUG=2 to get more debug info about the test's buffer management, though this is probably not relevant here)
Alternately, you can download the latest Windows test binaries from my site (http://fgouget.free.fr/wine/winetests.zip) and run them using Wine, or on Windows:
set WINETEST_INTERACTIVE=1 dsound_test dsound
Actually I would be interested in the results of this test on Windows: * the test tone is usually played three times. Are all three, and especially the first one, at the right pitch? * what is the output of the test? I'm interested in reported capabilities as stated by Windows and Wine for instance (to determine whether the primary buffer is emulated for instance). * what is the primary buffer's default format? 22050x16x2? (that's not supported by the sound card!) * does the winmm test work? set WINETEST_INTERACTIVE=1 winmm_test wave * in particular, what happens when the test tone is played with WAVE_FORMAT_DIRECT on Windows? (except Win9x platforms which don't support that flag)
On Sat, 28 Dec 2002, Francois Gouget wrote:
When DSound initializes it create a primary sound buffer and invokes winmm to actually get access to a sound device. The default format of the primary buffer is 22050x16x2 (i.e. 22050Hz, 16 bit sound, stereo). This means the device it's going to get will be handled by msacm.drv which will be responsible for performing the conversions on the fly.
However during the primary buffer creation DSDB_MapPrimary gets invoked in wineoss and does an mmap of the hardware sound buffer.
So when we then play sound in a secondary sound buffer, this sound gets mixed into the primary buffer, but because of the mmap this then goes straight to the sound card with no further conversion. So you end up with a chipmunk sound effect since you play a 22050x16x2 sound at 48000x16x2.
I will try to make more tests to confirm this but does it seem to make sense? Should I look in some other direction? Any suggestion about a fix?
I've been meaning to submit the winex wineoss modifications, but haven't got around to it yet, and it might be somewhat complicated by all the changes around it lately. But this particular issue doesn't take a big patch. Basically the 'true' sample rate should be propagated back to dsound, so that it knows that the primary buffer is 48kHz and can resample secondary buffers accordingly. No changes is needed to dsound for this, wineoss's wodOpen just need to modify its waveformat input parameter if the WAVE_DIRECTSOUND flag is present, then dsound will happily convert secondary buffers to that sample rate when mixing them into the primary buffer. (Primary buffers are different, but apps that writes directly into the primary buffer are few, and they should use GetFormat and resample on their own.)
Ove Kaaven wrote: [...]
Basically the 'true' sample rate should be propagated back to dsound, so that it knows that the primary buffer is 48kHz and can resample secondary buffers accordingly. No changes is needed to dsound for this, wineoss's wodOpen just need to modify its waveformat input parameter if the WAVE_DIRECTSOUND flag is present, then dsound will happily convert secondary buffers to that sample rate when mixing them into the primary buffer.
Thanks. After realizing the SetFormat on the primary buffer was causing my half-baked fix to break, that worked fine. So I sent a patch to wine-patches.
I'm not really sure this is what Windows is doing however. I would be interested if people with Windows NT/2000/XP and an i810 soundcard could run both the dsound and the winmm tests and send me the output.
I believe that Windows lets you call SetFormat on the primary buffer but I'm not sure if that actually has an effect or not. Another thing i wonder about is whether Windows lets you play 11kHz sounds with WAVE_FORMAT_DIRECT using winmm and an i810 soundcard. If not then my winmm test is incorrect, and if yes then I have to wonder what WAVE_FORMAT_DIRECT really means. So if someone can send me these results...
Anyway, it works :-)
So now I'm trying to get the DirectSound test to work with i810 soundcards+Alsa. The problem is that with this combination we fail to mmap the sound device:
err:wave:DSDB_MapPrimary (0x4038dcfc): Could not map sound device for direct access (Input/output error)
This can be simulated by adding the following to DSDB_MapPrimary in dlls/winmm/wineoss/audio.c:
if (getenv("I810A")) wwo->mapping=(LPBYTE)-1; else wwo->mapping = mmap(NULL, wwo->maplen, PROT_WRITE, MAP_SHARED, wwo->ossdev->fd, 0);
Then invoke the test with (quite handy to debug): I810A="1" WINETEST_INTERACTIVE=1 ../../../tools/runtest -P wine -M dsound.dll -T ../../.. -p dsound_test.exe.so dsound.c
I don't know why the mmap fails. I would tend to think this is a limitation or bug of the Alsa driver since it works with the OSS driver. However it seems that we should be able to cope with this situation. But I have some questions:
* in wodOpen we create a wodPlayer thread... except if we are being called by DirectSound. Won't we need this thread if we cannot mmap the device?
* when we do a SetFormat, DSOUND_PrimaryClose calls waveOutReset (only hwbuf, e.g. if mmap failed) which in turn calls OSS_AddRingMessage. However since we don't have the wodPlayer thread noone ever processes that message and we remain stuck there. What I'm currently experimenting with is not sending this message but I wonder if we should not make sure we have a wodPlayer thread instead (but then, when should we create it?).
* the next problem is that DSOUND_PrimaryClose calls waveOutUnprepareHeader with a bunch of WAVEHDR pointers. Adding a test to avoid this condition is simple but again I wonder if this is quite normal. Am I right in thinking that this code path (hwbuf==NULL) is rarely if ever used? <g>
On Mon, 6 Jan 2003, Francois Gouget wrote:
I'm not really sure this is what Windows is doing however. I would be interested if people with Windows NT/2000/XP and an i810 soundcard could run both the dsound and the winmm tests and send me the output.
Note that test results from Windows XP won't be very useful for this; under XP, dsound primary buffers doesn't map directly to the sound hardware any more, according to MSDN. If you create and use a dsound primary buffer under XP, it will just become another stream sent to XP's kernel-mode sound mixer, before it gets to the hardware.
I believe that Windows lets you call SetFormat on the primary buffer but I'm not sure if that actually has an effect or not.
It should if you have appropriate cooperative level (except under XP).
I don't know why the mmap fails. I would tend to think this is a limitation or bug of the Alsa driver since it works with the OSS driver.
ALSA has resampling features. If you open the sound device at e.g. 11kHz, then ALSA's OSS emulation will, instead of telling you that the chip is 48kHz, install a resampling filter to convert from 11kHz to the hardware's required 48kHz. Unfortunately, that filter gets in the way of direct access, making an mmap fail. (In WineX we test mmap on a range of common frequencies to work around this.)
However it seems that we should be able to cope with this situation. But I have some questions:
- in wodOpen we create a wodPlayer thread... except if we are being
called by DirectSound. Won't we need this thread if we cannot mmap the device?
dsound used to completely fail (and return errors to the app) in this case, so it wasn't a problem. The thread should be created if mmap isn't available, yes, but that would have to be verified beforehand (that's what the cap flag checking is for), since both wodOpen and dsound needs to know whether mmap is available (to avoid the problems below).
- when we do a SetFormat, DSOUND_PrimaryClose calls waveOutReset (only
hwbuf, e.g. if mmap failed) which in turn calls OSS_AddRingMessage. However since we don't have the wodPlayer thread noone ever processes that message and we remain stuck there. What I'm currently experimenting with is not sending this message but I wonder if we should not make sure we have a wodPlayer thread instead (but then, when should we create it?).
- the next problem is that DSOUND_PrimaryClose calls
waveOutUnprepareHeader with a bunch of WAVEHDR pointers. Adding a test to avoid this condition is simple but again I wonder if this is quite normal. Am I right in thinking that this code path (hwbuf==NULL) is rarely if ever used? <g>
Ove Kaaven wrote:
On Mon, 6 Jan 2003, Francois Gouget wrote:
[...]
Note that test results from Windows XP won't be very useful for this; under XP, dsound primary buffers doesn't map directly to the sound hardware any more, according to MSDN. If you create and use a dsound primary buffer under XP, it will just become another stream sent to XP's kernel-mode sound mixer, before it gets to the hardware.
Ah that probably explains some results I've seen then. Thanks.
I don't know why the mmap fails. I would tend to think this is a limitation or bug of the Alsa driver since it works with the OSS driver.
ALSA has resampling features. If you open the sound device at e.g. 11kHz, then ALSA's OSS emulation will, instead of telling you that the chip is 48kHz, install a resampling filter to convert from 11kHz to the hardware's required 48kHz. Unfortunately, that filter gets in the way of direct access, making an mmap fail. (In WineX we test mmap on a range of common frequencies to work around this.)
Ok. That makes sense. That's sort of the opposite direction of the DirectSound patch I sent to wine-patches (and which just got applied). The annoying thing is that the mmap, the device format bubbling up and the CreateThread are all in different places :-( Oh well, I'll try to come up with something. It seems hacking things to make mmap succeeds is the simplest approach. But what's the best approach?
1. if we tweak things so that the mmap call succeeds then Wine will be making the resampling. And Wine is not really good at that and tends to introduce audible distortion. It probably doesn't matter if you start from an 8kHz signal but if you're playing a 44kHz sound...
2. if we tweak things so that the CreateThread is done, then the resampling will be done by Alsa (or whichever back-end) which will presumably do it much better than us. howeve rin that case we have to make the almost never used case work and I'm not sure how well it would work anyway (I'm concerned about skips/cracks/pops).
To make 1. cleaner maybe we should have a Wine-specific message that asks the backend to return the formats supported by the device, i.e. for OSS returning an exact translation of the value returned by SNDCTL_DSP_GETFMTS. Then if our msacm gets improved to the Jack sound format conversion library we're golden... (what's the status of this btw?)
Francois Gouget wrote: [...]
- if we tweak things so that the CreateThread is done, then the
resampling will be done by Alsa (or whichever back-end) which will presumably do it much better than us. howeve rin that case we have to make the almost never used case work and I'm not sure how well it would work anyway (I'm concerned about skips/cracks/pops).
Option 2 turned out to be quite simple to get working so that's what I did. However the result does not work very well. When I run the DirectSound test I get a lot of 'stutter' :-( It seems like there are a lot of underruns. Other applications (e.g. Windows Media Player) seem to work better but that appears to depend on the machine too... It seems we should be able to do better.
For those who want to play with this but don't have an i810 souncard (e.g. me), I attached a nice patch that allows you to simulate all sorts of things: * by default the patch has no effect
* define I810O="1" (that's an O for OSS) Then the ioctls will return the same results they would return in an i810+OSS combination. This means only 48000x16x2 will be accepted. This simulates what happens in an i810+OSS configuration.
* define I810A="1" Then winmm will be able to use all the formats your soundcard supports but mmap will fail if the current format is not 48000x16x2. This simulates what happens in an i810+Alsa configuration.
* define NOMMAP="1" Then mmap will systematically fail. This does not simulate a specific configuration I know of but heck, why not.
So to run the dsound test as if you had an i810+Alsa configuration you would do:
cd dlls/dsound/tests I810A="1" WINETEST_INTERACTIVE="1" ../../../tools/runtest -P wine -M dsound.dll -T ../../.. -p dsound_test.exe.so dsound.c
Actually doing so you may notice that the i810+Alsa patch I sent to wine-patches does not support writing to the primary buffer (but who does that anyway)...
Another thing i wonder about is whether Windows lets you play 11kHz sounds with WAVE_FORMAT_DIRECT using winmm and an i810 soundcard. If not then my winmm test is incorrect, and if yes then I have to wonder what WAVE_FORMAT_DIRECT really means. So if someone can send me these results...
well, it may also depends on who is in charge to "understand" the WAVE_FORMAT_DIRECT: - from what I understand from the doc (I didn't actually tested under windows), WAVE_FORMAT_DIRECT tells winmm not to perform frequency modification if the driver doesn't support the requested format - it doesn't state that the driver shouldn't do any transformation by itself
A+
I don't know why the mmap fails. I would tend to think this is a limitation or bug of the Alsa driver since it works with the OSS driver.
do you mean - wine OSS driver on a Linux OSS emulation on ALSA driver - or wine ALSA driver on a Linux ALSA driver the later could have still quite a few issues
anyway, there are also a couple of issues we may have to look after: - the driver interface used always comes from a single audio driver (even if multiple audio drivers are installed in wine) - trying to get rid of wine's DRV_QUERYDSOUNDIFACE... a bit more hacky... one solution would be (as MS does) to install in the registry all the required settings for our sound drivers (including a guid for each wave out, wave in... so that we could bind dsound to each of those drivers) - getting rid of WAVE_DIRECTSOUND... I didn't look at all the details... but, wouldn't the semantics of open&setformat methods of the IDsDriver be enough for that
A+
Hi Eric,
Eric Pouech wrote:
Another thing i wonder about is whether Windows lets you play 11kHz sounds with WAVE_FORMAT_DIRECT using winmm and an i810 soundcard. If not then my winmm test is incorrect, and if yes then I have to wonder what WAVE_FORMAT_DIRECT really means. So if someone can send me these results...
well, it may also depends on who is in charge to "understand" the WAVE_FORMAT_DIRECT:
- from what I understand from the doc (I didn't actually tested under
windows), WAVE_FORMAT_DIRECT tells winmm not to perform frequency modification if the driver doesn't support the requested format
- it doesn't state that the driver shouldn't do any transformation by
itself
That's an interesting interpretation. How would one go about testing it under Windows? Under your interpretation, even if we specify WAVE_FORMAT_DIRECT the sample rate may still be performed by either the driver or the hardware. The values returned by Windows really muddle what's being done in software and what's being done in hardware :-( however if we assume that the above soundcard can really only handle 48kHz then yours seems like the correct hypothesis.
That actually reminds me of waveOutGetDevCaps. On Windows it seems to return an awful lot of supported formats. Up to now my hypothesis was that it returned everything that the wavemapper was able to convert to a supported format. But maybe this is wrong and it should only report what the *driver* can handle (possibly in software). So for us this means wineoss should perform a strict conversion of the mask returned by SNDCTL_DSP_GETFMTS to the Windows set of flags, with no stereo/mono, 8/16 bit muddling.
So with an i810 soundcard and the OSS driver we would only get 48000x16x2, and with Alsa (accessed through OSS) we would get whatever Alsa is able to convert to a format supported by the soundcard.
Hmm, I guess this means we're still stuck with trying multiple formats for mmap :-(
[...]
I don't know why the mmap fails. I would tend to think this is a limitation or bug of the Alsa driver since it works with the OSS driver.
do you mean
- wine OSS driver on a Linux OSS emulation on ALSA driver
- or wine ALSA driver on a Linux ALSA driver
the later could have still quite a few issues
I'm only working on the wineoss driver... at least for now.
anyway, there are also a couple of issues we may have to look after:
- the driver interface used always comes from a single audio driver
(even if multiple audio drivers are installed in wine)
- trying to get rid of wine's DRV_QUERYDSOUNDIFACE... a bit more
hacky... one solution would be (as MS does) to install in the registry all the required settings for our sound drivers (including a guid for each wave out, wave in... so that we could bind dsound to each of those drivers)
- getting rid of WAVE_DIRECTSOUND... I didn't look at all the details...
but, wouldn't the semantics of open&setformat methods of the IDsDriver be enough for that
These sound good but I don't really feel qualified enough yet to comment on them (or be able to contribute much I'm afraid).
That's an interesting interpretation. How would one go about testing it under Windows?
you would have to load the MM driver by hand and directly send the messages to it however, depending on the Windows version you're using, interfaces may differ.
But maybe this is wrong and it should only report what the *driver* can handle (possibly in software). So for us this means wineoss should perform a strict conversion of the mask returned by SNDCTL_DSP_GETFMTS to the Windows set of flags, with no stereo/mono, 8/16 bit muddling.
So with an i810 soundcard and the OSS driver we would only get 48000x16x2, and with Alsa (accessed through OSS) we would get whatever Alsa is able to convert to a format supported by the soundcard.
except if we decide at the driver level to support more format... we could also decide that if OSS fails to open a driver (even with the wave direct flag set) to do the format conversion at the driver level IMO, that's just an implementation decision. we're not obliged to stick to the pure capability of the MM driver we rely upon.
Hmm, I guess this means we're still stuck with trying multiple formats for mmap :-(
the current dsound implementation however, already supports the format conversion anyway... so basically the implementation would be, in order of trials (for OSS): - mmap with required format - mmap with any available format on the soundcard (using the OSS capability would help here) - regular wavehdr playing
anyway, there are also a couple of issues we may have to look after:
- the driver interface used always comes from a single audio driver
(even if multiple audio drivers are installed in wine)
- trying to get rid of wine's DRV_QUERYDSOUNDIFACE... a bit more
hacky... one solution would be (as MS does) to install in the registry all the required settings for our sound drivers (including a guid for each wave out, wave in... so that we could bind dsound to each of those drivers)
- getting rid of WAVE_DIRECTSOUND... I didn't look at all the
details... but, wouldn't the semantics of open&setformat methods of the IDsDriver be enough for that
These sound good but I don't really feel qualified enough yet to comment on them (or be able to contribute much I'm afraid).
since Ove jumped in to discuss the other issues, I found it appropriate to fire this part too ;-) A+