To all those who tested my previous patch, thanks.
I have a new patch which may help to resolve some of the (few) issues which cropped up. The "length not a multiple of block size" issue has been partially addressed. Also, buffer underruns should be far less frequent than the previous patch if direct hardware access is used (set registry key \HKEY_CURRENT_USER\Software\Wine\Alsa Driver\UseDirectHW to "y").
I'll leave this for a few days and if there are no reports of breakages I'll send it (as two seperate patches) to wine-patches.
Davin
Tested again with BattleZone2 and I'm still getting what sounds like serious buffer underruns with this new patch. This one seemed to 'skip' a bit more too, depending on action taking place on screen, but that could be due to other issues (system load etc...). Running with Alsa as the audio driver and full hardware acceleration, no emulation. Tested both with and without the registry key set, and stuttering and the looping sounds actually seemed to get worse WITH the key. Switched to OSS driver to test (this would be Alsa's OSS emulation) and I still get the repeating sounds but some of the other issues are better. BattleZone2 does have a demo that can still be found online, and there was a patch on the list not too long ago to get the demo up and running.
Again, let me know if any traces would be benneficial.
Randall Walls
Davin McCall wrote:
To all those who tested my previous patch, thanks.
I have a new patch which may help to resolve some of the (few) issues which cropped up. The "length not a multiple of block size" issue has been partially addressed. Also, buffer underruns should be far less frequent than the previous patch if direct hardware access is used (set registry key \HKEY_CURRENT_USER\Software\Wine\Alsa Driver\UseDirectHW to "y").
I'll leave this for a few days and if there are no reports of breakages I'll send it (as two seperate patches) to wine-patches.
Davin
Thanks for that -
There was at least one problem with the patch. I've attached a fix for that (apply over the top of the previous one).
I don't get buffer underruns in my own testing, but I do get an annoying clicking noise every so often (seems to be when streaming buffers wrap from the end back to the start). I think that's a mixer issue rather than an ALSA driver issue however as it occurs with OSS as well as ALSA. At the moment, I'm focusing primarily on the ALSA driver issues.
I'd be grateful if you could try this modification (best results should be with full acceleration, and UseDirectHW set to "y").
Davin
On Thu, 03 Nov 2005 02:18:48 +0000 Randall Walls rwalls@gwi.net wrote:
Tested again with BattleZone2 and I'm still getting what sounds like serious buffer underruns with this new patch. This one seemed to 'skip' a bit more too, depending on action taking place on screen, but that could be due to other issues (system load etc...). Running with Alsa as the audio driver and full hardware acceleration, no emulation. Tested both with and without the registry key set, and stuttering and the looping sounds actually seemed to get worse WITH the key. Switched to OSS driver to test (this would be Alsa's OSS emulation) and I still get the repeating sounds but some of the other issues are better. BattleZone2 does have a demo that can still be found online, and there was a patch on the list not too long ago to get the demo up and running.
Again, let me know if any traces would be benneficial.
Randall Walls
Here's another patch to fix the clicking noise. Once again apply over the top.
With this one, I think everything should be at least as good as it was with my very first patch. (Plus the full device buffer will be used if UseDirectHW="y", which should reduce underrun problems).
Davin
On Thu, 3 Nov 2005 18:42:50 +1100 Davin McCall davmac@davmac.org wrote:
Thanks for that -
There was at least one problem with the patch. I've attached a fix for that (apply over the top of the previous one).
I don't get buffer underruns in my own testing, but I do get an annoying clicking noise every so often (seems to be when streaming buffers wrap from the end back to the start). I think that's a mixer issue rather than an ALSA driver issue however as it occurs with OSS as well as ALSA. At the moment, I'm focusing primarily on the ALSA driver issues.
I'd be grateful if you could try this modification (best results should be with full acceleration, and UseDirectHW set to "y").
Davin
On Thu, 03 Nov 2005 02:18:48 +0000 Randall Walls rwalls@gwi.net wrote:
Tested again with BattleZone2 and I'm still getting what sounds like serious buffer underruns with this new patch. This one seemed to 'skip' a bit more too, depending on action taking place on screen, but that could be due to other issues (system load etc...). Running with Alsa as the audio driver and full hardware acceleration, no emulation. Tested both with and without the registry key set, and stuttering and the looping sounds actually seemed to get worse WITH the key. Switched to OSS driver to test (this would be Alsa's OSS emulation) and I still get the repeating sounds but some of the other issues are better. BattleZone2 does have a demo that can still be found online, and there was a patch on the list not too long ago to get the demo up and running.
Again, let me know if any traces would be benneficial.
Randall Walls
Dang. Forgot to attach.
--- Here's another patch to fix the clicking noise. Once again apply over the top.
With this one, I think everything should be at least as good as it was with my very first patch. (Plus the full device buffer will be used if UseDirectHW="y", which should reduce underrun problems).
Davin
On Thu, 3 Nov 2005 18:42:50 +1100 Davin McCall davmac@davmac.org wrote:
Thanks for that -
There was at least one problem with the patch. I've attached a fix for that (apply over the top of the previous one).
I don't get buffer underruns in my own testing, but I do get an annoying clicking noise every so often (seems to be when streaming buffers wrap from the end back to the start). I think that's a mixer issue rather than an ALSA driver issue however as it occurs with OSS as well as ALSA. At the moment, I'm focusing primarily on the ALSA driver issues.
I'd be grateful if you could try this modification (best results should be with full acceleration, and UseDirectHW set to "y").
Davin
On Thu, 03 Nov 2005 02:18:48 +0000 Randall Walls rwalls@gwi.net wrote:
Tested again with BattleZone2 and I'm still getting what sounds like serious buffer underruns with this new patch. This one seemed to 'skip' a bit more too, depending on action taking place on screen, but that could be due to other issues (system load etc...). Running with Alsa as the audio driver and full hardware acceleration, no emulation. Tested both with and without the registry key set, and stuttering and the looping sounds actually seemed to get worse WITH the key. Switched to OSS driver to test (this would be Alsa's OSS emulation) and I still get the repeating sounds but some of the other issues are better. BattleZone2 does have a demo that can still be found online, and there was a patch on the list not too long ago to get the demo up and running.
Again, let me know if any traces would be benneficial.
Randall Walls
Ok.... after a full recompile, rm-fr ~/.wine, and reinstall of the application all seems to be working fine now, with the exception that I still have the 'crackling' sound during gameplay (but that existed prior to your patch as well). Underruns are gone.
As for the crackling, Alex Villacis Lasso wrote a patch in August for the audio.c that was supposed to fix the crackling noise that was heard with some directsound games. The patch was apparently applied to CVS prior to 0.9 (from looking at the original audio.c source), but it didn't fix the crackling noise, at least not with Battlezone 2. The issue appears to be with the hardware pointer and playposition being 'out of sync' so to speak. The patch added this line:
if (hw_ptr >= period_size) hw_ptr -= period_size; else hw_ptr = 0;
Which seems to adjust the harware pointer to more closely match the play position by subtracting the period size from it (if hw_ptr is greater than period_size). This has since been changed with your patch to:
hw_ptr = This->mmap_ppos;
Just curious if you could shed a bit of light on this. As I said, the original patch did not fix my specific problem, but I would curious if you reimplemented the same logic elsewhere, as it did seem to fix the issue for Alex.
Randall Walls
Davin McCall wrote:
Dang. Forgot to attach.
Here's another patch to fix the clicking noise. Once again apply over the top.
With this one, I think everything should be at least as good as it was with my very first patch. (Plus the full device buffer will be used if UseDirectHW="y", which should reduce underrun problems).
Davin
On Thu, 3 Nov 2005 18:42:50 +1100 Davin McCall davmac@davmac.org wrote:
Thanks for that -
There was at least one problem with the patch. I've attached a fix for that (apply over the top of the previous one).
I don't get buffer underruns in my own testing, but I do get an annoying clicking noise every so often (seems to be when streaming buffers wrap from the end back to the start). I think that's a mixer issue rather than an ALSA driver issue however as it occurs with OSS as well as ALSA. At the moment, I'm focusing primarily on the ALSA driver issues.
I'd be grateful if you could try this modification (best results should be with full acceleration, and UseDirectHW set to "y").
Davin
On Thu, 03 Nov 2005 02:18:48 +0000 Randall Walls rwalls@gwi.net wrote:
Tested again with BattleZone2 and I'm still getting what sounds like serious buffer underruns with this new patch. This one seemed to 'skip' a bit more too, depending on action taking place on screen, but that could be due to other issues (system load etc...). Running with Alsa as the audio driver and full hardware acceleration, no emulation. Tested both with and without the registry key set, and stuttering and the looping sounds actually seemed to get worse WITH the key. Switched to OSS driver to test (this would be Alsa's OSS emulation) and I still get the repeating sounds but some of the other issues are better. BattleZone2 does have a demo that can still be found online, and there was a patch on the list not too long ago to get the demo up and running.
Again, let me know if any traces would be benneficial.
Randall Walls
On Mon, 07 Nov 2005 18:25:49 +0000 Randall Walls rwalls@gwi.net wrote:
This has since been changed with your patch to:
hw_ptr = This->mmap_ppos;
Just curious if you could shed a bit of light on this. As I said, the original patch did not fix my specific problem, but I would curious if you reimplemented the same logic elsewhere, as it did seem to fix the issue for Alex.
Here's a long description of what my Alsa-driver patch does. Probably goes into too much detail, but I figure too much is better than not enough and there may be others interested in this, who don't necessarily know how sound hardware works.
Basically, sound data is stored in a circular buffer. The hardware loops through the buffer and outputs the data as sound. When it gets to the end of the buffer, it immediately goes back to the beginning of the buffer and continues. So, after the hardware has gone past a particular point in the buffer it is up to software to fill buffer at that point with new data (otherwise the hardware will play the same sound again when it loops back). If the software doesn't provide the new data quickly enough, you have a situation known as a buffer underrun. Whatever sound or portion of sound was in the buffer might then be played several times as the hardware loops over the same data again and again (this may be why you heard "got it covered - ered - ered.." as mentioned in a previous post).
Previously Wine's Alsa driver was using an undocumented internal Alsa call to (_snd_pcm_hw_get_ptr, or something like that) to get the hardware's current buffer position (the "play position"). Apart from the fact that the method is only supposed to be used internally, there's the problem that Alsa provides "plug" devices (and others) which effectively insert another layer between the application and the hardware. The plug layer can do things like sample rate conversion (some hardware can only do a limited range of sample rates. AC'97 hardware for instance can generally only do 48000 samples per second or an integer division of that, whereas many software programs expect to be able to do 44100 samples/second. It's interesting that older hardware seems to be more flexible in this regard.)
If the Alsa plug layer is in use (which it is by default, unless you have used a variety of undocumented registry keys to change Wine's behaviour), a different buffer size may result and the raw hardware pointer value is less meaningful.
Alsa's model for buffer manipulation is that the application (i.e. Wine) requests a portion of the buffer using snd_pcm_mmap_begin() method, where you specify the maximum amount you want to work with at the moment. The call gives back the amount actually available, the location of the buffer, and the offset into the buffer of the available block. Then the application fills the block with sound samples and calls snd_pcm_mmap_commit() to tell Alsa that data is available.
The key thing here is that the amount of buffer actually available depends on the hardware pointer position. When snd_pcm_mmap_begin() is called Alsa will only allocate from the currently committed region (so called "application pointer") up to the current hardware playback position (the "hardware pointer"), or it's equivalent position in the translation buffer (if direct hardware access is not used). If you request a sufficiently large space when you call snd_pcm_mmap_begin() you can, therefore, determine the current playback position within the buffer.
This is one of the things that my patch does - when snd_pcm_mmap_begin() is called, the returned offset is saved in This->mmap_ppos. (It would be more correct, technically, to save the offset plus the allocated size - but in practice this isn't really necessary, seeing as the allocated size is always small. Furthermore, the reported position will actually about a single period behind the actual position, which makes the line you pointed out - "if (hw_ptr >= period_size) hw_ptr -= period_size; else hw_ptr = 0;" - redundant, which is why I removed it. Though to be honest I'm not sure if keeping the value behind by a period is even necessary anymore).
Why then is the allocated size always small? The answer depends on whether direct hardware access is used. If so, the whole buffer is initially allocated and committed, then more is allocated and committed as it becomes available. It becomes available in small chunks ("periods" in Alsa speak), so, the allocated area must be small.
If direct hardware access is not used, the initial buffer allocate and commit does not occur. The reason for this is, a commit operation actually copies and translates data from the translation buffer to the real hardware buffer. If a whole-buffer commit was performed initially, a whole buffers worth of data would be thus copied and this would be incorrect because the buffer is not even filled at that point. Instead, only a small chunk (2 periods worth) is initially committed, and even smaller chunks (1 period) are committed as each period passes (i.e. as the callback function is called). In this case, it is Alsa's responsibility to map the real hardware pointer into the user buffer.
I hope this makes sense.
Davin
Davin McCall wrote:
Here's a long description of what my Alsa-driver patch does.
Great explanation, thanks!!! Greatly appreciated by me and I'm sure a lot others.
Previously Wine's Alsa driver was using an undocumented internal Alsa call to (_snd_pcm_hw_get_ptr, or something like that) to get the hardware's current buffer position (the "play position").
This is one of the things that my patch does - when snd_pcm_mmap_begin() is called, the returned offset is saved in This->mmap_ppos.
I'm missing a point from your explanation here: Why do you even need the hardware play position? It seems that the ALSA folks went to some length to hide it from you..
On Mon, 7 Nov 2005 09:52:35 +0100 Molle Bestefich molle.bestefich@gmail.com wrote:
Great explanation, thanks!!! Greatly appreciated by me and I'm sure a lot others.
Good :-)
I'm missing a point from your explanation here: Why do you even need the hardware play position? It seems that the ALSA folks went to some length to hide it from you..
In short, you need it to know how much data in the buffer the hardware has already read, and therefore, up to which point in the buffer it is safe to overwrite.
Because some sounds are longer than the buffer (including continuous sound which is actually composed of multiple seperate sounds), you need to load the buffer full of data for the first part of the sound, then wait until that portion has played, and then store the next part of the sound in the buffer.
In practice the buffer is divided into several periods, let's say 10 for example. Also, we might not fill the whole buffer before we tell the hardware to start playing the sound (to reduce latency, among other reasons). So say we fill the buffer with 4 periods worth of sound data. In general the software mixer likes to stay a specific number of periods in front of the hardware (4 in this case). It doesn't want to get too far ahead (mainly because sounds are often dynamic and if you store too much into the buffer, you might need to cancel or alter it later which consumes processing capacity). Neither does it want to get little ahead, or the hardware might overtake the software (buffer underrun).
So, after the first period has been played, we want to fill in the next period (number 5 in the buffer) with data, and also to clear the period that has just played. Then when the next period (#2) plays we fill in another one (#6), and so one. We only know what periods have played be checking the hardware pointer.
Alsa tries to abstract this away somewhat. The good thing about the way Alsa does it is that the interface is (at least in theory) consistent regardless of whether you are using the hardware directly or you using Alsa's plug layer (or other virtual device). The problem is that at the moment, Wine's direct sound implementation (and Alsa driver) do not support the buffer locking interface for the primary buffer (the Alsa buffer, which may or may not also be the mapped hardware buffer).
The locking interface provides a mechanism for direct sound to signal to the driver which parts of the buffer it has changed. This would allow committing only as much as was actually ready. Alsa also provides a "rewind" function for cases where changes occur to the buffer between the current hardware and application pointers.
Davin
Thanks a lot, that answered my questions perfectly!
(I hope someone implements it the "right" ALSA way with buffer locking someday. I'll do it, if I ever get the time /and/ learn C.. which is not likely to happen anytime soon, hehe...)