Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winepulse.drv/pulse.c | 380 ++++++++++++++++++------------------- 1 file changed, 190 insertions(+), 190 deletions(-)
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 98b4a1ec964..590122cc7b1 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -270,6 +270,196 @@ static void pulse_started_callback(pa_stream *s, void *userdata) TRACE("%p: (Re)started playing\n", userdata); }
+static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes) +{ + memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes); +} + +static int write_buffer(const struct pulse_stream *stream, BYTE *buffer, UINT32 bytes) +{ + const float *vol = stream->vol; + UINT32 i, channels, mute = 0; + BOOL adjust = FALSE; + BYTE *end; + + if (!bytes) return 0; + + /* Adjust the buffer based on the volume for each channel */ + channels = stream->ss.channels; + for (i = 0; i < channels; i++) + { + adjust |= vol[i] != 1.0f; + if (vol[i] == 0.0f) + mute++; + } + if (mute == channels) + { + silence_buffer(stream->ss.format, buffer, bytes); + goto write; + } + if (!adjust) goto write; + + end = buffer + bytes; + switch (stream->ss.format) + { +#ifndef WORDS_BIGENDIAN +#define PROCESS_BUFFER(type) do \ +{ \ + type *p = (type*)buffer; \ + do \ + { \ + for (i = 0; i < channels; i++) \ + p[i] = p[i] * vol[i]; \ + p += i; \ + } while ((BYTE*)p != end); \ +} while (0) + case PA_SAMPLE_S16LE: + PROCESS_BUFFER(INT16); + break; + case PA_SAMPLE_S32LE: + PROCESS_BUFFER(INT32); + break; + case PA_SAMPLE_FLOAT32LE: + PROCESS_BUFFER(float); + break; +#undef PROCESS_BUFFER + case PA_SAMPLE_S24_32LE: + { + UINT32 *p = (UINT32*)buffer; + do + { + for (i = 0; i < channels; i++) + { + p[i] = (INT32)((INT32)(p[i] << 8) * vol[i]); + p[i] >>= 8; + } + p += i; + } while ((BYTE*)p != end); + break; + } + case PA_SAMPLE_S24LE: + { + /* do it 12 bytes at a time until it is no longer possible */ + UINT32 *q = (UINT32*)buffer; + BYTE *p; + + i = 0; + while (end - (BYTE*)q >= 12) + { + UINT32 v[4], k; + v[0] = q[0] << 8; + v[1] = q[1] << 16 | (q[0] >> 16 & ~0xff); + v[2] = q[2] << 24 | (q[1] >> 8 & ~0xff); + v[3] = q[2] & ~0xff; + for (k = 0; k < 4; k++) + { + v[k] = (INT32)((INT32)v[k] * vol[i]); + if (++i == channels) i = 0; + } + *q++ = v[0] >> 8 | (v[1] & ~0xff) << 16; + *q++ = v[1] >> 16 | (v[2] & ~0xff) << 8; + *q++ = v[2] >> 24 | (v[3] & ~0xff); + } + p = (BYTE*)q; + while (p != end) + { + UINT32 v = (INT32)((INT32)(p[0] << 8 | p[1] << 16 | p[2] << 24) * vol[i]); + *p++ = v >> 8 & 0xff; + *p++ = v >> 16 & 0xff; + *p++ = v >> 24; + if (++i == channels) i = 0; + } + break; + } +#endif + case PA_SAMPLE_U8: + { + UINT8 *p = (UINT8*)buffer; + do + { + for (i = 0; i < channels; i++) + p[i] = (int)((p[i] - 128) * vol[i]) + 128; + p += i; + } while ((BYTE*)p != end); + break; + } + case PA_SAMPLE_ALAW: + { + UINT8 *p = (UINT8*)buffer; + do + { + for (i = 0; i < channels; i++) + p[i] = mult_alaw_sample(p[i], vol[i]); + p += i; + } while ((BYTE*)p != end); + break; + } + case PA_SAMPLE_ULAW: + { + UINT8 *p = (UINT8*)buffer; + do + { + for (i = 0; i < channels; i++) + p[i] = mult_ulaw_sample(p[i], vol[i]); + p += i; + } while ((BYTE*)p != end); + break; + } + default: + TRACE("Unhandled format %i, not adjusting volume.\n", stream->ss.format); + break; + } + +write: + return pa_stream_write(stream->stream, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE); +} + +static void pulse_write(struct pulse_stream *stream) +{ + /* write as much data to PA as we can */ + UINT32 to_write; + BYTE *buf = stream->local_buffer + stream->pa_offs_bytes; + UINT32 bytes = pa_stream_writable_size(stream->stream); + + if (stream->just_underran) + { + /* prebuffer with silence if needed */ + if(stream->pa_held_bytes < bytes){ + to_write = bytes - stream->pa_held_bytes; + TRACE("prebuffering %u frames of silence\n", + (int)(to_write / pa_frame_size(&stream->ss))); + buf = calloc(1, to_write); + pa_stream_write(stream->stream, buf, to_write, NULL, 0, PA_SEEK_RELATIVE); + free(buf); + } + + stream->just_underran = FALSE; + } + + buf = stream->local_buffer + stream->pa_offs_bytes; + TRACE("held: %lu, avail: %u\n", stream->pa_held_bytes, bytes); + bytes = min(stream->pa_held_bytes, bytes); + + if (stream->pa_offs_bytes + bytes > stream->real_bufsize_bytes) + { + to_write = stream->real_bufsize_bytes - stream->pa_offs_bytes; + TRACE("writing small chunk of %u bytes\n", to_write); + write_buffer(stream, buf, to_write); + stream->pa_held_bytes -= to_write; + to_write = bytes - to_write; + stream->pa_offs_bytes = 0; + buf = stream->local_buffer; + } + else + to_write = bytes; + + TRACE("writing main chunk of %u bytes\n", to_write); + write_buffer(stream, buf, to_write); + stream->pa_offs_bytes += to_write; + stream->pa_offs_bytes %= stream->real_bufsize_bytes; + stream->pa_held_bytes -= to_write; +} + static void pulse_op_cb(pa_stream *s, int success, void *user) { TRACE("Success: %i\n", success); @@ -277,11 +467,6 @@ static void pulse_op_cb(pa_stream *s, int success, void *user) pulse_broadcast(); }
-static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes) -{ - memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes); -} - static BOOL pulse_stream_valid(struct pulse_stream *stream) { return pa_stream_get_state(stream->stream) == PA_STREAM_READY; @@ -958,191 +1143,6 @@ static NTSTATUS pulse_release_stream(void *args) return STATUS_SUCCESS; }
-static int write_buffer(const struct pulse_stream *stream, BYTE *buffer, UINT32 bytes) -{ - const float *vol = stream->vol; - UINT32 i, channels, mute = 0; - BOOL adjust = FALSE; - BYTE *end; - - if (!bytes) return 0; - - /* Adjust the buffer based on the volume for each channel */ - channels = stream->ss.channels; - for (i = 0; i < channels; i++) - { - adjust |= vol[i] != 1.0f; - if (vol[i] == 0.0f) - mute++; - } - if (mute == channels) - { - silence_buffer(stream->ss.format, buffer, bytes); - goto write; - } - if (!adjust) goto write; - - end = buffer + bytes; - switch (stream->ss.format) - { -#ifndef WORDS_BIGENDIAN -#define PROCESS_BUFFER(type) do \ -{ \ - type *p = (type*)buffer; \ - do \ - { \ - for (i = 0; i < channels; i++) \ - p[i] = p[i] * vol[i]; \ - p += i; \ - } while ((BYTE*)p != end); \ -} while (0) - case PA_SAMPLE_S16LE: - PROCESS_BUFFER(INT16); - break; - case PA_SAMPLE_S32LE: - PROCESS_BUFFER(INT32); - break; - case PA_SAMPLE_FLOAT32LE: - PROCESS_BUFFER(float); - break; -#undef PROCESS_BUFFER - case PA_SAMPLE_S24_32LE: - { - UINT32 *p = (UINT32*)buffer; - do - { - for (i = 0; i < channels; i++) - { - p[i] = (INT32)((INT32)(p[i] << 8) * vol[i]); - p[i] >>= 8; - } - p += i; - } while ((BYTE*)p != end); - break; - } - case PA_SAMPLE_S24LE: - { - /* do it 12 bytes at a time until it is no longer possible */ - UINT32 *q = (UINT32*)buffer; - BYTE *p; - - i = 0; - while (end - (BYTE*)q >= 12) - { - UINT32 v[4], k; - v[0] = q[0] << 8; - v[1] = q[1] << 16 | (q[0] >> 16 & ~0xff); - v[2] = q[2] << 24 | (q[1] >> 8 & ~0xff); - v[3] = q[2] & ~0xff; - for (k = 0; k < 4; k++) - { - v[k] = (INT32)((INT32)v[k] * vol[i]); - if (++i == channels) i = 0; - } - *q++ = v[0] >> 8 | (v[1] & ~0xff) << 16; - *q++ = v[1] >> 16 | (v[2] & ~0xff) << 8; - *q++ = v[2] >> 24 | (v[3] & ~0xff); - } - p = (BYTE*)q; - while (p != end) - { - UINT32 v = (INT32)((INT32)(p[0] << 8 | p[1] << 16 | p[2] << 24) * vol[i]); - *p++ = v >> 8 & 0xff; - *p++ = v >> 16 & 0xff; - *p++ = v >> 24; - if (++i == channels) i = 0; - } - break; - } -#endif - case PA_SAMPLE_U8: - { - UINT8 *p = (UINT8*)buffer; - do - { - for (i = 0; i < channels; i++) - p[i] = (int)((p[i] - 128) * vol[i]) + 128; - p += i; - } while ((BYTE*)p != end); - break; - } - case PA_SAMPLE_ALAW: - { - UINT8 *p = (UINT8*)buffer; - do - { - for (i = 0; i < channels; i++) - p[i] = mult_alaw_sample(p[i], vol[i]); - p += i; - } while ((BYTE*)p != end); - break; - } - case PA_SAMPLE_ULAW: - { - UINT8 *p = (UINT8*)buffer; - do - { - for (i = 0; i < channels; i++) - p[i] = mult_ulaw_sample(p[i], vol[i]); - p += i; - } while ((BYTE*)p != end); - break; - } - default: - TRACE("Unhandled format %i, not adjusting volume.\n", stream->ss.format); - break; - } - -write: - return pa_stream_write(stream->stream, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE); -} - -static void pulse_write(struct pulse_stream *stream) -{ - /* write as much data to PA as we can */ - UINT32 to_write; - BYTE *buf = stream->local_buffer + stream->pa_offs_bytes; - UINT32 bytes = pa_stream_writable_size(stream->stream); - - if (stream->just_underran) - { - /* prebuffer with silence if needed */ - if(stream->pa_held_bytes < bytes){ - to_write = bytes - stream->pa_held_bytes; - TRACE("prebuffering %u frames of silence\n", - (int)(to_write / pa_frame_size(&stream->ss))); - buf = calloc(1, to_write); - pa_stream_write(stream->stream, buf, to_write, NULL, 0, PA_SEEK_RELATIVE); - free(buf); - } - - stream->just_underran = FALSE; - } - - buf = stream->local_buffer + stream->pa_offs_bytes; - TRACE("held: %lu, avail: %u\n", stream->pa_held_bytes, bytes); - bytes = min(stream->pa_held_bytes, bytes); - - if (stream->pa_offs_bytes + bytes > stream->real_bufsize_bytes) - { - to_write = stream->real_bufsize_bytes - stream->pa_offs_bytes; - TRACE("writing small chunk of %u bytes\n", to_write); - write_buffer(stream, buf, to_write); - stream->pa_held_bytes -= to_write; - to_write = bytes - to_write; - stream->pa_offs_bytes = 0; - buf = stream->local_buffer; - } - else - to_write = bytes; - - TRACE("writing main chunk of %u bytes\n", to_write); - write_buffer(stream, buf, to_write); - stream->pa_offs_bytes += to_write; - stream->pa_offs_bytes %= stream->real_bufsize_bytes; - stream->pa_held_bytes -= to_write; -} - static void pulse_read(struct pulse_stream *stream) { size_t bytes = pa_stream_readable_size(stream->stream);
This allows us to write more data to pulse when it notifies us, in addition to the timed writes, which may not write anything if pulse isn't ready.
This greatly reduces the amount of underflows in Prince of Persia: The Forgotten Sands, which started clicking since we moved to PE Faudio.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
The reason is not completely clear, but may be related to the game creating a 6-channel master voice, even if the output is only stereo, perhaps changing pulse buffer requirements and making it harder to catch up missed writes.
dlls/winepulse.drv/pulse.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 590122cc7b1..10260dea106 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -414,12 +414,12 @@ write: return pa_stream_write(stream->stream, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE); }
-static void pulse_write(struct pulse_stream *stream) +static void pulse_write_callback(pa_stream *s, size_t bytes, void *userdata) { /* write as much data to PA as we can */ + struct pulse_stream *stream = userdata; UINT32 to_write; BYTE *buf = stream->local_buffer + stream->pa_offs_bytes; - UINT32 bytes = pa_stream_writable_size(stream->stream);
if (stream->just_underran) { @@ -437,7 +437,7 @@ static void pulse_write(struct pulse_stream *stream) }
buf = stream->local_buffer + stream->pa_offs_bytes; - TRACE("held: %lu, avail: %u\n", stream->pa_held_bytes, bytes); + TRACE("held: %lu, avail: %u\n", stream->pa_held_bytes, (UINT32)bytes); bytes = min(stream->pa_held_bytes, bytes);
if (stream->pa_offs_bytes + bytes > stream->real_bufsize_bytes) @@ -1004,6 +1004,7 @@ static HRESULT pulse_stream_connect(struct pulse_stream *stream, UINT32 period_b if (stream->dataflow == eRender) { pa_stream_set_underflow_callback(stream->stream, pulse_underflow_callback, stream); pa_stream_set_started_callback(stream->stream, pulse_started_callback, stream); + pa_stream_set_write_callback(stream->stream, pulse_write_callback, stream); } return S_OK; } @@ -1316,7 +1317,7 @@ static NTSTATUS pulse_timer_loop(void *args)
if (stream->dataflow == eRender) { - pulse_write(stream); + pulse_write_callback(stream->stream, pa_stream_writable_size(stream->stream), stream);
/* regardless of what PA does, advance one period */ adv_bytes = min(stream->period_bytes, stream->held_bytes); @@ -1380,7 +1381,7 @@ static NTSTATUS pulse_start(void *args) return STATUS_SUCCESS; }
- pulse_write(stream); + pulse_write_callback(stream->stream, pa_stream_writable_size(stream->stream), stream);
if (pa_stream_is_corked(stream->stream)) {
So that it rounds to integral audio frame count.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
Not entirely sure if this is useful, but it's done differently for render or capture allocation. This does it the same way as capture.
dlls/winepulse.drv/pulse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 10260dea106..f5d3a1ddca4 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -1065,7 +1065,7 @@ static NTSTATUS pulse_create_stream(void *args) stream->alloc_size = stream->real_bufsize_bytes = stream->bufsize_frames * 2 * pa_frame_size(&stream->ss); if (NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, - 0, &stream->real_bufsize_bytes, MEM_COMMIT, PAGE_READWRITE)) + 0, &stream->alloc_size, MEM_COMMIT, PAGE_READWRITE)) hr = E_OUTOFMEMORY; } else { UINT32 i, capture_packets;