2004-08-24 04:28:35 +02:00
|
|
|
/*
|
|
|
|
* Direct Sound Audio Renderer
|
|
|
|
*
|
|
|
|
* Copyright 2004 Christian Costa
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2006-05-18 14:49:52 +02:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2004-08-24 04:28:35 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "quartz_private.h"
|
|
|
|
|
|
|
|
#include "uuids.h"
|
|
|
|
#include "vfwmsgs.h"
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "dshow.h"
|
|
|
|
#include "evcode.h"
|
|
|
|
#include "strmif.h"
|
|
|
|
#include "dsound.h"
|
2010-02-07 21:17:59 +01:00
|
|
|
#include "amaudio.h"
|
2004-08-24 04:28:35 +02:00
|
|
|
|
|
|
|
#include "wine/debug.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
|
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
/* NOTE: buffer can still be filled completely,
|
|
|
|
* but we start waiting until only this amount is buffered
|
|
|
|
*/
|
|
|
|
static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000;
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
struct strmbase_filter filter;
|
|
|
|
struct strmbase_passthrough passthrough;
|
2012-04-03 21:28:37 +02:00
|
|
|
IAMDirectSound IAMDirectSound_iface;
|
2020-07-20 17:25:29 +02:00
|
|
|
IBasicAudio IBasicAudio_iface;
|
|
|
|
IQualityControl IQualityControl_iface;
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
IUnknown *system_clock;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
struct strmbase_sink sink;
|
|
|
|
|
|
|
|
/* Signaled when the filter has completed a state change. The filter waits
|
|
|
|
* for this event in IBaseFilter::GetState(). */
|
|
|
|
HANDLE state_event;
|
|
|
|
/* Signaled when a flush or state change occurs, i.e. anything that needs
|
|
|
|
* to immediately unblock the streaming thread. */
|
|
|
|
HANDLE flush_event;
|
|
|
|
REFERENCE_TIME stream_start;
|
|
|
|
BOOL eos;
|
|
|
|
|
2008-06-10 17:00:40 +02:00
|
|
|
IDirectSound8 *dsound;
|
2004-08-24 04:28:35 +02:00
|
|
|
LPDIRECTSOUNDBUFFER dsbuffer;
|
2007-04-01 14:50:44 +02:00
|
|
|
DWORD buf_size;
|
2010-11-09 23:42:51 +01:00
|
|
|
DWORD last_playpos, writepos;
|
2008-04-09 01:48:00 +02:00
|
|
|
|
2010-05-20 01:14:09 +02:00
|
|
|
LONG volume;
|
|
|
|
LONG pan;
|
2020-04-01 06:48:19 +02:00
|
|
|
};
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static struct dsound_render *impl_from_strmbase_pin(struct strmbase_pin *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
return CONTAINING_RECORD(iface, struct dsound_render, sink.pin);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dsound_render *impl_from_strmbase_filter(struct strmbase_filter *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct dsound_render, filter);
|
2012-03-31 03:08:16 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static struct dsound_render *impl_from_IBasicAudio(IBasicAudio *iface)
|
2012-04-03 21:28:37 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
return CONTAINING_RECORD(iface, struct dsound_render, IBasicAudio_iface);
|
2012-04-03 21:28:37 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static struct dsound_render *impl_from_IAMDirectSound(IAMDirectSound *iface)
|
2012-04-03 21:28:37 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
return CONTAINING_RECORD(iface, struct dsound_render, IAMDirectSound_iface);
|
2012-04-03 21:28:37 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static REFERENCE_TIME time_from_pos(struct dsound_render *This, DWORD pos)
|
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
|
2010-11-09 23:42:51 +01:00
|
|
|
REFERENCE_TIME ret = 10000000;
|
|
|
|
ret = ret * pos / wfx->nAvgBytesPerSec;
|
|
|
|
return ret;
|
|
|
|
}
|
2008-04-05 01:27:29 +02:00
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static DWORD pos_from_time(struct dsound_render *This, REFERENCE_TIME time)
|
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
|
2010-11-09 23:42:51 +01:00
|
|
|
REFERENCE_TIME ret = time;
|
2017-06-19 21:15:32 +02:00
|
|
|
ret *= wfx->nAvgBytesPerSec;
|
2010-11-09 23:42:51 +01:00
|
|
|
ret /= 10000000;
|
2017-06-19 21:15:32 +02:00
|
|
|
ret -= ret % wfx->nBlockAlign;
|
2010-11-09 23:42:51 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2008-04-05 01:27:29 +02:00
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static void DSoundRender_UpdatePositions(struct dsound_render *This, DWORD *seqwritepos, DWORD *minwritepos)
|
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
|
2010-11-09 23:42:51 +01:00
|
|
|
BYTE *buf1, *buf2;
|
|
|
|
DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv;
|
|
|
|
BOOL writepos_set = This->writepos < This->buf_size;
|
|
|
|
|
|
|
|
/* Update position and zero */
|
|
|
|
old_writepos = This->writepos;
|
|
|
|
old_playpos = This->last_playpos;
|
|
|
|
if (old_writepos <= old_playpos)
|
|
|
|
old_writepos += This->buf_size;
|
|
|
|
|
|
|
|
IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, &playpos, &writepos);
|
2021-05-13 02:00:32 +02:00
|
|
|
if (old_playpos > playpos)
|
2010-11-09 23:42:51 +01:00
|
|
|
adv = This->buf_size + playpos - old_playpos;
|
2021-05-13 02:00:32 +02:00
|
|
|
else
|
2010-11-09 23:42:51 +01:00
|
|
|
adv = playpos - old_playpos;
|
|
|
|
This->last_playpos = playpos;
|
|
|
|
if (adv) {
|
|
|
|
TRACE("Moving from %u to %u: clearing %u bytes\n", old_playpos, playpos, adv);
|
|
|
|
IDirectSoundBuffer_Lock(This->dsbuffer, old_playpos, adv, (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
|
|
|
|
memset(buf1, wfx->wBitsPerSample == 8 ? 128 : 0, size1);
|
|
|
|
memset(buf2, wfx->wBitsPerSample == 8 ? 128 : 0, size2);
|
|
|
|
IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
|
|
|
|
}
|
|
|
|
*minwritepos = writepos;
|
|
|
|
if (!writepos_set || old_writepos < writepos) {
|
|
|
|
if (writepos_set) {
|
|
|
|
This->writepos = This->buf_size;
|
2010-12-11 14:25:28 +01:00
|
|
|
FIXME("Underrun of data occurred!\n");
|
2008-04-05 01:27:29 +02:00
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
*seqwritepos = writepos;
|
|
|
|
} else
|
|
|
|
*seqwritepos = This->writepos;
|
|
|
|
}
|
2008-04-09 01:48:00 +02:00
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static HRESULT DSoundRender_GetWritePos(struct dsound_render *This,
|
|
|
|
DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
|
2010-11-09 23:42:51 +01:00
|
|
|
{
|
|
|
|
DWORD writepos, min_writepos, playpos;
|
|
|
|
REFERENCE_TIME max_lag = 50 * 10000;
|
|
|
|
REFERENCE_TIME cur, writepos_t, delta_t;
|
|
|
|
|
|
|
|
DSoundRender_UpdatePositions(This, &writepos, &min_writepos);
|
|
|
|
playpos = This->last_playpos;
|
2020-07-20 17:25:29 +02:00
|
|
|
if (This->filter.clock)
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
IReferenceClock_GetTime(This->filter.clock, &cur);
|
|
|
|
cur -= This->stream_start;
|
2010-11-09 23:42:51 +01:00
|
|
|
} else
|
2010-12-01 23:17:29 +01:00
|
|
|
write_at = -1;
|
2010-11-09 23:42:51 +01:00
|
|
|
|
|
|
|
if (writepos == min_writepos)
|
2010-12-01 13:14:58 +01:00
|
|
|
max_lag = 0;
|
2010-11-09 23:42:51 +01:00
|
|
|
|
|
|
|
*skip = 0;
|
2010-12-01 23:17:29 +01:00
|
|
|
if (write_at < 0) {
|
2010-11-09 23:42:51 +01:00
|
|
|
*ret_writepos = writepos;
|
|
|
|
goto end;
|
|
|
|
}
|
2007-04-06 12:48:03 +02:00
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
if (writepos >= playpos)
|
|
|
|
writepos_t = cur + time_from_pos(This, writepos - playpos);
|
|
|
|
else
|
|
|
|
writepos_t = cur + time_from_pos(This, This->buf_size + writepos - playpos);
|
|
|
|
|
|
|
|
/* write_at: Starting time of sample */
|
|
|
|
/* cur: current time of play position */
|
|
|
|
/* writepos_t: current time of our pointer play position */
|
|
|
|
delta_t = write_at - writepos_t;
|
|
|
|
if (delta_t >= -max_lag && delta_t <= max_lag) {
|
|
|
|
TRACE("Continuing from old position\n");
|
|
|
|
*ret_writepos = writepos;
|
|
|
|
} else if (delta_t < 0) {
|
|
|
|
REFERENCE_TIME past, min_writepos_t;
|
2020-01-31 02:05:17 +01:00
|
|
|
WARN("Delta too big %s/%s, overwriting old data or even skipping\n", debugstr_time(delta_t), debugstr_time(max_lag));
|
2010-11-09 23:42:51 +01:00
|
|
|
if (min_writepos >= playpos)
|
|
|
|
min_writepos_t = cur + time_from_pos(This, min_writepos - playpos);
|
|
|
|
else
|
|
|
|
min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos);
|
|
|
|
past = min_writepos_t - write_at;
|
|
|
|
if (past >= 0) {
|
|
|
|
DWORD skipbytes = pos_from_time(This, past);
|
2010-11-29 10:44:14 +01:00
|
|
|
WARN("Skipping %u bytes\n", skipbytes);
|
2010-11-09 23:42:51 +01:00
|
|
|
*skip = skipbytes;
|
|
|
|
*ret_writepos = min_writepos;
|
|
|
|
} else {
|
|
|
|
DWORD aheadbytes = pos_from_time(This, -past);
|
2010-11-29 10:44:14 +01:00
|
|
|
WARN("Advancing %u bytes\n", aheadbytes);
|
2010-11-09 23:42:51 +01:00
|
|
|
*ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
|
2007-04-06 12:48:03 +02:00
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
} else /* delta_t > 0 */ {
|
|
|
|
DWORD aheadbytes;
|
2020-01-31 02:05:17 +01:00
|
|
|
WARN("Delta too big %s/%s, too far ahead\n", debugstr_time(delta_t), debugstr_time(max_lag));
|
2010-11-09 23:42:51 +01:00
|
|
|
aheadbytes = pos_from_time(This, delta_t);
|
2010-11-29 10:44:14 +01:00
|
|
|
WARN("Advancing %u bytes\n", aheadbytes);
|
2010-11-09 23:42:51 +01:00
|
|
|
if (delta_t >= DSoundRenderer_Max_Fill)
|
|
|
|
return S_FALSE;
|
|
|
|
*ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
|
2007-04-06 12:48:03 +02:00
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
end:
|
2021-04-26 04:51:23 +02:00
|
|
|
if (playpos >= *ret_writepos)
|
2010-11-09 23:42:51 +01:00
|
|
|
*pfree = playpos - *ret_writepos;
|
|
|
|
else
|
|
|
|
*pfree = This->buf_size + playpos - *ret_writepos;
|
|
|
|
if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) {
|
2020-01-31 02:05:17 +01:00
|
|
|
TRACE("Blocked: too full %s / %s\n", debugstr_time(time_from_pos(This, This->buf_size - *pfree)),
|
|
|
|
debugstr_time(DSoundRenderer_Max_Fill));
|
2010-11-09 23:42:51 +01:00
|
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
return S_OK;
|
2007-04-06 12:48:03 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static HRESULT DSoundRender_HandleEndOfStream(struct dsound_render *This)
|
2010-12-10 16:06:57 +01:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
while (This->filter.state == State_Running)
|
2010-12-10 16:06:57 +01:00
|
|
|
{
|
|
|
|
DWORD pos1, pos2;
|
|
|
|
DSoundRender_UpdatePositions(This, &pos1, &pos2);
|
|
|
|
if (pos1 == pos2)
|
|
|
|
break;
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
WaitForSingleObject(This->flush_event, 10);
|
2010-12-10 16:06:57 +01:00
|
|
|
}
|
|
|
|
|
2012-05-11 20:20:18 +02:00
|
|
|
return S_OK;
|
2010-12-10 16:06:57 +01:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static HRESULT DSoundRender_SendSampleData(struct dsound_render *This,
|
|
|
|
REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2010-11-09 23:42:51 +01:00
|
|
|
HRESULT hr;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
while (size && This->filter.state != State_Stopped) {
|
2010-11-09 23:42:51 +01:00
|
|
|
DWORD writepos, skip = 0, free, size1, size2, ret;
|
|
|
|
BYTE *buf1, *buf2;
|
2008-04-05 01:27:29 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
if (This->filter.state == State_Running)
|
2010-12-01 23:17:28 +01:00
|
|
|
hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
|
|
|
|
else
|
|
|
|
hr = S_FALSE;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
if (hr != S_OK) {
|
2020-07-20 17:25:29 +02:00
|
|
|
ret = WaitForSingleObject(This->flush_event, 10);
|
|
|
|
if (This->sink.flushing || This->filter.state == State_Stopped)
|
|
|
|
return This->filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
|
2010-11-09 23:42:51 +01:00
|
|
|
if (ret != WAIT_TIMEOUT)
|
|
|
|
ERR("%x\n", ret);
|
2006-08-18 18:38:38 +02:00
|
|
|
continue;
|
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
tStart = -1;
|
|
|
|
|
|
|
|
if (skip)
|
|
|
|
FIXME("Sample dropped %u of %u bytes\n", skip, size);
|
|
|
|
if (skip >= size)
|
2010-12-01 23:17:28 +01:00
|
|
|
return S_OK;
|
2010-11-09 23:42:51 +01:00
|
|
|
data += skip;
|
|
|
|
size -= skip;
|
2006-08-18 18:38:38 +02:00
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
|
2005-04-25 12:49:22 +02:00
|
|
|
if (hr != DS_OK) {
|
2006-10-12 20:57:23 +02:00
|
|
|
ERR("Unable to lock sound buffer! (%x)\n", hr);
|
2005-04-25 12:49:22 +02:00
|
|
|
break;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
memcpy(buf1, data, size1);
|
|
|
|
if (size2)
|
|
|
|
memcpy(buf2, data+size1, size2);
|
|
|
|
IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
|
|
|
|
This->writepos = (writepos + size1 + size2) % This->buf_size;
|
|
|
|
TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1+size2, writepos, This->writepos, free, size);
|
|
|
|
data += size1 + size2;
|
|
|
|
size -= size1 + size2;
|
|
|
|
}
|
|
|
|
return S_OK;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_PrepareReceive(struct dsound_render *This, IMediaSample *pSample)
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
|
|
|
HRESULT hr;
|
2008-10-21 12:50:36 +02:00
|
|
|
AM_MEDIA_TYPE *amt;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2008-10-21 12:50:36 +02:00
|
|
|
if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
|
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
AM_MEDIA_TYPE *orig = &This->sink.pin.mt;
|
2008-10-21 12:50:36 +02:00
|
|
|
WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
|
|
|
|
WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
TRACE("Format change.\n");
|
|
|
|
strmbase_dump_media_type(amt);
|
|
|
|
|
2008-10-21 12:50:36 +02:00
|
|
|
if (origfmt->wFormatTag == newfmt->wFormatTag &&
|
|
|
|
origfmt->nChannels == newfmt->nChannels &&
|
|
|
|
origfmt->nBlockAlign == newfmt->nBlockAlign &&
|
|
|
|
origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
|
|
|
|
origfmt->cbSize == newfmt->cbSize)
|
|
|
|
{
|
|
|
|
if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
|
|
|
|
{
|
|
|
|
hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
|
|
|
|
newfmt->nSamplesPerSec);
|
|
|
|
if (FAILED(hr))
|
|
|
|
return VFW_E_TYPE_NOT_ACCEPTED;
|
|
|
|
FreeMediaType(orig);
|
|
|
|
CopyMediaType(orig, amt);
|
|
|
|
IMediaSample_SetMediaType(pSample, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return VFW_E_TYPE_NOT_ACCEPTED;
|
|
|
|
}
|
2012-03-31 03:08:16 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_DoRenderSample(struct dsound_render *This, IMediaSample *pSample)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
|
|
|
LPBYTE pbSrcStream = NULL;
|
|
|
|
LONG cbSrcStream = 0;
|
|
|
|
REFERENCE_TIME tStart, tStop;
|
|
|
|
HRESULT hr;
|
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
2006-10-12 20:57:23 +02:00
|
|
|
ERR("Cannot get pointer to sample data (%x)\n", hr);
|
2008-04-16 23:27:06 +02:00
|
|
|
return hr;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
|
2010-11-29 10:44:13 +01:00
|
|
|
if (FAILED(hr)) {
|
2006-10-12 20:57:23 +02:00
|
|
|
ERR("Cannot get sample time (%x)\n", hr);
|
2010-11-29 10:44:13 +01:00
|
|
|
tStart = tStop = -1;
|
|
|
|
}
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2008-04-19 07:04:55 +02:00
|
|
|
if (IMediaSample_IsPreroll(pSample) == S_OK)
|
|
|
|
{
|
|
|
|
TRACE("Preroll!\n");
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
cbSrcStream = IMediaSample_GetActualDataLength(pSample);
|
2010-05-20 01:14:09 +02:00
|
|
|
TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2020-07-17 21:51:21 +02:00
|
|
|
return DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IMediaSample *sample)
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
|
|
|
|
REFERENCE_TIME start, stop;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (filter->eos || filter->sink.flushing)
|
2008-06-10 17:00:40 +02:00
|
|
|
return S_FALSE;
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
if (filter->filter.state == State_Stopped)
|
|
|
|
return VFW_E_WRONG_STATE;
|
|
|
|
|
|
|
|
if (FAILED(hr = DSoundRender_PrepareReceive(filter, sample)))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
if (filter->filter.clock && SUCCEEDED(IMediaSample_GetTime(sample, &start, &stop)))
|
|
|
|
strmbase_passthrough_update_time(&filter->passthrough, start);
|
|
|
|
|
|
|
|
if (filter->filter.state == State_Paused)
|
|
|
|
SetEvent(filter->state_event);
|
|
|
|
|
2021-01-20 04:35:34 +01:00
|
|
|
return DSoundRender_DoRenderSample(filter, sample);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT dsound_render_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
|
2010-10-07 21:48:01 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
struct dsound_render *filter = impl_from_strmbase_pin(iface);
|
2010-10-07 21:48:01 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
if (IsEqualGUID(iid, &IID_IMemInputPin))
|
|
|
|
*out = &filter->sink.IMemInputPin_iface;
|
|
|
|
else
|
|
|
|
return E_NOINTERFACE;
|
2010-10-07 21:48:01 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
2010-10-07 21:48:01 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT dsound_render_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE * pmt)
|
2010-10-07 21:48:01 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
|
|
|
|
return S_FALSE;
|
2012-03-31 03:08:16 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
return S_OK;
|
2010-10-07 21:48:01 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT dsound_render_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_pin(&iface->pin);
|
2019-12-10 17:35:12 +01:00
|
|
|
const WAVEFORMATEX *format = (WAVEFORMATEX *)mt->pbFormat;
|
2012-03-31 03:08:16 +02:00
|
|
|
HRESULT hr = S_OK;
|
|
|
|
DSBUFFERDESC buf_desc;
|
|
|
|
|
|
|
|
This->buf_size = format->nAvgBytesPerSec;
|
2010-10-13 18:02:01 +02:00
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
memset(&buf_desc,0,sizeof(DSBUFFERDESC));
|
|
|
|
buf_desc.dwSize = sizeof(DSBUFFERDESC);
|
|
|
|
buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
|
|
|
|
DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS |
|
|
|
|
DSBCAPS_GETCURRENTPOSITION2;
|
|
|
|
buf_desc.dwBufferBytes = This->buf_size;
|
2019-12-10 17:35:12 +01:00
|
|
|
buf_desc.lpwfxFormat = (WAVEFORMATEX *)format;
|
2018-03-30 06:04:46 +02:00
|
|
|
hr = IDirectSound8_CreateSoundBuffer(This->dsound, &buf_desc, &This->dsbuffer, NULL);
|
2012-03-31 03:08:16 +02:00
|
|
|
This->writepos = This->buf_size;
|
|
|
|
if (FAILED(hr))
|
|
|
|
ERR("Can't create sound buffer (%x)\n", hr);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
hr = IDirectSoundBuffer_SetVolume(This->dsbuffer, This->volume);
|
|
|
|
if (FAILED(hr))
|
|
|
|
ERR("Can't set volume to %d (%x)\n", This->volume, hr);
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_SetPan(This->dsbuffer, This->pan);
|
|
|
|
if (FAILED(hr))
|
|
|
|
ERR("Can't set pan to %d (%x)\n", This->pan, hr);
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED)
|
|
|
|
{
|
|
|
|
if (This->dsbuffer)
|
|
|
|
IDirectSoundBuffer_Release(This->dsbuffer);
|
|
|
|
This->dsbuffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static void dsound_render_sink_disconnect(struct strmbase_sink *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_pin(&iface->pin);
|
2012-03-31 03:08:16 +02:00
|
|
|
|
|
|
|
TRACE("(%p)->()\n", iface);
|
|
|
|
|
|
|
|
if (This->dsbuffer)
|
|
|
|
IDirectSoundBuffer_Release(This->dsbuffer);
|
|
|
|
This->dsbuffer = NULL;
|
2020-07-20 17:25:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT dsound_render_sink_eos(struct strmbase_sink *iface)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
|
|
|
|
IFilterGraph *graph = filter->filter.graph;
|
|
|
|
IMediaEventSink *event_sink;
|
2020-07-24 02:29:23 +02:00
|
|
|
void *buffer;
|
|
|
|
DWORD size;
|
2020-07-20 17:25:29 +02:00
|
|
|
|
|
|
|
filter->eos = TRUE;
|
|
|
|
|
2021-05-26 18:46:45 +02:00
|
|
|
if (filter->filter.state == State_Running && graph
|
|
|
|
&& SUCCEEDED(IFilterGraph_QueryInterface(graph,
|
2020-07-20 17:25:29 +02:00
|
|
|
&IID_IMediaEventSink, (void **)&event_sink)))
|
|
|
|
{
|
|
|
|
IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK,
|
|
|
|
(LONG_PTR)&filter->filter.IBaseFilter_iface);
|
|
|
|
IMediaEventSink_Release(event_sink);
|
|
|
|
}
|
|
|
|
strmbase_passthrough_eos(&filter->passthrough);
|
|
|
|
SetEvent(filter->state_event);
|
|
|
|
|
|
|
|
DSoundRender_HandleEndOfStream(filter);
|
|
|
|
|
2020-07-24 02:29:23 +02:00
|
|
|
IDirectSoundBuffer_Lock(filter->dsbuffer, 0, 0, &buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
|
|
|
|
memset(buffer, 0, size);
|
|
|
|
IDirectSoundBuffer_Unlock(filter->dsbuffer, buffer, size, NULL, 0);
|
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT dsound_render_sink_begin_flush(struct strmbase_sink *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
|
|
|
|
|
|
|
|
SetEvent(filter->flush_event);
|
|
|
|
return S_OK;
|
2012-03-31 03:08:16 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT dsound_render_sink_end_flush(struct strmbase_sink *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
|
2012-03-31 03:08:16 +02:00
|
|
|
|
2021-01-19 04:57:21 +01:00
|
|
|
EnterCriticalSection(&filter->filter.stream_cs);
|
2020-07-20 17:25:29 +02:00
|
|
|
|
|
|
|
filter->eos = FALSE;
|
|
|
|
strmbase_passthrough_invalidate_time(&filter->passthrough);
|
|
|
|
ResetEvent(filter->flush_event);
|
|
|
|
|
|
|
|
if (filter->dsbuffer)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
void *buffer;
|
2012-03-31 03:08:16 +02:00
|
|
|
DWORD size;
|
|
|
|
|
|
|
|
/* Force a reset */
|
2020-07-20 17:25:29 +02:00
|
|
|
IDirectSoundBuffer_Lock(filter->dsbuffer, 0, 0, &buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
|
2012-03-31 03:08:16 +02:00
|
|
|
memset(buffer, 0, size);
|
2020-07-20 17:25:29 +02:00
|
|
|
IDirectSoundBuffer_Unlock(filter->dsbuffer, buffer, size, NULL, 0);
|
|
|
|
filter->writepos = filter->buf_size;
|
2012-03-31 03:08:16 +02:00
|
|
|
}
|
|
|
|
|
2021-01-19 04:57:21 +01:00
|
|
|
LeaveCriticalSection(&filter->filter.stream_cs);
|
2012-03-31 03:08:16 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static const struct strmbase_sink_ops sink_ops =
|
2019-05-28 06:09:56 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
.base.pin_query_interface = dsound_render_sink_query_interface,
|
|
|
|
.base.pin_query_accept = dsound_render_sink_query_accept,
|
|
|
|
.pfnReceive = dsound_render_sink_Receive,
|
|
|
|
.sink_connect = dsound_render_sink_connect,
|
|
|
|
.sink_disconnect = dsound_render_sink_disconnect,
|
|
|
|
.sink_eos = dsound_render_sink_eos,
|
|
|
|
.sink_begin_flush = dsound_render_sink_begin_flush,
|
|
|
|
.sink_end_flush = dsound_render_sink_end_flush,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void dsound_render_destroy(struct strmbase_filter *iface)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_strmbase_filter(iface);
|
2019-05-28 06:09:56 +02:00
|
|
|
|
|
|
|
if (filter->dsbuffer)
|
|
|
|
IDirectSoundBuffer_Release(filter->dsbuffer);
|
|
|
|
filter->dsbuffer = NULL;
|
|
|
|
if (filter->dsound)
|
|
|
|
IDirectSound8_Release(filter->dsound);
|
|
|
|
filter->dsound = NULL;
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
if (filter->sink.pin.peer)
|
|
|
|
IPin_Disconnect(filter->sink.pin.peer);
|
|
|
|
IPin_Disconnect(&filter->sink.pin.IPin_iface);
|
|
|
|
strmbase_sink_cleanup(&filter->sink);
|
|
|
|
|
|
|
|
CloseHandle(filter->state_event);
|
|
|
|
CloseHandle(filter->flush_event);
|
|
|
|
|
|
|
|
strmbase_passthrough_cleanup(&filter->passthrough);
|
|
|
|
strmbase_filter_cleanup(&filter->filter);
|
2020-04-01 06:48:18 +02:00
|
|
|
free(filter);
|
2020-03-13 03:34:12 +01:00
|
|
|
|
|
|
|
InterlockedDecrement(&object_locks);
|
2019-05-28 06:09:56 +02:00
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static struct strmbase_pin *dsound_render_get_pin(struct strmbase_filter *iface, unsigned int index)
|
2019-05-31 05:59:37 +02:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
struct dsound_render *filter = impl_from_strmbase_filter(iface);
|
2019-05-31 05:59:37 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
if (index == 0)
|
|
|
|
return &filter->sink.pin;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT dsound_render_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_strmbase_filter(iface);
|
|
|
|
|
|
|
|
if (IsEqualGUID(iid, &IID_IAMDirectSound))
|
|
|
|
*out = &filter->IAMDirectSound_iface;
|
|
|
|
else if (IsEqualGUID(iid, &IID_IBasicAudio))
|
2019-06-07 01:49:24 +02:00
|
|
|
*out = &filter->IBasicAudio_iface;
|
2020-07-20 17:25:29 +02:00
|
|
|
else if (IsEqualGUID(iid, &IID_IMediaPosition))
|
|
|
|
*out = &filter->passthrough.IMediaPosition_iface;
|
|
|
|
else if (IsEqualGUID(iid, &IID_IMediaSeeking))
|
|
|
|
*out = &filter->passthrough.IMediaSeeking_iface;
|
|
|
|
else if (IsEqualGUID(iid, &IID_IQualityControl))
|
|
|
|
*out = &filter->IQualityControl_iface;
|
2019-05-31 05:59:37 +02:00
|
|
|
else if (IsEqualGUID(iid, &IID_IReferenceClock))
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
return IUnknown_QueryInterface(filter->system_clock, iid, out);
|
2019-05-31 05:59:37 +02:00
|
|
|
else
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static HRESULT dsound_render_init_stream(struct strmbase_filter *iface)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_strmbase_filter(iface);
|
|
|
|
|
|
|
|
if (filter->sink.pin.peer)
|
|
|
|
ResetEvent(filter->state_event);
|
|
|
|
filter->eos = FALSE;
|
|
|
|
ResetEvent(filter->flush_event);
|
|
|
|
|
|
|
|
return filter->sink.pin.peer ? S_FALSE : S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT dsound_render_start_stream(struct strmbase_filter *iface, REFERENCE_TIME start)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_strmbase_filter(iface);
|
2021-05-26 18:46:45 +02:00
|
|
|
IFilterGraph *graph = filter->filter.graph;
|
|
|
|
IMediaEventSink *event_sink;
|
2020-07-20 17:25:29 +02:00
|
|
|
|
|
|
|
filter->stream_start = start;
|
|
|
|
|
|
|
|
SetEvent(filter->state_event);
|
|
|
|
|
|
|
|
if (filter->sink.pin.peer)
|
|
|
|
IDirectSoundBuffer_Play(filter->dsbuffer, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
|
2021-05-26 18:46:45 +02:00
|
|
|
if (filter->eos && graph
|
|
|
|
&& SUCCEEDED(IFilterGraph_QueryInterface(graph,
|
|
|
|
&IID_IMediaEventSink, (void **)&event_sink)))
|
|
|
|
{
|
|
|
|
IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK,
|
|
|
|
(LONG_PTR)&filter->filter.IBaseFilter_iface);
|
|
|
|
IMediaEventSink_Release(event_sink);
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT dsound_render_stop_stream(struct strmbase_filter *iface)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_strmbase_filter(iface);
|
|
|
|
|
|
|
|
if (filter->sink.pin.peer)
|
|
|
|
{
|
|
|
|
IDirectSoundBuffer_Stop(filter->dsbuffer);
|
|
|
|
filter->writepos = filter->buf_size;
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT dsound_render_cleanup_stream(struct strmbase_filter *iface)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_strmbase_filter(iface);
|
|
|
|
|
|
|
|
strmbase_passthrough_invalidate_time(&filter->passthrough);
|
|
|
|
SetEvent(filter->state_event);
|
|
|
|
SetEvent(filter->flush_event);
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT dsound_render_wait_state(struct strmbase_filter *iface, DWORD timeout)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_strmbase_filter(iface);
|
|
|
|
|
|
|
|
if (WaitForSingleObject(filter->state_event, timeout) == WAIT_TIMEOUT)
|
|
|
|
return VFW_S_STATE_INTERMEDIATE;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct strmbase_filter_ops filter_ops =
|
|
|
|
{
|
|
|
|
.filter_destroy = dsound_render_destroy,
|
|
|
|
.filter_get_pin = dsound_render_get_pin,
|
|
|
|
.filter_query_interface = dsound_render_query_interface,
|
|
|
|
.filter_init_stream = dsound_render_init_stream,
|
|
|
|
.filter_start_stream = dsound_render_start_stream,
|
|
|
|
.filter_stop_stream = dsound_render_stop_stream,
|
|
|
|
.filter_cleanup_stream = dsound_render_cleanup_stream,
|
|
|
|
.filter_wait_state = dsound_render_wait_state,
|
2010-10-13 18:02:01 +02:00
|
|
|
};
|
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
/*** IUnknown methods ***/
|
|
|
|
static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
|
|
|
|
REFIID riid,
|
|
|
|
LPVOID*ppvObj) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2016-08-10 13:03:39 +02:00
|
|
|
TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
return IUnknown_QueryInterface(This->filter.outer_unk, riid, ppvObj);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
return IUnknown_AddRef(This->filter.outer_unk);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
return IUnknown_Release(This->filter.outer_unk);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 01:49:24 +02:00
|
|
|
HRESULT WINAPI basic_audio_GetTypeInfoCount(IBasicAudio *iface, UINT *count)
|
|
|
|
{
|
|
|
|
TRACE("iface %p, count %p.\n", iface, count);
|
|
|
|
*count = 1;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI basic_audio_GetTypeInfo(IBasicAudio *iface, UINT index,
|
|
|
|
LCID lcid, ITypeInfo **typeinfo)
|
|
|
|
{
|
|
|
|
TRACE("iface %p, index %u, lcid %#x, typeinfo %p.\n", iface, index, lcid, typeinfo);
|
|
|
|
return strmbase_get_typeinfo(IBasicAudio_tid, typeinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI basic_audio_GetIDsOfNames(IBasicAudio *iface, REFIID iid,
|
|
|
|
LPOLESTR *names, UINT count, LCID lcid, DISPID *ids)
|
|
|
|
{
|
|
|
|
ITypeInfo *typeinfo;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("iface %p, iid %s, names %p, count %u, lcid %#x, ids %p.\n",
|
|
|
|
iface, debugstr_guid(iid), names, count, lcid, ids);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr = strmbase_get_typeinfo(IBasicAudio_tid, &typeinfo)))
|
|
|
|
{
|
|
|
|
hr = ITypeInfo_GetIDsOfNames(typeinfo, names, count, ids);
|
|
|
|
ITypeInfo_Release(typeinfo);
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI basic_audio_Invoke(IBasicAudio *iface, DISPID id, REFIID iid, LCID lcid,
|
|
|
|
WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *error_arg)
|
|
|
|
{
|
|
|
|
ITypeInfo *typeinfo;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("iface %p, id %d, iid %s, lcid %#x, flags %#x, params %p, result %p, excepinfo %p, error_arg %p.\n",
|
|
|
|
iface, id, debugstr_guid(iid), lcid, flags, params, result, excepinfo, error_arg);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr = strmbase_get_typeinfo(IBasicAudio_tid, &typeinfo)))
|
|
|
|
{
|
|
|
|
hr = ITypeInfo_Invoke(typeinfo, iface, id, flags, params, result, excepinfo, error_arg);
|
|
|
|
ITypeInfo_Release(typeinfo);
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
|
2009-03-11 00:31:26 +01:00
|
|
|
LONG lVolume) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2009-03-11 00:31:26 +01:00
|
|
|
TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
|
2007-03-26 05:13:53 +02:00
|
|
|
|
|
|
|
if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
if (This->dsbuffer) {
|
|
|
|
if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
This->volume = lVolume;
|
2004-08-24 04:28:35 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
|
2009-03-11 00:31:26 +01:00
|
|
|
LONG *plVolume) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
if (!plVolume)
|
|
|
|
return E_POINTER;
|
|
|
|
|
|
|
|
*plVolume = This->volume;
|
2004-08-24 04:28:35 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
|
2009-03-11 00:31:26 +01:00
|
|
|
LONG lBalance) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2009-03-11 00:31:26 +01:00
|
|
|
TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
|
2007-03-26 05:13:53 +02:00
|
|
|
|
|
|
|
if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
if (This->dsbuffer) {
|
|
|
|
if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
This->pan = lBalance;
|
2004-08-24 04:28:35 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
|
2009-03-11 00:31:26 +01:00
|
|
|
LONG *plBalance) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
|
|
|
|
|
|
|
|
if (!plBalance)
|
|
|
|
return E_POINTER;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
*plBalance = This->pan;
|
2004-08-24 04:28:35 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IBasicAudioVtbl IBasicAudio_Vtbl =
|
|
|
|
{
|
|
|
|
Basicaudio_QueryInterface,
|
|
|
|
Basicaudio_AddRef,
|
|
|
|
Basicaudio_Release,
|
2019-06-07 01:49:24 +02:00
|
|
|
basic_audio_GetTypeInfoCount,
|
|
|
|
basic_audio_GetTypeInfo,
|
|
|
|
basic_audio_GetIDsOfNames,
|
|
|
|
basic_audio_Invoke,
|
2004-08-24 04:28:35 +02:00
|
|
|
Basicaudio_put_Volume,
|
|
|
|
Basicaudio_get_Volume,
|
|
|
|
Basicaudio_put_Balance,
|
|
|
|
Basicaudio_get_Balance
|
|
|
|
};
|
2007-04-06 12:48:03 +02:00
|
|
|
|
2010-02-07 21:17:59 +01:00
|
|
|
/*** IUnknown methods ***/
|
|
|
|
static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
|
|
|
|
REFIID riid,
|
|
|
|
LPVOID*ppvObj)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
2016-08-10 13:03:39 +02:00
|
|
|
TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
return IUnknown_QueryInterface(This->filter.outer_unk, riid, ppvObj);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
return IUnknown_AddRef(This->filter.outer_unk);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
return IUnknown_Release(This->filter.outer_unk);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*** IAMDirectSound methods ***/
|
|
|
|
static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2013-07-25 20:26:49 +02:00
|
|
|
static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgaudible)
|
2010-02-07 21:17:59 +01:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
2013-07-25 20:26:49 +02:00
|
|
|
FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgaudible);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2013-07-25 20:26:49 +02:00
|
|
|
static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND *hwnd, BOOL *bgaudible)
|
2010-02-07 21:17:59 +01:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
2013-07-25 20:26:49 +02:00
|
|
|
FIXME("(%p/%p)->(%p,%p): stub\n", This, iface, hwnd, bgaudible);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
|
|
|
|
{
|
|
|
|
AMDirectSound_QueryInterface,
|
|
|
|
AMDirectSound_AddRef,
|
|
|
|
AMDirectSound_Release,
|
|
|
|
AMDirectSound_GetDirectSoundInterface,
|
|
|
|
AMDirectSound_GetPrimaryBufferInterface,
|
|
|
|
AMDirectSound_GetSecondaryBufferInterface,
|
|
|
|
AMDirectSound_ReleaseDirectSoundInterface,
|
|
|
|
AMDirectSound_ReleasePrimaryBufferInterface,
|
|
|
|
AMDirectSound_ReleaseSecondaryBufferInterface,
|
|
|
|
AMDirectSound_SetFocusWindow,
|
|
|
|
AMDirectSound_GetFocusWindow
|
|
|
|
};
|
2019-11-29 00:35:39 +01:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
static struct dsound_render *impl_from_IQualityControl(IQualityControl *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, struct dsound_render, IQualityControl_iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI dsound_render_qc_QueryInterface(IQualityControl *iface,
|
|
|
|
REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_IQualityControl(iface);
|
|
|
|
return IUnknown_QueryInterface(filter->filter.outer_unk, iid, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI dsound_render_qc_AddRef(IQualityControl *iface)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_IQualityControl(iface);
|
|
|
|
return IUnknown_AddRef(filter->filter.outer_unk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI dsound_render_qc_Release(IQualityControl *iface)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_IQualityControl(iface);
|
|
|
|
return IUnknown_AddRef(filter->filter.outer_unk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI dsound_render_qc_Notify(IQualityControl *iface,
|
|
|
|
IBaseFilter *sender, Quality q)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_IQualityControl(iface);
|
|
|
|
|
|
|
|
FIXME("filter %p, sender %p, type %#x, proportion %u, late %s, timestamp %s, stub!\n",
|
|
|
|
filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp));
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI dsound_render_qc_SetSink(IQualityControl *iface, IQualityControl *sink)
|
|
|
|
{
|
|
|
|
struct dsound_render *filter = impl_from_IQualityControl(iface);
|
|
|
|
|
|
|
|
FIXME("filter %p, sink %p, stub!\n", filter, sink);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IQualityControlVtbl dsound_render_qc_vtbl =
|
|
|
|
{
|
|
|
|
dsound_render_qc_QueryInterface,
|
|
|
|
dsound_render_qc_AddRef,
|
|
|
|
dsound_render_qc_Release,
|
|
|
|
dsound_render_qc_Notify,
|
|
|
|
dsound_render_qc_SetSink,
|
|
|
|
};
|
|
|
|
|
2020-03-12 03:10:10 +01:00
|
|
|
HRESULT dsound_render_create(IUnknown *outer, IUnknown **out)
|
2019-11-29 00:35:39 +01:00
|
|
|
{
|
|
|
|
static const DSBUFFERDESC buffer_desc = {
|
|
|
|
.dwSize = sizeof(DSBUFFERDESC),
|
|
|
|
.dwFlags = DSBCAPS_PRIMARYBUFFER,
|
|
|
|
};
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *object;
|
2019-11-29 00:35:39 +01:00
|
|
|
IDirectSoundBuffer *buffer;
|
|
|
|
HRESULT hr;
|
|
|
|
|
2020-04-01 06:48:18 +02:00
|
|
|
if (!(object = calloc(1, sizeof(*object))))
|
2019-11-29 00:35:39 +01:00
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
strmbase_filter_init(&object->filter, outer, &CLSID_DSoundRender, &filter_ops);
|
2019-11-29 00:35:39 +01:00
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
if (FAILED(hr = system_clock_create(&object->filter.IUnknown_inner, &object->system_clock)))
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
{
|
2020-07-20 17:25:29 +02:00
|
|
|
strmbase_filter_cleanup(&object->filter);
|
2020-04-01 06:48:18 +02:00
|
|
|
free(object);
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2019-11-29 00:35:39 +01:00
|
|
|
if (FAILED(hr = DirectSoundCreate8(NULL, &object->dsound, NULL)))
|
|
|
|
{
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
IUnknown_Release(object->system_clock);
|
2020-07-20 17:25:29 +02:00
|
|
|
strmbase_filter_cleanup(&object->filter);
|
2020-04-01 06:48:18 +02:00
|
|
|
free(object);
|
2019-11-29 00:35:39 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FAILED(hr = IDirectSound8_SetCooperativeLevel(object->dsound,
|
|
|
|
GetDesktopWindow(), DSSCL_PRIORITY)))
|
|
|
|
{
|
|
|
|
IDirectSound8_Release(object->dsound);
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
IUnknown_Release(object->system_clock);
|
2020-07-20 17:25:29 +02:00
|
|
|
strmbase_filter_cleanup(&object->filter);
|
2020-04-01 06:48:18 +02:00
|
|
|
free(object);
|
2019-11-29 00:35:39 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr = IDirectSound8_CreateSoundBuffer(object->dsound,
|
|
|
|
&buffer_desc, &buffer, NULL)))
|
|
|
|
{
|
|
|
|
IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
IDirectSoundBuffer_Release(buffer);
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:25:29 +02:00
|
|
|
strmbase_passthrough_init(&object->passthrough, (IUnknown *)&object->filter.IBaseFilter_iface);
|
|
|
|
ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, TRUE, &object->sink.pin.IPin_iface);
|
|
|
|
|
|
|
|
strmbase_sink_init(&object->sink, &object->filter, L"Audio Input pin (rendered)", &sink_ops, NULL);
|
|
|
|
|
|
|
|
object->state_event = CreateEventW(NULL, TRUE, TRUE, NULL);
|
|
|
|
object->flush_event = CreateEventW(NULL, TRUE, TRUE, NULL);
|
|
|
|
|
|
|
|
object->IBasicAudio_iface.lpVtbl = &IBasicAudio_Vtbl;
|
|
|
|
object->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
|
|
|
|
object->IQualityControl_iface.lpVtbl = &dsound_render_qc_vtbl;
|
|
|
|
|
2019-11-29 00:35:39 +01:00
|
|
|
TRACE("Created DirectSound renderer %p.\n", object);
|
2020-07-20 17:25:29 +02:00
|
|
|
*out = &object->filter.IUnknown_inner;
|
2019-11-29 00:35:39 +01:00
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|