My ultimate goal was to solve the dsound underruns which were so horrible that I had to disable sound in World of Warcraft. While I managed to get the sound working flawlessly (really... I never heard such clear sound under wine) in WoW, it required WoW-specific hacks so my patch will never make it (in its current form) into the official git tree, but maybe someone can use some of my ideas to improve dsound/winealsa.
The basic idea is to let alsa mix the sound instead of the infamous dsound mixer. The advantage is that if the hardware supports mixing, there is very little overhead, if not, there's still dmix which can do that, but dsound doesn't need to care how it's done, it's entirely up to alsa.
Now to the actual implementation, it's pretty straight-forward: I 'forced' CreateSoundBuffer to create a hardware buffer (right now it creates a hw buffer only for the primary buffer) and changed the winealsa driver to support that. Because winealsa driver opens only one 'connection' (snd_pcm_t) to the soundcar (allowing only one buffer per device), I simply opened a new 'connection' for each buffer and configured the hardware for 44100Hz/U16LE/stereo (that's what WoW expects). Up to this point it was easy. One of the big problems was that WoW requests a 16kb buffer, but alsa is unable to allocate a buffer with this exact size, and it caused problems if I passed the alsa buffer to WoW (so WoW could write directly to it). I had to allocate a separate buffer, keep track of the play/write positions in both buffers and copy the data from one to the other. I'm fairly sure this all could be done in a WoW-independent way, eg. configure the hardware as the application requests it (by passing LPCDSBUFFERDESC to the low-level driver etc) and keeping track of the read/write positions could also be done in a better way.
There are a free questions that should be answered before someone can go on and make a 'proper' implementation. I simply bypassed the primary buffer, it doesn't exist since the data from the secondary buffers are written directly to the soundcard. If it should be possible to read the data from the primary buffer then we should forget this all and look for another solution. I believe that it's not possible (it's not possible to write to the primary buffer and the API doesn't differentiate between reads and writes), but someone should test that under windows. (this is only one question but I'm sure you'll come up with more ;) )
Here is the complete patch, I also changed the IDirectSound interface and a lot unrelated code, so the patch is a bit big :-/ There also are a few memory leaks, I didn't bother freeing the memory etc. http://dbservice.com/ftpdir/tom/dsound-alsa.patch
tom
Tomas Carnecky wrote:
I'm fairly sure this all could be done in a WoW-independent way, eg. configure the hardware as the application requests it (by passing LPCDSBUFFERDESC to the low-level driver etc) and keeping track of the read/write positions could also be done in a better way.
A small update on that, I managed to indeed make it WoW-independent, I tested it with foobar2000 and the playback works fine, there is an issue with the sound volume, it keeps changing if I seek :-/
Right now I'm documenting the code, so my next patch will be a little bit better readable and easier to understand :)
tom
Keep us posted. I'm interested in getting better support for audio recording under Wine.
Hiji
----- Original Message ---- From: Tomas Carnecky tom@dbservice.com To: wine-devel@winehq.org Sent: Friday, September 22, 2006 9:34:14 AM Subject: Re: my dsound/winealsa hacks
Tomas Carnecky wrote:
I'm fairly sure this all could be done in a WoW-independent way, eg. configure the hardware as the application requests it (by passing LPCDSBUFFERDESC to the low-level driver etc) and keeping track of the read/write positions could also be done in a better way.
A small update on that, I managed to indeed make it WoW-independent, I tested it with foobar2000 and the playback works fine, there is an issue with the sound volume, it keeps changing if I seek :-/
Right now I'm documenting the code, so my next patch will be a little bit better readable and easier to understand :)
tom
.. another small update, now tries to create the buffer size as close as possible to what the app requested. The whole patch is available at the same URL, I also created a patch of only ./dlls/winmm/winealsa/audio.c to make it easier to read the patch, the patch is here: http://dbservice.com/ftpdir/tom/alsa-audio.patch
tom
Tomas Carnecky wrote:
.. another small update, now tries to create the buffer size as close as possible to what the app requested. The whole patch is available at the same URL, I also created a patch of only ./dlls/winmm/winealsa/audio.c to make it easier to read the patch, the patch is here: http://dbservice.com/ftpdir/tom/alsa-audio.patch
tom
This sounds beautiful under counter-strike and counter-strike:source.
Now if only microphones worked in those games...(never did anyways, lol)
Am Samstag 23 September 2006 10:57 schrieb Tomas Carnecky:
.. another small update, now tries to create the buffer size as close as possible to what the app requested. The whole patch is available at the same URL, I also created a patch of only ./dlls/winmm/winealsa/audio.c to make it easier to read the patch, the patch is here: http://dbservice.com/ftpdir/tom/alsa-audio.patch
Just asking, would it work if you just make sure that the created buffer is bigger than the requested buffer?
Tomas Carnecky wrote:
My ultimate goal was to solve the dsound underruns which were so horrible that I had to disable sound in World of Warcraft. While I managed to get the sound working flawlessly (really... I never heard such clear sound under wine) in WoW, it required WoW-specific hacks so my patch will never make it (in its current form) into the official git tree, but maybe someone can use some of my ideas to improve dsound/winealsa.
The basic idea is to let alsa mix the sound instead of the infamous dsound mixer. The advantage is that if the hardware supports mixing, there is very little overhead, if not, there's still dmix which can do that, but dsound doesn't need to care how it's done, it's entirely up to alsa.
I have place some documentation on the ALSA wiki site: https://bugtrack.alsa-project.org/wiki/wikka.php?wakka=ALSAresampler
It tries to explain the constraints that the current ALSA resampler works under.
You might like to read it as I think it will have impact on your plans.
James
James Courtier-Dutton wrote:
I have place some documentation on the ALSA wiki site: https://bugtrack.alsa-project.org/wiki/wikka.php?wakka=ALSAresampler
It tries to explain the constraints that the current ALSA resampler works under.
You might like to read it as I think it will have impact on your plans.
Thanks. One question though, if the app in in blocking mode and requests the said two periods, will alsa wait until the hardware has processed three 48000Hz-periods and then copy the two 44100Hz-periods to the application (because: 3 periods at 48000Hz > 2*1024 frames at 44100Hz)?
DirectSound doesn't know anything about periods, the windows application operates on bytes rather than frames or periods. So whether I'd have to wait for two or three periods wouldn't matter. The important thing is that I get X bytes in the right format to pass that back to the application.
tom
Tomas Carnecky wrote:
James Courtier-Dutton wrote:
I have place some documentation on the ALSA wiki site: https://bugtrack.alsa-project.org/wiki/wikka.php?wakka=ALSAresampler
It tries to explain the constraints that the current ALSA resampler works under.
You might like to read it as I think it will have impact on your plans.
Thanks. One question though, if the app in in blocking mode and requests the said two periods, will alsa wait until the hardware has processed three 48000Hz-periods and then copy the two 44100Hz-periods to the application (because: 3 periods at 48000Hz > 2*1024 frames at 44100Hz)?
DirectSound doesn't know anything about periods, the windows application operates on bytes rather than frames or periods. So whether I'd have to wait for two or three periods wouldn't matter. The important thing is that I get X bytes in the right format to pass that back to the application.
tom
Maybe the ALSA terminology needs to be clarified. A frame is equivalent of one sample being played, irrespective of the number of channels or the about of bits. e.g. 1 frame of a Stereo 48khz 16bit PCM stream is 4 bytes. 1 frame of a 5.1 48khz 16bit PCM stream is 12 bytes.
A period is the amount of frames in between each hardware interrupt. The poll() will return once a period.
The buffer is a ring buffer. The buffer size always has to be greater than one period size. Commonly this is 2*period size, but some hardware can do 8 periods per buffer. It is also possible for the buffer size to not be an integer multiple of the period size.
Now, if the hardware has been set to 48000Hz , 2 periods, of 1024 frames each, making a buffer size of 2048 frames. The hardware will interrupt 2 times per buffer. The application 44100Hz will also have 2 periods of 940 frames. ALSA will endeavor to keep the buffer as full as possible. Once the first period has been played, the third is transfered into the space the first one occupied while the second period is being played. (normal ring buffer behaviour).
So, once a period has been transfered to the sound card hardware, it cannot be modified, even though the sound card has not yet played it from the DACs.
As Direct Sound does not know anything about periods, I don't really know how you will be able to get it to work well with ALSA. I expect that some sort of double buffer will be required. Does Direct Sound have a concept of position of the ADC, and also a concept of where in the buffer it is sensible to fill with new samples?
James
James Courtier-Dutton wrote:
As Direct Sound does not know anything about periods, I don't really know how you will be able to get it to work well with ALSA. I expect that some sort of double buffer will be required. Does Direct Sound have a concept of position of the ADC, and also a concept of where in the buffer it is sensible to fill with new samples?
When the application creates a buffer, it passes a structure to CreateSoundBuffer() that describes what kind of sound the buffer will contain, and the data include: - format (PCM/ALAW/ULAW etc) - number of channels - bits per sample - rate (Hz) and - size of the buffer it wants, in bytes
The application can use Lock() to request s pointer to a buffer where it can write X bytes of sound data to, once it has written the data, it calls Unlock() and then DirectSound passes the data to the soundcard. World of Warcraft calls Lock to get a pointer where it can fill 4096 bytes (regardless of how big the period is, because DirectSound doesn't know about periods), writes the sound to the buffer and calls Unlock(), I'm using the async handler that invokes a function that reads the data from the intermediary buffer and passes it to the alsa). And yes, the app can call GetCurrentPosition() to find out the 'Play' and 'Write' positions in the buffer, play is where the soundcard is at the moment (eg. the position where I will read the data from and pass it to alsa), write is where the app can write the data to, currently I'm using 'playpos+period'.
I haven't implemented the DirectSoundCapture, but I guess that it will work the same: the app calls Lock() and when the call returns it will receive a pointer to a buffer where X bytes of the captured data is, so if the app wants 4096 bytes, I'll have to wait until alsa has returned Y periods (where frames_to_bytes(Y) > X) and then return to the app.
I'm sorry, I didn't explain myself clearly in the previous mail :-/
tom
Tomas Carnecky wrote:
My ultimate goal was to solve the dsound underruns which were so horrible that I had to disable sound in World of Warcraft. While I managed to get the sound working flawlessly (really... I never heard such clear sound under wine) in WoW, it required WoW-specific hacks so my patch will never make it (in its current form) into the official git tree, but maybe someone can use some of my ideas to improve dsound/winealsa.
The basic idea is to let alsa mix the sound instead of the infamous dsound mixer. The advantage is that if the hardware supports mixing, there is very little overhead, if not, there's still dmix which can do that, but dsound doesn't need to care how it's done, it's entirely up to alsa.
I have placed some documentation on the ALSA wiki site: https://bugtrack.alsa-project.org/wiki/wikka.php?wakka=ALSAresampler
It tries to explain the constraints that the current ALSA resampler works under.
You might like to read it as I think it will have impact on your plans.
James