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;
|
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
typedef struct DSoundRenderImpl
|
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
struct strmbase_renderer renderer;
|
2010-10-07 21:47:33 +02:00
|
|
|
|
2019-06-07 01:49:24 +02:00
|
|
|
IBasicAudio IBasicAudio_iface;
|
2012-04-03 21:28:37 +02:00
|
|
|
IAMDirectSound IAMDirectSound_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
|
|
|
|
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-05-30 19:17:26 +02:00
|
|
|
DWORD in_loop;
|
2010-11-09 23:42:51 +01:00
|
|
|
DWORD last_playpos, writepos;
|
2008-04-09 01:48:00 +02:00
|
|
|
|
2007-04-06 12:48:03 +02:00
|
|
|
REFERENCE_TIME play_time;
|
|
|
|
|
2010-05-20 01:14:09 +02:00
|
|
|
LONG volume;
|
|
|
|
LONG pan;
|
2004-08-24 04:28:35 +02:00
|
|
|
} DSoundRenderImpl;
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static inline DSoundRenderImpl *impl_from_strmbase_renderer(struct strmbase_renderer *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, DSoundRenderImpl, renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline DSoundRenderImpl *impl_from_IBaseFilter(IBaseFilter *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, DSoundRenderImpl, renderer.filter.IBaseFilter_iface);
|
|
|
|
}
|
|
|
|
|
2012-04-03 21:28:37 +02:00
|
|
|
static inline DSoundRenderImpl *impl_from_IBasicAudio(IBasicAudio *iface)
|
|
|
|
{
|
2019-06-07 01:49:24 +02:00
|
|
|
return CONTAINING_RECORD(iface, DSoundRenderImpl, IBasicAudio_iface);
|
2012-04-03 21:28:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline DSoundRenderImpl *impl_from_IAMDirectSound(IAMDirectSound *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, DSoundRenderImpl, IAMDirectSound_iface);
|
|
|
|
}
|
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
static REFERENCE_TIME time_from_pos(DSoundRenderImpl *This, DWORD pos) {
|
2019-11-19 01:51:20 +01:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.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
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
static DWORD pos_from_time(DSoundRenderImpl *This, REFERENCE_TIME time) {
|
2019-11-19 01:51:20 +01:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.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
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
static void DSoundRender_UpdatePositions(DSoundRenderImpl *This, DWORD *seqwritepos, DWORD *minwritepos) {
|
2019-11-19 01:51:20 +01:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.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);
|
|
|
|
if (old_playpos > playpos) {
|
|
|
|
adv = This->buf_size + playpos - old_playpos;
|
|
|
|
This->play_time += time_from_pos(This, This->buf_size);
|
|
|
|
} else
|
|
|
|
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
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
static HRESULT DSoundRender_GetWritePos(DSoundRenderImpl *This, DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
|
|
|
|
{
|
2019-11-19 01:51:20 +01:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
|
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;
|
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
|
|
|
if (This->renderer.filter.pClock)
|
|
|
|
{
|
2012-03-31 03:08:16 +02:00
|
|
|
IReferenceClock_GetTime(This->renderer.filter.pClock, &cur);
|
2019-10-04 17:02:45 +02:00
|
|
|
cur -= This->renderer.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;
|
2010-11-29 10:44:14 +01:00
|
|
|
WARN("Delta too big %i/%i, overwriting old data or even skipping\n", (int)delta_t / 10000, (int)max_lag / 10000);
|
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;
|
2010-11-29 10:44:14 +01:00
|
|
|
WARN("Delta too big %i/%i, too far ahead\n", (int)delta_t / 10000, (int)max_lag / 10000);
|
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:
|
|
|
|
if (playpos > *ret_writepos)
|
|
|
|
*pfree = playpos - *ret_writepos;
|
|
|
|
else if (playpos == *ret_writepos)
|
|
|
|
*pfree = This->buf_size - wfx->nBlockAlign;
|
|
|
|
else
|
|
|
|
*pfree = This->buf_size + playpos - *ret_writepos;
|
|
|
|
if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) {
|
|
|
|
TRACE("Blocked: too full %i / %i\n", (int)(time_from_pos(This, This->buf_size - *pfree)/10000), (int)(DSoundRenderer_Max_Fill / 10000));
|
|
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
return S_OK;
|
2007-04-06 12:48:03 +02:00
|
|
|
}
|
|
|
|
|
2010-12-10 16:06:57 +01:00
|
|
|
static HRESULT DSoundRender_HandleEndOfStream(DSoundRenderImpl *This)
|
|
|
|
{
|
2014-07-18 18:57:13 +02:00
|
|
|
while (This->renderer.filter.state == State_Running)
|
2010-12-10 16:06:57 +01:00
|
|
|
{
|
|
|
|
DWORD pos1, pos2;
|
|
|
|
DSoundRender_UpdatePositions(This, &pos1, &pos2);
|
|
|
|
if (pos1 == pos2)
|
|
|
|
break;
|
|
|
|
|
|
|
|
This->in_loop = 1;
|
2012-03-31 03:08:16 +02:00
|
|
|
LeaveCriticalSection(&This->renderer.csRenderLock);
|
2019-07-03 05:25:43 +02:00
|
|
|
WaitForSingleObject(This->renderer.flush_event, 10);
|
2012-03-31 03:08:16 +02:00
|
|
|
EnterCriticalSection(&This->renderer.csRenderLock);
|
2010-12-10 16:06:57 +01:00
|
|
|
This->in_loop = 0;
|
|
|
|
}
|
|
|
|
|
2012-05-11 20:20:18 +02:00
|
|
|
return S_OK;
|
2010-12-10 16:06:57 +01:00
|
|
|
}
|
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* 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
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
while (size && This->renderer.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
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
if (This->renderer.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) {
|
2010-05-30 19:17:26 +02:00
|
|
|
This->in_loop = 1;
|
2012-03-31 03:08:16 +02:00
|
|
|
LeaveCriticalSection(&This->renderer.csRenderLock);
|
2019-07-03 05:25:43 +02:00
|
|
|
ret = WaitForSingleObject(This->renderer.flush_event, 10);
|
2012-03-31 03:08:16 +02:00
|
|
|
EnterCriticalSection(&This->renderer.csRenderLock);
|
2010-05-30 19:17:26 +02:00
|
|
|
This->in_loop = 0;
|
2019-06-21 03:13:19 +02:00
|
|
|
if (This->renderer.sink.flushing || This->renderer.filter.state == State_Stopped)
|
2012-03-31 03:08:16 +02:00
|
|
|
return This->renderer.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
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_ShouldDrawSampleNow(struct strmbase_renderer *iface,
|
|
|
|
IMediaSample *sample, REFERENCE_TIME *start, REFERENCE_TIME *end)
|
2012-04-02 14:52:08 +02:00
|
|
|
{
|
|
|
|
/* We time ourselves do not use the base renderers timing */
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_PrepareReceive(struct strmbase_renderer *iface, IMediaSample *pSample)
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
|
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)
|
|
|
|
{
|
2019-11-19 01:51:20 +01:00
|
|
|
AM_MEDIA_TYPE *orig = &This->renderer.sink.pin.mt;
|
2008-10-21 12:50:36 +02:00
|
|
|
WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
|
|
|
|
WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_DoRenderSample(struct strmbase_renderer *iface, IMediaSample *pSample)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
|
2012-03-31 03:08:16 +02:00
|
|
|
LPBYTE pbSrcStream = NULL;
|
|
|
|
LONG cbSrcStream = 0;
|
|
|
|
REFERENCE_TIME tStart, tStop;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("%p %p\n", iface, pSample);
|
|
|
|
|
|
|
|
/* Slightly incorrect, Pause completes when a frame is received so we should signal
|
|
|
|
* pause completion here, but for sound playing a single frame doesn't make sense
|
|
|
|
*/
|
2008-10-21 12:50:36 +02:00
|
|
|
|
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
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
IMediaSample_IsDiscontinuity(pSample);
|
2008-04-16 23:27:06 +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
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
hr = DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
|
2012-03-31 03:08:16 +02:00
|
|
|
if (This->renderer.filter.state == State_Running && This->renderer.filter.pClock && tStart >= 0) {
|
2010-12-01 23:17:30 +01:00
|
|
|
REFERENCE_TIME jitter, now = 0;
|
|
|
|
Quality q;
|
2012-03-31 03:08:16 +02:00
|
|
|
IReferenceClock_GetTime(This->renderer.filter.pClock, &now);
|
2019-10-04 17:02:45 +02:00
|
|
|
jitter = now - This->renderer.stream_start - tStart;
|
2010-12-01 23:17:30 +01:00
|
|
|
if (jitter <= -DSoundRenderer_Max_Fill)
|
|
|
|
jitter += DSoundRenderer_Max_Fill;
|
|
|
|
else if (jitter < 0)
|
|
|
|
jitter = 0;
|
|
|
|
q.Type = (jitter > 0 ? Famine : Flood);
|
2016-05-17 20:43:32 +02:00
|
|
|
q.Proportion = 1000;
|
2010-12-01 23:17:30 +01:00
|
|
|
q.Late = jitter;
|
|
|
|
q.TimeStamp = tStart;
|
2019-09-02 12:18:35 +02:00
|
|
|
IQualityControl_Notify((IQualityControl *)This->renderer.qcimpl, &This->renderer.filter.IBaseFilter_iface, q);
|
2010-12-01 23:17:30 +01:00
|
|
|
}
|
2008-07-05 01:59:22 +02:00
|
|
|
return hr;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_CheckMediaType(struct strmbase_renderer *iface, const AM_MEDIA_TYPE * pmt)
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2008-06-10 17:00:40 +02:00
|
|
|
if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
|
|
|
|
return S_FALSE;
|
|
|
|
|
|
|
|
if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
|
|
|
|
return S_FALSE;
|
|
|
|
|
|
|
|
return S_OK;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static void dsound_render_stop_stream(struct strmbase_renderer *iface)
|
2010-10-07 21:48:01 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
|
2010-10-07 21:48:01 +02:00
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
2010-10-07 21:48:01 +02:00
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
IDirectSoundBuffer_Stop(This->dsbuffer);
|
|
|
|
This->writepos = This->buf_size;
|
2010-10-07 21:48:01 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static void dsound_render_start_stream(struct strmbase_renderer *iface)
|
2010-10-07 21:48:01 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
|
2012-03-31 03:08:16 +02:00
|
|
|
|
|
|
|
TRACE("(%p)\n", This);
|
|
|
|
|
2019-09-27 04:40:53 +02:00
|
|
|
if (This->renderer.sink.pin.peer)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
|
|
|
IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
}
|
2010-10-07 21:48:01 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 17:35:12 +01:00
|
|
|
static HRESULT dsound_render_connect(struct strmbase_renderer *iface, const AM_MEDIA_TYPE *mt)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
|
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;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_BreakConnect(struct strmbase_renderer *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
|
2012-03-31 03:08:16 +02:00
|
|
|
|
|
|
|
TRACE("(%p)->()\n", iface);
|
|
|
|
|
|
|
|
if (This->dsbuffer)
|
|
|
|
IDirectSoundBuffer_Release(This->dsbuffer);
|
|
|
|
This->dsbuffer = NULL;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_EndOfStream(struct strmbase_renderer *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
|
2019-11-28 02:27:17 +01:00
|
|
|
return DSoundRender_HandleEndOfStream(This);
|
2012-03-31 03:08:16 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_EndFlush(struct strmbase_renderer *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_strmbase_renderer(iface);
|
2012-03-31 03:08:16 +02:00
|
|
|
|
|
|
|
if (This->dsbuffer)
|
|
|
|
{
|
|
|
|
LPBYTE buffer;
|
|
|
|
DWORD size;
|
|
|
|
|
|
|
|
/* Force a reset */
|
|
|
|
IDirectSoundBuffer_Lock(This->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
|
|
|
|
memset(buffer, 0, size);
|
|
|
|
IDirectSoundBuffer_Unlock(This->dsbuffer, buffer, size, NULL, 0);
|
|
|
|
This->writepos = This->buf_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static void dsound_render_destroy(struct strmbase_renderer *iface)
|
2019-05-28 06:09:56 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *filter = impl_from_strmbase_renderer(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;
|
|
|
|
|
|
|
|
strmbase_renderer_cleanup(&filter->renderer);
|
|
|
|
CoTaskMemFree(filter);
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT dsound_render_query_interface(struct strmbase_renderer *iface, REFIID iid, void **out)
|
2019-05-31 05:59:37 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
DSoundRenderImpl *filter = impl_from_strmbase_renderer(iface);
|
2019-05-31 05:59:37 +02:00
|
|
|
|
|
|
|
if (IsEqualGUID(iid, &IID_IBasicAudio))
|
2019-06-07 01:49:24 +02:00
|
|
|
*out = &filter->IBasicAudio_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 if (IsEqualGUID(iid, &IID_IAMDirectSound))
|
|
|
|
*out = &filter->IAMDirectSound_iface;
|
|
|
|
else
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:24 +02:00
|
|
|
static const struct strmbase_renderer_ops renderer_ops =
|
2019-07-03 05:25:45 +02:00
|
|
|
{
|
|
|
|
.pfnCheckMediaType = DSoundRender_CheckMediaType,
|
|
|
|
.pfnDoRenderSample = DSoundRender_DoRenderSample,
|
2019-07-03 05:25:46 +02:00
|
|
|
.renderer_start_stream = dsound_render_start_stream,
|
2019-07-03 05:25:47 +02:00
|
|
|
.renderer_stop_stream = dsound_render_stop_stream,
|
2019-07-03 05:25:45 +02:00
|
|
|
.pfnShouldDrawSampleNow = DSoundRender_ShouldDrawSampleNow,
|
|
|
|
.pfnPrepareReceive = DSoundRender_PrepareReceive,
|
2019-12-10 17:35:12 +01:00
|
|
|
.renderer_connect = dsound_render_connect,
|
2019-07-03 05:25:45 +02:00
|
|
|
.pfnBreakConnect = DSoundRender_BreakConnect,
|
|
|
|
.pfnEndOfStream = DSoundRender_EndOfStream,
|
|
|
|
.pfnEndFlush = DSoundRender_EndFlush,
|
|
|
|
.renderer_destroy = dsound_render_destroy,
|
|
|
|
.renderer_query_interface = dsound_render_query_interface,
|
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) {
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_QueryInterface(This->renderer.filter.outer_unk, riid, ppvObj);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_AddRef(This->renderer.filter.outer_unk);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_Release(This->renderer.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) {
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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) {
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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) {
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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) {
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_QueryInterface(This->renderer.filter.outer_unk, riid, ppvObj);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_AddRef(This->renderer.filter.outer_unk);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_Release(This->renderer.filter.outer_unk);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*** IAMDirectSound methods ***/
|
|
|
|
static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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)
|
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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
|
|
|
{
|
2012-04-03 21:28:37 +02:00
|
|
|
DSoundRenderImpl *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
|
|
|
|
|
|
|
HRESULT dsound_render_create(IUnknown *outer, void **out)
|
|
|
|
{
|
|
|
|
static const DSBUFFERDESC buffer_desc = {
|
|
|
|
.dwSize = sizeof(DSBUFFERDESC),
|
|
|
|
.dwFlags = DSBCAPS_PRIMARYBUFFER,
|
|
|
|
};
|
|
|
|
|
|
|
|
IDirectSoundBuffer *buffer;
|
|
|
|
DSoundRenderImpl *object;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!(object = CoTaskMemAlloc(sizeof(*object))))
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
memset(object, 0, sizeof(*object));
|
|
|
|
|
|
|
|
if (FAILED(hr = strmbase_renderer_init(&object->renderer, outer,
|
|
|
|
&CLSID_DSoundRender, L"Audio Input pin (rendered)", &renderer_ops)))
|
|
|
|
{
|
|
|
|
CoTaskMemFree(object);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
if (FAILED(hr = QUARTZ_CreateSystemClock(&object->renderer.filter.IUnknown_inner,
|
|
|
|
(void **)&object->system_clock)))
|
|
|
|
{
|
|
|
|
strmbase_renderer_cleanup(&object->renderer);
|
|
|
|
CoTaskMemFree(object);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2019-11-29 00:35:39 +01:00
|
|
|
object->IBasicAudio_iface.lpVtbl = &IBasicAudio_Vtbl;
|
|
|
|
object->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
|
|
|
|
|
|
|
|
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);
|
2019-11-29 00:35:39 +01:00
|
|
|
strmbase_renderer_cleanup(&object->renderer);
|
|
|
|
CoTaskMemFree(object);
|
|
|
|
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);
|
2019-11-29 00:35:39 +01:00
|
|
|
strmbase_renderer_cleanup(&object->renderer);
|
|
|
|
CoTaskMemFree(object);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr = IDirectSound8_CreateSoundBuffer(object->dsound,
|
|
|
|
&buffer_desc, &buffer, NULL)))
|
|
|
|
{
|
|
|
|
IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
IDirectSoundBuffer_Release(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("Created DirectSound renderer %p.\n", object);
|
|
|
|
*out = &object->renderer.filter.IUnknown_inner;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|